• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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