dumpfile.cc
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 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: 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. 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: 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. 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 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. 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 143 Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) { 144 return PrintLogContents(env, fname, VersionEditPrinter, dst); 145 } 146 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 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