From 37c4f0559ae2e75022bfd738b1dff3fed889d56e Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 23 Jan 2019 00:47:34 +0100 Subject: [PATCH 1/2] report: refactor JSON writer - Support non-string entry types - Prefer single-character writes over string writes - Rename the state constants and adjust style to match more common Node.js style --- src/node_report.h | 101 +++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/src/node_report.h b/src/node_report.h index 4c29c13afc3670..434504ef6a4149 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -69,93 +70,109 @@ extern double prog_start_time; // JSON compiler definitions. class JSONWriter { public: - explicit JSONWriter(std::ostream& out) - : out_(out), indent_(0), state_(JSONOBJECT) {} + explicit JSONWriter(std::ostream& out) : out_(out) {} inline void indent() { indent_ += 2; } inline void deindent() { indent_ -= 2; } inline void advance() { - for (int i = 0; i < indent_; i++) out_ << " "; + for (int i = 0; i < indent_; i++) out_ << ' '; } inline void json_start() { - if (state_ == JSONVALUE) out_ << ","; - out_ << "\n"; + if (state_ == kAfterValue) out_ << ','; + out_ << '\n'; advance(); - out_ << "{"; + out_ << '{'; indent(); - state_ = JSONOBJECT; + state_ = kObjectStart; } inline void json_end() { - out_ << "\n"; + out_ << '\n'; deindent(); advance(); - out_ << "}"; - state_ = JSONVALUE; + out_ << '}'; + state_ = kAfterValue; } template inline void json_objectstart(T key) { - if (state_ == JSONVALUE) out_ << ","; - out_ << "\n"; + if (state_ == kAfterValue) out_ << ','; + out_ << '\n'; advance(); - out_ << "\"" << key << "\"" - << ": {"; + write_string(key); + out_ << ": {"; indent(); - state_ = JSONOBJECT; + state_ = kObjectStart; } template inline void json_arraystart(T key) { - if (state_ == JSONVALUE) out_ << ","; - out_ << "\n"; + if (state_ == kAfterValue) out_ << ','; + out_ << '\n'; advance(); - out_ << "\"" << key << "\"" - << ": ["; + write_string(key); + out_ << ": ["; indent(); - state_ = JSONOBJECT; + state_ = kObjectStart; } inline void json_objectend() { - out_ << "\n"; + out_ << '\n'; deindent(); advance(); - out_ << "}"; - state_ = JSONVALUE; + out_ << '}'; + state_ = kAfterValue; } inline void json_arrayend() { - out_ << "\n"; + out_ << '\n'; deindent(); advance(); - out_ << "]"; - state_ = JSONVALUE; + out_ << ']'; + state_ = kAfterValue; } template - inline void json_keyvalue(T key, U value) { - if (state_ == JSONVALUE) out_ << ","; - out_ << "\n"; + inline void json_keyvalue(const T& key, const U& value) { + if (state_ == kAfterValue) out_ << ','; + out_ << '\n'; advance(); - out_ << "\"" << key << "\"" - << ": " - << "\""; - out_ << EscapeJsonChars(value) << "\""; - state_ = JSONVALUE; + write_string(key); + out_ << ": "; + write_value(value); + state_ = kAfterValue; } template - inline void json_element(U value) { - if (state_ == JSONVALUE) out_ << ","; - out_ << "\n"; + inline void json_element(const U& value) { + if (state_ == kAfterValue) out_ << ','; + out_ << '\n'; advance(); - out_ << "\"" << EscapeJsonChars(value) << "\""; - state_ = JSONVALUE; + write_value(value); + state_ = kAfterValue; } private: - enum JSONState { JSONOBJECT, JSONVALUE }; + template ::is_specialized, bool>::type> + inline void write_value(T number) { + if (std::is_same::value) + out_ << (number ? "true" : "false"); + else + out_ << number; + } + + inline void write_value(const char* str) { write_string(str); } + inline void write_value(const std::string& str) { write_string(str); } + + inline void write_string(const std::string& str) { + out_ << '"' << EscapeJsonChars(str) << '"'; + } + inline void write_string(const char* str) { write_string(std::string(str)); } + + enum JSONState { kObjectStart, kAfterValue }; std::ostream& out_; - int indent_; - int state_; + int indent_ = 0; + int state_ = kObjectStart; }; } // namespace report From ee1b9ce9122601aeaf1c78c5e2aedfc8fae80c58 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 23 Jan 2019 00:49:17 +0100 Subject: [PATCH 2/2] report: represent numbers as numbers Do not stringify numbers and boolean values when writing JSON. --- doc/api/report.md | 142 ++++++++++++++++----------------- src/node_report.cc | 168 ++++++++++++--------------------------- src/node_report_utils.cc | 4 +- 3 files changed, 124 insertions(+), 190 deletions(-) diff --git a/doc/api/report.md b/doc/api/report.md index 00340444a86b98..dfb9debd9f6250 100644 --- a/doc/api/report.md +++ b/doc/api/report.md @@ -20,7 +20,7 @@ is provided below for reference. "filename": "report.20181221.005011.8974.001.json", "dumpEventTime": "2018-12-21T00:50:11Z", "dumpEventTimeStamp": "1545371411331", - "processId": "8974", + "processId": 8974, "commandLine": [ "/home/nodeuser/project/node/out/Release/node", "--experimental-report", @@ -74,133 +74,133 @@ is provided below for reference. " [pc=0x1930cae] [/home/nodeuser/project/node/out/Release/node]" ], "javascriptHeap": { - "totalMemory": "6127616", - "totalCommittedMemory": "4357352", - "usedMemory": "3221136", - "availableMemory": "1521370240", - "memoryLimit": "1526909922", + "totalMemory": 6127616, + "totalCommittedMemory": 4357352, + "usedMemory": 3221136, + "availableMemory": 1521370240, + "memoryLimit": 1526909922, "heapSpaces": { "read_only_space": { - "memorySize": "524288", - "committedMemory": "39208", - "capacity": "515584", - "used": "30504", - "available": "485080" + "memorySize": 524288, + "committedMemory": 39208, + "capacity": 515584, + "used": 30504, + "available": 485080 }, "new_space": { - "memorySize": "2097152", - "committedMemory": "2019312", - "capacity": "1031168", - "used": "985496", - "available": "45672" + "memorySize": 2097152, + "committedMemory": 2019312, + "capacity": 1031168, + "used": 985496, + "available": 45672 }, "old_space": { - "memorySize": "2273280", - "committedMemory": "1769008", - "capacity": "1974640", - "used": "1725488", - "available": "249152" + "memorySize": 2273280, + "committedMemory": 1769008, + "capacity": 1974640, + "used": 1725488, + "available": 249152 }, "code_space": { - "memorySize": "696320", - "committedMemory": "184896", - "capacity": "152128", - "used": "152128", - "available": "0" + "memorySize": 696320, + "committedMemory": 184896, + "capacity": 152128, + "used": 152128, + "available": 0 }, "map_space": { - "memorySize": "536576", - "committedMemory": "344928", - "capacity": "327520", - "used": "327520", - "available": "0" + "memorySize": 536576, + "committedMemory": 344928, + "capacity": 327520, + "used": 327520, + "available": 0 }, "large_object_space": { - "memorySize": "0", - "committedMemory": "0", - "capacity": "1520590336", - "used": "0", - "available": "1520590336" + "memorySize": 0, + "committedMemory": 0, + "capacity": 1520590336, + "used": 0, + "available": 1520590336 }, "new_large_object_space": { - "memorySize": "0", - "committedMemory": "0", - "capacity": "0", - "used": "0", - "available": "0" + "memorySize": 0, + "committedMemory": 0, + "capacity": 0, + "used": 0, + "available": 0 } } }, "resourceUsage": { - "userCpuSeconds": "0.069595", - "kernelCpuSeconds": "0.019163", - "cpuConsumptionPercent": "0.000000", - "maxRss": "18079744", + "userCpuSeconds": 0.069595, + "kernelCpuSeconds": 0.019163, + "cpuConsumptionPercent": 0.000000, + "maxRss": 18079744, "pageFaults": { - "IORequired": "0", - "IONotRequired": "4610" + "IORequired": 0, + "IONotRequired": 4610 }, "fsActivity": { - "reads": "0", - "writes": "0" + "reads": 0, + "writes": 0 } }, "uvthreadResourceUsage": { - "userCpuSeconds": "0.068457", - "kernelCpuSeconds": "0.019127", - "cpuConsumptionPercent": "0.000000", + "userCpuSeconds": 0.068457, + "kernelCpuSeconds": 0.019127, + "cpuConsumptionPercent": 0.000000, "fsActivity": { - "reads": "0", - "writes": "0" + "reads": 0, + "writes": 0 } }, "libuv": [ { "type": "async", - "is_active": "1", - "is_referenced": "0", + "is_active": true, + "is_referenced": false, "address": "68090592", "details": "" }, { "type": "timer", - "is_active": "0", - "is_referenced": "0", + "is_active": false, + "is_referenced": false, "address": "140723513949920", "details": "repeat: 0, timeout expired: 18075165916 ms ago" }, { "type": "check", - "is_active": "1", - "is_referenced": "0", + "is_active": true, + "is_referenced": false, "address": "140723513950072", "details": "" }, { "type": "idle", - "is_active": "0", - "is_referenced": "1", + "is_active": false, + "is_referenced": true, "address": "140723513950192", "details": "" }, { "type": "prepare", - "is_active": "0", - "is_referenced": "0", + "is_active": false, + "is_referenced": false, "address": "140723513950312", "details": "" }, { "type": "check", - "is_active": "0", - "is_referenced": "0", + "is_active": false, + "is_referenced": false, "address": "140723513950432", "details": "" }, { "type": "async", - "is_active": "1", - "is_referenced": "0", + "is_active": true, + "is_referenced": false, "address": "39353856", "details": "" } @@ -257,7 +257,7 @@ is provided below for reference. }, "max_locked_memory_bytes": { "soft": "unlimited", - "hard": "65536" + "hard": 65536 }, "max_memory_size_kbytes": { "soft": "unlimited", @@ -265,7 +265,7 @@ is provided below for reference. }, "open_files": { "soft": "unlimited", - "hard": "4096" + "hard": 4096 }, "stack_size_bytes": { "soft": "unlimited", @@ -277,7 +277,7 @@ is provided below for reference. }, "max_user_processes": { "soft": "unlimited", - "hard": "4127290" + "hard": 4127290 }, "virtual_memory_kbytes": { "soft": "unlimited", diff --git a/src/node_report.cc b/src/node_report.cc index 9b936a506d332a..9e09fc1677893b 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -222,7 +222,6 @@ static void WriteNodeReport(Isolate* isolate, std::ostream& out, Local stackstr, TIME_TYPE* tm_struct) { - std::ostringstream buf; uv_pid_t pid = uv_os_getpid(); // Save formatting for output stream. @@ -241,7 +240,7 @@ static void WriteNodeReport(Isolate* isolate, if (!filename.empty()) writer.json_keyvalue("filename", filename); else - writer.json_keyvalue("filename", std::string("''")); + writer.json_keyvalue("filename", "''"); // Report dump event and module load date/time stamps char timebuf[64]; @@ -273,9 +272,7 @@ static void WriteNodeReport(Isolate* isolate, 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(); + writer.json_keyvalue("processId", pid); // Report out the command line. if (!node::per_process::cli_options->cmdline.empty()) { @@ -334,9 +331,7 @@ static void PrintVersionInformation(JSONWriter* writer) { buf.str(""); #endif // Report Process word size - buf << sizeof(void*) * 8 << " bit"; - writer->json_keyvalue("wordSize", buf.str()); - buf.str(""); + writer->json_keyvalue("wordSize", sizeof(void*) * 8); // Report deps component versions PrintComponentVersions(writer); @@ -470,7 +465,7 @@ static void PrintJavaScriptStack(JSONWriter* writer, } int line = ss.find("\n"); if (line == -1) { - writer->json_keyvalue("message", ss.c_str()); + writer->json_keyvalue("message", ss); writer->json_objectend(); } else { std::string l = ss.substr(0, line); @@ -524,16 +519,13 @@ static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate) { 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("totalMemory", 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())); + v8_heap_stats.total_physical_size()); + writer->json_keyvalue("usedMemory", 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())); + v8_heap_stats.total_available_size()); + writer->json_keyvalue("memoryLimit", v8_heap_stats.heap_size_limit()); writer->json_objectstart("heapSpaces"); // Loop through heap spaces @@ -541,37 +533,31 @@ static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate) { 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("memorySize", v8_heap_space_stats.space_size()); writer->json_keyvalue( "committedMemory", - std::to_string(v8_heap_space_stats.physical_space_size())); + 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())); + v8_heap_space_stats.space_used_size() + + v8_heap_space_stats.space_available_size()); + writer->json_keyvalue("used", v8_heap_space_stats.space_used_size()); writer->json_keyvalue( - "available", - std::to_string(v8_heap_space_stats.space_available_size())); + "available", 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("memorySize", v8_heap_space_stats.space_size()); writer->json_keyvalue( - "committedMemory", - std::to_string(v8_heap_space_stats.physical_space_size())); + "committedMemory", 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())); + v8_heap_space_stats.space_used_size() + + v8_heap_space_stats.space_available_size()); + writer->json_keyvalue("used", v8_heap_space_stats.space_used_size()); writer->json_keyvalue( - "available", std::to_string(v8_heap_space_stats.space_available_size())); + "available", v8_heap_space_stats.space_available_size()); writer->json_objectend(); writer->json_objectend(); writer->json_objectend(); @@ -580,9 +566,6 @@ static void PrintGCStatistics(JSONWriter* writer, Isolate* isolate) { #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(¤t_time); size_t boot_time = static_cast(node::per_process::prog_start_time / @@ -594,87 +577,39 @@ static void PrintResourceUsage(JSONWriter* writer) { 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)); + double user_cpu = stats.ru_utime.tv_sec + 0.000001 * stats.ru_utime.tv_usec; + double kernel_cpu = + stats.ru_utime.tv_sec + 0.000001 * stats.ru_utime.tv_usec; + writer->json_keyvalue("userCpuSeconds", user_cpu); + writer->json_keyvalue("kernelCpuSeconds", kernel_cpu); + double cpu_abs = user_cpu + kernel_cpu; + double cpu_percentage = (cpu_abs / uptime) * 100.0; + writer->json_keyvalue("cpuConsumptionPercent", cpu_percentage); + writer->json_keyvalue("maxRss", 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_keyvalue("IORequired", stats.ru_majflt); + writer->json_keyvalue("IONotRequired", 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_keyvalue("reads", stats.ru_inblock); + writer->json_keyvalue("writes", 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)); + double user_cpu = stats.ru_utime.tv_sec + 0.000001 * stats.ru_utime.tv_usec; + double kernel_cpu = + stats.ru_utime.tv_sec + 0.000001 * stats.ru_utime.tv_usec; + writer->json_keyvalue("userCpuSeconds", user_cpu); + writer->json_keyvalue("kernelCpuSeconds", kernel_cpu); + double cpu_abs = user_cpu + kernel_cpu; + double cpu_percentage = (cpu_abs / uptime) * 100.0; + writer->json_keyvalue("cpuConsumptionPercent", 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_keyvalue("reads", stats.ru_inblock); + writer->json_keyvalue("writes", stats.ru_oublock); writer->json_objectend(); writer->json_objectend(); } @@ -752,19 +687,18 @@ static void PrintSystemInformation(JSONWriter* writer) { for (size_t i = 0; i < arraysize(rlimit_strings); i++) { if (getrlimit(rlimit_strings[i].id, &limit) == 0) { + writer->json_objectstart(rlimit_strings[i].description); + if (limit.rlim_cur == RLIM_INFINITY) - soft = std::string("unlimited"); + writer->json_keyvalue("soft", "unlimited"); else - soft = std::to_string(limit.rlim_cur); + writer->json_keyvalue("soft", limit.rlim_cur); if (limit.rlim_max == RLIM_INFINITY) - hard = std::string("unlimited"); + writer->json_keyvalue("hard", "unlimited"); else - hard = std::to_string(limit.rlim_max); + writer->json_keyvalue("hard", limit.rlim_max); - writer->json_objectstart(rlimit_strings[i].description); - writer->json_keyvalue("soft", soft); - writer->json_keyvalue("hard", hard); writer->json_objectend(); } } @@ -790,7 +724,7 @@ static void PrintComponentVersions(JSONWriter* writer) { writer->json_objectstart("componentVersions"); #define V(key) \ - writer->json_keyvalue(#key, node::per_process::metadata.versions.key.c_str()); + writer->json_keyvalue(#key, node::per_process::metadata.versions.key); NODE_VERSIONS_KEYS(V) #undef V diff --git a/src/node_report_utils.cc b/src/node_report_utils.cc index f09ee507d3aeb5..a9b8dc2ceb2efe 100644 --- a/src/node_report_utils.cc +++ b/src/node_report_utils.cc @@ -257,8 +257,8 @@ void WalkHandle(uv_handle_t* h, void* arg) { writer->json_start(); writer->json_keyvalue("type", type); - writer->json_keyvalue("is_active", std::to_string(uv_is_active(h))); - writer->json_keyvalue("is_referenced", std::to_string(uv_has_ref(h))); + writer->json_keyvalue("is_active", static_cast(uv_is_active(h))); + writer->json_keyvalue("is_referenced", static_cast(uv_has_ref(h))); writer->json_keyvalue("address", std::to_string(reinterpret_cast(h))); writer->json_keyvalue("details", data.str());