2020-09-24 00:49:11 +02:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
|
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
|
|
|
|
#ifndef ROCKSDB_LITE
|
|
|
|
#ifdef GFLAGS
|
|
|
|
#include "tools/io_tracer_parser_tool.h"
|
|
|
|
|
|
|
|
#include <cinttypes>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <iomanip>
|
|
|
|
#include <memory>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include "port/lang.h"
|
2021-01-26 07:07:26 +01:00
|
|
|
#include "rocksdb/trace_reader_writer.h"
|
2020-09-24 00:49:11 +02:00
|
|
|
#include "trace_replay/io_tracer.h"
|
|
|
|
#include "util/gflags_compat.h"
|
|
|
|
|
|
|
|
using GFLAGS_NAMESPACE::ParseCommandLineFlags;
|
|
|
|
|
|
|
|
DEFINE_string(io_trace_file, "", "The IO trace file path.");
|
|
|
|
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
|
|
|
|
|
|
IOTraceRecordParser::IOTraceRecordParser(const std::string& input_file)
|
|
|
|
: input_file_(input_file) {}
|
|
|
|
|
|
|
|
void IOTraceRecordParser::PrintHumanReadableHeader(
|
|
|
|
const IOTraceHeader& header) {
|
|
|
|
std::stringstream ss;
|
|
|
|
ss << "Start Time: " << header.start_time
|
|
|
|
<< "\nRocksDB Major Version: " << header.rocksdb_major_version
|
|
|
|
<< "\nRocksDB Minor Version: " << header.rocksdb_minor_version << "\n";
|
|
|
|
fprintf(stdout, "%s", ss.str().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
void IOTraceRecordParser::PrintHumanReadableIOTraceRecord(
|
|
|
|
const IOTraceRecord& record) {
|
|
|
|
std::stringstream ss;
|
2021-01-25 23:35:45 +01:00
|
|
|
ss << "Access Time : " << std::setw(20) << std::left
|
|
|
|
<< record.access_timestamp << ", File Name: " << std::setw(20) << std::left
|
|
|
|
<< record.file_name.c_str() << ", File Operation: " << std::setw(18)
|
2020-09-24 00:49:11 +02:00
|
|
|
<< std::left << record.file_operation.c_str()
|
2021-01-25 23:35:45 +01:00
|
|
|
<< ", Latency: " << std::setw(10) << std::left << record.latency
|
2020-09-24 00:49:11 +02:00
|
|
|
<< ", IO Status: " << record.io_status.c_str();
|
|
|
|
|
2021-01-25 23:35:45 +01:00
|
|
|
// Each bit in io_op_data stores which corresponding info from IOTraceOp will
|
|
|
|
// be added in the trace. Foreg, if bit at position 1 is set then
|
|
|
|
// IOTraceOp::kIOLen (length) will be logged in the record (Since
|
|
|
|
// IOTraceOp::kIOLen = 1 in the enum). So find all the set positions in
|
|
|
|
// io_op_data one by one and, update corresponsing info in the trace record,
|
|
|
|
// unset that bit to find other set bits until io_op_data = 0.
|
|
|
|
/* Read remaining options based on io_op_data set by file operation */
|
|
|
|
int64_t io_op_data = static_cast<int64_t>(record.io_op_data);
|
|
|
|
while (io_op_data) {
|
|
|
|
// Find the rightmost set bit.
|
|
|
|
uint32_t set_pos = static_cast<uint32_t>(log2(io_op_data & -io_op_data));
|
|
|
|
switch (set_pos) {
|
|
|
|
case IOTraceOp::kIOFileSize:
|
|
|
|
ss << ", File Size: " << record.file_size;
|
|
|
|
break;
|
|
|
|
case IOTraceOp::kIOLen:
|
|
|
|
ss << ", Length: " << record.len;
|
|
|
|
break;
|
|
|
|
case IOTraceOp::kIOOffset:
|
|
|
|
ss << ", Offset: " << record.offset;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
2020-09-24 00:49:11 +02:00
|
|
|
}
|
2021-01-25 23:35:45 +01:00
|
|
|
// unset the rightmost bit.
|
|
|
|
io_op_data &= (io_op_data - 1);
|
2020-09-24 00:49:11 +02:00
|
|
|
}
|
2021-04-01 22:14:01 +02:00
|
|
|
|
|
|
|
int64_t trace_data = static_cast<int64_t>(record.trace_data);
|
|
|
|
while (trace_data) {
|
|
|
|
// Find the rightmost set bit.
|
|
|
|
uint32_t set_pos = static_cast<uint32_t>(log2(trace_data & -trace_data));
|
|
|
|
switch (set_pos) {
|
|
|
|
case IODebugContext::TraceData::kRequestID:
|
|
|
|
ss << ", Request Id: " << record.request_id;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
// unset the rightmost bit.
|
|
|
|
trace_data &= (trace_data - 1);
|
|
|
|
}
|
|
|
|
|
2020-09-24 00:49:11 +02:00
|
|
|
ss << "\n";
|
|
|
|
fprintf(stdout, "%s", ss.str().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
int IOTraceRecordParser::ReadIOTraceRecords() {
|
|
|
|
Status status;
|
|
|
|
Env* env(Env::Default());
|
|
|
|
std::unique_ptr<TraceReader> trace_reader;
|
|
|
|
std::unique_ptr<IOTraceReader> io_trace_reader;
|
|
|
|
|
|
|
|
status = NewFileTraceReader(env, EnvOptions(), input_file_, &trace_reader);
|
|
|
|
if (!status.ok()) {
|
|
|
|
fprintf(stderr, "%s: %s\n", input_file_.c_str(), status.ToString().c_str());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
io_trace_reader.reset(new IOTraceReader(std::move(trace_reader)));
|
|
|
|
|
|
|
|
// Read the header and dump it in a file.
|
|
|
|
IOTraceHeader header;
|
|
|
|
status = io_trace_reader->ReadHeader(&header);
|
|
|
|
if (!status.ok()) {
|
|
|
|
fprintf(stderr, "%s: %s\n", input_file_.c_str(), status.ToString().c_str());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
PrintHumanReadableHeader(header);
|
|
|
|
|
|
|
|
// Read the records one by one and print them in human readable format.
|
|
|
|
while (status.ok()) {
|
|
|
|
IOTraceRecord record;
|
|
|
|
status = io_trace_reader->ReadIOOp(&record);
|
|
|
|
if (!status.ok()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
PrintHumanReadableIOTraceRecord(record);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int io_tracer_parser(int argc, char** argv) {
|
|
|
|
ParseCommandLineFlags(&argc, &argv, true);
|
|
|
|
|
|
|
|
if (FLAGS_io_trace_file.empty()) {
|
|
|
|
fprintf(stderr, "IO Trace file path is empty\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
IOTraceRecordParser io_tracer_parser(FLAGS_io_trace_file);
|
|
|
|
return io_tracer_parser.ReadIOTraceRecords();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|
|
#endif // GFLAGS
|
|
|
|
#endif // ROCKSDB_LITE
|