YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/trace.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include "yb/util/trace.h"
34
35
#include <iomanip>
36
#include <iostream>
37
#include <string>
38
#include <vector>
39
40
#include <boost/range/adaptor/indirected.hpp>
41
42
#include "yb/gutil/strings/substitute.h"
43
#include "yb/gutil/walltime.h"
44
45
#include "yb/util/flag_tags.h"
46
#include "yb/util/memory/arena.h"
47
#include "yb/util/memory/memory.h"
48
#include "yb/util/object_pool.h"
49
#include "yb/util/size_literals.h"
50
51
DEFINE_bool(enable_tracing, false, "Flag to enable/disable tracing across the code.");
52
TAG_FLAG(enable_tracing, advanced);
53
TAG_FLAG(enable_tracing, runtime);
54
55
DEFINE_bool(use_monotime_for_traces, false, "Flag to enable use of MonoTime::Now() instead of "
56
    "CoarseMonoClock::Now(). CoarseMonoClock is much cheaper so it is better to use it. However "
57
    "if we need more accurate sub-millisecond level breakdown, we could use MonoTime.");
58
TAG_FLAG(use_monotime_for_traces, advanced);
59
TAG_FLAG(use_monotime_for_traces, runtime);
60
61
DEFINE_int32(tracing_level, 0, "verbosity levels (like --v) up to which tracing is enabled.");
62
TAG_FLAG(tracing_level, advanced);
63
TAG_FLAG(tracing_level, runtime);
64
65
DEFINE_int32(print_nesting_levels, 5, "controls the depth of the child traces to be printed.");
66
TAG_FLAG(print_nesting_levels, advanced);
67
TAG_FLAG(print_nesting_levels, runtime);
68
69
namespace yb {
70
71
using strings::internal::SubstituteArg;
72
73
__thread Trace* Trace::threadlocal_trace_;
74
75
namespace {
76
77
const char* kNestedChildPrefix = "..  ";
78
79
// Get the part of filepath after the last path separator.
80
// (Doesn't modify filepath, contrary to basename() in libgen.h.)
81
// Borrowed from glog.
82
26.6k
const char* const_basename(const char* filepath) {
83
26.6k
  const char* base = strrchr(filepath, '/');
84
26.6k
  return base ? (base + 1) : filepath;
85
26.6k
}
86
87
template <class Children>
88
void DumpChildren(
89
7.96k
    std::ostream* out, int32_t tracing_depth, bool include_time_deltas, const Children* children) {
90
7.96k
  if (tracing_depth > GetAtomicFlag(&FLAGS_print_nesting_levels)) {
91
0
    return;
92
0
  }
93
7.96k
  for (auto &child_trace : *children) {
94
17.0k
    for (int i = 0; i < tracing_depth; i++) {
95
11.5k
      *out << kNestedChildPrefix;
96
11.5k
    }
97
5.42k
    *out << "Related trace:" << std::endl;
98
5.42k
    *out << child_trace->DumpToString(tracing_depth, include_time_deltas);
99
5.42k
  }
100
7.96k
}
101
102
void DumpChildren(
103
0
    std::ostream* out, int32_t tracing_depth, bool include_time_deltas, std::nullptr_t children) {}
104
105
template <class Entries>
106
void DumpEntries(
107
    std::ostream* out,
108
    int32_t tracing_depth,
109
    bool include_time_deltas,
110
    int64_t start,
111
7.96k
    const Entries& entries) {
112
7.96k
  if (entries.empty()) {
113
228
    return;
114
228
  }
115
116
7.73k
  auto time_usec = MonoDelta(entries.begin()->timestamp.time_since_epoch()).ToMicroseconds();
117
7.73k
  const int64_t time_correction_usec = start - time_usec;
118
7.73k
  int64_t prev_usecs = time_usec;
119
26.6k
  for (const auto& e : entries) {
120
26.6k
    time_usec = MonoDelta(e.timestamp.time_since_epoch()).ToMicroseconds();
121
26.6k
    const int64_t usecs_since_prev = time_usec - prev_usecs;
122
26.6k
    prev_usecs = time_usec;
123
124
26.6k
    const auto absolute_time_usec = time_usec + time_correction_usec;
125
26.6k
    const time_t secs_since_epoch = absolute_time_usec / 1000000;
126
26.6k
    const int usecs = absolute_time_usec % 1000000;
127
26.6k
    struct tm tm_time;
128
26.6k
    localtime_r(&secs_since_epoch, &tm_time);
129
130
65.5k
    for (int i = 0; i < tracing_depth; i++) {
131
38.9k
      *out << kNestedChildPrefix;
132
38.9k
    }
133
    // Log format borrowed from glog/logging.cc
134
26.6k
    using std::setw;
135
26.6k
    out->fill('0');
136
137
26.6k
    *out << setw(2) << (1 + tm_time.tm_mon)
138
26.6k
         << setw(2) << tm_time.tm_mday
139
26.6k
         << ' '
140
26.6k
         << setw(2) << tm_time.tm_hour  << ':'
141
26.6k
         << setw(2) << tm_time.tm_min   << ':'
142
26.6k
         << setw(2) << tm_time.tm_sec   << '.'
143
26.6k
         << setw(6) << usecs << ' ';
144
26.6k
    if (include_time_deltas) {
145
26.6k
      out->fill(' ');
146
26.6k
      *out << "(+" << setw(6) << usecs_since_prev << "us) ";
147
26.6k
    }
148
26.6k
    e.Dump(out);
149
26.6k
    *out << std::endl;
150
26.6k
  }
151
7.73k
}
trace.cc:_ZN2yb12_GLOBAL__N_111DumpEntriesIN5boost12range_detail16indirected_rangeINSt3__16vectorIPNS_10TraceEntryENS5_9allocatorIS8_EEEEEEEEvPNS5_13basic_ostreamIcNS5_11char_traitsIcEEEEibxRKT_
Line
Count
Source
111
7.96k
    const Entries& entries) {
112
7.96k
  if (entries.empty()) {
113
228
    return;
114
228
  }
115
116
7.73k
  auto time_usec = MonoDelta(entries.begin()->timestamp.time_since_epoch()).ToMicroseconds();
117
7.73k
  const int64_t time_correction_usec = start - time_usec;
118
7.73k
  int64_t prev_usecs = time_usec;
119
26.6k
  for (const auto& e : entries) {
120
26.6k
    time_usec = MonoDelta(e.timestamp.time_since_epoch()).ToMicroseconds();
121
26.6k
    const int64_t usecs_since_prev = time_usec - prev_usecs;
122
26.6k
    prev_usecs = time_usec;
123
124
26.6k
    const auto absolute_time_usec = time_usec + time_correction_usec;
125
26.6k
    const time_t secs_since_epoch = absolute_time_usec / 1000000;
126
26.6k
    const int usecs = absolute_time_usec % 1000000;
127
26.6k
    struct tm tm_time;
128
26.6k
    localtime_r(&secs_since_epoch, &tm_time);
129
130
65.5k
    for (int i = 0; i < tracing_depth; i++) {
131
38.9k
      *out << kNestedChildPrefix;
132
38.9k
    }
133
    // Log format borrowed from glog/logging.cc
134
26.6k
    using std::setw;
135
26.6k
    out->fill('0');
136
137
26.6k
    *out << setw(2) << (1 + tm_time.tm_mon)
138
26.6k
         << setw(2) << tm_time.tm_mday
139
26.6k
         << ' '
140
26.6k
         << setw(2) << tm_time.tm_hour  << ':'
141
26.6k
         << setw(2) << tm_time.tm_min   << ':'
142
26.6k
         << setw(2) << tm_time.tm_sec   << '.'
143
26.6k
         << setw(6) << usecs << ' ';
144
26.6k
    if (include_time_deltas) {
145
26.6k
      out->fill(' ');
146
26.6k
      *out << "(+" << setw(6) << usecs_since_prev << "us) ";
147
26.6k
    }
148
26.6k
    e.Dump(out);
149
26.6k
    *out << std::endl;
150
26.6k
  }
151
7.73k
}
Unexecuted instantiation: trace.cc:_ZN2yb12_GLOBAL__N_111DumpEntriesIN5boost14iterator_rangeIPKNS_10PlainTrace5EntryEEEEEvPNSt3__113basic_ostreamIcNS9_11char_traitsIcEEEEibxRKT_
152
153
template <class Entries, class Children>
154
void DoDump(
155
    std::ostream* out,
156
    int32_t tracing_depth,
157
    bool include_time_deltas,
158
    int64_t start,
159
    const Entries& entries,
160
7.96k
    Children children) {
161
  // Save original flags.
162
7.96k
  std::ios::fmtflags save_flags(out->flags());
163
164
7.96k
  DumpEntries(out, tracing_depth, include_time_deltas, start, entries);
165
7.96k
  DumpChildren(out, tracing_depth + 1, include_time_deltas, children);
166
167
  // Restore stream flags.
168
7.96k
  out->flags(save_flags);
169
7.96k
}
trace.cc:_ZN2yb12_GLOBAL__N_16DoDumpIN5boost12range_detail16indirected_rangeINSt3__16vectorIPNS_10TraceEntryENS5_9allocatorIS8_EEEEEEPNS6_I13scoped_refptrINS_5TraceEENS9_ISF_EEEEEEvPNS5_13basic_ostreamIcNS5_11char_traitsIcEEEEibxRKT_T0_
Line
Count
Source
160
7.96k
    Children children) {
161
  // Save original flags.
162
7.96k
  std::ios::fmtflags save_flags(out->flags());
163
164
7.96k
  DumpEntries(out, tracing_depth, include_time_deltas, start, entries);
165
7.96k
  DumpChildren(out, tracing_depth + 1, include_time_deltas, children);
166
167
  // Restore stream flags.
168
7.96k
  out->flags(save_flags);
169
7.96k
}
Unexecuted instantiation: trace.cc:_ZN2yb12_GLOBAL__N_16DoDumpIN5boost14iterator_rangeIPKNS_10PlainTrace5EntryEEEDnEEvPNSt3__113basic_ostreamIcNS9_11char_traitsIcEEEEibxRKT_T0_
170
171
std::once_flag init_get_current_micros_fast_flag;
172
int64_t initial_micros_offset;
173
174
1.87k
void InitGetCurrentMicrosFast() {
175
1.87k
  auto before = CoarseMonoClock::Now();
176
1.87k
  initial_micros_offset = GetCurrentTimeMicros();
177
1.87k
  auto after = CoarseMonoClock::Now();
178
1.87k
  auto mid = MonoDelta(after.time_since_epoch() + before.time_since_epoch()).ToMicroseconds() / 2;
179
1.87k
  initial_micros_offset -= mid;
180
1.87k
}
181
182
8.33M
int64_t GetCurrentMicrosFast(CoarseTimePoint now) {
183
8.33M
  std::call_once(init_get_current_micros_fast_flag, InitGetCurrentMicrosFast);
184
8.33M
  return initial_micros_offset + MonoDelta(now.time_since_epoch()).ToMicroseconds();
185
8.33M
}
186
187
} // namespace
188
189
ScopedAdoptTrace::ScopedAdoptTrace(Trace* t)
190
121M
    : old_trace_(Trace::threadlocal_trace_), is_enabled_(GetAtomicFlag(&FLAGS_enable_tracing)) {
191
121M
  if (is_enabled_) {
192
13.5M
    trace_ = t;
193
13.5M
    Trace::threadlocal_trace_ = t;
194
13.5M
    DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_);
195
13.5M
  }
