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
Parse(Environment * env)61 void EnabledDebugList::Parse(Environment* env) {
62 std::string cats;
63 credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env);
64 Parse(cats, true);
65 }
66
Parse(const std::string & cats,bool enabled)67 void EnabledDebugList::Parse(const std::string& cats, bool enabled) {
68 std::string debug_categories = cats;
69 while (!debug_categories.empty()) {
70 std::string::size_type comma_pos = debug_categories.find(',');
71 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 DEBUG_CATEGORY_NAMES(V)
81 #undef V
82
83 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 }
88
89 #ifdef __POSIX__
90 #if HAVE_EXECINFO_H
91 class PosixSymbolDebuggingContext final : public NativeSymbolDebuggingContext {
92 public:
PosixSymbolDebuggingContext()93 PosixSymbolDebuggingContext() : pagesize_(getpagesize()) { }
94
LookupSymbol(void * address)95 SymbolInfo LookupSymbol(void* address) override {
96 Dl_info info;
97 const bool have_info = dladdr(address, &info);
98 SymbolInfo ret;
99 if (!have_info)
100 return ret;
101
102 if (info.dli_sname != nullptr) {
103 if (char* demangled =
104 abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr)) {
105 ret.name = demangled;
106 free(demangled);
107 } else {
108 ret.name = info.dli_sname;
109 }
110 }
111
112 if (info.dli_fname != nullptr) {
113 ret.filename = info.dli_fname;
114 }
115
116 return ret;
117 }
118
IsMapped(void * address)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
GetStackTrace(void ** frames,int count)125 int GetStackTrace(void** frames, int count) override {
126 return backtrace(frames, count);
127 }
128
129 private:
130 uintptr_t pagesize_;
131 };
132
133 std::unique_ptr<NativeSymbolDebuggingContext>
New()134 NativeSymbolDebuggingContext::New() {
135 return std::make_unique<PosixSymbolDebuggingContext>();
136 }
137
138 #else // HAVE_EXECINFO_H
139
140 std::unique_ptr<NativeSymbolDebuggingContext>
New()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:
Win32SymbolDebuggingContext()151 Win32SymbolDebuggingContext() {
152 current_process_ = GetCurrentProcess();
153 USE(SymInitialize(current_process_, nullptr, true));
154 }
155
~Win32SymbolDebuggingContext()156 ~Win32SymbolDebuggingContext() override {
157 USE(SymCleanup(current_process_));
158 }
159
160 using NameAndDisplacement = std::pair<std::string, DWORD64>;
WrappedSymFromAddr(DWORD64 dwAddress) const161 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
WrappedGetLine(DWORD64 dwAddress) const191 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`
WrappedUnDecorateSymbolName(const char * name) const227 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
LookupSymbol(void * address)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
IsMapped(void * address)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
GetStackTrace(void ** frames,int count)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>
New()282 NativeSymbolDebuggingContext::New() {
283 return std::unique_ptr<NativeSymbolDebuggingContext>(
284 new Win32SymbolDebuggingContext());
285 }
286
287 #endif // __POSIX__
288
Display() const289 std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const {
290 std::ostringstream oss;
291 oss << name;
292 if (dis != 0) {
293 oss << "+" << dis;
294 }
295 if (!filename.empty()) {
296 oss << " [" << filename << ']';
297 }
298 if (line != 0) {
299 oss << ":L" << line;
300 }
301 return oss.str();
302 }
303
DumpBacktrace(FILE * fp)304 void DumpBacktrace(FILE* fp) {
305 auto sym_ctx = NativeSymbolDebuggingContext::New();
306 void* frames[256];
307 const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
308 for (int i = 1; i < size; i += 1) {
309 void* frame = frames[i];
310 NativeSymbolDebuggingContext::SymbolInfo s = sym_ctx->LookupSymbol(frame);
311 fprintf(fp, "%2d: %p %s\n", i, frame, s.Display().c_str());
312 }
313 }
314
CheckedUvLoopClose(uv_loop_t * loop)315 void CheckedUvLoopClose(uv_loop_t* loop) {
316 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
PrintLibuvHandleInformation(uv_loop_t * loop,FILE * stream)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
GetLoadedLibraries()372 std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() {
373 std::vector<std::string> list;
374 #if defined(__linux__) || defined(__FreeBSD__) || \
375 defined(__OpenBSD__) || defined(__DragonFly__)
376 dl_iterate_phdr(
377 [](struct dl_phdr_info* info, size_t size, void* data) {
378 auto list = static_cast<std::vector<std::string>*>(data);
379 if (*info->dlpi_name != '\0') {
380 list->emplace_back(info->dlpi_name);
381 }
382 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 return list;
472 }
473
FWrite(FILE * file,const std::string & str)474 void FWrite(FILE* file, const std::string& str) {
475 auto simple_fwrite = [&]() {
476 // The return value is ignored because there's no good way to handle it.
477 fwrite(str.data(), str.size(), 1, file);
478 };
479
480 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 simple_fwrite();
510 }
511
512 } // namespace node
513
__DumpBacktrace(FILE * fp)514 extern "C" void __DumpBacktrace(FILE* fp) {
515 node::DumpBacktrace(fp);
516 }
517