GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: debug_utils.cc Lines: 64 102 62.7 %
Date: 2022-11-09 04:21:35 Branches: 289 430 67.2 %

Line Branch Exec Source
1
#include "debug_utils-inl.h"  // NOLINT(build/include)
2
#include "env-inl.h"
3
#include "node_internals.h"
4
#include "util.h"
5
6
#ifdef __POSIX__
7
#if defined(__linux__)
8
#include <features.h>
9
#endif
10
11
#ifdef __ANDROID__
12
#include <android/log.h>
13
#endif
14
15
#if defined(__linux__) && !defined(__GLIBC__) || \
16
    defined(__UCLIBC__) || \
17
    defined(_AIX)
18
#define HAVE_EXECINFO_H 0
19
#else
20
#define HAVE_EXECINFO_H 1
21
#endif
22
23
#if HAVE_EXECINFO_H
24
#include <cxxabi.h>
25
#include <dlfcn.h>
26
#include <execinfo.h>
27
#include <unistd.h>
28
#include <sys/mman.h>
29
#include <cstdio>
30
#endif
31
32
#endif  // __POSIX__
33
34
#if defined(__linux__) || defined(__sun) || \
35
    defined(__FreeBSD__) || defined(__OpenBSD__) || \
36
    defined(__DragonFly__)