196
121M
}
197
198
121M
ScopedAdoptTrace::~ScopedAdoptTrace() {
199
121M
  if (is_enabled_) {
200
13.5M
    Trace::threadlocal_trace_ = old_trace_;
201
    // It's critical that we Release() the reference count on 't' only
202
    // after we've unset the thread-local variable. Otherwise, we can hit
203
    // a nasty interaction with tcmalloc contention profiling. Consider
204
    // the following sequence:
205
    //
206
    //   1. threadlocal_trace_ has refcount = 1
207
    //   2. we call threadlocal_trace_->Release() which decrements refcount to 0
208
    //   3. this calls 'delete' on the Trace object
209
    //   3a. this calls tcmalloc free() on the Trace and various sub-objects
210
    //   3b. the free() calls may end up experiencing contention in tcmalloc
211
    //   3c. we try to account the contention in threadlocal_trace_'s TraceMetrics,
212
    //       but it has already been freed.
213
    //
214
    // In the best case, we just scribble into some free tcmalloc memory. In the
215
    // worst case, tcmalloc would have already re-used this memory for a new
216
    // allocation on another thread, and we end up overwriting someone else's memory.
217
    //
218
    // Waiting to Release() only after 'unpublishing' the trace solves this.
219
13.5M
    trace_.reset();
220
13.5M
    DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_);
221
13.5M
  }
