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).
6 // Copyright (c) 2012 The LevelDB Authors. All rights reserved.
7 // Use of this source code is governed by a BSD-style license that can be
8 // found in the LICENSE file. See the AUTHORS file for names of contributors.
13 #include "file/random_access_file_reader.h"
14 #include "port/stack_trace.h"
15 #include "rocksdb/convenience.h"
16 #include "rocksdb/filter_policy.h"
17 #include "rocksdb/sst_dump_tool.h"
18 #include "table/block_based/block_based_table_factory.h"
19 #include "table/table_builder.h"
20 #include "test_util/testharness.h"
21 #include "test_util/testutil.h"
23 namespace ROCKSDB_NAMESPACE
{
25 const uint32_t kOptLength
= 1024;
28 static std::string
MakeKey(int i
) {
30 snprintf(buf
, sizeof(buf
), "k_%04d", i
);
31 InternalKey
key(std::string(buf
), 0, ValueType::kTypeValue
);
32 return key
.Encode().ToString();
35 static std::string
MakeKeyWithTimeStamp(int i
, uint64_t ts
) {
37 snprintf(buf
, sizeof(buf
), "k_%04d", i
);
38 return test::KeyStr(ts
, std::string(buf
), /*seq=*/0, kTypeValue
);
41 static std::string
MakeValue(int i
) {
43 snprintf(buf
, sizeof(buf
), "v_%04d", i
);
44 InternalKey
key(std::string(buf
), 0, ValueType::kTypeValue
);
45 return key
.Encode().ToString();
48 void cleanup(const Options
& opts
, const std::string
& file_name
) {
50 ASSERT_OK(env
->DeleteFile(file_name
));
51 std::string outfile_name
= file_name
.substr(0, file_name
.length() - 4);
52 outfile_name
.append("_dump.txt");
53 env
->DeleteFile(outfile_name
).PermitUncheckedError();
57 // Test for sst dump tool "raw" mode
58 class SSTDumpToolTest
: public testing::Test
{
59 std::string test_dir_
;
61 std::shared_ptr
<Env
> env_guard_
;
64 SSTDumpToolTest() : env_(Env::Default()) {
65 EXPECT_OK(test::CreateEnvFromSystem(ConfigOptions(), &env_
, &env_guard_
));
66 test_dir_
= test::PerThreadDBPath(env_
, "sst_dump_test_db");
67 Status s
= env_
->CreateDirIfMissing(test_dir_
);
71 ~SSTDumpToolTest() override
{
72 if (getenv("KEEP_DB")) {
73 fprintf(stdout
, "Data is still at %s\n", test_dir_
.c_str());
75 EXPECT_OK(env_
->DeleteDir(test_dir_
));
79 Env
* env() { return env_
; }
81 std::string
MakeFilePath(const std::string
& file_name
) const {
82 std::string
path(test_dir_
);
83 path
.append("/").append(file_name
);
87 template <std::size_t N
>
88 void PopulateCommandArgs(const std::string
& file_path
, const char* command
,
89 char* (&usage
)[N
]) const {
90 for (int i
= 0; i
< static_cast<int>(N
); ++i
) {
91 usage
[i
] = new char[kOptLength
];
93 snprintf(usage
[0], kOptLength
, "./sst_dump");
94 snprintf(usage
[1], kOptLength
, "%s", command
);
95 snprintf(usage
[2], kOptLength
, "--file=%s", file_path
.c_str());
98 void createSST(const Options
& opts
, const std::string
& file_name
) {
99 Env
* test_env
= opts
.env
;
100 FileOptions
file_options(opts
);
101 ReadOptions read_options
;
102 const ImmutableOptions
imoptions(opts
);
103 const MutableCFOptions
moptions(opts
);
104 ROCKSDB_NAMESPACE::InternalKeyComparator
ikc(opts
.comparator
);
105 std::unique_ptr
<TableBuilder
> tb
;
107 IntTblPropCollectorFactories int_tbl_prop_collector_factories
;
108 std::unique_ptr
<WritableFileWriter
> file_writer
;
109 ASSERT_OK(WritableFileWriter::Create(test_env
->GetFileSystem(), file_name
,
110 file_options
, &file_writer
, nullptr));
112 std::string column_family_name
;
113 int unknown_level
= -1;
114 tb
.reset(opts
.table_factory
->NewTableBuilder(
116 imoptions
, moptions
, ikc
, &int_tbl_prop_collector_factories
,
117 CompressionType::kNoCompression
, CompressionOptions(),
118 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily
,
119 column_family_name
, unknown_level
),
122 // Populate slightly more than 1K keys
123 uint32_t num_keys
= kNumKey
;
124 const char* comparator_name
= ikc
.user_comparator()->Name();
125 if (strcmp(comparator_name
, ReverseBytewiseComparator()->Name()) == 0) {
126 for (int32_t i
= num_keys
; i
>= 0; i
--) {
127 tb
->Add(MakeKey(i
), MakeValue(i
));
129 } else if (strcmp(comparator_name
,
130 test::BytewiseComparatorWithU64TsWrapper()->Name()) ==
132 for (uint32_t i
= 0; i
< num_keys
; i
++) {
133 tb
->Add(MakeKeyWithTimeStamp(i
, 100 + i
), MakeValue(i
));
136 for (uint32_t i
= 0; i
< num_keys
; i
++) {
137 tb
->Add(MakeKey(i
), MakeValue(i
));
140 ASSERT_OK(tb
->Finish());
141 ASSERT_OK(file_writer
->Close());
145 constexpr static int kNumKey
= 1024;
148 constexpr int SSTDumpToolTest::kNumKey
;
150 TEST_F(SSTDumpToolTest
, HelpAndVersion
) {
154 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
156 static const char* help
[] = {"./sst_dump", "--help"};
157 ASSERT_TRUE(!tool
.Run(2, help
, opts
));
158 static const char* version
[] = {"./sst_dump", "--version"};
159 ASSERT_TRUE(!tool
.Run(2, version
, opts
));
160 static const char* bad
[] = {"./sst_dump", "--not_an_option"};
161 ASSERT_TRUE(tool
.Run(2, bad
, opts
));
164 TEST_F(SSTDumpToolTest
, EmptyFilter
) {
167 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
168 createSST(opts
, file_path
);
171 PopulateCommandArgs(file_path
, "--command=raw", usage
);
173 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
174 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
176 cleanup(opts
, file_path
);
177 for (int i
= 0; i
< 3; i
++) {
182 TEST_F(SSTDumpToolTest
, SstDumpReverseBytewiseComparator
) {
185 opts
.comparator
= ReverseBytewiseComparator();
186 BlockBasedTableOptions table_opts
;
187 table_opts
.filter_policy
.reset(
188 ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
189 opts
.table_factory
.reset(new BlockBasedTableFactory(table_opts
));
190 std::string file_path
=
191 MakeFilePath("rocksdb_sst_reverse_bytewise_comparator.sst");
192 createSST(opts
, file_path
);
195 PopulateCommandArgs(file_path
, "--command=raw", usage
);
197 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
198 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
200 cleanup(opts
, file_path
);
201 for (int i
= 0; i
< 3; i
++) {
206 TEST_F(SSTDumpToolTest
, SstDumpComparatorWithU64Ts
) {
209 opts
.comparator
= test::BytewiseComparatorWithU64TsWrapper();
210 BlockBasedTableOptions table_opts
;
211 table_opts
.filter_policy
.reset(
212 ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
213 opts
.table_factory
.reset(new BlockBasedTableFactory(table_opts
));
214 std::string file_path
=
215 MakeFilePath("rocksdb_sst_comparator_with_u64_ts.sst");
216 createSST(opts
, file_path
);
219 PopulateCommandArgs(file_path
, "--command=raw", usage
);
221 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
222 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
224 cleanup(opts
, file_path
);
225 for (int i
= 0; i
< 3; i
++) {
230 TEST_F(SSTDumpToolTest
, FilterBlock
) {
233 BlockBasedTableOptions table_opts
;
234 table_opts
.filter_policy
.reset(
235 ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, true));
236 opts
.table_factory
.reset(new BlockBasedTableFactory(table_opts
));
237 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
238 createSST(opts
, file_path
);
241 PopulateCommandArgs(file_path
, "--command=raw", usage
);
243 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
244 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
246 cleanup(opts
, file_path
);
247 for (int i
= 0; i
< 3; i
++) {
252 TEST_F(SSTDumpToolTest
, FullFilterBlock
) {
255 BlockBasedTableOptions table_opts
;
256 table_opts
.filter_policy
.reset(
257 ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
258 opts
.table_factory
.reset(new BlockBasedTableFactory(table_opts
));
259 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
260 createSST(opts
, file_path
);
263 PopulateCommandArgs(file_path
, "--command=raw", usage
);
265 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
266 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
268 cleanup(opts
, file_path
);
269 for (int i
= 0; i
< 3; i
++) {
274 TEST_F(SSTDumpToolTest
, GetProperties
) {
277 BlockBasedTableOptions table_opts
;
278 table_opts
.filter_policy
.reset(
279 ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
280 opts
.table_factory
.reset(new BlockBasedTableFactory(table_opts
));
281 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
282 createSST(opts
, file_path
);
285 PopulateCommandArgs(file_path
, "--show_properties", usage
);
287 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
288 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
290 cleanup(opts
, file_path
);
291 for (int i
= 0; i
< 3; i
++) {
296 TEST_F(SSTDumpToolTest
, CompressedSizes
) {
299 BlockBasedTableOptions table_opts
;
300 table_opts
.filter_policy
.reset(
301 ROCKSDB_NAMESPACE::NewBloomFilterPolicy(10, false));
302 opts
.table_factory
.reset(new BlockBasedTableFactory(table_opts
));
303 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
304 createSST(opts
, file_path
);
307 PopulateCommandArgs(file_path
, "--command=recompress", usage
);
309 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
310 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
312 cleanup(opts
, file_path
);
313 for (int i
= 0; i
< 3; i
++) {
318 TEST_F(SSTDumpToolTest
, MemEnv
) {
319 std::unique_ptr
<Env
> mem_env(NewMemEnv(env()));
321 opts
.env
= mem_env
.get();
322 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
323 createSST(opts
, file_path
);
326 PopulateCommandArgs(file_path
, "--command=verify_checksum", usage
);
328 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
329 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
331 cleanup(opts
, file_path
);
332 for (int i
= 0; i
< 3; i
++) {
337 TEST_F(SSTDumpToolTest
, ReadaheadSize
) {
340 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
341 createSST(opts
, file_path
);
344 PopulateCommandArgs(file_path
, "--command=verify", usage
);
345 snprintf(usage
[3], kOptLength
, "--readahead_size=4000000");
348 SyncPoint::GetInstance()->SetCallBack("RandomAccessFileReader::Read",
349 [&](void*) { num_reads
++; });
350 SyncPoint::GetInstance()->EnableProcessing();
353 ASSERT_TRUE(!tool
.Run(4, usage
, opts
));
355 // The file is approximately 10MB. Readahead is 4MB.
356 // We usually need 3 reads + one metadata read.
357 // One extra read is needed before opening the file for metadata.
358 ASSERT_EQ(5, num_reads
);
360 SyncPoint::GetInstance()->ClearAllCallBacks();
361 SyncPoint::GetInstance()->DisableProcessing();
363 cleanup(opts
, file_path
);
364 for (int i
= 0; i
< 4; i
++) {
369 TEST_F(SSTDumpToolTest
, NoSstFile
) {
372 std::string file_path
= MakeFilePath("no_such_file.sst");
374 PopulateCommandArgs(file_path
, "", usage
);
375 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
376 for (const auto& command
:
377 {"--command=check", "--command=dump", "--command=raw",
378 "--command=verify", "--command=recompress", "--command=verify_checksum",
379 "--show_properties"}) {
380 snprintf(usage
[1], kOptLength
, "%s", command
);
381 ASSERT_TRUE(tool
.Run(3, usage
, opts
));
383 for (int i
= 0; i
< 3; i
++) {
388 TEST_F(SSTDumpToolTest
, ValidSSTPath
) {
392 PopulateCommandArgs("", "", usage
);
394 std::string file_not_exists
= MakeFilePath("file_not_exists.sst");
395 std::string sst_file
= MakeFilePath("rocksdb_sst_test.sst");
396 createSST(opts
, sst_file
);
397 std::string text_file
= MakeFilePath("text_file");
398 ASSERT_OK(WriteStringToFile(opts
.env
, "Hello World!", text_file
));
399 std::string fake_sst
= MakeFilePath("fake_sst.sst");
400 ASSERT_OK(WriteStringToFile(opts
.env
, "Not an SST file!", fake_sst
));
402 for (const auto& command_arg
: {"--command=verify", "--command=identify"}) {
403 snprintf(usage
[1], kOptLength
, "%s", command_arg
);
405 snprintf(usage
[2], kOptLength
, "--file=%s", file_not_exists
.c_str());
406 ASSERT_TRUE(tool
.Run(3, usage
, opts
));
408 snprintf(usage
[2], kOptLength
, "--file=%s", sst_file
.c_str());
409 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
411 snprintf(usage
[2], kOptLength
, "--file=%s", text_file
.c_str());
412 ASSERT_TRUE(tool
.Run(3, usage
, opts
));
414 snprintf(usage
[2], kOptLength
, "--file=%s", fake_sst
.c_str());
415 ASSERT_TRUE(tool
.Run(3, usage
, opts
));
417 ASSERT_OK(opts
.env
->DeleteFile(sst_file
));
418 ASSERT_OK(opts
.env
->DeleteFile(text_file
));
419 ASSERT_OK(opts
.env
->DeleteFile(fake_sst
));
421 for (int i
= 0; i
< 3; i
++) {
426 TEST_F(SSTDumpToolTest
, RawOutput
) {
429 std::string file_path
= MakeFilePath("rocksdb_sst_test.sst");
430 createSST(opts
, file_path
);
433 PopulateCommandArgs(file_path
, "--command=raw", usage
);
435 ROCKSDB_NAMESPACE::SSTDumpTool tool
;
436 ASSERT_TRUE(!tool
.Run(3, usage
, opts
));
438 const std::string raw_path
= MakeFilePath("rocksdb_sst_test_dump.txt");
439 std::ifstream
raw_file(raw_path
);
442 bool is_data_block
= false;
444 while (getline(raw_file
, tp
)) {
445 if (tp
.find("Data Block #") != std::string::npos
) {
446 is_data_block
= true;
449 if (is_data_block
&& tp
.find("HEX") != std::string::npos
) {
454 ASSERT_EQ(kNumKey
, key_count
);
458 cleanup(opts
, file_path
);
459 for (int i
= 0; i
< 3; i
++) {
464 } // namespace ROCKSDB_NAMESPACE
466 int main(int argc
, char** argv
) {
467 ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
468 ::testing::InitGoogleTest(&argc
, argv
);
469 RegisterCustomObjects(argc
, argv
);
470 return RUN_ALL_TESTS();
476 int main(int /*argc*/, char** /*argv*/) {
477 fprintf(stderr
, "SKIPPED as SSTDumpTool is not supported in ROCKSDB_LITE\n");
481 #endif // !ROCKSDB_LITE return RUN_ALL_TESTS();