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 |
|
266 |
void EnabledDebugList::Parse(Environment* env) { |
62 |
|
532 |
std::string cats; |
63 |
|
266 |
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env); |
64 |
|
266 |
Parse(cats, true); |
65 |
|
266 |
} |
66 |
|
|
|
67 |
|
266 |
void EnabledDebugList::Parse(const std::string& cats, bool enabled) { |
68 |
|
532 |
std::string debug_categories = cats; |
69 |
✗✓ |
266 |
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 |
|
266 |
} |
88 |
|
|
|
89 |
|
|
#ifdef __POSIX__ |
90 |
|
|
#if HAVE_EXECINFO_H |
91 |
|
|
class PosixSymbolDebuggingContext final : public NativeSymbolDebuggingContext { |
92 |
|
|
public: |
93 |
|
|
PosixSymbolDebuggingContext() : pagesize_(getpagesize()) { } |
94 |
|
|
|
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 |
|
|
|
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 |
|
|
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> |
134 |
|
|
NativeSymbolDebuggingContext::New() { |
135 |
|
|
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 |
|
|
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 |
|
|
|
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 |
|
|
|
315 |
|
244 |
void CheckedUvLoopClose(uv_loop_t* loop) { |
316 |
✓✗ |
488 |
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 |
|
|
// dereferencable 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 |
|
|
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 |
|
|
|
474 |
|
2 |
void FWrite(FILE* file, const std::string& str) { |
475 |
|
2 |
auto simple_fwrite = [&]() { |
476 |
|
|
// The return value is ignored because there's no good way to handle it. |
477 |
|
2 |
fwrite(str.data(), str.size(), 1, file); |
478 |
|
4 |
}; |
479 |
|
|
|
480 |
✗✓✗✗
|
2 |
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 |
|
2 |
simple_fwrite(); |
510 |
|
|
} |
511 |
|
|
|
512 |
|
|
} // namespace node |
513 |
|
|
|
514 |
|
|
extern "C" void __DumpBacktrace(FILE* fp) { |
515 |
|
|
node::DumpBacktrace(fp); |
516 |
✓✗✓✗
|
366 |
} |