blob_dump_tool.cc 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. #ifndef ROCKSDB_LITE
  6. #include "utilities/blob_db/blob_dump_tool.h"
  7. #include <stdio.h>
  8. #include <cinttypes>
  9. #include <iostream>
  10. #include <memory>
  11. #include <string>
  12. #include "env/composite_env_wrapper.h"
  13. #include "file/random_access_file_reader.h"
  14. #include "file/readahead_raf.h"
  15. #include "port/port.h"
  16. #include "rocksdb/convenience.h"
  17. #include "rocksdb/env.h"
  18. #include "table/format.h"
  19. #include "util/coding.h"
  20. #include "util/string_util.h"
  21. namespace ROCKSDB_NAMESPACE {
  22. namespace blob_db {
  23. BlobDumpTool::BlobDumpTool()
  24. : reader_(nullptr), buffer_(nullptr), buffer_size_(0) {}
  25. Status BlobDumpTool::Run(const std::string& filename, DisplayType show_key,
  26. DisplayType show_blob,
  27. DisplayType show_uncompressed_blob,
  28. bool show_summary) {
  29. constexpr size_t kReadaheadSize = 2 * 1024 * 1024;
  30. Status s;
  31. Env* env = Env::Default();
  32. s = env->FileExists(filename);
  33. if (!s.ok()) {
  34. return s;
  35. }
  36. uint64_t file_size = 0;
  37. s = env->GetFileSize(filename, &file_size);
  38. if (!s.ok()) {
  39. return s;
  40. }
  41. std::unique_ptr<RandomAccessFile> file;
  42. s = env->NewRandomAccessFile(filename, &file, EnvOptions());
  43. if (!s.ok()) {
  44. return s;
  45. }
  46. file = NewReadaheadRandomAccessFile(std::move(file), kReadaheadSize);
  47. if (file_size == 0) {
  48. return Status::Corruption("File is empty.");
  49. }
  50. reader_.reset(new RandomAccessFileReader(
  51. NewLegacyRandomAccessFileWrapper(file), filename));
  52. uint64_t offset = 0;
  53. uint64_t footer_offset = 0;
  54. CompressionType compression = kNoCompression;
  55. s = DumpBlobLogHeader(&offset, &compression);
  56. if (!s.ok()) {
  57. return s;
  58. }
  59. s = DumpBlobLogFooter(file_size, &footer_offset);
  60. if (!s.ok()) {
  61. return s;
  62. }
  63. uint64_t total_records = 0;
  64. uint64_t total_key_size = 0;
  65. uint64_t total_blob_size = 0;
  66. uint64_t total_uncompressed_blob_size = 0;
  67. if (show_key != DisplayType::kNone || show_summary) {
  68. while (offset < footer_offset) {
  69. s = DumpRecord(show_key, show_blob, show_uncompressed_blob, show_summary,
  70. compression, &offset, &total_records, &total_key_size,
  71. &total_blob_size, &total_uncompressed_blob_size);
  72. if (!s.ok()) {
  73. break;
  74. }
  75. }
  76. }
  77. if (show_summary) {
  78. fprintf(stdout, "Summary:\n");
  79. fprintf(stdout, " total records: %" PRIu64 "\n", total_records);
  80. fprintf(stdout, " total key size: %" PRIu64 "\n", total_key_size);
  81. fprintf(stdout, " total blob size: %" PRIu64 "\n", total_blob_size);
  82. if (compression != kNoCompression) {
  83. fprintf(stdout, " total raw blob size: %" PRIu64 "\n",
  84. total_uncompressed_blob_size);
  85. }
  86. }
  87. return s;
  88. }
  89. Status BlobDumpTool::Read(uint64_t offset, size_t size, Slice* result) {
  90. if (buffer_size_ < size) {
  91. if (buffer_size_ == 0) {
  92. buffer_size_ = 4096;
  93. }
  94. while (buffer_size_ < size) {
  95. buffer_size_ *= 2;
  96. }
  97. buffer_.reset(new char[buffer_size_]);
  98. }
  99. Status s = reader_->Read(offset, size, result, buffer_.get());
  100. if (!s.ok()) {
  101. return s;
  102. }
  103. if (result->size() != size) {
  104. return Status::Corruption("Reach the end of the file unexpectedly.");
  105. }
  106. return s;
  107. }
  108. Status BlobDumpTool::DumpBlobLogHeader(uint64_t* offset,
  109. CompressionType* compression) {
  110. Slice slice;
  111. Status s = Read(0, BlobLogHeader::kSize, &slice);
  112. if (!s.ok()) {
  113. return s;
  114. }
  115. BlobLogHeader header;
  116. s = header.DecodeFrom(slice);
  117. if (!s.ok()) {
  118. return s;
  119. }
  120. fprintf(stdout, "Blob log header:\n");
  121. fprintf(stdout, " Version : %" PRIu32 "\n", header.version);
  122. fprintf(stdout, " Column Family ID : %" PRIu32 "\n",
  123. header.column_family_id);
  124. std::string compression_str;
  125. if (!GetStringFromCompressionType(&compression_str, header.compression)
  126. .ok()) {
  127. compression_str = "Unrecongnized compression type (" +
  128. ToString((int)header.compression) + ")";
  129. }
  130. fprintf(stdout, " Compression : %s\n", compression_str.c_str());
  131. fprintf(stdout, " Expiration range : %s\n",
  132. GetString(header.expiration_range).c_str());
  133. *offset = BlobLogHeader::kSize;
  134. *compression = header.compression;
  135. return s;
  136. }
  137. Status BlobDumpTool::DumpBlobLogFooter(uint64_t file_size,
  138. uint64_t* footer_offset) {
  139. auto no_footer = [&]() {
  140. *footer_offset = file_size;
  141. fprintf(stdout, "No blob log footer.\n");
  142. return Status::OK();
  143. };
  144. if (file_size < BlobLogHeader::kSize + BlobLogFooter::kSize) {
  145. return no_footer();
  146. }
  147. Slice slice;
  148. *footer_offset = file_size - BlobLogFooter::kSize;
  149. Status s = Read(*footer_offset, BlobLogFooter::kSize, &slice);
  150. if (!s.ok()) {
  151. return s;
  152. }
  153. BlobLogFooter footer;
  154. s = footer.DecodeFrom(slice);
  155. if (!s.ok()) {
  156. return no_footer();
  157. }
  158. fprintf(stdout, "Blob log footer:\n");
  159. fprintf(stdout, " Blob count : %" PRIu64 "\n", footer.blob_count);
  160. fprintf(stdout, " Expiration Range : %s\n",
  161. GetString(footer.expiration_range).c_str());
  162. return s;
  163. }
  164. Status BlobDumpTool::DumpRecord(DisplayType show_key, DisplayType show_blob,
  165. DisplayType show_uncompressed_blob,
  166. bool show_summary, CompressionType compression,
  167. uint64_t* offset, uint64_t* total_records,
  168. uint64_t* total_key_size,
  169. uint64_t* total_blob_size,
  170. uint64_t* total_uncompressed_blob_size) {
  171. if (show_key != DisplayType::kNone) {
  172. fprintf(stdout, "Read record with offset 0x%" PRIx64 " (%" PRIu64 "):\n",
  173. *offset, *offset);
  174. }
  175. Slice slice;
  176. Status s = Read(*offset, BlobLogRecord::kHeaderSize, &slice);
  177. if (!s.ok()) {
  178. return s;
  179. }
  180. BlobLogRecord record;
  181. s = record.DecodeHeaderFrom(slice);
  182. if (!s.ok()) {
  183. return s;
  184. }
  185. uint64_t key_size = record.key_size;
  186. uint64_t value_size = record.value_size;
  187. if (show_key != DisplayType::kNone) {
  188. fprintf(stdout, " key size : %" PRIu64 "\n", key_size);
  189. fprintf(stdout, " value size : %" PRIu64 "\n", value_size);
  190. fprintf(stdout, " expiration : %" PRIu64 "\n", record.expiration);
  191. }
  192. *offset += BlobLogRecord::kHeaderSize;
  193. s = Read(*offset, static_cast<size_t>(key_size + value_size), &slice);
  194. if (!s.ok()) {
  195. return s;
  196. }
  197. // Decompress value
  198. std::string uncompressed_value;
  199. if (compression != kNoCompression &&
  200. (show_uncompressed_blob != DisplayType::kNone || show_summary)) {
  201. BlockContents contents;
  202. UncompressionContext context(compression);
  203. UncompressionInfo info(context, UncompressionDict::GetEmptyDict(),
  204. compression);
  205. s = UncompressBlockContentsForCompressionType(
  206. info, slice.data() + key_size, static_cast<size_t>(value_size),
  207. &contents, 2 /*compress_format_version*/,
  208. ImmutableCFOptions(Options()));
  209. if (!s.ok()) {
  210. return s;
  211. }
  212. uncompressed_value = contents.data.ToString();
  213. }
  214. if (show_key != DisplayType::kNone) {
  215. fprintf(stdout, " key : ");
  216. DumpSlice(Slice(slice.data(), static_cast<size_t>(key_size)), show_key);
  217. if (show_blob != DisplayType::kNone) {
  218. fprintf(stdout, " blob : ");
  219. DumpSlice(Slice(slice.data() + static_cast<size_t>(key_size), static_cast<size_t>(value_size)), show_blob);
  220. }
  221. if (show_uncompressed_blob != DisplayType::kNone) {
  222. fprintf(stdout, " raw blob : ");
  223. DumpSlice(Slice(uncompressed_value), show_uncompressed_blob);
  224. }
  225. }
  226. *offset += key_size + value_size;
  227. *total_records += 1;
  228. *total_key_size += key_size;
  229. *total_blob_size += value_size;
  230. *total_uncompressed_blob_size += uncompressed_value.size();
  231. return s;
  232. }
  233. void BlobDumpTool::DumpSlice(const Slice s, DisplayType type) {
  234. if (type == DisplayType::kRaw) {
  235. fprintf(stdout, "%s\n", s.ToString().c_str());
  236. } else if (type == DisplayType::kHex) {
  237. fprintf(stdout, "%s\n", s.ToString(true /*hex*/).c_str());
  238. } else if (type == DisplayType::kDetail) {
  239. char buf[100];
  240. for (size_t i = 0; i < s.size(); i += 16) {
  241. memset(buf, 0, sizeof(buf));
  242. for (size_t j = 0; j < 16 && i + j < s.size(); j++) {
  243. unsigned char c = s[i + j];
  244. snprintf(buf + j * 3 + 15, 2, "%x", c >> 4);
  245. snprintf(buf + j * 3 + 16, 2, "%x", c & 0xf);
  246. snprintf(buf + j + 65, 2, "%c", (0x20 <= c && c <= 0x7e) ? c : '.');
  247. }
  248. for (size_t p = 0; p < sizeof(buf) - 1; p++) {
  249. if (buf[p] == 0) {
  250. buf[p] = ' ';
  251. }
  252. }
  253. fprintf(stdout, "%s\n", i == 0 ? buf + 15 : buf);
  254. }
  255. }
  256. }
  257. template <class T>
  258. std::string BlobDumpTool::GetString(std::pair<T, T> p) {
  259. if (p.first == 0 && p.second == 0) {
  260. return "nil";
  261. }
  262. return "(" + ToString(p.first) + ", " + ToString(p.second) + ")";
  263. }
  264. } // namespace blob_db
  265. } // namespace ROCKSDB_NAMESPACE
  266. #endif // ROCKSDB_LITE