37
#include <link.h>
38
#endif
39
40
#ifdef __APPLE__
41
#include <mach-o/dyld.h>  // _dyld_get_image_name()
42
#endif                    // __APPLE__
43
44
#ifdef _AIX
45
#include <sys/ldr.h>  // ld_info structure
46
#endif                // _AIX
47
48
#ifdef _WIN32
49
#include <Lm.h>
50
#include <Windows.h>
51
#include <dbghelp.h>
52
#include <process.h>
53
#include <psapi.h>
54
#include <tchar.h>
55
#endif  // _WIN32
56
57
namespace node {
58
namespace per_process {
59
EnabledDebugList enabled_debug_list;
60
}
61
62
12056
void EnabledDebugList::Parse(std::shared_ptr<KVStore> env_vars,
63
                             v8::Isolate* isolate) {
64
24112
  std::string cats;
65
12056
  credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env_vars, isolate);
66
12056
  Parse(cats);
67
12056
}
68
69
12056
void EnabledDebugList::Parse(const std::string& cats) {
70
24112
  std::string debug_categories = cats;
71
12056
  while (!debug_categories.empty()) {
72
117
    std::string::size_type comma_pos = debug_categories.find(',');
73
117
    std::string wanted = ToLower(debug_categories.substr(0, comma_pos));
74
75
#define V(name)                                                                \
76
  {                                                                            \
77
    static const std::string available_category = ToLower(#name);              \
78
    if (available_category.find(wanted) != std::string::npos)                  \
79
      set_enabled(DebugCategory::name);                                        \
80
  }
81
82



































































































117
    DEBUG_CATEGORY_NAMES(V)
83
#undef V
84
85
117
    if (comma_pos == std::string::npos) break;
86
    // Use everything after the `,` as the list for the next iteration.
87
    debug_categories = debug_categories.substr(comma_pos + 1);
88
  }
89
12056
}
90
91
#ifdef __POSIX__
92
#if HAVE_EXECINFO_H
93
class PosixSymbolDebuggingContext final : public NativeSymbolDebuggingContext {
94
 public:
95
39
  PosixSymbolDebuggingContext() : pagesize_(getpagesize()) { }
96
97
405
  SymbolInfo LookupSymbol(void* address) override {
98
    Dl_info info;
99
405
    const bool have_info = dladdr(address, &info);
100
405
    SymbolInfo ret;
101
405
    if (!have_info)
102
      return ret;
103
104
405
    if (info.dli_sname != nullptr) {
105
234
      if (char* demangled =
106
234
              abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr)) {
107
211
        ret.name = demangled;
108
211
        free(demangled);
109
      } else {
110
23
        ret.name = info.dli_sname;
111
      }
112
    }
113
114
405
    if (info.dli_fname != nullptr) {
115
405
      ret.filename = info.dli_fname;
116
    }
117
118
405
    return ret;
119
  }
120
121
  bool IsMapped(void* address) override {
122
    void* page_aligned = reinterpret_cast<void*>(
123
        reinterpret_cast<uintptr_t>(address) & ~(pagesize_ - 1));
124
    return msync(page_aligned, pagesize_, MS_ASYNC) == 0;
125
  }
126
127
39
  int GetStackTrace(void** frames, int count) override {
128
39
    return backtrace(frames, count);
129
  }
130
131
 private:
132
  uintptr_t pagesize_;
133
};
134
135
std::unique_ptr<NativeSymbolDebuggingContext>
136
39
NativeSymbolDebuggingContext::New() {
137
39
  return std::make_unique<PosixSymbolDebuggingContext>();
138
}
139
140
#else  // HAVE_EXECINFO_H
141
142
std::unique_ptr<NativeSymbolDebuggingContext>
143
NativeSymbolDebuggingContext::New() {
144
  return std::make_unique<NativeSymbolDebuggingContext>();
145
}
146
147
#endif  // HAVE_EXECINFO_H
148
149
#else  // __POSIX__
150
151
class Win32SymbolDebuggingContext final : public NativeSymbolDebuggingContext {
152
 public:
153
  Win32SymbolDebuggingContext() {
154
    current_process_ = GetCurrentProcess();
155
    USE(SymInitialize(current_process_, nullptr, true));
156
  }
157
158
  ~Win32SymbolDebuggingContext() override {
159
    USE(SymCleanup(current_process_));
160
  }
161
162
  using NameAndDisplacement = std::pair<std::string, DWORD64>;
163
  NameAndDisplacement WrappedSymFromAddr(DWORD64 dwAddress) const {
164
    // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
165
    // Patches:
166
    // Use `fprintf(stderr, ` instead of `printf`
167
    // `sym.filename = pSymbol->Name` on success
168
    // `current_process_` instead of `hProcess.
169
    DWORD64 dwDisplacement = 0;
170
    // Patch: made into arg - DWORD64  dwAddress = SOME_ADDRESS;
171
172
    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
173
    const auto pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
174
175
    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
176
    pSymbol->MaxNameLen = MAX_SYM_NAME;
177
178
    if (SymFromAddr(current_process_, dwAddress, &dwDisplacement, pSymbol)) {
179
      // SymFromAddr returned success
180
      return NameAndDisplacement(pSymbol->Name, dwDisplacement);
181
    } else {
182
      // SymFromAddr failed
183
      const DWORD error = GetLastError();  // "eat" the error anyway
184
#ifdef DEBUG
185
      fprintf(stderr, "SymFromAddr returned error : %lu\n", error);
186
#endif
187
    }
188
    // End MSDN code
189
190
    return NameAndDisplacement();
191
  }
192
193
  SymbolInfo WrappedGetLine(DWORD64 dwAddress) const {
194
    SymbolInfo sym{};
195
196
    // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-symbol-information-by-address
197
    // Patches:
198
    // Use `fprintf(stderr, ` instead of `printf`.
199
    // Assign values to `sym` on success.
200
    // `current_process_` instead of `hProcess.
201
202
    // Patch: made into arg - DWORD64  dwAddress;
203
    DWORD dwDisplacement;
204
    IMAGEHLP_LINE64 line;
205
206
    SymSetOptions(SYMOPT_LOAD_LINES);
207
208
    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
209
    // Patch: made into arg - dwAddress = 0x1000000;
210
211
    if (SymGetLineFromAddr64(current_process_, dwAddress,
212
                             &dwDisplacement, &line)) {
213
      // SymGetLineFromAddr64 returned success
214
      sym.filename = line.FileName;
215
      sym.line = line.LineNumber;
216
    } else {
217
      // SymGetLineFromAddr64 failed
218
      const DWORD error = GetLastError();  // "eat" the error anyway
219
#ifdef DEBUG
220
      fprintf(stderr, "SymGetLineFromAddr64 returned error : %lu\n", error);
221
#endif
222
    }
223
    // End MSDN code
224
225
    return sym;
226
  }
227
228
  // Fills the SymbolInfo::name of the io/out argument `sym`
229
  std::string WrappedUnDecorateSymbolName(const char* name) const {
230
    // Refs: https://docs.microsoft.com/en-us/windows/desktop/Debug/retrieving-undecorated-symbol-names
231
    // Patches:
232
    // Use `fprintf(stderr, ` instead of `printf`.
233
    // return `szUndName` instead of `printf` on success
234
    char szUndName[MAX_SYM_NAME];
235
    if (UnDecorateSymbolName(name, szUndName, sizeof(szUndName),
236
                             UNDNAME_COMPLETE)) {
237
      // UnDecorateSymbolName returned success
238
      return szUndName;
239
    } else {
240
      // UnDecorateSymbolName failed
241
      const DWORD error = GetLastError();  // "eat" the error anyway
242
#ifdef DEBUG
243
      fprintf(stderr, "UnDecorateSymbolName returned error %lu\n", error);
244
#endif
245
    }
246
    return nullptr;
247
  }
248
249
  SymbolInfo LookupSymbol(void* address) override {
250
    const DWORD64 dw_address = reinterpret_cast<DWORD64>(address);
251
    SymbolInfo ret = WrappedGetLine(dw_address);
252
    std::tie(ret.name, ret.dis) = WrappedSymFromAddr(dw_address);
253
    if (!ret.name.empty()) {
254
      ret.name = WrappedUnDecorateSymbolName(ret.name.c_str());
255
    }
256
    return ret;
257
  }
258
259
  bool IsMapped(void* address) override {
260
    MEMORY_BASIC_INFORMATION info;
261
262
    if (VirtualQuery(address, &info, sizeof(info)) != sizeof(info))
263
      return false;
264
265
    return info.State == MEM_COMMIT && info.Protect != 0;
266
  }
267
268
  int GetStackTrace(void** frames, int count) override {
269
    return CaptureStackBackTrace(0, count, frames, nullptr);
270
  }
271
272
  Win32SymbolDebuggingContext(const Win32SymbolDebuggingContext&) = delete;
273
  Win32SymbolDebuggingContext(Win32SymbolDebuggingContext&&) = delete;
274
  Win32SymbolDebuggingContext operator=(const Win32SymbolDebuggingContext&)
275
    = delete;
276
  Win32SymbolDebuggingContext operator=(Win32SymbolDebuggingContext&&)
277
    = delete;
278
279
 private:
280
  HANDLE current_process_;
281
};
282
283
std::unique_ptr<NativeSymbolDebuggingContext>
284
NativeSymbolDebuggingContext::New() {
285
  return std::unique_ptr<NativeSymbolDebuggingContext>(
286
      new Win32SymbolDebuggingContext());
287
}
288
289
#endif  // __POSIX__
290
291
405
std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const {
292
810
  std::ostringstream oss;
293
405
  oss << name;
294
405
  if (dis != 0) {
295
    oss << "+" << dis;
296
  }
297
405
  if (!filename.empty()) {
298
405
    oss << " [" << filename << ']';
299
  }
300
405
  if (line != 0) {
301
    oss << ":L" << line;
302
  }
303
405
  return oss.str();
304
}
305
306
4
void DumpBacktrace(FILE* fp) {
307
8
  auto sym_ctx = NativeSymbolDebuggingContext::New();
308
  void* frames[256];
309
4
  const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
310
40
  for (int i = 1; i < size; i += 1) {
311
36
    void* frame = frames[i];
312
36
    NativeSymbolDebuggingContext::SymbolInfo s = sym_ctx->LookupSymbol(frame);
313
36
    fprintf(fp, "%2d: %p %s\n", i, frame, s.Display().c_str());
314
  }
315
4
}
316
317
13099
void CheckedUvLoopClose(uv_loop_t* loop) {
318
13099
  if (uv_loop_close(loop) == 0) return;
319
320
  PrintLibuvHandleInformation(loop, stderr);
321
322
  fflush(stderr);
323
  // Finally, abort.
324
  CHECK(0 && "uv_loop_close() while having open handles");
325
}
326
327
void PrintLibuvHandleInformation(uv_loop_t* loop, FILE* stream) {
328
  struct Info {
329
    std::unique_ptr<NativeSymbolDebuggingContext> ctx;
330
    FILE* stream;
331
    size_t num_handles;
332
  };
333
334
  Info info { NativeSymbolDebuggingContext::New(), stream, 0 };
335
336
  fprintf(stream, "uv loop at [%p] has open handles:\n", loop);
337
338
  uv_walk(loop, [](uv_handle_t* handle, void* arg) {
339
    Info* info = static_cast<Info*>(arg);
340
    NativeSymbolDebuggingContext* sym_ctx = info->ctx.get();
341
    FILE* stream = info->stream;
342
    info->num_handles++;
343
344
    fprintf(stream, "[%p] %s%s\n", handle, uv_handle_type_name(handle->type),
345
            uv_is_active(handle) ? " (active)" : "");
346
347
    void* close_cb = reinterpret_cast<void*>(handle->close_cb);
348
    fprintf(stream, "\tClose callback: %p %s\n",
349
        close_cb, sym_ctx->LookupSymbol(close_cb).Display().c_str());
350
351
    fprintf(stream, "\tData: %p %s\n",
352
        handle->data, sym_ctx->LookupSymbol(handle->data).Display().c_str());
353
354
    // We are also interested in the first field of what `handle->data`
355
    // points to, because for C++ code that is usually the virtual table pointer
356
    // and gives us information about the exact kind of object we're looking at.
357
    void* first_field = nullptr;
358
    // `handle->data` might be any value, including `nullptr`, or something
359
    // cast from a completely different type; therefore, check that it’s
360
    // dereferenceable first.
361
    if (sym_ctx->IsMapped(handle->data))
362
      first_field = *reinterpret_cast<void**>(handle->data);
363
364
    if (first_field != nullptr) {
365
      fprintf(stream, "\t(First field): %p %s\n",
366
          first_field, sym_ctx->LookupSymbol(first_field).Display().c_str());
367
    }
368
  }, &info);
369
370
  fprintf(stream, "uv loop at [%p] has %zu open handles in total\n",
371
          loop, info.num_handles);
372
}
373
374
35
std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() {
375
35
  std::vector<std::string> list;
376
#if defined(__linux__) || defined(__FreeBSD__) || \
377
    defined(__OpenBSD__) || defined(__DragonFly__)
378
35
  dl_iterate_phdr(
379
325
      [](struct dl_phdr_info* info, size_t size, void* data) {
380
325
        auto list = static_cast<std::vector<std::string>*>(data);
381
325
        if (*info->dlpi_name != '\0') {
382
290
          list->emplace_back(info->dlpi_name);
383
        }
384
325
        return 0;
385
      },
386
      &list);
387
#elif __APPLE__
388
  uint32_t i = 0;
389
  for (const char* name = _dyld_get_image_name(i); name != nullptr;
390
       name = _dyld_get_image_name(++i)) {
391
    list.emplace_back(name);
392
  }
393
394
#elif _AIX
395
  // We can't tell in advance how large the buffer needs to be.
396
  // Retry until we reach too large a size (1Mb).
397
  const unsigned int kBufferGrowStep = 4096;
398
  MallocedBuffer<char> buffer(kBufferGrowStep);
399
  int rc = -1;
400
  do {
401
    rc = loadquery(L_GETINFO, buffer.data, buffer.size);
402
    if (rc == 0) break;
403
    buffer = MallocedBuffer<char>(buffer.size + kBufferGrowStep);
404
  } while (buffer.size < 1024 * 1024);
405
406
  if (rc == 0) {
407
    char* buf = buffer.data;
408
    ld_info* cur_info = nullptr;
409
    do {
410
      std::ostringstream str;
411
      cur_info = reinterpret_cast<ld_info*>(buf);
412
      char* member_name = cur_info->ldinfo_filename +
413
          strlen(cur_info->ldinfo_filename) + 1;
414
      if (*member_name != '\0') {
415
        str << cur_info->ldinfo_filename << "(" << member_name << ")";
416
        list.emplace_back(str.str());
417
        str.str("");
418
      } else {
419
        list.emplace_back(cur_info->ldinfo_filename);
420
      }
421
      buf += cur_info->ldinfo_next;
422
    } while (cur_info->ldinfo_next != 0);
423
  }
424
#elif __sun
425
  Link_map* p;
426
427
  if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &p) != -1) {
428
    for (Link_map* l = p; l != nullptr; l = l->l_next) {
429
      list.emplace_back(l->l_name);
430
    }
431
  }
