GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 |
11510 |
void EnabledDebugList::Parse(std::shared_ptr<KVStore> env_vars, |
|
63 |
v8::Isolate* isolate) { |
||
64 |
23020 |
std::string cats; |
|
65 |
11510 |
credentials::SafeGetenv("NODE_DEBUG_NATIVE", &cats, env_vars, isolate); |
|
66 |
11510 |
Parse(cats, true); |
|
67 |
11510 |
} |
|
68 |
|||
69 |
11510 |
void EnabledDebugList::Parse(const std::string& cats, bool enabled) { |
|
70 |
23020 |
std::string debug_categories = cats; |
|
71 |
✓✓ | 11510 |
while (!debug_categories.empty()) { |
72 |
114 |
std::string::size_type comma_pos = debug_categories.find(','); |
|
73 |
114 |
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, enabled); \ |
||
80 |
} |
||
81 |
|||
82 |
✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✓✓ ✓✓✓✗ ✓✓✓✓ ✓✗✓✓ ✓✓✓✗ ✓✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✓✓✓✓ ✓✗✗✓ ✓✓✓✗ ✗✓✓✓ ✓✗✓✓ ✓✓✓✗ ✗✓✓✓ ✓✗✗✓ ✓✓✓✗ ✓✓✓✓ ✓✗✗✓ |
114 |
DEBUG_CATEGORY_NAMES(V) |
83 |
#undef V |
||
84 |
|||
85 |
✓✗ | 114 |
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 |
11510 |
} |
|
90 |
|||
91 |
#ifdef __POSIX__ |
||
92 |
#if HAVE_EXECINFO_H |
||
93 |
class PosixSymbolDebuggingContext final : public NativeSymbolDebuggingContext { |
||
94 |
public: |
||
95 |
29 |
PosixSymbolDebuggingContext() : pagesize_(getpagesize()) { } |
|
96 |
|||
97 |
226 |
SymbolInfo LookupSymbol(void* address) override { |
|
98 |
Dl_info info; |
||
99 |
226 |
const bool have_info = dladdr(address, &info); |
|
100 |
226 |
SymbolInfo ret; |
|
101 |
✗✓ | 226 |
if (!have_info) |
102 |
return ret; |
||
103 |
|||
104 |
✓✓ | 226 |
if (info.dli_sname != nullptr) { |
105 |
✓✓ | 110 |
if (char* demangled = |
106 |
110 |
abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, nullptr)) { |
|
107 |
105 |
ret.name = demangled; |
|
108 |
105 |
free(demangled); |
|
109 |
} else { |
||
110 |
5 |
ret.name = info.dli_sname; |
|
111 |
} |
||
112 |
} |
||
113 |
|||
114 |
✓✗ | 226 |
if (info.dli_fname != nullptr) { |
115 |
226 |
ret.filename = info.dli_fname; |
|
116 |
} |
||
117 |
|||
118 |
226 |
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 |
29 |
int GetStackTrace(void** frames, int count) override { |
|
128 |
29 |
return backtrace(frames, count); |
|
129 |
} |
||
130 |
|||
131 |
private: |
||
132 |
uintptr_t pagesize_; |
||
133 |
}; |
||
134 |
|||
135 |
std::unique_ptr<NativeSymbolDebuggingContext> |
||
136 |
29 |
NativeSymbolDebuggingContext::New() { |
|
137 |
29 |
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 |
226 |
std::string NativeSymbolDebuggingContext::SymbolInfo::Display() const { |
|
292 |
452 |
std::ostringstream oss; |
|
293 |
226 |
oss << name; |
|
294 |
✗✓ | 226 |
if (dis != 0) { |
295 |
oss << "+" << dis; |
||
296 |
} |
||
297 |
✓✗ | 226 |
if (!filename.empty()) { |
298 |
226 |
oss << " [" << filename << ']'; |
|
299 |
} |
||
300 |
✗✓ | 226 |
if (line != 0) { |
301 |
oss << ":L" << line; |
||
302 |
} |
||
303 |
226 |
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 |
✓✓ | 39 |
for (int i = 1; i < size; i += 1) { |
311 |
35 |
void* frame = frames[i]; |
|
312 |
35 |
NativeSymbolDebuggingContext::SymbolInfo s = sym_ctx->LookupSymbol(frame); |
|
313 |
35 |
fprintf(fp, "%2d: %p %s\n", i, frame, s.Display().c_str()); |
|
314 |
} |
||
315 |
4 |
} |
|
316 |
|||
317 |
12429 |
void CheckedUvLoopClose(uv_loop_t* loop) { |
|
318 |
✓✗ | 12429 |
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 |
25 |
std::vector<std::string> NativeSymbolDebuggingContext::GetLoadedLibraries() { |
|
375 |
25 |
std::vector<std::string> list; |
|
376 |
#if defined(__linux__) || defined(__FreeBSD__) || \ |
||
377 |
defined(__OpenBSD__) || defined(__DragonFly__) |
||
378 |
25 |
dl_iterate_phdr( |
|
379 |
229 |
[](struct dl_phdr_info* info, size_t size, void* data) { |
|
380 |
229 |
auto list = static_cast<std::vector<std::string>*>(data); |
|
381 |
✓✓ | 229 |
if (*info->dlpi_name != '\0') { |
382 |
179 |
list->emplace_back(info->dlpi_name); |
|
383 |
} |
||
384 |
229 |
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 |
25 |
return list; |
|
474 |
} |
||
475 |
|||
476 |
2539 |
void FWrite(FILE* file, const std::string& str) { |
|
477 |
2539 |
auto simple_fwrite = [&]() { |
|
478 |
// The return value is ignored because there's no good way to handle it. |
||
479 |
2539 |
fwrite(str.data(), str.size(), 1, file); |
|
480 |
5078 |
}; |
|
481 |
|||
482 |
✗✓✗✗ |
2539 |
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 |
2539 |
simple_fwrite(); |
|
512 |
} |
||
513 |
|||
514 |
} // namespace node |
||
515 |
|||
516 |
extern "C" void __DumpBacktrace(FILE* fp) { |
||
517 |
node::DumpBacktrace(fp); |
||
518 |
} |
Generated by: GCOVR (Version 4.2) |