1// Copyright (c) 2012 The LevelDB Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file. See the AUTHORS file for names of contributors.
4
5#include "leveldb/dumpfile.h"
6
7#include <cstdio>
8
9#include "db/dbformat.h"
10#include "db/filename.h"
11#include "db/log_reader.h"
12#include "db/version_edit.h"
13#include "db/write_batch_internal.h"
14#include "leveldb/env.h"
15#include "leveldb/iterator.h"
16#include "leveldb/options.h"
17#include "leveldb/status.h"
18#include "leveldb/table.h"
19#include "leveldb/write_batch.h"
20#include "util/logging.h"
21
22namespace leveldb {
23
24namespace {
25
26bool GuessType(const std::string& fname, FileType* type) {
27 size_t pos = fname.rfind('/');
28 std::string basename;
29 if (pos == std::string::npos) {
30 basename = fname;
31 } else {
32 basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1);
33 }
34 uint64_t ignored;
35 return ParseFileName(basename, &ignored, type);
36}
37
38// Notified when log reader encounters corruption.
39class CorruptionReporter : public log::Reader::Reporter {
40 public:
41 void Corruption(size_t bytes, const Status& status) override {
42 std::string r = "corruption: ";
43 AppendNumberTo(&r, bytes);
44 r += " bytes; ";
45 r += status.ToString();
46 r.push_back('\n');
47 dst_->Append(r);
48 }
49
50 WritableFile* dst_;
51};
52
53// Print contents of a log file. (*func)() is called on every record.
54Status PrintLogContents(Env* env, const std::string& fname,
55 void (*func)(uint64_t, Slice, WritableFile*),
56 WritableFile* dst) {
57 SequentialFile* file;
58 Status s = env->NewSequentialFile(fname, &file);
59 if (!s.ok()) {
60 return s;
61 }
62 CorruptionReporter reporter;
63 reporter.dst_ = dst;
64 log::Reader reader(file, &reporter, true, 0);
65 Slice record;
66 std::string scratch;
67 while (reader.ReadRecord(&record, &scratch)) {
68 (*func)(reader.LastRecordOffset(), record, dst);
69 }
70 delete file;
71 return Status::OK();
72}
73
74// Called on every item found in a WriteBatch.
75class WriteBatchItemPrinter : public WriteBatch::Handler {
76 public:
77 void Put(const Slice& key, const Slice& value) override {
78 std::string r = " put '";
79 AppendEscapedStringTo(&r, key);
80 r += "' '";
81 AppendEscapedStringTo(&r, value);
82 r += "'\n";
83 dst_->Append(r);
84 }
85 void Delete(const Slice& key) override {
86 std::string r = " del '";
87 AppendEscapedStringTo(&r, key);
88 r += "'\n";
89 dst_->Append(r);
90 }
91
92 WritableFile* dst_;
93};
94
95// Called on every log record (each one of which is a WriteBatch)
96// found in a kLogFile.
97static void WriteBatchPrinter(uint64_t pos, Slice record, WritableFile* dst) {
98 std::string r = "--- offset ";
99 AppendNumberTo(&r, pos);
100 r += "; ";
101 if (record.size() < 12) {
102 r += "log record length ";
103 AppendNumberTo(&r, record.size());
104 r += " is too small\n";
105 dst->Append(r);
106 return;
107 }
108 WriteBatch batch;
109 WriteBatchInternal::SetContents(&batch, record);
110 r += "sequence ";
111 AppendNumberTo(&r, WriteBatchInternal::Sequence(&batch));
112 r.push_back('\n');
113 dst->Append(r);
114 WriteBatchItemPrinter batch_item_printer;
115 batch_item_printer.dst_ = dst;
116 Status s = batch.Iterate(&batch_item_printer);
117 if (!s.ok()) {
118 dst->Append(" error: " + s.ToString() + "\n");
119 }
120}
121
122Status DumpLog(Env* env, const std::string& fname, WritableFile* dst) {
123 return PrintLogContents(env, fname, WriteBatchPrinter, dst);
124}
125
126// Called on every log record (each one of which is a WriteBatch)
127// found in a kDescriptorFile.
128static void VersionEditPrinter(uint64_t pos, Slice record, WritableFile* dst) {
129 std::string r = "--- offset ";
130 AppendNumberTo(&r, pos);
131 r += "; ";
132 VersionEdit edit;
133 Status s = edit.DecodeFrom(record);
134 if (!s.ok()) {
135 r += s.ToString();
136 r.push_back('\n');
137 } else {
138 r += edit.DebugString();
139 }
140 dst->Append(r);
141}
142
143Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) {
144 return PrintLogContents(env, fname, VersionEditPrinter, dst);
145}
146
147Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) {
148 uint64_t file_size;
149 RandomAccessFile* file = nullptr;
150 Table* table = nullptr;
151 Status s = env->GetFileSize(fname, &file_size);
152 if (s.ok()) {
153 s = env->NewRandomAccessFile(fname, &file);
154 }
155 if (s.ok()) {
156 // We use the default comparator, which may or may not match the
157 // comparator used in this database. However this should not cause
158 // problems since we only use Table operations that do not require
159 // any comparisons. In particular, we do not call Seek or Prev.
160 s = Table::Open(Options(), file, file_size, &table);
161 }
162 if (!s.ok()) {
163 delete table;
164 delete file;
165 return s;
166 }
167
168 ReadOptions ro;
169 ro.fill_cache = false;
170 Iterator* iter = table->NewIterator(ro);
171 std::string r;
172 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
173 r.clear();
174 ParsedInternalKey key;
175 if (!ParseInternalKey(iter->key(), &key)) {
176 r = "badkey '";
177 AppendEscapedStringTo(&r, iter->key());
178 r += "' => '";
179 AppendEscapedStringTo(&r, iter->value());
180 r += "'\n";
181 dst->Append(r);
182 } else {
183 r = "'";
184 AppendEscapedStringTo(&r, key.user_key);
185 r += "' @ ";
186 AppendNumberTo(&r, key.sequence);
187 r += " : ";
188 if (key.type == kTypeDeletion) {
189 r += "del";
190 } else if (key.type == kTypeValue) {
191 r += "val";
192 } else {
193 AppendNumberTo(&r, key.type);
194 }
195 r += " => '";
196 AppendEscapedStringTo(&r, iter->value());
197 r += "'\n";
198 dst->Append(r);
199 }
200 }
201 s = iter->status();
202 if (!s.ok()) {
203 dst->Append("iterator error: " + s.ToString() + "\n");
204 }
205
206 delete iter;
207 delete table;
208 delete file;
209 return Status::OK();
210}
211
212} // namespace
213
214Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) {
215 FileType ftype;
216 if (!GuessType(fname, &ftype)) {
217 return Status::InvalidArgument(fname + ": unknown file type");
218 }
219 switch (ftype) {
220 case kLogFile:
221 return DumpLog(env, fname, dst);
222 case kDescriptorFile:
223 return DumpDescriptor(env, fname, dst);
224 case kTableFile:
225 return DumpTable(env, fname, dst);
226 default:
227 break;
228 }
229 return Status::InvalidArgument(fname + ": not a dump-able file type");
230}
231
232} // namespace leveldb
233