432
433
#elif _WIN32
434
  // Windows implementation - get a handle to the process.
435
  HANDLE process_handle = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
436
                                      FALSE, GetCurrentProcessId());
437
  if (process_handle == nullptr) {
438
    // Cannot proceed, return an empty list.
439
    return list;
440
  }
441
  // Get a list of all the modules in this process
442
  DWORD size_1 = 0;
443
  DWORD size_2 = 0;
444
  // First call to get the size of module array needed
445
  if (EnumProcessModules(process_handle, nullptr, 0, &size_1)) {
446
    MallocedBuffer<HMODULE> modules(size_1);
447
448
    // Second call to populate the module array
449
    if (EnumProcessModules(process_handle, modules.data, size_1, &size_2)) {
450
      for (DWORD i = 0;
451
           i < (size_1 / sizeof(HMODULE)) && i < (size_2 / sizeof(HMODULE));
452
           i++) {
453
        WCHAR module_name[MAX_PATH];
454
        // Obtain and report the full pathname for each module
455
        if (GetModuleFileNameExW(process_handle,
456
                                 modules.data[i],
457
                                 module_name,
458
                                 arraysize(module_name) / sizeof(WCHAR))) {
459
          DWORD size = WideCharToMultiByte(
460
              CP_UTF8, 0, module_name, -1, nullptr, 0, nullptr, nullptr);
461
          char* str = new char[size];
462
          WideCharToMultiByte(
463
              CP_UTF8, 0, module_name, -1, str, size, nullptr, nullptr);
464
          list.emplace_back(str);
465
        }
466
      }
467
    }
468
  }
