Coverage Report

Created: 2026-11-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/trace_replay/io_tracer.cc
Line
Count
Source
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
6
#include "trace_replay/io_tracer.h"
7
8
#include <cinttypes>
9
#include <cstdio>
10
#include <cstdlib>
11
12
#include "db/db_impl/db_impl.h"
13
#include "db/dbformat.h"
14
#include "rocksdb/slice.h"
15
#include "rocksdb/system_clock.h"
16
#include "rocksdb/trace_reader_writer.h"
17
#include "util/coding.h"
18
#include "util/hash.h"
19
#include "util/string_util.h"
20
21
namespace ROCKSDB_NAMESPACE {
22
IOTraceWriter::IOTraceWriter(SystemClock* clock,
23
                             const TraceOptions& trace_options,
24
                             std::unique_ptr<TraceWriter>&& trace_writer)
25
0
    : clock_(clock),
26
0
      trace_options_(trace_options),
27
0
      trace_writer_(std::move(trace_writer)) {}
28
29
Status IOTraceWriter::WriteIOOp(const IOTraceRecord& record,
30
0
                                IODebugContext* dbg) {
31
0
  uint64_t trace_file_size = trace_writer_->GetFileSize();
32
0
  if (trace_file_size > trace_options_.max_trace_file_size) {
33
0
    return Status::OK();
34
0
  }
35
0
  Trace trace;
36
0
  trace.ts = record.access_timestamp;
37
0
  trace.type = record.trace_type;
38
0
  PutFixed64(&trace.payload, record.io_op_data);
39
0
  Slice file_operation(record.file_operation);
40
0
  PutLengthPrefixedSlice(&trace.payload, file_operation);
41
0
  PutFixed64(&trace.payload, record.latency);
42
0
  Slice io_status(record.io_status);
43
0
  PutLengthPrefixedSlice(&trace.payload, io_status);
44
0
  Slice file_name(record.file_name);
45
0
  PutLengthPrefixedSlice(&trace.payload, file_name);
46
47
  // Each bit in io_op_data stores which corresponding info from IOTraceOp will
48
  // be added in the trace. Foreg, if bit at position 1 is set then
49
  // IOTraceOp::kIOLen (length) will be logged in the record (Since
50
  // IOTraceOp::kIOLen = 1 in the enum). So find all the set positions in
51
  // io_op_data one by one and, update corresponsing info in the trace record,
52
  // unset that bit to find other set bits until io_op_data = 0.
53
  /* Write remaining options based on io_op_data set by file operation */
54
0
  int64_t io_op_data = static_cast<int64_t>(record.io_op_data);
55
0
  while (io_op_data) {
56
    // Find the rightmost set bit.
57
0
    uint32_t set_pos = static_cast<uint32_t>(log2(io_op_data & -io_op_data));
58
0
    switch (set_pos) {
59
0
      case IOTraceOp::kIOFileSize:
60
0
        PutFixed64(&trace.payload, record.file_size);
61
0
        break;
62
0
      case IOTraceOp::kIOLen:
63
0
        PutFixed64(&trace.payload, record.len);
64
0
        break;
65
0
      case IOTraceOp::kIOOffset:
66
0
        PutFixed64(&trace.payload, record.offset);
67
0
        break;
68
0
      default:
69
0
        assert(false);
70
0
    }
71
    // unset the rightmost bit.
72
0
    io_op_data &= (io_op_data - 1);
73
0
  }
74
75
0
  int64_t trace_data = 0;
76
0
  if (dbg) {
77
0
    trace_data = static_cast<int64_t>(dbg->trace_data);
78
0
  }
79
0
  PutFixed64(&trace.payload, trace_data);
80
0
  while (trace_data) {
81
    // Find the rightmost set bit.
82
0
    uint32_t set_pos = static_cast<uint32_t>(log2(trace_data & -trace_data));
83
0
    switch (set_pos) {
84
0
      case IODebugContext::TraceData::kRequestID: {
85
0
        Slice request_id(*dbg->request_id);
86
0
        PutLengthPrefixedSlice(&trace.payload, request_id);
87
0
      } break;
88
0
      default:
89
0
        assert(false);
90
0
    }
91
    // unset the rightmost bit.
92
0
    trace_data &= (trace_data - 1);
93
0
  }
94
95
0
  std::string encoded_trace;
96
0
  TracerHelper::EncodeTrace(trace, &encoded_trace);
97
0
  return trace_writer_->Write(encoded_trace);
98
0
}
99
100
0
Status IOTraceWriter::WriteHeader() {
101
0
  Trace trace;
102
0
  trace.ts = clock_->NowMicros();
103
0
  trace.type = TraceType::kTraceBegin;
104
0
  PutLengthPrefixedSlice(&trace.payload, kTraceMagic);
105
0
  PutFixed32(&trace.payload, kMajorVersion);
106
0
  PutFixed32(&trace.payload, kMinorVersion);
107
0
  std::string encoded_trace;
108
0
  TracerHelper::EncodeTrace(trace, &encoded_trace);
109
0
  return trace_writer_->Write(encoded_trace);
110
0
}
111
112
IOTraceReader::IOTraceReader(std::unique_ptr<TraceReader>&& reader)
113
0
    : trace_reader_(std::move(reader)) {}
114
115
0
Status IOTraceReader::ReadHeader(IOTraceHeader* header) {
116
0
  assert(header != nullptr);
117
0
  std::string encoded_trace;
118
0
  Status s = trace_reader_->Read(&encoded_trace);
119
0
  if (!s.ok()) {
120
0
    return s;
121
0
  }
122
0
  Trace trace;
123
0
  s = TracerHelper::DecodeTrace(encoded_trace, &trace);
124
0
  if (!s.ok()) {
125
0
    return s;
126
0
  }
127
0
  header->start_time = trace.ts;
128
0
  Slice enc_slice = Slice(trace.payload);
129
0
  Slice magic_number;
130
0
  if (!GetLengthPrefixedSlice(&enc_slice, &magic_number)) {
131
0
    return Status::Corruption(
132
0
        "Corrupted header in the trace file: Failed to read the magic number.");
133
0
  }
134
0
  if (magic_number.ToString() != kTraceMagic) {
135
0
    return Status::Corruption(
136
0
        "Corrupted header in the trace file: Magic number does not match.");
137
0
  }
138
0
  if (!GetFixed32(&enc_slice, &header->rocksdb_major_version)) {
139
0
    return Status::Corruption(
140
0
        "Corrupted header in the trace file: Failed to read rocksdb major "
141
0
        "version number.");
142
0
  }
143
0
  if (!GetFixed32(&enc_slice, &header->rocksdb_minor_version)) {
144
0
    return Status::Corruption(
145
0
        "Corrupted header in the trace file: Failed to read rocksdb minor "
146
0
        "version number.");
147
0
  }
148
  // We should have retrieved all information in the header.
149
0
  if (!enc_slice.empty()) {
150
0
    return Status::Corruption(
151
0
        "Corrupted header in the trace file: The length of header is too "
152
0
        "long.");
153
0
  }
154
0
  return Status::OK();
155
0
}
156
157
0
Status IOTraceReader::ReadIOOp(IOTraceRecord* record) {
158
0
  assert(record);
159
0
  std::string encoded_trace;
160
0
  Status s = trace_reader_->Read(&encoded_trace);
161
0
  if (!s.ok()) {
162
0
    return s;
163
0
  }
164
0
  Trace trace;
165
0
  s = TracerHelper::DecodeTrace(encoded_trace, &trace);
166
0
  if (!s.ok()) {
167
0
    return s;
168
0
  }
169
0
  record->access_timestamp = trace.ts;
170
0
  record->trace_type = trace.type;
171
0
  Slice enc_slice = Slice(trace.payload);
172
173
0
  if (!GetFixed64(&enc_slice, &record->io_op_data)) {
174
0
    return Status::Incomplete(
175
0
        "Incomplete access record: Failed to read trace data.");
176
0
  }
177
0
  Slice file_operation;
178
0
  if (!GetLengthPrefixedSlice(&enc_slice, &file_operation)) {
179
0
    return Status::Incomplete(
180
0
        "Incomplete access record: Failed to read file operation.");
181
0
  }
182
0
  record->file_operation = file_operation.ToString();
183
0
  if (!GetFixed64(&enc_slice, &record->latency)) {
184
0
    return Status::Incomplete(
185
0
        "Incomplete access record: Failed to read latency.");
186
0
  }
187
0
  Slice io_status;
188
0
  if (!GetLengthPrefixedSlice(&enc_slice, &io_status)) {
189
0
    return Status::Incomplete(
190
0
        "Incomplete access record: Failed to read IO status.");
191
0
  }
192
0
  record->io_status = io_status.ToString();
193
0
  Slice file_name;
194
0
  if (!GetLengthPrefixedSlice(&enc_slice, &file_name)) {
195
0
    return Status::Incomplete(
196
0
        "Incomplete access record: Failed to read file name.");
197
0
  }
198
0
  record->file_name = file_name.ToString();
199
200
  // Each bit in io_op_data stores which corresponding info from IOTraceOp will
201
  // be added in the trace. Foreg, if bit at position 1 is set then
202
  // IOTraceOp::kIOLen (length) will be logged in the record (Since
203
  // IOTraceOp::kIOLen = 1 in the enum). So find all the set positions in
204
  // io_op_data one by one and, update corresponsing info in the trace record,
205
  // unset that bit to find other set bits until io_op_data = 0.
206
  /* Read remaining options based on io_op_data set by file operation */
207
  // Assuming 63 bits will be used at max.
208
0
  int64_t io_op_data = static_cast<int64_t>(record->io_op_data);
209
0
  while (io_op_data) {
210
    // Find the rightmost set bit.
211
0
    uint32_t set_pos = static_cast<uint32_t>(log2(io_op_data & -io_op_data));
212
0
    switch (set_pos) {
213
0
      case IOTraceOp::kIOFileSize:
214
0
        if (!GetFixed64(&enc_slice, &record->file_size)) {
215
0
          return Status::Incomplete(
216
0
              "Incomplete access record: Failed to read file size.");
217
0
        }
218
0
        break;
219
0
      case IOTraceOp::kIOLen:
220
0
        if (!GetFixed64(&enc_slice, &record->len)) {
221
0
          return Status::Incomplete(
222
0
              "Incomplete access record: Failed to read length.");
223
0
        }
224
0
        break;
225
0
      case IOTraceOp::kIOOffset:
226
0
        if (!GetFixed64(&enc_slice, &record->offset)) {
227
0
          return Status::Incomplete(
228
0
              "Incomplete access record: Failed to read offset.");
229
0
        }
230
0
        break;
231
0
      default:
232
0
        assert(false);
233
0
    }
234
    // unset the rightmost bit.
235
0
    io_op_data &= (io_op_data - 1);
236
0
  }
237
238
0
  if (!GetFixed64(&enc_slice, &record->trace_data)) {
239
0
    return Status::Incomplete(
240
0
        "Incomplete access record: Failed to read trace op.");
241
0
  }
242
0
  int64_t trace_data = static_cast<int64_t>(record->trace_data);
243
0
  while (trace_data) {
244
    // Find the rightmost set bit.
245
0
    uint32_t set_pos = static_cast<uint32_t>(log2(trace_data & -trace_data));
246
0
    switch (set_pos) {
247
0
      case IODebugContext::TraceData::kRequestID: {
248
0
        Slice request_id;
249
0
        if (!GetLengthPrefixedSlice(&enc_slice, &request_id)) {
250
0
          return Status::Incomplete(
251
0
              "Incomplete access record: Failed to request id.");
252
0
        }
253
0
        record->request_id = request_id.ToString();
254
0
      } break;
255
0
      default:
256
0
        assert(false);
257
0
    }
258
    // unset the rightmost bit.
259
0
    trace_data &= (trace_data - 1);
260
0
  }
261
262
0
  return Status::OK();
263
0
}
264
265
44.8k
IOTracer::IOTracer() : tracing_enabled(false) { writer_.store(nullptr); }
266
267
44.8k
IOTracer::~IOTracer() { EndIOTrace(); }
268
269
Status IOTracer::StartIOTrace(SystemClock* clock,
270
                              const TraceOptions& trace_options,
271
0
                              std::unique_ptr<TraceWriter>&& trace_writer) {
272
0
  InstrumentedMutexLock lock_guard(&trace_writer_mutex_);
273
0
  if (writer_.load()) {
274
0
    return Status::Busy();
275
0
  }
276
0
  trace_options_ = trace_options;
277
0
  writer_.store(
278
0
      new IOTraceWriter(clock, trace_options, std::move(trace_writer)));
279
0
  tracing_enabled = true;
280
0
  return writer_.load()->WriteHeader();
281
0
}
282
283
44.8k
void IOTracer::EndIOTrace() {
284
44.8k
  InstrumentedMutexLock lock_guard(&trace_writer_mutex_);
285
44.8k
  if (!writer_.load()) {
286
44.8k
    return;
287
44.8k
  }
288
0
  delete writer_.load();
289
0
  writer_.store(nullptr);
290
0
  tracing_enabled = false;
291
0
}
292
293
0
void IOTracer::WriteIOOp(const IOTraceRecord& record, IODebugContext* dbg) {
294
0
  if (!writer_.load()) {
295
0
    return;
296
0
  }
297
0
  InstrumentedMutexLock lock_guard(&trace_writer_mutex_);
298
0
  if (!writer_.load()) {
299
0
    return;
300
0
  }
301
0
  writer_.load()->WriteIOOp(record, dbg).PermitUncheckedError();
302
0
}
303
}  // namespace ROCKSDB_NAMESPACE