GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: debug_utils.cc Lines: 64 102 62.7 %
Date: 2022-05-22 04:15:48 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
5
#ifdef __POSIX__
6
#if defined(__linux__)
7
#include <features.h>
8
#endif
9
10
#ifdef __ANDROID__
11
#include <android/log.h>
12
#endif
13
14
#if defined(__linux__) && !defined(__GLIBC__) || \
15
    defined(__UCLIBC__) || \
16
    defined(_AIX)
17
#define HAVE_EXECINFO_H 0
18
#else
19
#define HAVE_EXECINFO_H 1
20
#endif
21
22
#if HAVE_EXECINFO_H
23
#include <cxxabi.h>
24
#include <dlfcn.h>
25
#include <execinfo.h>
26
#include <unistd.h>
27
#include <sys/mman.h>
28
#include <cstdio>
29
#endif
30
31
#endif  // __POSIX__
32
33
#if defined(__linux__) || defined(__sun) || \
34
    defined(__FreeBSD__) || defined(__OpenBSD__) || \
35
    defined(__DragonFly__)
36
#include <link.h>
37
#endif
38
39
#ifdef __APPLE__
40
#include <mach-o/dyld.h>  // _dyld_get_image_name()
41
#endif                    // __APPLE__
42
43
#ifdef _AIX
44
#include <sys/ldr.h>  // ld_info structure
45
#endif                // _AIX
46
47
#ifdef _WIN32
48
#include <Lm.h>
49
#include <Windows.h>
50
#include <dbghelp.h>
51
#include <process.h>
52
#include <psapi.h>
53
#include <tchar.h>
54
#endif  // _WIN32
55
56
namespace node {
57
namespace per_process {
58
EnabledDebugList enabled_debug_list;
59
}
60
61
11270
void EnabledDebugList::Parse(Environment* env) {
62
22540
  std::string cats;
63
11270
  credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env);
64
11270
  Parse(cats, true);
65
11270
}
66
67
11270
void EnabledDebugList::Parse(const std::string& cats, bool enabled) {
68
22540
  std::string debug_categories = cats;
69
11270
  while (!debug_categories.empty()) {
70
114
    std::string::size_type comma_pos = debug_categories.find(',');
71
114
    std::string wanted = ToLower(debug_categories.substr(0, comma_pos));
72
73
#define V(name)                                                                \
74
  {                                                                            \
75
    static const std::string available_category = ToLower(#name);              \
76
    if (available_category.find(wanted) != std::string::npos)                  \
77
      set_enabled(DebugCategory::name, enabled);                               \
78
  }
79
80



































































































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

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