222
121M
}
223
224
// Struct which precedes each entry in the trace.
225
struct TraceEntry {
226
  CoarseTimePoint timestamp;
227
228
  // The source file and line number which generated the trace message.
229
  const char* file_path;
230
  int line_number;
231
232
  size_t message_len;
233
  TraceEntry* next;
234
  char message[0];
235
236
26.6k
  void Dump(std::ostream* out) const {
237
26.6k
    *out << const_basename(file_path) << ':' << line_number
238
26.6k
         << "] ";
239
26.6k
    out->write(message, message_len);
240
26.6k
  }
241
};
242
243
81.7M
Trace::Trace() {
244
81.7M
}
245
246
16.6M
ThreadSafeObjectPool<ThreadSafeArena>& ArenaPool() {
247
2.27M
  static ThreadSafeObjectPool<ThreadSafeArena> result([] {
248
2.27M
    return new ThreadSafeArena(8_KB, 128_KB);
249
2.27M
  });
250
16.6M
  return result;
251
16.6M
}
252
253
81.7M
Trace::~Trace() {
254
81.7M
  auto* arena = arena_.load(std::memory_order_acquire);
255
81.7M
  if (arena) {
256
8.30M
    arena->Reset();
257
8.30M
    ArenaPool().Release(arena);
258
8.30M
  }
259
81.7M
}
260
261
51.6M
ThreadSafeArena* Trace::GetAndInitArena() {
262
51.6M
  auto* arena = arena_.load(std::memory_order_acquire);
263
51.6M
  if (arena == nullptr) {
264
8.31M
    arena = ArenaPool().Take();
265
8.31M
    ThreadSafeArena* existing_arena = nullptr;
266
8.32M
    if (arena_.compare_exchange_strong(existing_arena, arena, std::memory_order_release)) {
267
8.32M
      return arena;
268
18.4E
    } else {
269
18.4E
      ArenaPool().Release(arena);
270
18.4E
      return existing_arena;
271
18.4E
    }
272
43.3M
  }
273
43.3M
  return arena;
274
43.3M
}
275
276
void Trace::SubstituteAndTrace(
277
41.0M
    const char* file_path, int line_number, CoarseTimePoint now, GStringPiece format) {
278
41.0M
  auto msg_len = format.size();
279
0
  DCHECK_NE(msg_len, 0) << "Bad format specification";
280
41.0M
  TraceEntry* entry = NewEntry(msg_len, file_path, line_number, now);
281
41.0M
  if (entry == nullptr) return;
282
41.0M
  memcpy(entry->message, format.data(), msg_len);
283
41.0M
  AddEntry(entry);
284
41.0M
}
285
286
void Trace::SubstituteAndTrace(const char* file_path,
287
                               int line_number,
288
                               CoarseTimePoint now,
289
                               GStringPiece format,
290
                               const SubstituteArg& arg0, const SubstituteArg& arg1,
291
                               const SubstituteArg& arg2, const SubstituteArg& arg3,
292
                               const SubstituteArg& arg4, const SubstituteArg& arg5,
293
                               const SubstituteArg& arg6, const SubstituteArg& arg7,
294
10.6M
                               const SubstituteArg& arg8, const SubstituteArg& arg9) {
295
10.6M
  const SubstituteArg* const args_array[] = {
296
10.6M
    &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, nullptr
297
10.6M
  };
298
299
10.6M
  int msg_len = strings::internal::SubstitutedSize(format, args_array);
300
0
  DCHECK_NE(msg_len, 0) << "Bad format specification";
301
10.6M
  TraceEntry* entry = NewEntry(msg_len, file_path, line_number, now);
302
10.6M
  if (entry == nullptr) return;
303
10.6M
  SubstituteToBuffer(format, args_array, entry->message);
304
10.6M
  AddEntry(entry);
305
10.6M
}
306
307
TraceEntry* Trace::NewEntry(
308
51.6M
    size_t msg_len, const char* file_path, int line_number, CoarseTimePoint now) {
309
51.6M
  auto* arena = GetAndInitArena();
310
51.6M
  size_t size = offsetof(TraceEntry, message) + msg_len;
311
51.6M
  void* dst = arena->AllocateBytesAligned(size, alignof(TraceEntry));
312
51.6M
  if (dst == nullptr) {
313
0
    LOG(ERROR) << "NewEntry(msg_len, " << file_path << ", " << line_number
314
0
        << ") received nullptr from AllocateBytes.\n So far:" << DumpToString(true);
315
0
    return nullptr;
316
0
  }
317
51.6M
  TraceEntry* entry = new (dst) TraceEntry;
318
51.6M
  entry->timestamp = now;
319
51.6M
  entry->message_len = msg_len;
320
51.6M
  entry->file_path = file_path;
321
51.6M
  entry->line_number = line_number;
322
51.6M
  return entry;
323
51.6M
}
324
325
51.7M
void Trace::AddEntry(TraceEntry* entry) {
326
51.7M
  std::lock_guard<simple_spinlock> l(lock_);
327
51.7M
  entry->next = nullptr;
328
329
51.7M
  if (entries_tail_ != nullptr) {
330
43.4M
    entries_tail_->next = entry;
331
8.25M
  } else {
332
8.25M
    DCHECK(entries_head_ == nullptr);
333
8.25M
    entries_head_ = entry;
334
8.25M
    trace_start_time_usec_ = GetCurrentMicrosFast(entry->timestamp);
335
8.25M
  }
336
51.7M
  entries_tail_ = entry;
337
51.7M
}
338
339
124
void Trace::Dump(std::ostream *out, bool include_time_deltas) const {
340
124
  Dump(out, 0, include_time_deltas);
341
124
}
342
343
7.96k
void Trace::Dump(std::ostream* out, int32_t tracing_depth, bool include_time_deltas) const {
344
  // Gather a copy of the list of entries under the lock. This is fast
345
  // enough that we aren't worried about stalling concurrent tracers
346
  // (whereas doing the logging itself while holding the lock might be
347
  // too slow, if the output stream is a file, for example).
348
7.96k
  vector<TraceEntry*> entries;
349
7.96k
  vector<scoped_refptr<Trace> > child_traces;
350
7.96k
  decltype(trace_start_time_usec_) trace_start_time_usec;
351
7.96k
  {
352
7.96k
    std::lock_guard<simple_spinlock> l(lock_);
353
7.96k
    for (TraceEntry* cur = entries_head_;
354
34.6k
        cur != nullptr;
355
26.6k
        cur = cur->next) {
356
26.6k
      entries.push_back(cur);
357
26.6k
    }
358
359
7.96k
    child_traces = child_traces_;
360
7.96k
    trace_start_time_usec = trace_start_time_usec_;
361
7.96k
  }
362
363
7.96k
  DoDump(
364
7.96k
      out, tracing_depth, include_time_deltas, trace_start_time_usec,
365
7.96k
      entries | boost::adaptors::indirected, &child_traces);
366
7.96k
}
367
368
7.84k
string Trace::DumpToString(int32_t tracing_depth, bool include_time_deltas) const {
369
7.84k
  std::stringstream s;
370
7.84k
  Dump(&s, tracing_depth, include_time_deltas);
371
7.84k
  return s.str();
372
7.84k
}
373
374
0
void Trace::DumpCurrentTrace() {
375
0
  Trace* t = CurrentTrace();
376
0
  if (t == nullptr) {
377
0
    LOG(INFO) << "No trace is currently active.";
378
0
    return;
379
0
  }
380
0
  t->Dump(&std::cerr, true);
381
0
}
382
383
2.78M
void Trace::AddChildTrace(Trace* child_trace) {
384
2.78M
  CHECK_NOTNULL(child_trace);
385
2.78M
  {
386
2.78M
    std::lock_guard<simple_spinlock> l(lock_);
387
2.78M
    scoped_refptr<Trace> ptr(child_trace);
388
2.78M
    child_traces_.push_back(ptr);
389
2.78M
  }
390
2.78M
  CHECK(!child_trace->HasOneRef());
391
2.78M
}
392
393
39.2M
size_t Trace::DynamicMemoryUsage() const {
394
39.2M
  auto arena = arena_.load();
395
33.1M
  return arena ? arena->memory_footprint() : 0;
396
39.2M
}
397
398
0
PlainTrace::PlainTrace() {
399
0
}
400
401
0
void PlainTrace::Trace(const char *file_path, int line_number, const char *message) {
402
0
  auto timestamp = CoarseMonoClock::Now();
403
0
  {
404
0
    std::lock_guard<decltype(mutex_)> lock(mutex_);
405
0
    if (size_ < kMaxEntries) {
406
0
      if (size_ == 0) {
407
0
        trace_start_time_usec_ = GetCurrentMicrosFast(timestamp);
408
0
      }
409
0
      entries_[size_] = {file_path, line_number, message, timestamp};
410
0
      ++size_;
411
0
    }
412
0
  }
413
0
}
414
415
0
void PlainTrace::Dump(std::ostream *out, bool include_time_deltas) const {
416
0
  Dump(out, 0, include_time_deltas);
417
0
}
418
419
0
void PlainTrace::Dump(std::ostream* out, int32_t tracing_depth, bool include_time_deltas) const {
420
0
  size_t size;
421
0
  decltype(trace_start_time_usec_) trace_start_time_usec;
422
0
  {
423
0
    std::lock_guard<decltype(mutex_)> lock(mutex_);
424
0
    size = size_;
425
0
    trace_start_time_usec = trace_start_time_usec_;
426
0
  }
427
0
  auto entries = boost::make_iterator_range(entries_, entries_ + size);
428
0
  DoDump(
429
0
      out, tracing_depth, include_time_deltas, trace_start_time_usec, entries,
430
0
      /* children */ nullptr);
431
0
}
432
433
0
std::string PlainTrace::DumpToString(int32_t tracing_depth, bool include_time_deltas) const {
434
0
  std::stringstream s;
435
0
  Dump(&s, tracing_depth, include_time_deltas);
436
0
  return s.str();
437
0
}
438
439
0
void PlainTrace::Entry::Dump(std::ostream *out) const {
440
0
  *out << const_basename(file_path) << ':' << line_number << "] " << message;
441
0
}
442
443
} // namespace yb