YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/debug-util.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/debug-util.h"
34
35
#include <execinfo.h>
36
#include <dirent.h>
37
#include <sys/syscall.h>
38
39
#ifdef __linux__
40
#include <link.h>
41
#include <cxxabi.h>
42
#endif // __linux__
43
44
#ifdef __linux__
45
#include <backtrace.h>
46
#endif // __linux__
47
48
#include <string>
49
#include <iostream>
50
#include <mutex>
51
#include <regex>
52
53
#include <glog/logging.h>
54
55
#include "yb/gutil/hash/city.h"
56
#include "yb/gutil/linux_syscall_support.h"
57
#include "yb/gutil/macros.h"
58
#include "yb/gutil/singleton.h"
59
#include "yb/gutil/stringprintf.h"
60
#include "yb/gutil/strings/numbers.h"
61
62
#include "yb/util/enums.h"
63
#include "yb/util/errno.h"
64
#include "yb/util/format.h"
65
#include "yb/util/lockfree.h"
66
#include "yb/util/monotime.h"
67
#include "yb/util/source_location.h"
68
#include "yb/util/status.h"
69
#include "yb/util/thread.h"
70
71
using namespace std::literals;
72
73
#if defined(__linux__) && !defined(NDEBUG)
74
constexpr bool kDefaultUseLibbacktrace = true;
75
#else
76
constexpr bool kDefaultUseLibbacktrace = false;
77
#endif
78
79
DEFINE_bool(use_libbacktrace, kDefaultUseLibbacktrace,
80
            "Whether to use the libbacktrace library for symbolizing stack traces");
