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:
Corruption(size_t bytes,const Status & status)38 virtual void Corruption(size_t bytes, const Status& status) {
39 printf("corruption: %d bytes; %s\n",
40 static_cast<int>(bytes),
41 status.ToString().c_str());
42 }
43 };
44
45 // Print contents of a log file. (*func)() is called on every record.
PrintLogContents(Env * env,const std::string & fname,void (* func)(Slice))46 bool PrintLogContents(Env* env, const std::string& fname,
47 void (*func)(Slice)) {
48 SequentialFile* file;
49 Status s = env->NewSequentialFile(fname, &file);
50 if (!s.ok()) {
51 fprintf(stderr, "%s\n", s.ToString().c_str());
52 return false;
53 }
54 CorruptionReporter reporter;
55 log::Reader reader(file, &reporter, true, 0);
56 Slice record;
57 std::string scratch;
58 while (reader.ReadRecord(&record, &scratch)) {
59 printf("--- offset %llu; ",
60 static_cast<unsigned long long>(reader.LastRecordOffset()));
61 (*func)(record);
62 }
63 delete file;
64 return true;
65 }
66
67 // Called on every item found in a WriteBatch.
68 class WriteBatchItemPrinter : public WriteBatch::Handler {
69 public:
70 uint64_t offset_;
71 uint64_t sequence_;
72
Put(const Slice & key,const Slice & value)73 virtual void Put(const Slice& key, const Slice& value) {
74 printf(" put '%s' '%s'\n",
75 EscapeString(key).c_str(),
76 EscapeString(value).c_str());
77 }
Delete(const Slice & key)78 virtual void Delete(const Slice& key) {
79 printf(" del '%s'\n",
80 EscapeString(key).c_str());
81 }
82 };
83
84
85 // Called on every log record (each one of which is a WriteBatch)
86 // found in a kLogFile.
WriteBatchPrinter(Slice record)87 static void WriteBatchPrinter(Slice record) {
88 if (record.size() < 12) {
89 printf("log record length %d is too small\n",
90 static_cast<int>(record.size()));
91 return;
92 }
93 WriteBatch batch;
94 WriteBatchInternal::SetContents(&batch, record);
95 printf("sequence %llu\n",
96 static_cast<unsigned long long>(WriteBatchInternal::Sequence(&batch)));
97 WriteBatchItemPrinter batch_item_printer;
98 Status s = batch.Iterate(&batch_item_printer);
99 if (!s.ok()) {
100 printf(" error: %s\n", s.ToString().c_str());
101 }
102 }
103
DumpLog(Env * env,const std::string & fname)104 bool DumpLog(Env* env, const std::string& fname) {
105 return PrintLogContents(env, fname, WriteBatchPrinter);
106 }
107
108 // Called on every log record (each one of which is a WriteBatch)
109 // found in a kDescriptorFile.
VersionEditPrinter(Slice record)110 static void VersionEditPrinter(Slice record) {
111 VersionEdit edit;
112 Status s = edit.DecodeFrom(record);
113 if (!s.ok()) {
114 printf("%s\n", s.ToString().c_str());
115 return;
116 }
117 printf("%s", edit.DebugString().c_str());
118 }
119
DumpDescriptor(Env * env,const std::string & fname)120 bool DumpDescriptor(Env* env, const std::string& fname) {
121 return PrintLogContents(env, fname, VersionEditPrinter);
122 }
123
DumpTable(Env * env,const std::string & fname)124 bool DumpTable(Env* env, const std::string& fname) {
125 uint64_t file_size;
126 RandomAccessFile* file = NULL;
127 Table* table = NULL;
128 Status s = env->GetFileSize(fname, &file_size);
129 if (s.ok()) {
130 s = env->NewRandomAccessFile(fname, &file);
131 }
132 if (s.ok()) {
133 // We use the default comparator, which may or may not match the
134 // comparator used in this database. However this should not cause
135 // problems since we only use Table operations that do not require
136 // any comparisons. In particular, we do not call Seek or Prev.
137 s = Table::Open(Options(), file, file_size, &table);
138 }
139 if (!s.ok()) {
140 fprintf(stderr, "%s\n", s.ToString().c_str());
141 delete table;
142 delete file;
143 return false;
144 }
145
146 ReadOptions ro;
147 ro.fill_cache = false;
148 Iterator* iter = table->NewIterator(ro);
149 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
150 ParsedInternalKey key;
151 if (!ParseInternalKey(iter->key(), &key)) {
152 printf("badkey '%s' => '%s'\n",
153 EscapeString(iter->key()).c_str(),
154 EscapeString(iter->value()).c_str());
155 } else {
156 char kbuf[20];
157 const char* type;
158 if (key.type == kTypeDeletion) {
159 type = "del";
160 } else if (key.type == kTypeValue) {
161 type = "val";
162 } else {
163 snprintf(kbuf, sizeof(kbuf), "%d", static_cast<int>(key.type));
164 type = kbuf;
165 }
166 printf("'%s' @ %8llu : %s => '%s'\n",
167 EscapeString(key.user_key).c_str(),
168 static_cast<unsigned long long>(key.sequence),
169 type,
170 EscapeString(iter->value()).c_str());
171 }
172 }
173 s = iter->status();
174 if (!s.ok()) {
175 printf("iterator error: %s\n", s.ToString().c_str());
176 }
177
178 delete iter;
179 delete table;
180 delete file;
181 return true;
182 }
183
DumpFile(Env * env,const std::string & fname)184 bool DumpFile(Env* env, const std::string& fname) {
185 FileType ftype;
186 if (!GuessType(fname, &ftype)) {
187 fprintf(stderr, "%s: unknown file type\n", fname.c_str());
188 return false;
189 }
190 switch (ftype) {
191 case kLogFile: return DumpLog(env, fname);
192 case kDescriptorFile: return DumpDescriptor(env, fname);
193 case kTableFile: return DumpTable(env, fname);
194
195 default: {
196 fprintf(stderr, "%s: not a dump-able file type\n", fname.c_str());
197 break;
198 }
199 }
200 return false;
201 }
202
HandleDumpCommand(Env * env,char ** files,int num)203 bool HandleDumpCommand(Env* env, char** files, int num) {
204 bool ok = true;
205 for (int i = 0; i < num; i++) {
206 ok &= DumpFile(env, files[i]);
207 }
208 return ok;
209 }
210
211 }
212 } // namespace leveldb
213
Usage()214 static void Usage() {
215 fprintf(
216 stderr,
217 "Usage: leveldbutil command...\n"
218 " dump files... -- dump contents of specified files\n"
219 );
220 }
221
main(int argc,char ** argv)222 int main(int argc, char** argv) {
223 leveldb::Env* env = leveldb::Env::Default();
224 bool ok = true;
225 if (argc < 2) {
226 Usage();
227 ok = false;
228 } else {
229 std::string command = argv[1];
230 if (command == "dump") {
231 ok = leveldb::HandleDumpCommand(env, argv+2, argc-2);
232 } else {
233 Usage();
234 ok = false;
235 }
236 }
237 return (ok ? 0 : 1);
238 }
239