1 #include "env-inl.h"
2 #include "json_utils.h"
3 #include "node_report.h"
4 #include "debug_utils-inl.h"
5 #include "diagnosticfilename-inl.h"
6 #include "node_internals.h"
7 #include "node_metadata.h"
8 #include "node_mutex.h"
9 #include "node_worker.h"
10 #include "util.h"
11
12 #ifdef _WIN32
13 #include <Windows.h>
14 #else // !_WIN32
15 #include <sys/resource.h>
16 #include <cxxabi.h>
17 #include <dlfcn.h>
18 #endif
19
20 #include <iostream>
21 #include <cstring>
22 #include <ctime>
23 #include <cwctype>
24 #include <fstream>
25
26 constexpr int NODE_REPORT_VERSION = 2;
27 constexpr int NANOS_PER_SEC = 1000 * 1000 * 1000;
28 constexpr double SEC_PER_MICROS = 1e-6;
29
30 namespace report {
31 using node::arraysize;
32 using node::ConditionVariable;
33 using node::DiagnosticFilename;
34 using node::Environment;
35 using node::JSONWriter;
36 using node::Mutex;
37 using node::NativeSymbolDebuggingContext;
38 using node::TIME_TYPE;
39 using node::worker::Worker;
40 using v8::Array;
41 using v8::Context;
42 using v8::HeapSpaceStatistics;
43 using v8::HeapStatistics;
44 using v8::Isolate;
45 using v8::Local;
46 using v8::Number;
47 using v8::Object;
48 using v8::StackTrace;
49 using v8::String;
50 using v8::TryCatch;
51 using v8::Value;
52 using v8::V8;
53
54 namespace per_process = node::per_process;
55
56 // Internal/static function declarations
57 static void WriteNodeReport(Isolate* isolate,
58 Environment* env,
59 const char* message,
60 const char* trigger,
61 const std::string& filename,
62 std::ostream& out,
63 Local<Object> error,
64 bool compact);
65 static void PrintVersionInformation(JSONWriter* writer);
66 static void PrintJavaScriptErrorStack(JSONWriter* writer,
67 Isolate* isolate,
68 Local<Object> error,
69 const char* trigger);
70 static void PrintJavaScriptErrorProperties(JSONWriter* writer,
71 Isolate* isolate,
72 Local<Object> error);
73 static void PrintNativeStack(JSONWriter* writer);
74 static void PrintResourceUsage(JSONWriter* writer);
75 static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate);
76 static void PrintSystemInformation(JSONWriter* writer);
77 static void PrintLoadedLibraries(JSONWriter* writer);
78 static void PrintComponentVersions(JSONWriter* writer);
79 static void PrintRelease(JSONWriter* writer);
80 static void PrintCpuInfo(JSONWriter* writer);
81 static void PrintNetworkInterfaceInfo(JSONWriter* writer);
82
83 // External function to trigger a report, writing to file.
TriggerNodeReport(Isolate * isolate,Environment * env,const char * message,const char * trigger,const std::string & name,Local<Object> error)84 std::string TriggerNodeReport(Isolate* isolate,
85 Environment* env,
86 const char* message,
87 const char* trigger,
88 const std::string& name,
89 Local<Object> error) {
90 std::string filename;
91
92 // Determine the required report filename. In order of priority:
93 // 1) supplied on API 2) configured on startup 3) default generated
94 if (!name.empty()) {
95 // Filename was specified as API parameter.
96 filename = name;
97 } else {
98 std::string report_filename;
99 {
100 Mutex::ScopedLock lock(per_process::cli_options_mutex);
101 report_filename = per_process::cli_options->report_filename;
102 }
103 if (report_filename.length() > 0) {
104 // File name was supplied via start-up option.
105 filename = report_filename;
106 } else {
107 filename = *DiagnosticFilename(env != nullptr ? env->thread_id() : 0,
108 "report", "json");
109 }
110 }
111
112 // Open the report file stream for writing. Supports stdout/err,
113 // user-specified or (default) generated name
114 std::ofstream outfile;
115 std::ostream* outstream;
116 if (filename == "stdout") {
117 outstream = &std::cout;
118 } else if (filename == "stderr") {
119 outstream = &std::cerr;
120 } else {
121 std::string report_directory;
122 {
123 Mutex::ScopedLock lock(per_process::cli_options_mutex);
124 report_directory = per_process::cli_options->report_directory;
125 }
126 // Regular file. Append filename to directory path if one was specified
127 if (report_directory.length() > 0) {
128 std::string pathname = report_directory;
129 pathname += node::kPathSeparator;
130 pathname += filename;
131 outfile.open(pathname, std::ios::out | std::ios::binary);
132 } else {
133 outfile.open(filename, std::ios::out | std::ios::binary);
134 }
135 // Check for errors on the file open
136 if (!outfile.is_open()) {
137 std::cerr << "\nFailed to open Node.js report file: " << filename;
138
139 if (report_directory.length() > 0)
140 std::cerr << " directory: " << report_directory;
141
142 std::cerr << " (errno: " << errno << ")" << std::endl;
143 return "";
144 }
145 outstream = &outfile;
146 std::cerr << "\nWriting Node.js report to file: " << filename;
147 }
148
149 bool compact;
150 {
151 Mutex::ScopedLock lock(per_process::cli_options_mutex);
152 compact = per_process::cli_options->report_compact;
153 }
154 WriteNodeReport(isolate, env, message, trigger, filename, *outstream,
155 error, compact);
156
157 // Do not close stdout/stderr, only close files we opened.
158 if (outfile.is_open()) {
159 outfile.close();
160 }
161
162 // Do not mix JSON and free-form text on stderr.
163 if (filename != "stderr") {
164 std::cerr << "\nNode.js report completed" << std::endl;
165 }
166 return filename;
167 }
168
169 // External function to trigger a report, writing to a supplied stream.
GetNodeReport(Isolate * isolate,Environment * env,const char * message,const char * trigger,Local<Object> error,std::ostream & out)170 void GetNodeReport(Isolate* isolate,
171 Environment* env,
172 const char* message,
173 const char* trigger,
174 Local<Object> error,
175 std::ostream& out) {
176 WriteNodeReport(isolate, env, message, trigger, "", out, error, false);
177 }
178
179 // Internal function to coordinate and write the various
180 // sections of the report to the supplied stream
WriteNodeReport(Isolate * isolate,Environment * env,const char * message,const char * trigger,const std::string & filename,std::ostream & out,Local<Object> error,bool compact)181 static void WriteNodeReport(Isolate* isolate,
182 Environment* env,
183 const char* message,
184 const char* trigger,
185 const std::string& filename,
186 std::ostream& out,
187 Local<Object> error,
188 bool compact) {
189 // Obtain the current time and the pid.
190 TIME_TYPE tm_struct;
191 DiagnosticFilename::LocalTime(&tm_struct);
192 uv_pid_t pid = uv_os_getpid();
193
194 // Save formatting for output stream.
195 std::ios old_state(nullptr);
196 old_state.copyfmt(out);
197
198 // File stream opened OK, now start printing the report content:
199 // the title and header information (event, filename, timestamp and pid)
200
201 JSONWriter writer(out, compact);
202 writer.json_start();
203 writer.json_objectstart("header");
204 writer.json_keyvalue("reportVersion", NODE_REPORT_VERSION);
205 writer.json_keyvalue("event", message);
206 writer.json_keyvalue("trigger", trigger);
207 if (!filename.empty())
208 writer.json_keyvalue("filename", filename);
209 else
210 writer.json_keyvalue("filename", JSONWriter::Null{});
211
212 // Report dump event and module load date/time stamps
213 char timebuf[64];
214 #ifdef _WIN32
215 snprintf(timebuf,
216 sizeof(timebuf),
217 "%4d-%02d-%02dT%02d:%02d:%02dZ",
218 tm_struct.wYear,
219 tm_struct.wMonth,
220 tm_struct.wDay,
221 tm_struct.wHour,
222 tm_struct.wMinute,
223 tm_struct.wSecond);
224 writer.json_keyvalue("dumpEventTime", timebuf);
225 #else // UNIX, OSX
226 snprintf(timebuf,
227 sizeof(timebuf),
228 "%4d-%02d-%02dT%02d:%02d:%02dZ",
229 tm_struct.tm_year + 1900,
230 tm_struct.tm_mon + 1,
231 tm_struct.tm_mday,
232 tm_struct.tm_hour,
233 tm_struct.tm_min,
234 tm_struct.tm_sec);
235 writer.json_keyvalue("dumpEventTime", timebuf);
236 #endif
237
238 uv_timeval64_t ts;
239 if (uv_gettimeofday(&ts) == 0) {
240 writer.json_keyvalue("dumpEventTimeStamp",
241 std::to_string(ts.tv_sec * 1000 + ts.tv_usec / 1000));
242 }
243
244 // Report native process ID
245 writer.json_keyvalue("processId", pid);
246 if (env != nullptr)
247 writer.json_keyvalue("threadId", env->thread_id());
248 else
249 writer.json_keyvalue("threadId", JSONWriter::Null{});
250
251 {
252 // Report the process cwd.
253 char buf[PATH_MAX_BYTES];
254 size_t cwd_size = sizeof(buf);
255 if (uv_cwd(buf, &cwd_size) == 0)
256 writer.json_keyvalue("cwd", buf);
257 }
258
259 // Report out the command line.
260 if (!node::per_process::cli_options->cmdline.empty()) {
261 writer.json_arraystart("commandLine");
262 for (const std::string& arg : node::per_process::cli_options->cmdline) {
263 writer.json_element(arg);
264 }
265 writer.json_arrayend();
266 }
267
268 // Report Node.js and OS version information
269 PrintVersionInformation(&writer);
270 writer.json_objectend();
271
272 writer.json_objectstart("javascriptStack");
273 // Report summary JavaScript error stack backtrace
274 PrintJavaScriptErrorStack(&writer, isolate, error, trigger);
275
276 // Report summary JavaScript error properties backtrace
277 PrintJavaScriptErrorProperties(&writer, isolate, error);
278 writer.json_objectend(); // the end of 'javascriptStack'
279
280 // Report native stack backtrace
281 PrintNativeStack(&writer);
282
283 // Report V8 Heap and Garbage Collector information
284 PrintGCStatistics(&writer, isolate);
285
286 // Report OS and current thread resource usage
287 PrintResourceUsage(&writer);
288
289 writer.json_arraystart("libuv");
290 if (env != nullptr) {
291 uv_walk(env->event_loop(), WalkHandle, static_cast<void*>(&writer));
292
293 writer.json_start();
294 writer.json_keyvalue("type", "loop");
295 writer.json_keyvalue("is_active",
296 static_cast<bool>(uv_loop_alive(env->event_loop())));
297 writer.json_keyvalue("address",
298 ValueToHexString(reinterpret_cast<int64_t>(env->event_loop())));
299 writer.json_end();
300 }
301
302 writer.json_arrayend();
303
304 writer.json_arraystart("workers");
305 if (env != nullptr) {
306 Mutex workers_mutex;
307 ConditionVariable notify;
308 std::vector<std::string> worker_infos;
309 size_t expected_results = 0;
310
311 env->ForEachWorker([&](Worker* w) {
312 expected_results += w->RequestInterrupt([&](Environment* env) {
313 std::ostringstream os;
314
315 GetNodeReport(env->isolate(),
316 env,
317 "Worker thread subreport",
318 trigger,
319 Local<Object>(),
320 os);
321
322 Mutex::ScopedLock lock(workers_mutex);
323 worker_infos.emplace_back(os.str());
324 notify.Signal(lock);
325 });
326 });
327
328 Mutex::ScopedLock lock(workers_mutex);
329 worker_infos.reserve(expected_results);
330 while (worker_infos.size() < expected_results)
331 notify.Wait(lock);
332 for (const std::string& worker_info : worker_infos)
333 writer.json_element(JSONWriter::ForeignJSON { worker_info });
334 }
335 writer.json_arrayend();
336
337 // Report operating system information
338 PrintSystemInformation(&writer);
339
340 writer.json_objectend();
341
342 // Restore output stream formatting.
343 out.copyfmt(old_state);
344 }
345
346 // Report Node.js version, OS version and machine information.
PrintVersionInformation(JSONWriter * writer)347 static void PrintVersionInformation(JSONWriter* writer) {
348 std::ostringstream buf;
349 // Report Node version
350 buf << "v" << NODE_VERSION_STRING;
351 writer->json_keyvalue("nodejsVersion", buf.str());
352 buf.str("");
353
354 #ifndef _WIN32
355 // Report compiler and runtime glibc versions where possible.
356 const char* (*libc_version)();
357 *(reinterpret_cast<void**>(&libc_version)) =
358 dlsym(RTLD_DEFAULT, "gnu_get_libc_version");
359 if (libc_version != nullptr)
360 writer->json_keyvalue("glibcVersionRuntime", (*libc_version)());
361 #endif /* _WIN32 */
362
363 #ifdef __GLIBC__
364 buf << __GLIBC__ << "." << __GLIBC_MINOR__;
365 writer->json_keyvalue("glibcVersionCompiler", buf.str());
366 buf.str("");
367 #endif
368
369 // Report Process word size
370 writer->json_keyvalue("wordSize", sizeof(void*) * 8);
371 writer->json_keyvalue("arch", node::per_process::metadata.arch);
372 writer->json_keyvalue("platform", node::per_process::metadata.platform);
373
374 // Report deps component versions
375 PrintComponentVersions(writer);
376
377 // Report release metadata.
378 PrintRelease(writer);
379
380 // Report operating system and machine information
381 uv_utsname_t os_info;
382
383 if (uv_os_uname(&os_info) == 0) {
384 writer->json_keyvalue("osName", os_info.sysname);
385 writer->json_keyvalue("osRelease", os_info.release);
386 writer->json_keyvalue("osVersion", os_info.version);
387 writer->json_keyvalue("osMachine", os_info.machine);
388 }
389
390 PrintCpuInfo(writer);
391 PrintNetworkInterfaceInfo(writer);
392
393 char host[UV_MAXHOSTNAMESIZE];
394 size_t host_size = sizeof(host);
395
396 if (uv_os_gethostname(host, &host_size) == 0)
397 writer->json_keyvalue("host", host);
398 }
399
400 // Report CPU info
PrintCpuInfo(JSONWriter * writer)401 static void PrintCpuInfo(JSONWriter* writer) {
402 uv_cpu_info_t* cpu_info;
403 int count;
404 if (uv_cpu_info(&cpu_info, &count) == 0) {
405 writer->json_arraystart("cpus");
406 for (int i = 0; i < count; i++) {
407 writer->json_start();
408 writer->json_keyvalue("model", cpu_info[i].model);
409 writer->json_keyvalue("speed", cpu_info[i].speed);
410 writer->json_keyvalue("user", cpu_info[i].cpu_times.user);
411 writer->json_keyvalue("nice", cpu_info[i].cpu_times.nice);
412 writer->json_keyvalue("sys", cpu_info[i].cpu_times.sys);
413 writer->json_keyvalue("idle", cpu_info[i].cpu_times.idle);
414 writer->json_keyvalue("irq", cpu_info[i].cpu_times.irq);
415 writer->json_end();
416 }
417 writer->json_arrayend();
418 uv_free_cpu_info(cpu_info, count);
419 }
420 }
421
PrintNetworkInterfaceInfo(JSONWriter * writer)422 static void PrintNetworkInterfaceInfo(JSONWriter* writer) {
423 uv_interface_address_t* interfaces;
424 char ip[INET6_ADDRSTRLEN];
425 char netmask[INET6_ADDRSTRLEN];
426 char mac[18];
427 int count;
428
429 if (uv_interface_addresses(&interfaces, &count) == 0) {
430 writer->json_arraystart("networkInterfaces");
431
432 for (int i = 0; i < count; i++) {
433 writer->json_start();
434 writer->json_keyvalue("name", interfaces[i].name);
435 writer->json_keyvalue("internal", !!interfaces[i].is_internal);
436 snprintf(mac,
437 sizeof(mac),
438 "%02x:%02x:%02x:%02x:%02x:%02x",
439 static_cast<unsigned char>(interfaces[i].phys_addr[0]),
440 static_cast<unsigned char>(interfaces[i].phys_addr[1]),
441 static_cast<unsigned char>(interfaces[i].phys_addr[2]),
442 static_cast<unsigned char>(interfaces[i].phys_addr[3]),
443 static_cast<unsigned char>(interfaces[i].phys_addr[4]),
444 static_cast<unsigned char>(interfaces[i].phys_addr[5]));
445 writer->json_keyvalue("mac", mac);
446
447 if (interfaces[i].address.address4.sin_family == AF_INET) {
448 uv_ip4_name(&interfaces[i].address.address4, ip, sizeof(ip));
449 uv_ip4_name(&interfaces[i].netmask.netmask4, netmask, sizeof(netmask));
450 writer->json_keyvalue("address", ip);
451 writer->json_keyvalue("netmask", netmask);
452 writer->json_keyvalue("family", "IPv4");
453 } else if (interfaces[i].address.address4.sin_family == AF_INET6) {
454 uv_ip6_name(&interfaces[i].address.address6, ip, sizeof(ip));
455 uv_ip6_name(&interfaces[i].netmask.netmask6, netmask, sizeof(netmask));
456 writer->json_keyvalue("address", ip);
457 writer->json_keyvalue("netmask", netmask);
458 writer->json_keyvalue("family", "IPv6");
459 writer->json_keyvalue("scopeid",
460 interfaces[i].address.address6.sin6_scope_id);
461 } else {
462 writer->json_keyvalue("family", "unknown");
463 }
464
465 writer->json_end();
466 }
467
468 writer->json_arrayend();
469 uv_free_interface_addresses(interfaces, count);
470 }
471 }
472
PrintJavaScriptErrorProperties(JSONWriter * writer,Isolate * isolate,Local<Object> error)473 static void PrintJavaScriptErrorProperties(JSONWriter* writer,
474 Isolate* isolate,
475 Local<Object> error) {
476 writer->json_objectstart("errorProperties");
477 if (!error.IsEmpty()) {
478 TryCatch try_catch(isolate);
479 Local<Context> context = error->GetIsolate()->GetCurrentContext();
480 Local<Array> keys;
481 if (!error->GetOwnPropertyNames(context).ToLocal(&keys)) {
482 return writer->json_objectend(); // the end of 'errorProperties'
483 }
484 uint32_t keys_length = keys->Length();
485 for (uint32_t i = 0; i < keys_length; i++) {
486 Local<Value> key;
487 if (!keys->Get(context, i).ToLocal(&key) || !key->IsString()) {
488 continue;
489 }
490 Local<Value> value;
491 Local<String> value_string;
492 if (!error->Get(context, key).ToLocal(&value) ||
493 !value->ToString(context).ToLocal(&value_string)) {
494 continue;
495 }
496 String::Utf8Value k(isolate, key);
497 if (!strcmp(*k, "stack") || !strcmp(*k, "message")) continue;
498 String::Utf8Value v(isolate, value_string);
499 writer->json_keyvalue(std::string(*k, k.length()),
500 std::string(*v, v.length()));
501 }
502 }
503 writer->json_objectend(); // the end of 'errorProperties'
504 }
505
506 // Report the JavaScript stack.
PrintJavaScriptErrorStack(JSONWriter * writer,Isolate * isolate,Local<Object> error,const char * trigger)507 static void PrintJavaScriptErrorStack(JSONWriter* writer,
508 Isolate* isolate,
509 Local<Object> error,
510 const char* trigger) {
511 Local<Value> stackstr;
512 std::string ss = "";
513 TryCatch try_catch(isolate);
514 if ((!strcmp(trigger, "FatalError")) ||
515 (!strcmp(trigger, "Signal"))) {
516 ss = "No stack.\nUnavailable.\n";
517 } else if (!error.IsEmpty() &&
518 error
519 ->Get(isolate->GetCurrentContext(),
520 node::FIXED_ONE_BYTE_STRING(isolate,
521 "stack"))
522 .ToLocal(&stackstr)) {
523 String::Utf8Value sv(isolate, stackstr);
524 ss = std::string(*sv, sv.length());
525 }
526 int line = ss.find('\n');
527 if (line == -1) {
528 writer->json_keyvalue("message", ss);
529 } else {
530 std::string l = ss.substr(0, line);
531 writer->json_keyvalue("message", l);
532 writer->json_arraystart("stack");
533 ss = ss.substr(line + 1);
534 line = ss.find('\n');
535 while (line != -1) {
536 l = ss.substr(0, line);
537 l.erase(l.begin(), std::find_if(l.begin(), l.end(), [](int ch) {
538 return !std::iswspace(ch);
539 }));
540 writer->json_element(l);
541 ss = ss.substr(line + 1);
542 line = ss.find('\n');
543 }
544 writer->json_arrayend();
545 }
546 }
547
548 // Report a native stack backtrace
PrintNativeStack(JSONWriter * writer)549 static void PrintNativeStack(JSONWriter* writer) {
550 auto sym_ctx = NativeSymbolDebuggingContext::New();
551 void* frames[256];
552 const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
553 writer->json_arraystart("nativeStack");
554 int i;
555 for (i = 1; i < size; i++) {
556 void* frame = frames[i];
557 writer->json_start();
558 writer->json_keyvalue("pc",
559 ValueToHexString(reinterpret_cast<uintptr_t>(frame)));
560 writer->json_keyvalue("symbol", sym_ctx->LookupSymbol(frame).Display());
561 writer->json_end();
562 }
563 writer->json_arrayend();
564 }
565
566 // Report V8 JavaScript heap information.
567 // This uses the existing V8 HeapStatistics and HeapSpaceStatistics APIs.
568 // The isolate->GetGCStatistics(&heap_stats) internal V8 API could potentially
569 // provide some more useful information - the GC history and the handle counts
PrintGCStatistics(JSONWriter * writer,Isolate * isolate)570 static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate) {
571 HeapStatistics v8_heap_stats;
572 isolate->GetHeapStatistics(&v8_heap_stats);
573 HeapSpaceStatistics v8_heap_space_stats;
574
575 writer->json_objectstart("javascriptHeap");
576 writer->json_keyvalue("totalMemory", v8_heap_stats.total_heap_size());
577 writer->json_keyvalue("totalCommittedMemory",
578 v8_heap_stats.total_physical_size());
579 writer->json_keyvalue("usedMemory", v8_heap_stats.used_heap_size());
580 writer->json_keyvalue("availableMemory",
581 v8_heap_stats.total_available_size());
582 writer->json_keyvalue("memoryLimit", v8_heap_stats.heap_size_limit());
583
584 writer->json_objectstart("heapSpaces");
585 // Loop through heap spaces
586 for (size_t i = 0; i < isolate->NumberOfHeapSpaces(); i++) {
587 isolate->GetHeapSpaceStatistics(&v8_heap_space_stats, i);
588 writer->json_objectstart(v8_heap_space_stats.space_name());
589 writer->json_keyvalue("memorySize", v8_heap_space_stats.space_size());
590 writer->json_keyvalue(
591 "committedMemory",
592 v8_heap_space_stats.physical_space_size());
593 writer->json_keyvalue(
594 "capacity",
595 v8_heap_space_stats.space_used_size() +
596 v8_heap_space_stats.space_available_size());
597 writer->json_keyvalue("used", v8_heap_space_stats.space_used_size());
598 writer->json_keyvalue(
599 "available", v8_heap_space_stats.space_available_size());
600 writer->json_objectend();
601 }
602
603 writer->json_objectend();
604 writer->json_objectend();
605 }
606
PrintResourceUsage(JSONWriter * writer)607 static void PrintResourceUsage(JSONWriter* writer) {
608 // Get process uptime in seconds
609 uint64_t uptime =
610 (uv_hrtime() - node::per_process::node_start_time) / (NANOS_PER_SEC);
611 if (uptime == 0) uptime = 1; // avoid division by zero.
612
613 // Process and current thread usage statistics
614 uv_rusage_t rusage;
615 writer->json_objectstart("resourceUsage");
616 if (uv_getrusage(&rusage) == 0) {
617 double user_cpu =
618 rusage.ru_utime.tv_sec + SEC_PER_MICROS * rusage.ru_utime.tv_usec;
619 double kernel_cpu =
620 rusage.ru_stime.tv_sec + SEC_PER_MICROS * rusage.ru_stime.tv_usec;
621 writer->json_keyvalue("userCpuSeconds", user_cpu);
622 writer->json_keyvalue("kernelCpuSeconds", kernel_cpu);
623 double cpu_abs = user_cpu + kernel_cpu;
624 double cpu_percentage = (cpu_abs / uptime) * 100.0;
625 writer->json_keyvalue("cpuConsumptionPercent", cpu_percentage);
626 writer->json_keyvalue("maxRss", rusage.ru_maxrss * 1024);
627 writer->json_objectstart("pageFaults");
628 writer->json_keyvalue("IORequired", rusage.ru_majflt);
629 writer->json_keyvalue("IONotRequired", rusage.ru_minflt);
630 writer->json_objectend();
631 writer->json_objectstart("fsActivity");
632 writer->json_keyvalue("reads", rusage.ru_inblock);
633 writer->json_keyvalue("writes", rusage.ru_oublock);
634 writer->json_objectend();
635 }
636 writer->json_objectend();
637 #ifdef RUSAGE_THREAD
638 struct rusage stats;
639 if (getrusage(RUSAGE_THREAD, &stats) == 0) {
640 writer->json_objectstart("uvthreadResourceUsage");
641 double user_cpu =
642 stats.ru_utime.tv_sec + SEC_PER_MICROS * stats.ru_utime.tv_usec;
643 double kernel_cpu =
644 stats.ru_stime.tv_sec + SEC_PER_MICROS * stats.ru_stime.tv_usec;
645 writer->json_keyvalue("userCpuSeconds", user_cpu);
646 writer->json_keyvalue("kernelCpuSeconds", kernel_cpu);
647 double cpu_abs = user_cpu + kernel_cpu;
648 double cpu_percentage = (cpu_abs / uptime) * 100.0;
649 writer->json_keyvalue("cpuConsumptionPercent", cpu_percentage);
650 writer->json_objectstart("fsActivity");
651 writer->json_keyvalue("reads", stats.ru_inblock);
652 writer->json_keyvalue("writes", stats.ru_oublock);
653 writer->json_objectend();
654 writer->json_objectend();
655 }
656 #endif
657 }
658
659 // Report operating system information.
PrintSystemInformation(JSONWriter * writer)660 static void PrintSystemInformation(JSONWriter* writer) {
661 uv_env_item_t* envitems;
662 int envcount;
663 int r;
664
665 writer->json_objectstart("environmentVariables");
666
667 {
668 Mutex::ScopedLock lock(node::per_process::env_var_mutex);
669 r = uv_os_environ(&envitems, &envcount);
670 }
671
672 if (r == 0) {
673 for (int i = 0; i < envcount; i++)
674 writer->json_keyvalue(envitems[i].name, envitems[i].value);
675
676 uv_os_free_environ(envitems, envcount);
677 }
678
679 writer->json_objectend();
680
681 #ifndef _WIN32
682 static struct {
683 const char* description;
684 int id;
685 } rlimit_strings[] = {
686 {"core_file_size_blocks", RLIMIT_CORE},
687 {"data_seg_size_kbytes", RLIMIT_DATA},
688 {"file_size_blocks", RLIMIT_FSIZE},
689 #if !(defined(_AIX) || defined(__sun))
690 {"max_locked_memory_bytes", RLIMIT_MEMLOCK},
691 #endif
692 #ifndef __sun
693 {"max_memory_size_kbytes", RLIMIT_RSS},
694 #endif
695 {"open_files", RLIMIT_NOFILE},
696 {"stack_size_bytes", RLIMIT_STACK},
697 {"cpu_time_seconds", RLIMIT_CPU},
698 #ifndef __sun
699 {"max_user_processes", RLIMIT_NPROC},
700 #endif
701 #ifndef __OpenBSD__
702 {"virtual_memory_kbytes", RLIMIT_AS}
703 #endif
704 };
705
706 writer->json_objectstart("userLimits");
707 struct rlimit limit;
708 std::string soft, hard;
709
710 for (size_t i = 0; i < arraysize(rlimit_strings); i++) {
711 if (getrlimit(rlimit_strings[i].id, &limit) == 0) {
712 writer->json_objectstart(rlimit_strings[i].description);
713
714 if (limit.rlim_cur == RLIM_INFINITY)
715 writer->json_keyvalue("soft", "unlimited");
716 else
717 writer->json_keyvalue("soft", limit.rlim_cur);
718
719 if (limit.rlim_max == RLIM_INFINITY)
720 writer->json_keyvalue("hard", "unlimited");
721 else
722 writer->json_keyvalue("hard", limit.rlim_max);
723
724 writer->json_objectend();
725 }
726 }
727 writer->json_objectend();
728 #endif // _WIN32
729
730 PrintLoadedLibraries(writer);
731 }
732
733 // Report a list of loaded native libraries.
PrintLoadedLibraries(JSONWriter * writer)734 static void PrintLoadedLibraries(JSONWriter* writer) {
735 writer->json_arraystart("sharedObjects");
736 std::vector<std::string> modules =
737 NativeSymbolDebuggingContext::GetLoadedLibraries();
738 for (auto const& module_name : modules) writer->json_element(module_name);
739 writer->json_arrayend();
740 }
741
742 // Obtain and report the node and subcomponent version strings.
PrintComponentVersions(JSONWriter * writer)743 static void PrintComponentVersions(JSONWriter* writer) {
744 std::stringstream buf;
745
746 writer->json_objectstart("componentVersions");
747
748 #define V(key) \
749 writer->json_keyvalue(#key, node::per_process::metadata.versions.key);
750 NODE_VERSIONS_KEYS(V)
751 #undef V
752
753 writer->json_objectend();
754 }
755
756 // Report runtime release information.
PrintRelease(JSONWriter * writer)757 static void PrintRelease(JSONWriter* writer) {
758 writer->json_objectstart("release");
759 writer->json_keyvalue("name", node::per_process::metadata.release.name);
760 #if NODE_VERSION_IS_LTS
761 writer->json_keyvalue("lts", node::per_process::metadata.release.lts);
762 #endif
763
764 #ifdef NODE_HAS_RELEASE_URLS
765 writer->json_keyvalue("headersUrl",
766 node::per_process::metadata.release.headers_url);
767 writer->json_keyvalue("sourceUrl",
768 node::per_process::metadata.release.source_url);
769 #ifdef _WIN32
770 writer->json_keyvalue("libUrl", node::per_process::metadata.release.lib_url);
771 #endif // _WIN32
772 #endif // NODE_HAS_RELEASE_URLS
773
774 writer->json_objectend();
775 }
776
777 } // namespace report
778