From 10fa1f5a67e2dc757ee63b2e46d2742a9515942c Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Thu, 24 Nov 2016 14:57:45 -0500 Subject: [PATCH] Fix version reporting in NodeReport section Report the versions of Node.js and its components based on the runtime and not compile time constants. Report NodeReport version and the version of Node.js it was built against. Extend the tests to validate the versions reported in the report. Fixes: nodejs/nodereport#29 --- binding.gyp | 3 + src/module.cc | 1 + src/node_report.cc | 119 +++++++++++++++++++++++++-- src/node_report.h | 1 + test/common.js | 54 ++++++++++-- test/test-api-bad-processobj.js | 24 ++++++ test/test-api-bad-processversion.js | 24 ++++++ test/test-api-bad-processversions.js | 26 ++++++ test/test-api-noversioninfo.js | 28 +++++++ test/test-api.js | 2 +- test/test-exception.js | 2 +- test/test-fatal-error.js | 2 +- test/test-signal.js | 2 +- 13 files changed, 269 insertions(+), 19 deletions(-) create mode 100644 test/test-api-bad-processobj.js create mode 100644 test/test-api-bad-processversion.js create mode 100644 test/test-api-bad-processversions.js create mode 100644 test/test-api-noversioninfo.js diff --git a/binding.gyp b/binding.gyp index 3d0be4e..c665e5b 100644 --- a/binding.gyp +++ b/binding.gyp @@ -14,6 +14,9 @@ "dll_files": [ "dbghelp.dll", "Netapi32.dll" ], }], ], + "defines": [ + 'NODEREPORT_VERSION=" exports) { node_isolate = isolate; SetLoadTime(); + SetVersionString(isolate); const char* verbose_switch = secure_getenv("NODEREPORT_VERBOSE"); if (verbose_switch != nullptr) { diff --git a/src/node_report.cc b/src/node_report.cc index a54d8ec..bb20b02 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -1,11 +1,9 @@ #include "node_report.h" -#include "node_version.h" #include "v8.h" #include "time.h" -#include "zlib.h" -#include "ares.h" #include +#include #include #include #include @@ -35,6 +33,14 @@ #include #endif +#if !defined(NODEREPORT_VERSION) +#define NODEREPORT_VERSION "dev" +#endif + +#if !defined(UNKNOWN_NODEVERSION_STRING) +#define UNKNOWN_NODEVERSION_STRING "Unable to determine Node.js version\n" +#endif + #ifndef _WIN32 extern char** environ; #endif @@ -52,7 +58,7 @@ using v8::String; using v8::V8; // Internal/static function declarations -static void PrintVersionInformation(FILE* fp); +static void PrintVersionInformation(FILE* fp, Isolate* isolate); static void PrintJavaScriptStack(FILE* fp, Isolate* isolate, DumpEvent event, const char* location); static void PrintStackFromStackTrace(FILE* fp, Isolate* isolate, DumpEvent event); static void PrintStackFrame(FILE* fp, Isolate* isolate, Local frame, int index, void* pc); @@ -70,6 +76,7 @@ const char* v8_states[] = {"JS", "GC", "COMPILER", "OTHER", "EXTERNAL", "IDLE"}; static bool report_active = false; // recursion protection static char report_filename[NR_MAXNAME + 1] = ""; static char report_directory[NR_MAXPATH + 1] = ""; // defaults to current working directory +static std::string version_string = UNKNOWN_NODEVERSION_STRING; #ifdef _WIN32 static SYSTEMTIME loadtime_tm_struct; // module load time #else // UNIX, OSX @@ -189,6 +196,97 @@ unsigned int ProcessNodeReportVerboseSwitch(const char* args) { return 0; // Default is verbose mode off } +void SetVersionString(Isolate* isolate) { + // Catch anything thrown and gracefully return + Nan::TryCatch trycatch; + version_string = UNKNOWN_NODEVERSION_STRING; + + // Retrieve the process object + v8::Local process_prop; + if (!Nan::New("process").ToLocal(&process_prop)) return; + v8::Local global_obj = isolate->GetCurrentContext()->Global(); + v8::Local process_value; + if (!Nan::Get(global_obj, process_prop).ToLocal(&process_value)) return; + if (!process_value->IsObject()) return; + v8::Local process_obj = process_value.As(); + + // Get process.version + v8::Local version_prop; + if (!Nan::New("version").ToLocal(&version_prop)) return; + v8::Local version; + if (!Nan::Get(process_obj, version_prop).ToLocal(&version)) return; + + // e.g. Node.js version: v6.9.1 + if (version->IsString()) { + Nan::Utf8String node_version(version); + version_string = "Node.js version: "; + version_string += *node_version; + version_string += "\n"; + } + + // Get process.versions + v8::Local versions_prop; + if (!Nan::New("versions").ToLocal(&versions_prop)) return; + v8::Local versions_value; + if (!Nan::Get(process_obj, versions_prop).ToLocal(&versions_value)) return; + if (!versions_value->IsObject()) return; + v8::Local versions_obj = versions_value.As(); + + // Get component names and versions from process.versions + v8::Local components; + if (!Nan::GetOwnPropertyNames(versions_obj).ToLocal(&components)) return; + v8::Local components_obj = components.As(); + std::map comp_versions; + uint32_t total_components = (*components)->Length(); + for (uint32_t i = 0; i < total_components; i++) { + v8::Local name_value; + if (!Nan::Get(components_obj, i).ToLocal(&name_value)) continue; + v8::Local version_value; + if (!Nan::Get(versions_obj, name_value).ToLocal(&version_value)) continue; + + Nan::Utf8String component_name(name_value); + Nan::Utf8String component_version(version_value); + if (*component_name == nullptr || *component_version == nullptr) continue; + + // Don't duplicate the Node.js version + if (!strcmp("node", *component_name)) { + if (version_string == UNKNOWN_NODEVERSION_STRING) { + version_string = "Node.js version: v"; + version_string += *component_version; + version_string += "\n"; + } + } else { + comp_versions[*component_name] = *component_version; + } + } + + // Format sorted component versions surrounded by (), wrapped + // e.g. + // (ares: 1.10.1-DEV, http_parser: 2.7.0, icu: 57.1, modules: 48, + // openssl: 1.0.2j, uv: 1.9.1, v8: 5.1.281.84, zlib: 1.2.8) + const size_t wrap = 80; + size_t line_length = 1; + version_string += "("; + for (auto it : comp_versions) { + std::string component_name = it.first; + std::string component_version = it.second; + size_t length = component_name.length() + component_version.length() + 2; + if (line_length + length + 1 >= wrap) { + version_string += "\n "; + line_length = 1; + } + version_string += component_name; + version_string += ": "; + version_string += component_version; + line_length += length; + if (it != *std::prev(comp_versions.end())) { + version_string += ", "; + line_length += 2; + } + } + version_string += ")\n"; +} + /******************************************************************************* * Function to save the nodereport module load time value *******************************************************************************/ @@ -317,7 +415,7 @@ void TriggerNodeReport(Isolate* isolate, DumpEvent event, const char* message, c fflush(fp); // Print Node.js and OS version information - PrintVersionInformation(fp); + PrintVersionInformation(fp, isolate); fflush(fp); // Print summary JavaScript stack backtrace @@ -369,12 +467,15 @@ void TriggerNodeReport(Isolate* isolate, DumpEvent event, const char* message, c * Function to print Node.js version, OS version and machine information * ******************************************************************************/ -static void PrintVersionInformation(FILE* fp) { +static void PrintVersionInformation(FILE* fp, Isolate* isolate) { // Print Node.js and deps component versions - fprintf(fp, "\nNode.js version: %s\n", NODE_VERSION); - fprintf(fp, "(v8: %s, libuv: %s, zlib: %s, ares: %s)\n", - V8::GetVersion(), uv_version_string(), ZLIB_VERSION, ARES_VERSION_STR); + fprintf(fp, "\n%s", version_string.c_str()); + + // Print NodeReport version + // e.g. NodeReport version: 1.0.6 (built against Node.js v6.9.1) + fprintf(fp, "\nNodeReport version: %s (built against Node.js v%s)\n", + NODEREPORT_VERSION, NODE_VERSION_STRING); // Print operating system and machine information (Windows) #ifdef _WIN32 diff --git a/src/node_report.h b/src/node_report.h index 624b2b1..210669e 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -43,6 +43,7 @@ void ProcessNodeReportDirectory(const char* args); unsigned int ProcessNodeReportVerboseSwitch(const char* args); void SetLoadTime(); +void SetVersionString(Isolate* isolate); // Local implementation of secure_getenv() inline const char* secure_getenv(const char* key) { diff --git a/test/common.js b/test/common.js index e50fd33..bab9cf8 100644 --- a/test/common.js +++ b/test/common.js @@ -9,6 +9,8 @@ const REPORT_SECTIONS = [ 'System Information' ]; +const reNewline = '(?:\\r*\\n)'; + exports.findReports = (pid) => { // Default filenames are of the form NodeReport..