summaryrefslogtreecommitdiff
path: root/src/node_report.cc
diff options
context:
space:
mode:
authorGireesh Punathil <gpunathi@in.ibm.com>2018-09-05 10:06:59 -0400
committerGireesh Punathil <gpunathi@in.ibm.com>2019-01-18 10:34:04 +0530
commit4f6797378eb6dd290dd5d8dc6417fea22d42ebad (patch)
tree88967037a1ba9aec47faea701efecf4f44007587 /src/node_report.cc
parent01cd21973b26a2cbacbe143c5983cb4adf8e7681 (diff)
downloadandroid-node-v8-4f6797378eb6dd290dd5d8dc6417fea22d42ebad.tar.gz
android-node-v8-4f6797378eb6dd290dd5d8dc6417fea22d42ebad.tar.bz2
android-node-v8-4f6797378eb6dd290dd5d8dc6417fea22d42ebad.zip
src: merge into core
Make node-report part of core runtime because: 1. When enabled, node-report significantly helps root cause various types of problems, including support issues sent to the various repos of the Node.js organization. 2. The requirement of explicitly adding the dependency to node-report in user applications often represents a blocker to adoption. Major deviation from the module version of the node-report is that the report is generated in JSON format, as opposed to human readable text. No new functionalities have been added, changes that are required for melding it as a built-in capability has been affected on the module version of node-report (https://github.com/nodejs/node-report) Co-authored-by: Bidisha Pyne <bidipyne@in.ibm.com> Co-authored-by: Howard Hellyer <hhellyer@uk.ibm.com> Co-authored-by: Jeremiah Senkpiel <fishrock123@rocketmail.com> Co-authored-by: Julian Alimin <dmastag@yahoo.com> Co-authored-by: Lakshmi Swetha Gopireddy <lakshmigopireddy@in.ibm.com> Co-authored-by: Manusaporn Treerungroj <m.treerungroj@gmail.com> Co-authored-by: Michael Dawson <michael_dawson@ca.ibm.com> Co-authored-by: Richard Chamberlain <richard_chamberlain@uk.ibm.com> Co-authored-by: Richard Lau <riclau@uk.ibm.com> Co-authored-by: Sam Roberts <vieuxtech@gmail.com> Co-authored-by: Vipin Menon <vipinmv1@in.ibm.com> PR-URL: https://github.com/nodejs/node/pull/22712 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michael Dawson <Michael_Dawson@ca.ibm.com> Reviewed-By: Vse Mozhet Byt <vsemozhetbyt@gmail.com>
Diffstat (limited to 'src/node_report.cc')
-rw-r--r--src/node_report.cc833
1 files changed, 833 insertions, 0 deletions
diff --git a/src/node_report.cc b/src/node_report.cc
new file mode 100644
index 0000000000..276ce93095
--- /dev/null
+++ b/src/node_report.cc
@@ -0,0 +1,833 @@
+
+#include "node_report.h"
+#include "ares.h"
+#include "debug_utils.h"
+#include "http_parser.h"
+#include "nghttp2/nghttp2ver.h"
+#include "node_internals.h"
+#include "node_metadata.h"
+#include "zlib.h"
+
+#include <atomic>
+#include <fstream>
+
+#ifdef _WIN32
+#include <Lm.h>
+#include <Windows.h>
+#include <dbghelp.h>
+#include <process.h>
+#include <psapi.h>
+#include <tchar.h>
+#include <cwctype>
+#else
+#include <sys/resource.h>
+// Get the standard printf format macros for C99 stdint types.
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+#include <cxxabi.h>
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <sys/utsname.h>
+#endif
+
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <iomanip>
+
+#ifndef _MSC_VER
+#include <strings.h>
+#endif
+
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
+#ifndef _WIN32
+extern char** environ;
+#endif
+
+namespace report {
+using node::arraysize;
+using node::Environment;
+using node::Mutex;
+using node::NativeSymbolDebuggingContext;
+using node::PerIsolateOptions;
+using v8::HeapSpaceStatistics;
+using v8::HeapStatistics;
+using v8::Isolate;
+using v8::Local;
+using v8::Number;
+using v8::StackTrace;
+using v8::String;
+using v8::V8;
+using v8::Value;
+
+// Internal/static function declarations
+static void WriteNodeReport(Isolate* isolate,
+ Environment* env,
+ const char* message,
+ const char* location,
+ const std::string& filename,
+ std::ostream& out,
+ Local<String> stackstr,
+ TIME_TYPE* time);
+static void PrintVersionInformation(JSONWriter* writer);
+static void PrintJavaScriptStack(JSONWriter* writer,
+ Isolate* isolate,
+ Local<String> stackstr,
+ const char* location);
+static void PrintNativeStack(JSONWriter* writer);
+#ifndef _WIN32
+static void PrintResourceUsage(JSONWriter* writer);
+#endif
+static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate);
+static void PrintSystemInformation(JSONWriter* writer);
+static void PrintLoadedLibraries(JSONWriter* writer);
+static void PrintComponentVersions(JSONWriter* writer);
+static void LocalTime(TIME_TYPE* tm_struct);
+
+// Global variables
+static std::atomic_int seq = {0}; // sequence number for report filenames
+
+// External function to trigger a report, writing to file.
+// The 'name' parameter is in/out: an input filename is used
+// if supplied, and the actual filename is returned.
+std::string TriggerNodeReport(Isolate* isolate,
+ Environment* env,
+ const char* message,
+ const char* location,
+ std::string name,
+ Local<String> stackstr) {
+ std::ostringstream oss;
+ std::string filename;
+ std::shared_ptr<PerIsolateOptions> options;
+ if (env != nullptr) options = env->isolate_data()->options();
+
+ // Obtain the current time and the pid (platform dependent)
+ TIME_TYPE tm_struct;
+ PID_TYPE pid;
+ LocalTime(&tm_struct);
+ pid = uv_os_getpid();
+ // Determine the required report filename. In order of priority:
+ // 1) supplied on API 2) configured on startup 3) default generated
+ if (!name.empty()) {
+ // Filename was specified as API parameter, use that
+ oss << name;
+ } else if (env != nullptr && options->report_filename.length() > 0) {
+ // File name was supplied via start-up option, use that
+ oss << options->report_filename;
+ } else {
+ // Construct the report filename, with timestamp, pid and sequence number
+ oss << "report";
+ seq++;
+#ifdef _WIN32
+ oss << "." << std::setfill('0') << std::setw(4) << tm_struct.wYear;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.wMonth;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.wDay;
+ oss << "." << std::setfill('0') << std::setw(2) << tm_struct.wHour;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.wMinute;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.wSecond;
+ oss << "." << pid;
+ oss << "." << std::setfill('0') << std::setw(3) << seq.load();
+#else // UNIX, OSX
+ oss << "." << std::setfill('0') << std::setw(4) << tm_struct.tm_year + 1900;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.tm_mon + 1;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.tm_mday;
+ oss << "." << std::setfill('0') << std::setw(2) << tm_struct.tm_hour;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.tm_min;
+ oss << std::setfill('0') << std::setw(2) << tm_struct.tm_sec;
+ oss << "." << pid;
+ oss << "." << std::setfill('0') << std::setw(3) << seq.load();
+#endif
+ oss << ".json";
+ }
+
+ filename = oss.str();
+ // Open the report file stream for writing. Supports stdout/err,
+ // user-specified or (default) generated name
+ std::ofstream outfile;
+ std::ostream* outstream = &std::cout;
+ if (filename == "stdout") {
+ outstream = &std::cout;
+ } else if (filename == "stderr") {
+ outstream = &std::cerr;
+ } else {
+ // Regular file. Append filename to directory path if one was specified
+ if (env != nullptr && options->report_directory.length() > 0) {
+ std::string pathname = options->report_directory;
+ pathname += PATHSEP;
+ pathname += filename;
+ outfile.open(pathname, std::ios::out | std::ios::binary);
+ } else {
+ outfile.open(filename, std::ios::out | std::ios::binary);
+ }
+ // Check for errors on the file open
+ if (!outfile.is_open()) {
+ if (env != nullptr && options->report_directory.length() > 0) {
+ std::cerr << std::endl
+ << "Failed to open Node.js report file: " << filename
+ << " directory: " << options->report_directory
+ << " (errno: " << errno << ")" << std::endl;
+ } else {
+ std::cerr << std::endl
+ << "Failed to open Node.js report file: " << filename
+ << " (errno: " << errno << ")" << std::endl;
+ }
+ return "";
+ } else {
+ std::cerr << std::endl
+ << "Writing Node.js report to file: " << filename << std::endl;
+ }
+ }
+
+ // Pass our stream about by reference, not by copying it.
+ std::ostream& out = outfile.is_open() ? outfile : *outstream;
+
+ WriteNodeReport(
+ isolate, env, message, location, filename, out, stackstr, &tm_struct);
+
+ // Do not close stdout/stderr, only close files we opened.
+ if (outfile.is_open()) {
+ outfile.close();
+ }
+
+ std::cerr << "Node.js report completed" << std::endl;
+ if (name.empty()) return filename;
+ return name;
+}
+
+// External function to trigger a report, writing to a supplied stream.
+void GetNodeReport(Isolate* isolate,
+ Environment* env,
+ const char* message,
+ const char* location,
+ Local<String> stackstr,
+ std::ostream& out) {
+ // Obtain the current time and the pid (platform dependent)
+ TIME_TYPE tm_struct;
+ LocalTime(&tm_struct);
+ std::string str = "NA";
+ WriteNodeReport(
+ isolate, env, message, location, str, out, stackstr, &tm_struct);
+}
+
+// Internal function to coordinate and write the various
+// sections of the report to the supplied stream
+static void WriteNodeReport(Isolate* isolate,
+ Environment* env,
+ const char* message,
+ const char* location,
+ const std::string& filename,
+ std::ostream& out,
+ Local<String> stackstr,
+ TIME_TYPE* tm_struct) {
+ std::ostringstream buf;
+ PID_TYPE pid = uv_os_getpid();
+
+ // Save formatting for output stream.
+ std::ios old_state(nullptr);
+ old_state.copyfmt(out);
+
+ // File stream opened OK, now start printing the report content:
+ // the title and header information (event, filename, timestamp and pid)
+
+ JSONWriter writer(out);
+ writer.json_start();
+ writer.json_objectstart("header");
+
+ writer.json_keyvalue("event", message);
+ writer.json_keyvalue("location", location);
+ if (!filename.empty())
+ writer.json_keyvalue("filename", filename);
+ else
+ writer.json_keyvalue("filename", std::string("''"));
+
+ // Report dump event and module load date/time stamps
+ char timebuf[64];
+#ifdef _WIN32
+ snprintf(timebuf,
+ sizeof(timebuf),
+ "%4d/%02d/%02d %02d:%02d:%02d",
+ tm_struct->wYear,
+ tm_struct->wMonth,
+ tm_struct->wDay,
+ tm_struct->wHour,
+ tm_struct->wMinute,
+ tm_struct->wSecond);
+ writer.json_keyvalue("dumpEventTime", timebuf);
+#else // UNIX, OSX
+ snprintf(timebuf,
+ sizeof(timebuf),
+ "%4d-%02d-%02dT%02d:%02d:%02dZ",
+ tm_struct->tm_year + 1900,
+ tm_struct->tm_mon + 1,
+ tm_struct->tm_mday,
+ tm_struct->tm_hour,
+ tm_struct->tm_min,
+ tm_struct->tm_sec);
+ writer.json_keyvalue("dumpEventTime", timebuf);
+ struct timeval ts;
+ gettimeofday(&ts, nullptr);
+ writer.json_keyvalue("dumpEventTimeStamp",
+ std::to_string(ts.tv_sec * 1000 + ts.tv_usec / 1000));
+#endif
+ // Report native process ID
+ buf << pid;
+ writer.json_keyvalue("processId", buf.str());
+ buf.flush();
+
+ // Report out the command line.
+ if (!node::per_process::cli_options->cmdline.empty()) {
+ writer.json_arraystart("commandLine");
+ for (std::string arg : node::per_process::cli_options->cmdline) {
+ writer.json_element(arg);
+ }
+ writer.json_arrayend();
+ }
+
+ // Report Node.js and OS version information
+ PrintVersionInformation(&writer);
+ writer.json_objectend();
+
+ // Report summary JavaScript stack backtrace
+ PrintJavaScriptStack(&writer, isolate, stackstr, location);
+
+ // Report native stack backtrace
+ PrintNativeStack(&writer);
+
+ // Report V8 Heap and Garbage Collector information
+ PrintGCStatistics(&writer, isolate);
+
+ // Report OS and current thread resource usage
+#ifndef _WIN32
+ PrintResourceUsage(&writer);
+#endif
+
+ writer.json_arraystart("libuv");
+ if (env != nullptr)
+ uv_walk(env->event_loop(), WalkHandle, static_cast<void*>(&writer));
+ else
+ uv_walk(uv_default_loop(), WalkHandle, static_cast<void*>(&writer));
+
+ writer.json_arrayend();
+
+ // Report operating system information
+ PrintSystemInformation(&writer);
+
+ writer.json_objectend();
+
+ // Restore output stream formatting.
+ out.copyfmt(old_state);
+}
+
+// Report Node.js version, OS version and machine information.
+static void PrintVersionInformation(JSONWriter* writer) {
+ std::ostringstream buf;
+ // Report Node version
+ buf << "v" << NODE_VERSION_STRING;
+ writer->json_keyvalue("nodejsVersion", buf.str());
+ buf.str("");
+#ifdef __GLIBC__
+ buf << __GLIBC__ << "." << __GLIBC_MINOR__;
+ writer->json_keyvalue("glibcVersion", buf.str());
+ buf.str("");
+#endif
+ // Report Process word size
+ buf << sizeof(void*) * 8 << " bit";
+ writer->json_keyvalue("wordSize", buf.str());
+ buf.str("");
+
+ // Report deps component versions
+ PrintComponentVersions(writer);
+
+ // Report operating system and machine information (Windows)
+#ifdef _WIN32
+ {
+ // Level 101 to obtain the server name, type, and associated details.
+ // ref: https://docs.microsoft.com/en-us/windows/desktop/
+ // api/lmserver/nf-lmserver-netservergetinfo
+ const DWORD level = 101;
+ LPSERVER_INFO_101 os_info = nullptr;
+ NET_API_STATUS nStatus =
+ NetServerGetInfo(nullptr, level, reinterpret_cast<LPBYTE*>(&os_info));
+ if (nStatus == NERR_Success) {
+ LPSTR os_name = "Windows";
+ const DWORD major = os_info->sv101_version_major & MAJOR_VERSION_MASK;
+ const DWORD type = os_info->sv101_type;
+ const bool isServer = (type & SV_TYPE_DOMAIN_CTRL) ||
+ (type & SV_TYPE_DOMAIN_BAKCTRL) ||
+ (type & SV_TYPE_SERVER_NT);
+ switch (major) {
+ case 5:
+ switch (os_info->sv101_version_minor) {
+ case 0:
+ os_name = "Windows 2000";
+ break;
+ default:
+ os_name = (isServer ? "Windows Server 2003" : "Windows XP");
+ }
+ break;
+ case 6:
+ switch (os_info->sv101_version_minor) {
+ case 0:
+ os_name = (isServer ? "Windows Server 2008" : "Windows Vista");
+ break;
+ case 1:
+ os_name = (isServer ? "Windows Server 2008 R2" : "Windows 7");
+ break;
+ case 2:
+ os_name = (isServer ? "Windows Server 2012" : "Windows 8");
+ break;
+ case 3:
+ os_name = (isServer ? "Windows Server 2012 R2" : "Windows 8.1");
+ break;
+ default:
+ os_name = (isServer ? "Windows Server" : "Windows Client");
+ }
+ break;
+ case 10:
+ os_name = (isServer ? "Windows Server 2016" : "Windows 10");
+ break;
+ default:
+ os_name = (isServer ? "Windows Server" : "Windows Client");
+ }
+ writer->json_keyvalue("osVersion", os_name);
+
+ // Convert and report the machine name and comment fields
+ // (these are LPWSTR types)
+ size_t count;
+ char name_buf[256];
+ wcstombs_s(
+ &count, name_buf, sizeof(name_buf), os_info->sv101_name, _TRUNCATE);
+ if (os_info->sv101_comment != nullptr) {
+ char comment_buf[256];
+ wcstombs_s(&count,
+ comment_buf,
+ sizeof(comment_buf),
+ os_info->sv101_comment,
+ _TRUNCATE);
+ buf << name_buf << " " << comment_buf;
+ writer->json_keyvalue("machine", buf.str());
+ buf.flush();
+ } else {
+ writer->json_keyvalue("machine", name_buf);
+ }
+
+ if (os_info != nullptr) {
+ NetApiBufferFree(os_info);
+ }
+ } else {
+ // NetServerGetInfo() failed, fallback to use GetComputerName() instead
+ TCHAR machine_name[256];
+ DWORD machine_name_size = 256;
+ writer->json_keyvalue("osVersion", "Windows");
+ if (GetComputerName(machine_name, &machine_name_size)) {
+ writer->json_keyvalue("machine", machine_name);
+ }
+ }
+ }
+#else
+ // Report operating system and machine information (Unix/OSX)
+ struct utsname os_info;
+ if (uname(&os_info) >= 0) {
+#ifdef _AIX
+ buf << os_info.sysname << " " << os_info.version << "." << os_info.release;
+ writer->json_keyvalue("osVersion", buf.str());
+ buf.flush();
+#else
+ buf << os_info.sysname << " " << os_info.release << " " << os_info.version;
+ writer->json_keyvalue("osVersion", buf.str());
+ buf.flush();
+#endif
+ const char* (*libc_version)();
+ *(reinterpret_cast<void**>(&libc_version)) =
+ dlsym(RTLD_DEFAULT, "gnu_get_libc_version");
+ if (libc_version != nullptr) {
+ writer->json_keyvalue("glibc", (*libc_version)());
+ }
+ buf << os_info.nodename << " " << os_info.machine;
+ writer->json_keyvalue("machine", buf.str());
+ buf.flush();
+ }
+#endif
+}
+
+// Report the JavaScript stack.
+static void PrintJavaScriptStack(JSONWriter* writer,
+ Isolate* isolate,
+ Local<String> stackstr,
+ const char* location) {
+ writer->json_objectstart("javascriptStack");
+
+ std::string ss;
+ if ((!strcmp(location, "OnFatalError")) ||
+ (!strcmp(location, "OnUserSignal"))) {
+ ss = "No stack.\nUnavailable.\n";
+ } else {
+ String::Utf8Value sv(isolate, stackstr);
+ ss = std::string(*sv, sv.length());
+ }
+ int line = ss.find("\n");
+ if (line == -1) {
+ writer->json_keyvalue("message", ss.c_str());
+ writer->json_objectend();
+ } else {
+ std::string l = ss.substr(0, line);
+ writer->json_keyvalue("message", l);
+ writer->json_arraystart("stack");
+ ss = ss.substr(line + 1);
+ line = ss.find("\n");
+ while (line != -1) {
+ l = ss.substr(0, line);
+ l.erase(l.begin(), std::find_if(l.begin(), l.end(), [](int ch) {
+ return !std::iswspace(ch);
+ }));
+ writer->json_element(l);
+ ss = ss.substr(line + 1);
+ line = ss.find("\n");
+ }
+ }
+ writer->json_arrayend();
+ writer->json_objectend();
+}
+
+// Report a native stack backtrace
+static void PrintNativeStack(JSONWriter* writer) {
+ auto sym_ctx = NativeSymbolDebuggingContext::New();
+ void* frames[256];
+ const int size = sym_ctx->GetStackTrace(frames, arraysize(frames));
+ writer->json_arraystart("nativeStack");
+ int i;
+ std::ostringstream buf;
+ for (i = 1; i < size - 1; i += 1) {
+ void* frame = frames[i];
+ buf.str("");
+ buf << " [pc=" << frame << "] ";
+ buf << sym_ctx->LookupSymbol(frame).Display().c_str();
+ writer->json_element(buf.str());
+ }
+ buf.str("");
+ buf << " [pc=" << frames[i] << "] ";
+ buf << sym_ctx->LookupSymbol(frames[i]).Display().c_str();
+ writer->json_element(buf.str());
+ writer->json_arrayend();
+}
+
+// Report V8 JavaScript heap information.
+// This uses the existing V8 HeapStatistics and HeapSpaceStatistics APIs.
+// The isolate->GetGCStatistics(&heap_stats) internal V8 API could potentially
+// provide some more useful information - the GC history and the handle counts
+static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate) {
+ HeapStatistics v8_heap_stats;
+ isolate->GetHeapStatistics(&v8_heap_stats);
+ HeapSpaceStatistics v8_heap_space_stats;
+
+ writer->json_objectstart("javascriptHeap");
+ writer->json_keyvalue("totalMemory",
+ std::to_string(v8_heap_stats.total_heap_size()));
+ writer->json_keyvalue("totalCommittedMemory",
+ std::to_string(v8_heap_stats.total_physical_size()));
+ writer->json_keyvalue("usedMemory",
+ std::to_string(v8_heap_stats.used_heap_size()));
+ writer->json_keyvalue("availableMemory",
+ std::to_string(v8_heap_stats.total_available_size()));
+ writer->json_keyvalue("memoryLimit",
+ std::to_string(v8_heap_stats.heap_size_limit()));
+
+ writer->json_objectstart("heapSpaces");
+ // Loop through heap spaces
+ size_t i;
+ for (i = 0; i < isolate->NumberOfHeapSpaces() - 1; i++) {
+ isolate->GetHeapSpaceStatistics(&v8_heap_space_stats, i);
+ writer->json_objectstart(v8_heap_space_stats.space_name());
+ writer->json_keyvalue("memorySize",
+ std::to_string(v8_heap_space_stats.space_size()));
+ writer->json_keyvalue(
+ "committedMemory",
+ std::to_string(v8_heap_space_stats.physical_space_size()));
+ writer->json_keyvalue(
+ "capacity",
+ std::to_string(v8_heap_space_stats.space_used_size() +
+ v8_heap_space_stats.space_available_size()));
+ writer->json_keyvalue(
+ "used", std::to_string(v8_heap_space_stats.space_used_size()));
+ writer->json_keyvalue(
+ "available",
+ std::to_string(v8_heap_space_stats.space_available_size()));
+ writer->json_objectend();
+ }
+ isolate->GetHeapSpaceStatistics(&v8_heap_space_stats, i);
+ writer->json_objectstart(v8_heap_space_stats.space_name());
+ writer->json_keyvalue("memorySize",
+ std::to_string(v8_heap_space_stats.space_size()));
+ writer->json_keyvalue(
+ "committedMemory",
+ std::to_string(v8_heap_space_stats.physical_space_size()));
+ writer->json_keyvalue(
+ "capacity",
+ std::to_string(v8_heap_space_stats.space_used_size() +
+ v8_heap_space_stats.space_available_size()));
+ writer->json_keyvalue("used",
+ std::to_string(v8_heap_space_stats.space_used_size()));
+ writer->json_keyvalue(
+ "available", std::to_string(v8_heap_space_stats.space_available_size()));
+ writer->json_objectend();
+ writer->json_objectend();
+ writer->json_objectend();
+}
+
+#ifndef _WIN32
+// Report resource usage (Linux/OSX only).
+static void PrintResourceUsage(JSONWriter* writer) {
+ char buf[64];
+ double cpu_abs;
+ double cpu_percentage;
+ time_t current_time; // current time absolute
+ time(&current_time);
+ size_t boot_time = static_cast<time_t>(node::per_process::prog_start_time /
+ (1000 * 1000 * 1000));
+ auto uptime = difftime(current_time, boot_time);
+ if (uptime == 0) uptime = 1; // avoid division by zero.
+
+ // Process and current thread usage statistics
+ struct rusage stats;
+ writer->json_objectstart("resourceUsage");
+ if (getrusage(RUSAGE_SELF, &stats) == 0) {
+#if defined(__APPLE__) || defined(_AIX)
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06d",
+ stats.ru_utime.tv_sec,
+ stats.ru_utime.tv_usec);
+ writer->json_keyvalue("userCpuSeconds", buf);
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06d",
+ stats.ru_stime.tv_sec,
+ stats.ru_stime.tv_usec);
+ writer->json_keyvalue("kernelCpuSeconds", buf);
+#else
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06ld",
+ stats.ru_utime.tv_sec,
+ stats.ru_utime.tv_usec);
+ writer->json_keyvalue("userCpuSeconds", buf);
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06ld",
+ stats.ru_stime.tv_sec,
+ stats.ru_stime.tv_usec);
+ writer->json_keyvalue("kernelCpuSeconds", buf);
+#endif
+ cpu_abs = stats.ru_utime.tv_sec + 0.000001 * stats.ru_utime.tv_usec +
+ stats.ru_stime.tv_sec + 0.000001 * stats.ru_stime.tv_usec;
+ cpu_percentage = (cpu_abs / uptime) * 100.0;
+ writer->json_keyvalue("cpuConsumptionPercent",
+ std::to_string(cpu_percentage));
+ writer->json_keyvalue("maxRss", std::to_string(stats.ru_maxrss * 1024));
+ writer->json_objectstart("pageFaults");
+ writer->json_keyvalue("IORequired", std::to_string(stats.ru_majflt));
+ writer->json_keyvalue("IONotRequired", std::to_string(stats.ru_minflt));
+ writer->json_objectend();
+ writer->json_objectstart("fsActivity");
+ writer->json_keyvalue("reads", std::to_string(stats.ru_inblock));
+ writer->json_keyvalue("writes", std::to_string(stats.ru_oublock));
+ writer->json_objectend();
+ }
+ writer->json_objectend();
+#ifdef RUSAGE_THREAD
+ if (getrusage(RUSAGE_THREAD, &stats) == 0) {
+ writer->json_objectstart("uvthreadResourceUsage");
+#if defined(__APPLE__) || defined(_AIX)
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06d",
+ stats.ru_utime.tv_sec,
+ stats.ru_utime.tv_usec);
+ writer->json_keyvalue("userCpuSeconds", buf);
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06d",
+ stats.ru_stime.tv_sec,
+ stats.ru_stime.tv_usec);
+ writer->json_keyvalue("kernelCpuSeconds", buf);
+#else
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06ld",
+ stats.ru_utime.tv_sec,
+ stats.ru_utime.tv_usec);
+ writer->json_keyvalue("userCpuSeconds", buf);
+ snprintf(buf,
+ sizeof(buf),
+ "%ld.%06ld",
+ stats.ru_stime.tv_sec,
+ stats.ru_stime.tv_usec);
+ writer->json_keyvalue("kernelCpuSeconds", buf);
+#endif
+ cpu_abs = stats.ru_utime.tv_sec + 0.000001 * stats.ru_utime.tv_usec +
+ stats.ru_stime.tv_sec + 0.000001 * stats.ru_stime.tv_usec;
+ cpu_percentage = (cpu_abs / uptime) * 100.0;
+ writer->json_keyvalue("cpuConsumptionPercent",
+ std::to_string(cpu_percentage));
+ writer->json_objectstart("fsActivity");
+ writer->json_keyvalue("reads", std::to_string(stats.ru_inblock));
+ writer->json_keyvalue("writes", std::to_string(stats.ru_oublock));
+ writer->json_objectend();
+ writer->json_objectend();
+ }
+#endif
+}
+#endif
+
+// Report operating system information.
+static void PrintSystemInformation(JSONWriter* writer) {
+#ifndef _WIN32
+ static struct {
+ const char* description;
+ int id;
+ } rlimit_strings[] = {
+ {"core_file_size_blocks", RLIMIT_CORE},
+ {"data_seg_size_kbytes", RLIMIT_DATA},
+ {"file_size_blocks", RLIMIT_FSIZE},
+#if !(defined(_AIX) || defined(__sun))
+ {"max_locked_memory_bytes", RLIMIT_MEMLOCK},
+#endif
+#ifndef __sun
+ {"max_memory_size_kbytes", RLIMIT_RSS},
+#endif
+ {"open_files", RLIMIT_NOFILE},
+ {"stack_size_bytes", RLIMIT_STACK},
+ {"cpu_time_seconds", RLIMIT_CPU},
+#ifndef __sun
+ {"max_user_processes", RLIMIT_NPROC},
+#endif
+ {"virtual_memory_kbytes", RLIMIT_AS}
+ };
+#endif // _WIN32
+ writer->json_objectstart("environmentVariables");
+ Mutex::ScopedLock lock(node::per_process::env_var_mutex);
+#ifdef _WIN32
+ LPWSTR lpszVariable;
+ LPWCH lpvEnv;
+
+ // Get pointer to the environment block
+ lpvEnv = GetEnvironmentStringsW();
+ if (lpvEnv != nullptr) {
+ // Variable strings are separated by null bytes,
+ // and the block is terminated by a null byte.
+ lpszVariable = reinterpret_cast<LPWSTR>(lpvEnv);
+ while (*lpszVariable) {
+ DWORD size = WideCharToMultiByte(
+ CP_UTF8, 0, lpszVariable, -1, nullptr, 0, nullptr, nullptr);
+ char* str = new char[size];
+ WideCharToMultiByte(
+ CP_UTF8, 0, lpszVariable, -1, str, size, nullptr, nullptr);
+ std::string env(str);
+ int sep = env.rfind("=");
+ std::string key = env.substr(0, sep);
+ std::string value = env.substr(sep + 1);
+ writer->json_keyvalue(key, value);
+ lpszVariable += lstrlenW(lpszVariable) + 1;
+ }
+ FreeEnvironmentStringsW(lpvEnv);
+ }
+ writer->json_objectend();
+#else
+ std::string pair;
+ for (char** env = environ; *env != nullptr; ++env) {
+ std::string pair(*env);
+ int separator = pair.find('=');
+ std::string key = pair.substr(0, separator);
+ std::string str = pair.substr(separator + 1);
+ writer->json_keyvalue(key, str);
+ }
+ writer->json_objectend();
+
+ writer->json_objectstart("userLimits");
+ struct rlimit limit;
+ char buf[64];
+ std::string soft, hard;
+
+ for (size_t i = 0; i < arraysize(rlimit_strings); i++) {
+ if (getrlimit(rlimit_strings[i].id, &limit) == 0) {
+ if (limit.rlim_cur == RLIM_INFINITY) {
+ soft = std::string("unlimited");
+ } else {
+#if defined(_AIX) || defined(__sun)
+ snprintf(buf, sizeof(buf), "%ld", limit.rlim_cur);
+ soft = std::string(buf);
+#elif defined(__linux__) && !defined(__GLIBC__)
+ snprintf(buf, sizeof(buf), "%ld", limit.rlim_cur);
+ soft = std::string(buf);
+#else
+ snprintf(buf, sizeof(buf), "%16" PRIu64, limit.rlim_cur);
+ soft = std::string(soft);
+#endif
+ }
+ if (limit.rlim_max == RLIM_INFINITY) {
+ hard = std::string("unlimited");
+ } else {
+#ifdef _AIX
+ snprintf(buf, sizeof(buf), "%lu", limit.rlim_max);
+ hard = std::string(buf);
+#else
+ snprintf(buf, sizeof(buf), "%lu", limit.rlim_max);
+ hard = std::string(buf);
+#endif
+ }
+ writer->json_objectstart(rlimit_strings[i].description);
+ writer->json_keyvalue("soft", soft);
+ writer->json_keyvalue("hard", hard);
+ writer->json_objectend();
+ }
+ }
+ writer->json_objectend();
+#endif
+
+ PrintLoadedLibraries(writer);
+}
+
+// Report a list of loaded native libraries.
+static void PrintLoadedLibraries(JSONWriter* writer) {
+ writer->json_arraystart("sharedObjects");
+ std::vector<std::string> modules =
+ NativeSymbolDebuggingContext::GetLoadedLibraries();
+ for (auto const& module_name : modules) writer->json_element(module_name);
+ writer->json_arrayend();
+}
+
+// Obtain and report the node and subcomponent version strings.
+static void PrintComponentVersions(JSONWriter* writer) {
+ std::stringstream buf;
+
+ writer->json_objectstart("componentVersions");
+
+#define V(key) \
+ writer->json_keyvalue(#key, node::per_process::metadata.versions.key.c_str());
+ NODE_VERSIONS_KEYS(V)
+#undef V
+
+ // Some extra information that is not present in node_metadata.
+ writer->json_keyvalue("arch", NODE_ARCH);
+ writer->json_keyvalue("platform", NODE_PLATFORM);
+ writer->json_keyvalue("release", NODE_RELEASE);
+ if (NODE_VERSION_IS_LTS != 0)
+ writer->json_keyvalue("lts", NODE_VERSION_LTS_CODENAME);
+ writer->json_objectend();
+}
+
+static void LocalTime(TIME_TYPE* tm_struct) {
+#ifdef _WIN32
+ GetLocalTime(tm_struct);
+#else // UNIX, OSX
+ struct timeval time_val;
+ gettimeofday(&time_val, nullptr);
+ localtime_r(&time_val.tv_sec, tm_struct);
+#endif
+}
+
+} // namespace report