Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

report: expose report public native apis #44255

Merged
merged 1 commit into from Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions node.gyp
Expand Up @@ -993,6 +993,7 @@
'test/cctest/test_node_api.cc',
'test/cctest/test_per_process.cc',
'test/cctest/test_platform.cc',
'test/cctest/test_report.cc',
'test/cctest/test_json_utils.cc',
'test/cctest/test_sockaddr.cc',
'test/cctest/test_traced_value.cc',
Expand Down
30 changes: 29 additions & 1 deletion src/node.h
Expand Up @@ -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.
Expand Down Expand Up @@ -617,6 +618,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,
legendecas marked this conversation as resolved.
Show resolved Hide resolved
gireeshpunathil marked this conversation as resolved.
Show resolved Hide resolved
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);
legendecas marked this conversation as resolved.
Show resolved Hide resolved

// This returns the MultiIsolatePlatform used for an Environment or IsolateData
// instance, if one exists.
NODE_EXTERN MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env);
Expand Down
17 changes: 3 additions & 14 deletions src/node_errors.cc
Expand Up @@ -449,8 +449,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) {
Expand Down Expand Up @@ -482,19 +481,14 @@ 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);
report_on_fatalerror = per_process::cli_options->report_on_fatalerror;
}

if (report_on_fatalerror) {
report::TriggerNodeReport(
isolate, env, message, "FatalError", "", Local<Object>());
TriggerNodeReport(isolate, message, "FatalError", "", Local<Object>());
}

fflush(stderr);
Expand All @@ -512,19 +506,14 @@ 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);
report_on_fatalerror = per_process::cli_options->report_on_fatalerror;
}

if (report_on_fatalerror) {
report::TriggerNodeReport(
isolate, env, message, "OOMError", "", Local<Object>());
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>());
}

fflush(stderr);
Expand Down
243 changes: 137 additions & 106 deletions 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"
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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