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 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 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. 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 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 } 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. 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 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. 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 120 bool DumpDescriptor(Env* env, const std::string& fname) { 121 return PrintLogContents(env, fname, VersionEditPrinter); 122 } 123 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 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 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 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 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