81
82
// Evil hack to grab a few useful functions from glog
83
namespace google {
84
85
extern int GetStackTrace(void** result, int max_depth, int skip_count);
86
87
// Symbolizes a program counter.  On success, returns true and write the
88
// symbol name to "out".  The symbol name is demangled if possible
89
// (supports symbols generated by GCC 3.x or newer).  Otherwise,
90
// returns false.
91
bool Symbolize(void *pc, char *out, int out_size);
92
93
namespace glog_internal_namespace_ {
94
extern void DumpStackTraceToString(std::string *s);
95
} // namespace glog_internal_namespace_
96
} // namespace google
97
98
// The %p field width for printf() functions is two characters per byte.
99
// For some environments, add two extra bytes for the leading "0x".
100
static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
101
102
using std::string;
103
104
namespace yb {
105
106
// https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html
107
enum DemangleStatus : int {
108
  kDemangleOk = 0,
109
  kDemangleMemAllocFailure = -1,
110
  kDemangleInvalidMangledName = -2,
111
  kDemangleInvalidArgument = -3
112
};
113
114
115
namespace {
116
117
const char kUnknownSymbol[] = "(unknown)";
118
const char kStackTraceEntryFormat[] = "    @ %*p  %s";
119
120
#ifdef __linux__
121
122
// Remove path prefixes up to what looks like the root of the YB source tree, or the source tree
123
// of other recognizable codebases.
124
const char* NormalizeSourceFilePath(const char* file_path) {
125
  if (file_path == nullptr) {
126
    return file_path;
127
  }
128
129
  // Remove the leading "../../../" stuff.
130
  while (strncmp(file_path, "../", 3) == 0) {
131
    file_path += 3;
132
  }
133
134
  // This could be called arbitrarily early or late in program execution as part of backtrace,
135
  // so we're not using any std::string static constants here.
136
#define YB_HANDLE_SOURCE_SUBPATH(subpath, prefix_len_to_remove) \
137
    do { \
138
      const char* const subpath_ptr = strstr(file_path, (subpath)); \
139
      if (subpath_ptr != nullptr) { \
140
        return subpath_ptr + (prefix_len_to_remove); \
141
      } \
142
    } while (0);
143
144
  YB_HANDLE_SOURCE_SUBPATH("/src/yb/", 5);
145
  YB_HANDLE_SOURCE_SUBPATH("/src/postgres/src/", 5);
146
  YB_HANDLE_SOURCE_SUBPATH("/src/rocksdb/", 5);
147
  YB_HANDLE_SOURCE_SUBPATH("/thirdparty/build/", 1);
148
149
  // These are Linuxbrew gcc's standard headers. Keep the path starting from "gcc/...".
150
  YB_HANDLE_SOURCE_SUBPATH("/Cellar/gcc/", 8);
151
152
  // TODO: replace postgres_build with just postgres.
153
  YB_HANDLE_SOURCE_SUBPATH("/postgres_build/src/", 1);
154
155
#undef YB_HANDLE_SOURCE_SUBPATH
156
157
  return file_path;
158
}
159
160
struct SymbolizationContext {
161
  StackTraceLineFormat stack_trace_line_format = StackTraceLineFormat::DEFAULT;
162
  string* buf = nullptr;
163
};
164
165
void BacktraceErrorCallback(void* data, const char* msg, int errnum) {
166
  bool reported = false;
167
  string* buf_ptr = nullptr;
168
  if (data) {
169
    auto* context = static_cast<SymbolizationContext*>(data);
170
    if (context->buf) {
171
      buf_ptr = context->buf;
172
      buf_ptr->append(StringPrintf("Backtrace error: %s (errnum=%d)\n", msg, errnum));
173
      reported = true;
174
    }
175
  }
176
177
  if (!reported) {
178
    // A backup mechanism for error reporting.
179
    fprintf(stderr, "%s called with data=%p, msg=%s, errnum=%d, buf_ptr=%p\n", __func__,
180
            data, msg, errnum, buf_ptr);
181
  }
182
}
183
184
class GlobalBacktraceState {
185
 public:
186
  GlobalBacktraceState() {
187
    bt_state_ = backtrace_create_state(
188
        /* filename */ nullptr,
189
        /* threaded = */ 0,
190
        BacktraceErrorCallback,
191
        /* data */ nullptr);
192
193
    // To complete initialization we should call backtrace, otherwise it could fail in case of
194
    // concurrent initialization.
195
    // We do this in a separate thread to avoid deadlock.
196
    // TODO: implement proper fix to avoid other potential deadlocks/stuck threads related
197
    // to locking common mutexes from signal handler. See
198
    // https://github.com/yugabyte/yugabyte-db/issues/6672 for more details.
199
    std::thread backtrace_thread([&] {
200
      backtrace_full(bt_state_, /* skip = */ 1, DummyCallback,
201
                     BacktraceErrorCallback, nullptr);
202
    });
203
    backtrace_thread.join();
204
  }
205
206
  backtrace_state* GetState() { return bt_state_; }
207
208
  std::mutex* mutex() { return &mutex_; }
209
210
 private:
211
  static int DummyCallback(void *const data, const uintptr_t pc,
212
                    const char* const filename, const int lineno,
213
                    const char* const original_function_name) {
214
    return 0;
215
  }
216
217
  struct backtrace_state* bt_state_;
218
  std::mutex mutex_;
219
};
220
221
int BacktraceFullCallback(void *const data, const uintptr_t pc,
222
                          const char* const filename, const int lineno,
223
                          const char* const original_function_name) {
224
  assert(data != nullptr);
225
  const SymbolizationContext& context = *pointer_cast<SymbolizationContext*>(data);
226
  string* const buf = context.buf;
227
  int demangle_status = 0;
228
  char* const demangled_function_name =
229
      original_function_name != nullptr ?
230
      abi::__cxa_demangle(original_function_name,
231
                          nullptr,  // output_buffer
232
                          nullptr,  // length
233
                          &demangle_status) :
234
      nullptr;
235
  const char* function_name_to_use = original_function_name;
236
  if (original_function_name != nullptr) {
237
    if (demangle_status != kDemangleOk) {
238
      if (demangle_status != kDemangleInvalidMangledName) {
239
        // -2 means the mangled name is not a valid name under the C++ ABI mangling rules.
240
        // This happens when the name is e.g. "main", so we don't report the error.
241
        StringAppendF(buf, "Error: __cxa_demangle failed for '%s' with error code %d\n",
242
            original_function_name, demangle_status);
243
      }
244
      // Regardless of the exact reason for demangle failure, we use the original function name
245
      // provided by libbacktrace.
246
    } else if (demangled_function_name != nullptr) {
247
      // If __cxa_demangle returns 0 and a non-null string, we use that instead of the original
248
      // function name.
249
      function_name_to_use = demangled_function_name;
250
    } else {
251
      StringAppendF(buf,
252
          "Error: __cxa_demangle returned zero status but nullptr demangled function for '%s'\n",
253
          original_function_name);
254
    }
255
  }
256
257
  string pretty_function_name;
258
  if (function_name_to_use == nullptr) {
259
    pretty_function_name = kUnknownSymbol;
260
  } else {
261
    // Allocating regexes on the heap so that they would never get deallocated. This is because
262
    // the kernel watchdog thread could still be symbolizing stack traces as global destructors
263
    // are being called.
264
    static const std::regex* kStdColonColonOneRE = new std::regex("\\bstd::__1::\\b");
265
    pretty_function_name = std::regex_replace(function_name_to_use, *kStdColonColonOneRE, "std::");
266
267
    static const std::regex* kStringRE = new std::regex(
268
        "\\bstd::basic_string<char, std::char_traits<char>, std::allocator<char> >");
269
    pretty_function_name = std::regex_replace(pretty_function_name, *kStringRE, "string");
270
271
    static const std::regex* kRemoveStdPrefixRE =
272
        new std::regex("\\bstd::(string|tuple|shared_ptr|unique_ptr)\\b");
273
    pretty_function_name = std::regex_replace(pretty_function_name, *kRemoveStdPrefixRE, "$1");
274
  }
275
276
  const bool is_symbol_only_fmt =
277
      context.stack_trace_line_format == StackTraceLineFormat::SYMBOL_ONLY;
278
279
  // We have not appended an end-of-line character yet. Let's see if we have file name / line number
280
  // information first. BTW kStackTraceEntryFormat is used both here and in glog-based
281
  // symbolization.
282
  if (filename == nullptr) {
283
    // libbacktrace failed to produce an address. We still need to produce a line of the form:
284
    // "    @     0x7f2d98f9bd87  "
285
    char hex_pc_buf[32];
286
    snprintf(hex_pc_buf, sizeof(hex_pc_buf), "0x%" PRIxPTR, pc);
287
    if (is_symbol_only_fmt) {
288
      // At least print out the hex address, otherwise we'd end up with an empty string.
289
      *buf += hex_pc_buf;
290
    } else {
291
      StringAppendF(buf, "    @ %18s ", hex_pc_buf);
292
    }
293
  } else {
294
    const string frame_without_file_line =
295
        is_symbol_only_fmt ? pretty_function_name
296
                           : StringPrintf(
297
                               kStackTraceEntryFormat, kPrintfPointerFieldWidth,
298
                               reinterpret_cast<void*>(pc), pretty_function_name.c_str());
299
300
    // Got filename and line number from libbacktrace! No need to filter the output through
301
    // addr2line, etc.
302
    switch (context.stack_trace_line_format) {
303
      case StackTraceLineFormat::CLION_CLICKABLE: {
304
        const string file_line_prefix = StringPrintf("%s:%d: ", filename, lineno);
305
        StringAppendF(buf, "%-100s", file_line_prefix.c_str());
306
        *buf += frame_without_file_line;
307
        break;
308
      }
309
      case StackTraceLineFormat::SHORT: {
310
        *buf += frame_without_file_line;
311
        StringAppendF(buf, " (%s:%d)", NormalizeSourceFilePath(filename), lineno);
312
        break;
313
      }
314
      case StackTraceLineFormat::SYMBOL_ONLY: {
315
        *buf += frame_without_file_line;
316
        StringAppendF(buf, " (%s:%d)", NormalizeSourceFilePath(filename), lineno);
317
        break;
318
      }
319
    }
320
  }
321
322
  buf->push_back('\n');
323
324
  // No need to check for nullptr, free is a no-op in that case.
325
  free(demangled_function_name);
326
  return 0;
327
}
328
#endif  // __linux__
329
330
bool IsDoubleUnderscoredAndInList(
331
0
    const char* symbol, const std::initializer_list<const char*>& list) {
332
0
  if (symbol[0] != '_' || symbol[1] != '_') {
333
0
    return false;
334
0
  }
335
0
  for (const auto* idle_function : list) {
336
0
    if (!strcmp(symbol, idle_function)) {
337
0
      return true;
338
0
    }
339
0
  }
340
0
  return false;
341
0
}
342
343
0
bool IsIdle(const char* symbol) {
344
0
  return IsDoubleUnderscoredAndInList(symbol,
345
0
                                      { "__GI_epoll_wait",
346
0
                                        "__pthread_cond_timedwait",
347
0
                                        "__pthread_cond_wait" });
348
0
}
349
350
0
bool IsWaiting(const char* symbol) {
351
0
  return IsDoubleUnderscoredAndInList(symbol,
352
0
                                      { "__GI___pthread_mutex_lock" });
353
0
}
354
355
}  // anonymous namespace
356
357
0
Status ListThreads(std::vector<pid_t> *tids) {
358
#if defined(__linux__)
359
  DIR *dir = opendir("/proc/self/task/");
360
  if (dir == NULL) {
361
    return STATUS(IOError, "failed to open task dir", Errno(errno));
362
  }
363
  struct dirent *d;
364
  while ((d = readdir(dir)) != NULL) {
365
    if (d->d_name[0] != '.') {
366
      uint32_t tid;
367
      if (!safe_strtou32(d->d_name, &tid)) {
368
        LOG(WARNING) << "bad tid found in procfs: " << d->d_name;
369
        continue;
370
      }
371
      tids->push_back(tid);
372
    }
373
  }
374
  closedir(dir);
375
#endif // defined(__linux__)
376
0
  return Status::OK();
377
0
}
378
379
std::string GetStackTrace(StackTraceLineFormat stack_trace_line_format,
380
11.7k
                          int num_top_frames_to_skip) {
381
11.7k
  std::string buf;
382
#ifdef __linux__
383
  if (FLAGS_use_libbacktrace) {
384
    SymbolizationContext context;
385
    context.buf = &buf;
386
    context.stack_trace_line_format = stack_trace_line_format;
387
388
    // Use libbacktrace on Linux because that gives us file names and line numbers.
389
    auto* global_backtrace_state = Singleton<GlobalBacktraceState>::get();
390
    struct backtrace_state* const backtrace_state = global_backtrace_state->GetState();
391
392
    // Avoid multi-threaded access to libbacktrace which causes high memory consumption.
393
    std::lock_guard<std::mutex> l(*global_backtrace_state->mutex());
394
395
    // TODO: https://yugabyte.atlassian.net/browse/ENG-4729
396
397
    const int backtrace_full_rv = backtrace_full(
398
        backtrace_state, /* skip = */ num_top_frames_to_skip + 1, BacktraceFullCallback,
399
        BacktraceErrorCallback, &context);
400
    if (backtrace_full_rv != 0) {
401
      StringAppendF(&buf, "Error: backtrace_full return value is %d", backtrace_full_rv);
402
    }
403
    return buf;
404
  }
405
#endif
406
11.7k
  google::glog_internal_namespace_::DumpStackTraceToString(&buf);
407
11.7k
  return buf;
408
11.7k
}
409
410
14.8k
std::string GetStackTraceHex() {
411
14.8k
  char buf[1024];
412
14.8k
  HexStackTraceToString(buf, 1024);
413
14.8k
  return std::string(buf);
414
14.8k
}
415
416
14.8k
void HexStackTraceToString(char* buf, size_t size) {
417
14.8k
  StackTrace trace;
418
14.8k
  trace.Collect(1);
419
14.8k
  trace.StringifyToHex(buf, size);
420
14.8k
}
421
422
0
string GetLogFormatStackTraceHex() {
423
0
  StackTrace trace;
424
0
  trace.Collect(1);
425
0
  return trace.ToLogFormatHexString();
426
0
}
427
428
590M
void StackTrace::Collect(int skip_frames) {
429
#if THREAD_SANITIZER || ADDRESS_SANITIZER
430
  num_frames_ = google::GetStackTrace(frames_, arraysize(frames_), skip_frames);
431
#else
432
590M
  int max_frames = skip_frames + arraysize(frames_);
433
590M
  void** buffer = static_cast<void**>(alloca((max_frames) * sizeof(void*)));
434
590M
  num_frames_ = backtrace(buffer, max_frames);
435
590M
  if (num_frames_ > skip_frames) {
436
590M
    num_frames_ -= skip_frames;
437
590M
    memmove(frames_, buffer + skip_frames, num_frames_ * sizeof(void*));
438
18.4E
  } else {
439
18.4E
    num_frames_ = 0;
440
18.4E
  }
441
590M
#endif
442
590M
}
443
444
14.8k
void StackTrace::StringifyToHex(char* buf, size_t size, int flags) const {
445
14.8k
  char* dst = buf;
446
447
  // Reserve kHexEntryLength for the first iteration of the loop, 1 byte for a
448
  // space (which we may not need if there's just one frame), and 1 for a nul
449
  // terminator.
450
14.8k
  char* limit = dst + size - kHexEntryLength - 2;
451
251k
  for (int i = 0; i < num_frames_ && dst < limit; i++) {
452
237k
    if (i != 0) {
453
222k
      *dst++ = ' ';
454
222k
    }
455
    // See note in Symbolize() below about why we subtract 1 from each address here.
456
237k
    uintptr_t addr = reinterpret_cast<uintptr_t>(frames_[i]);
457
237k
    if (!(flags & NO_FIX_CALLER_ADDRESSES)) {
458
237k
      addr--;
459
237k
    }
460
237k
    FastHex64ToBuffer(addr, dst);
461
237k
    dst += kHexEntryLength;
462
237k
  }
463
14.8k
  *dst = '\0';
464
14.8k
}
465
466
1
string StackTrace::ToHexString(int flags) const {
467
  // Each frame requires kHexEntryLength, plus a space
468
  // We also need one more byte at the end for '\0'
469
1
  char buf[kMaxFrames * (kHexEntryLength + 1) + 1];
470
1
  StringifyToHex(buf, arraysize(buf), flags);
471
1
  return string(buf);
472
1
}
473
474
// If group is specified it is filled with value corresponding to this stack trace.
475
void SymbolizeAddress(
476
    const StackTraceLineFormat stack_trace_line_format,
477
    void* pc,
478
    string* buf,
479
    StackTraceGroup* group = nullptr
480
#ifdef __linux__
481
    , GlobalBacktraceState* global_backtrace_state = nullptr
482
#endif
483
106k
    ) {
484
  // The return address 'pc' on the stack is the address of the instruction
485
  // following the 'call' instruction. In the case of calling a function annotated
486
  // 'noreturn', this address may actually be the first instruction of the next
487
  // function, because the function we care about ends with the 'call'.
488
  // So, we subtract 1 from 'pc' so that we're pointing at the 'call' instead
489
  // of the return address.
490
  //
491
  // For example, compiling a C program with -O2 that simply calls 'abort()' yields
492
  // the following disassembly:
493
  //     Disassembly of section .text:
494
  //
495
  //     0000000000400440 <main>:
496
  //       400440:   48 83 ec 08             sub    $0x8,%rsp
497
  //       400444:   e8 c7 ff ff ff          callq  400410 <abort@plt>
498
  //
499
  //     0000000000400449 <_start>:
500
  //       400449:   31 ed                   xor    %ebp,%ebp
501
  //       ...
502
  //
503
  // If we were to take a stack trace while inside 'abort', the return pointer
504
  // on the stack would be 0x400449 (the first instruction of '_start'). By subtracting
505
  // 1, we end up with 0x400448, which is still within 'main'.
506
  //
507
  // This also ensures that we point at the correct line number when using addr2line
508
  // on logged stacks.
509
106k
  pc = reinterpret_cast<void*>(reinterpret_cast<size_t>(pc) - 1);
510
#ifdef __linux__
511
  if (FLAGS_use_libbacktrace) {
512
    if (!global_backtrace_state) {
513
      global_backtrace_state = Singleton<GlobalBacktraceState>::get();
514
    }
515
    struct backtrace_state* const backtrace_state = global_backtrace_state->GetState();
516
517
    // Avoid multi-threaded access to libbacktrace which causes high memory consumption.
518
    std::lock_guard<std::mutex> l(*global_backtrace_state->mutex());
519
520
    SymbolizationContext context;
521
    context.stack_trace_line_format = stack_trace_line_format;
522
    context.buf = buf;
523
    backtrace_pcinfo(backtrace_state, reinterpret_cast<uintptr_t>(pc),
524
                    BacktraceFullCallback, BacktraceErrorCallback, &context);
525
    return;
526
  }
527
#endif
528
106k
  char tmp[1024];
529
106k
  const char* symbol = kUnknownSymbol;
530
531
106k
  if (google::Symbolize(pc, tmp, sizeof(tmp))) {
532
97.6k
    symbol = tmp;
533
97.6k
    if (group) {
534
0
      if (IsWaiting(symbol)) {
535
0
        *group = StackTraceGroup::kWaiting;
536
0
      } else if (IsIdle(symbol)) {
537
0
        *group = StackTraceGroup::kIdle;
538
0
      }
539
0
    }
540
97.6k
  }
541
542
106k
  StringAppendF(buf, kStackTraceEntryFormat, kPrintfPointerFieldWidth, pc, symbol);
543
  // We are appending the end-of-line character separately because we want to reuse the same
544
  // format string for libbacktrace callback and glog-based symbolization, and we have an extra
545
  // file name / line number component before the end-of-line in the libbacktrace case.
546
106k
  buf->push_back('\n');
547
106k
}
548
549
// Symbolization function borrowed from glog and modified to use libbacktrace on Linux.
550
string StackTrace::Symbolize(
551
2.11k
    const StackTraceLineFormat stack_trace_line_format, StackTraceGroup* group) const {
552
2.11k
  string buf;
553
#ifdef __linux__
554
  // Use libbacktrace for symbolization.
555
  auto* global_backtrace_state =
556
      FLAGS_use_libbacktrace ? Singleton<GlobalBacktraceState>::get() : nullptr;
557
#endif
558
559
2.11k
  if (group) {
560
0
    *group = StackTraceGroup::kActive;
561
0
  }
562
563
35.9k
  for (int i = 0; i < num_frames_; i++) {
564
33.8k
    void* const pc = frames_[i];
565
566
33.8k
    SymbolizeAddress(stack_trace_line_format, pc, &buf, group
567
#ifdef __linux__
568
        , global_backtrace_state
569
#endif
570
33.8k
        );
571
33.8k
  }
572
573
2.11k
  return buf;
574
2.11k
}
575
576
0
string StackTrace::ToLogFormatHexString() const {
577
0
  string buf;
578
0
  for (int i = 0; i < num_frames_; i++) {
579
0
    void* pc = frames_[i];
580
0
    StringAppendF(&buf, "    @ %*p\n", kPrintfPointerFieldWidth, pc);
581
0
  }
582
0
  return buf;
583
0
}
584
585
1
uint64_t StackTrace::HashCode() const {
586
1
  return util_hash::CityHash64(reinterpret_cast<const char*>(frames_),
587
1
                               sizeof(frames_[0]) * num_frames_);
588
1
}
589
590
namespace {
591
#ifdef __linux__
592
int DynamcLibraryListCallback(struct dl_phdr_info *info, size_t size, void *data) {
593
  if (*info->dlpi_name != '\0') {
594
    // We can't use LOG(...) yet because Google Logging might not be initialized.
595
    // It is also important to write the entire line at once so that it is less likely to be
596
    // interleaved with pieces of similar lines from other processes.
597
    std::cerr << StringPrintf(
598
        "Shared library '%s' loaded at address 0x%" PRIx64 "\n", info->dlpi_name, info->dlpi_addr);
599
  }
600
  return 0;
601
}
602
#endif
603
604
0
void PrintLoadedDynamicLibraries() {
605
#ifdef __linux__
606
  // Supported on Linux only.
607
  dl_iterate_phdr(DynamcLibraryListCallback, nullptr);
608
#endif
609
0
}
610
611
20.6k
bool PrintLoadedDynamicLibrariesOnceHelper() {
612
20.6k
  const char* list_dl_env_var = std::getenv("YB_LIST_LOADED_DYNAMIC_LIBS");
613
20.6k
  if (list_dl_env_var != nullptr && *list_dl_env_var != '\0') {
614
0
    PrintLoadedDynamicLibraries();
615
0
  }
616
20.6k
  return true;
617
20.6k
}
618
619
} // anonymous namespace
620
621
73.1k
string SymbolizeAddress(void *pc, const StackTraceLineFormat stack_trace_line_format) {
622
73.1k
  string s;
623
73.1k
  SymbolizeAddress(stack_trace_line_format, pc, &s);
624
73.1k
  return s;
625
73.1k
}
626
627
// ------------------------------------------------------------------------------------------------
628
// Tracing function calls
629
// ------------------------------------------------------------------------------------------------
630
631
namespace {
632
633
// List the load addresses of dynamic libraries once on process startup if required.
634
const bool  __attribute__((unused)) kPrintedLoadedDynamicLibraries =
635
    PrintLoadedDynamicLibrariesOnceHelper();
636
637
}  // namespace
638
639
0
std::string DemangleName(const char* mangled_name) {
640
0
  int demangle_status = 0;
641
0
  char* demangled_name =
642
0
      abi::__cxa_demangle(mangled_name, nullptr /* output_buffer */, nullptr /* length */,
643
0
                          &demangle_status);
644
0
  string ret_val = demangle_status == kDemangleOk ? demangled_name : mangled_name;
645
0
  free(demangled_name);
646
0
  return ret_val;
647
0
}
648
649
2
std::string SourceLocation::ToString() const {
650
2
  return Format("$0:$1", file_name, line_number);
651
2
}
652
653
ScopeLogger::ScopeLogger(const std::string& msg, std::function<void()> on_scope_bounds)
654
0
    : msg_(msg), on_scope_bounds_(std::move(on_scope_bounds)) {
655
0
  LOG(INFO) << ">>> " << msg_;
656
0
  on_scope_bounds_();
657
0
}
658
659
0
ScopeLogger::~ScopeLogger() {
660
0
  on_scope_bounds_();
661
0
  LOG(INFO) << "<<< " << msg_;
662
0
}
663
664
}  // namespace yb