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 <stdio.h>
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 
22 namespace leveldb {
23 
24 namespace {
25 
GuessType(const std::string & fname,FileType * type)26 bool 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.
39 class CorruptionReporter : public log::Reader::Reporter {
40  public:
Corruption(size_t bytes,const Status & status)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.
PrintLogContents(Env * env,const std::string & fname,void (* func)(uint64_t,Slice,WritableFile *),WritableFile * dst)54 Status 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.
75 class WriteBatchItemPrinter : public WriteBatch::Handler {
76  public:
Put(const Slice & key,const Slice & value)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   }
Delete(const Slice & key)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.
WriteBatchPrinter(uint64_t pos,Slice record,WritableFile * dst)97 static 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 
DumpLog(Env * env,const std::string & fname,WritableFile * dst)122 Status 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.
VersionEditPrinter(uint64_t pos,Slice record,WritableFile * dst)128 static 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 
DumpDescriptor(Env * env,const std::string & fname,WritableFile * dst)143 Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) {
144   return PrintLogContents(env, fname, VersionEditPrinter, dst);
145 }
146 
DumpTable(Env * env,const std::string & fname,WritableFile * dst)147 Status 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 
DumpFile(Env * env,const std::string & fname,WritableFile * dst)214 Status 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