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