diff options
author | Chengzhong Wu <chengzhong.wcz@alibaba-inc.com> | 2022-08-24 20:02:26 +0300 |
---|---|---|
committer | Juan José Arboleda <soyjuanarbol@gmail.com> | 2022-10-11 22:45:21 +0300 |
commit | 381e11e18e20e7d10ce29c019df28573cc4594fc (patch) | |
tree | ca1bc3e44edf979c234ab029e848734c3f054d75 /src | |
parent | b53ea08d7b07a5b8a84d8e9da2d199dfa253a65b (diff) |
report: expose report public native apis
Allows APM vendors to generate a diagnostic report without calling into
JavaScript. Like, from their own message channels interrupting the
isolate and generating a report on demand.
PR-URL: https://github.com/nodejs/node/pull/44255
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/node.h | 30 | ||||
-rw-r--r-- | src/node_errors.cc | 17 | ||||
-rw-r--r-- | src/node_report.cc | 243 | ||||
-rw-r--r-- | src/node_report.h | 16 | ||||
-rw-r--r-- | src/node_report_module.cc | 6 |
5 files changed, 172 insertions, 140 deletions
diff --git a/src/node.h b/src/node.h index 4be002ac18f..b787a474888 100644 --- a/src/node.h +++ b/src/node.h @@ -75,8 +75,9 @@ #include "v8-platform.h" // NOLINT(build/include_order) #include "node_version.h" // NODE_MODULE_VERSION -#include <memory> #include <functional> +#include <memory> +#include <ostream> // We cannot use __POSIX__ in this header because that's only defined when // building Node.js. @@ -528,6 +529,33 @@ NODE_EXTERN v8::MaybeLocal<v8::Value> PrepareStackTraceCallback( v8::Local<v8::Value> exception, v8::Local<v8::Array> trace); +// Writes a diagnostic report to a file. If filename is not provided, the +// default filename includes the date, time, PID, and a sequence number. +// The report's JavaScript stack trace is taken from err, if present. +// If isolate is nullptr, no information about the JavaScript environment +// is included in the report. +// Returns the filename of the written report. +NODE_EXTERN std::string TriggerNodeReport(v8::Isolate* isolate, + const char* message, + const char* trigger, + const std::string& filename, + v8::Local<v8::Value> error); +NODE_EXTERN std::string TriggerNodeReport(Environment* env, + const char* message, + const char* trigger, + const std::string& filename, + v8::Local<v8::Value> error); +NODE_EXTERN void GetNodeReport(v8::Isolate* isolate, + const char* message, + const char* trigger, + v8::Local<v8::Value> error, + std::ostream& out); +NODE_EXTERN void GetNodeReport(Environment* env, + const char* message, + const char* trigger, + v8::Local<v8::Value> error, + std::ostream& out); + // This returns the MultiIsolatePlatform used for an Environment or IsolateData // instance, if one exists. NODE_EXTERN MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env); diff --git a/src/node_errors.cc b/src/node_errors.cc index b464a37e378..bfc93ec70d2 100644 --- a/src/node_errors.cc +++ b/src/node_errors.cc @@ -411,8 +411,7 @@ static void ReportFatalException(Environment* env, } if (env->isolate_data()->options()->report_uncaught_exception) { - report::TriggerNodeReport( - isolate, env, report_message.c_str(), "Exception", "", error); + TriggerNodeReport(env, report_message.c_str(), "Exception", "", error); } if (env->options()->trace_uncaught) { @@ -440,10 +439,6 @@ void OnFatalError(const char* location, const char* message) { } Isolate* isolate = Isolate::TryGetCurrent(); - Environment* env = nullptr; - if (isolate != nullptr) { - env = Environment::GetCurrent(isolate); - } bool report_on_fatalerror; { Mutex::ScopedLock lock(node::per_process::cli_options_mutex); @@ -451,8 +446,7 @@ void OnFatalError(const char* location, const char* message) { } if (report_on_fatalerror) { - report::TriggerNodeReport( - isolate, env, message, "FatalError", "", Local<Object>()); + TriggerNodeReport(isolate, message, "FatalError", "", Local<Object>()); } fflush(stderr); @@ -470,10 +464,6 @@ void OOMErrorHandler(const char* location, bool is_heap_oom) { } Isolate* isolate = Isolate::TryGetCurrent(); - Environment* env = nullptr; - if (isolate != nullptr) { - env = Environment::GetCurrent(isolate); - } bool report_on_fatalerror; { Mutex::ScopedLock lock(node::per_process::cli_options_mutex); @@ -481,8 +471,7 @@ void OOMErrorHandler(const char* location, bool is_heap_oom) { } if (report_on_fatalerror) { - report::TriggerNodeReport( - isolate, env, message, "OOMError", "", Local<Object>()); + TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>()); } fflush(stderr); diff --git a/src/node_report.cc b/src/node_report.cc index 446b88303d8..3970f4ec531 100644 --- a/src/node_report.cc +++ b/src/node_report.cc @@ -1,8 +1,8 @@ -#include "env-inl.h" -#include "json_utils.h" #include "node_report.h" #include "debug_utils-inl.h" #include "diagnosticfilename-inl.h" +#include "env-inl.h" +#include "json_utils.h" #include "node_internals.h" #include "node_metadata.h" #include "node_mutex.h" @@ -29,8 +29,6 @@ constexpr double SEC_PER_MICROS = 1e-6; constexpr int MAX_FRAME_COUNT = 10; namespace node { -namespace report { - using node::worker::Worker; using v8::Array; using v8::Context; @@ -53,6 +51,7 @@ using v8::TryCatch; using v8::V8; using v8::Value; +namespace report { // Internal/static function declarations static void WriteNodeReport(Isolate* isolate, Environment* env, @@ -83,102 +82,6 @@ static void PrintRelease(JSONWriter* writer); static void PrintCpuInfo(JSONWriter* writer); static void PrintNetworkInterfaceInfo(JSONWriter* writer); -// External function to trigger a report, writing to file. -std::string TriggerNodeReport(Isolate* isolate, - Environment* env, - const char* message, - const char* trigger, - const std::string& name, - Local<Value> error) { - std::string filename; - - // 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. - filename = name; - } else { - std::string report_filename; - { - Mutex::ScopedLock lock(per_process::cli_options_mutex); - report_filename = per_process::cli_options->report_filename; - } - if (report_filename.length() > 0) { - // File name was supplied via start-up option. - filename = report_filename; - } else { - filename = *DiagnosticFilename(env != nullptr ? env->thread_id() : 0, - "report", "json"); - } - } - - // Open the report file stream for writing. Supports stdout/err, - // user-specified or (default) generated name - std::ofstream outfile; - std::ostream* outstream; - if (filename == "stdout") { - outstream = &std::cout; - } else if (filename == "stderr") { - outstream = &std::cerr; - } else { - std::string report_directory; - { - Mutex::ScopedLock lock(per_process::cli_options_mutex); - report_directory = per_process::cli_options->report_directory; - } - // Regular file. Append filename to directory path if one was specified - if (report_directory.length() > 0) { - std::string pathname = report_directory; - pathname += kPathSeparator; - 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()) { - std::cerr << "\nFailed to open Node.js report file: " << filename; - - if (report_directory.length() > 0) - std::cerr << " directory: " << report_directory; - - std::cerr << " (errno: " << errno << ")" << std::endl; - return ""; - } - outstream = &outfile; - std::cerr << "\nWriting Node.js report to file: " << filename; - } - - bool compact; - { - Mutex::ScopedLock lock(per_process::cli_options_mutex); - compact = per_process::cli_options->report_compact; - } - WriteNodeReport(isolate, env, message, trigger, filename, *outstream, - error, compact); - - // Do not close stdout/stderr, only close files we opened. - if (outfile.is_open()) { - outfile.close(); - } - - // Do not mix JSON and free-form text on stderr. - if (filename != "stderr") { - std::cerr << "\nNode.js report completed" << std::endl; - } - return filename; -} - -// External function to trigger a report, writing to a supplied stream. -void GetNodeReport(Isolate* isolate, - Environment* env, - const char* message, - const char* trigger, - Local<Value> error, - std::ostream& out) { - WriteNodeReport(isolate, env, message, trigger, "", out, error, false); -} - // Internal function to coordinate and write the various // sections of the report to the supplied stream static void WriteNodeReport(Isolate* isolate, @@ -319,12 +222,8 @@ static void WriteNodeReport(Isolate* isolate, expected_results += w->RequestInterrupt([&](Environment* env) { std::ostringstream os; - GetNodeReport(env->isolate(), - env, - "Worker thread subreport", - trigger, - Local<Value>(), - os); + GetNodeReport( + env, "Worker thread subreport", trigger, Local<Value>(), os); Mutex::ScopedLock lock(workers_mutex); worker_infos.emplace_back(os.str()); @@ -884,4 +783,136 @@ static void PrintRelease(JSONWriter* writer) { } } // namespace report + +// External function to trigger a report, writing to file. +std::string TriggerNodeReport(Isolate* isolate, + const char* message, + const char* trigger, + const std::string& name, + Local<Value> error) { + Environment* env = nullptr; + if (isolate != nullptr) { + env = Environment::GetCurrent(isolate); + } + return TriggerNodeReport(env, message, trigger, name, error); +} + +// External function to trigger a report, writing to file. +std::string TriggerNodeReport(Environment* env, + const char* message, + const char* trigger, + const std::string& name, + Local<Value> error) { + std::string filename; + + // 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. + filename = name; + } else { + std::string report_filename; + { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + report_filename = per_process::cli_options->report_filename; + } + if (report_filename.length() > 0) { + // File name was supplied via start-up option. + filename = report_filename; + } else { + filename = *DiagnosticFilename( + env != nullptr ? env->thread_id() : 0, "report", "json"); + } + } + + // Open the report file stream for writing. Supports stdout/err, + // user-specified or (default) generated name + std::ofstream outfile; + std::ostream* outstream; + if (filename == "stdout") { + outstream = &std::cout; + } else if (filename == "stderr") { + outstream = &std::cerr; + } else { + std::string report_directory; + { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + report_directory = per_process::cli_options->report_directory; + } + // Regular file. Append filename to directory path if one was specified + if (report_directory.length() > 0) { + std::string pathname = report_directory; + pathname += kPathSeparator; + 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()) { + std::cerr << "\nFailed to open Node.js report file: " << filename; + + if (report_directory.length() > 0) + std::cerr << " directory: " << report_directory; + + std::cerr << " (errno: " << errno << ")" << std::endl; + return ""; + } + outstream = &outfile; + std::cerr << "\nWriting Node.js report to file: " << filename; + } + + bool compact; + { + Mutex::ScopedLock lock(per_process::cli_options_mutex); + compact = per_process::cli_options->report_compact; + } + + Isolate* isolate = nullptr; + if (env != nullptr) { + isolate = env->isolate(); + } + report::WriteNodeReport( + isolate, env, message, trigger, filename, *outstream, error, compact); + + // Do not close stdout/stderr, only close files we opened. + if (outfile.is_open()) { + outfile.close(); + } + + // Do not mix JSON and free-form text on stderr. + if (filename != "stderr") { + std::cerr << "\nNode.js report completed" << std::endl; + } + return filename; +} + +// External function to trigger a report, writing to a supplied stream. +void GetNodeReport(Isolate* isolate, + const char* message, + const char* trigger, + Local<Value> error, + std::ostream& out) { + Environment* env = nullptr; + if (isolate != nullptr) { + env = Environment::GetCurrent(isolate); + } + report::WriteNodeReport( + isolate, env, message, trigger, "", out, error, false); +} + +// External function to trigger a report, writing to a supplied stream. +void GetNodeReport(Environment* env, + const char* message, + const char* trigger, + Local<Value> error, + std::ostream& out) { + Isolate* isolate = nullptr; + if (env != nullptr) { + isolate = env->isolate(); + } + report::WriteNodeReport( + isolate, env, message, trigger, "", out, error, false); +} + } // namespace node diff --git a/src/node_report.h b/src/node_report.h index dde48f14ec0..7a2e817ac82 100644 --- a/src/node_report.h +++ b/src/node_report.h @@ -14,24 +14,10 @@ #endif #include <iomanip> +#include <sstream> namespace node { namespace report { - -// Function declarations - functions in src/node_report.cc -std::string TriggerNodeReport(v8::Isolate* isolate, - Environment* env, - const char* message, - const char* trigger, - const std::string& name, - v8::Local<v8::Value> error); -void GetNodeReport(v8::Isolate* isolate, - Environment* env, - const char* message, - const char* trigger, - v8::Local<v8::Value> error, - std::ostream& out); - // Function declarations - utility functions in src/node_report_utils.cc void WalkHandle(uv_handle_t* h, void* arg); diff --git a/src/node_report_module.cc b/src/node_report_module.cc index b720ef33281..36db9add273 100644 --- a/src/node_report_module.cc +++ b/src/node_report_module.cc @@ -47,8 +47,7 @@ void WriteReport(const FunctionCallbackInfo<Value>& info) { else error = Local<Value>(); - filename = TriggerNodeReport( - isolate, env, *message, *trigger, filename, error); + filename = TriggerNodeReport(env, *message, *trigger, filename, error); // Return value is the report filename info.GetReturnValue().Set( String::NewFromUtf8(isolate, filename.c_str()).ToLocalChecked()); @@ -68,8 +67,7 @@ void GetReport(const FunctionCallbackInfo<Value>& info) { else error = Local<Object>(); - GetNodeReport( - isolate, env, "JavaScript API", __func__, error, out); + GetNodeReport(env, "JavaScript API", __func__, error, out); // Return value is the contents of a report as a string. info.GetReturnValue().Set( |