469
470
  // Release the handle to the process.
471
  CloseHandle(process_handle);
472
#endif
473
35
  return list;
474
}
475
476
2655
void FWrite(FILE* file, const std::string& str) {
477
2655
  auto simple_fwrite = [&]() {
478
    // The return value is ignored because there's no good way to handle it.
479
2655
    fwrite(str.data(), str.size(), 1, file);
480
5310
  };
481
482

2655
  if (file != stderr && file != stdout) {
483
    simple_fwrite();
484
    return;
485
  }
486
#ifdef _WIN32
487
  HANDLE handle =
488
      GetStdHandle(file == stdout ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE);
489
490
  // Check if stderr is something other than a tty/console
491
  if (handle == INVALID_HANDLE_VALUE || handle == nullptr ||
492
      uv_guess_handle(_fileno(file)) != UV_TTY) {
493
    simple_fwrite();
494
    return;
495
  }
496
497
  // Get required wide buffer size
498
  int n = MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), nullptr, 0);
499
500
  std::vector<wchar_t> wbuf(n);
501
  MultiByteToWideChar(CP_UTF8, 0, str.data(), str.size(), wbuf.data(), n);
502
503
  WriteConsoleW(handle, wbuf.data(), n, nullptr, nullptr);
504
  return;
505
#elif defined(__ANDROID__)
506
  if (file == stderr) {
507
    __android_log_print(ANDROID_LOG_ERROR, "nodejs", "%s", str.data());
508
    return;
509
  }
510
#endif
511
2655
  simple_fwrite();
512
}
513
514
}  // namespace node
515
516
extern "C" void __DumpBacktrace(FILE* fp) {
517
  node::DumpBacktrace(fp);
518
}