Skip to content

Commit 381e11e

Browse files
legendecasjuanarbol
authored andcommittedOct 11, 2022
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: #44255 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com>
1 parent b53ea08 commit 381e11e

File tree

10 files changed

+404
-140
lines changed

10 files changed

+404
-140
lines changed
 

‎node.gyp

+1
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,7 @@
11781178
'test/cctest/test_node_api.cc',
11791179
'test/cctest/test_per_process.cc',
11801180
'test/cctest/test_platform.cc',
1181+
'test/cctest/test_report.cc',
11811182
'test/cctest/test_json_utils.cc',
11821183
'test/cctest/test_sockaddr.cc',
11831184
'test/cctest/test_traced_value.cc',

‎src/node.h

+29-1
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@
7575
#include "v8-platform.h" // NOLINT(build/include_order)
7676
#include "node_version.h" // NODE_MODULE_VERSION
7777

78-
#include <memory>
7978
#include <functional>
79+
#include <memory>
80+
#include <ostream>
8081

8182
// We cannot use __POSIX__ in this header because that's only defined when
8283
// building Node.js.
@@ -528,6 +529,33 @@ NODE_EXTERN v8::MaybeLocal<v8::Value> PrepareStackTraceCallback(
528529
v8::Local<v8::Value> exception,
529530
v8::Local<v8::Array> trace);
530531

532+
// Writes a diagnostic report to a file. If filename is not provided, the
533+
// default filename includes the date, time, PID, and a sequence number.
534+
// The report's JavaScript stack trace is taken from err, if present.
535+
// If isolate is nullptr, no information about the JavaScript environment
536+
// is included in the report.
537+
// Returns the filename of the written report.
538+
NODE_EXTERN std::string TriggerNodeReport(v8::Isolate* isolate,
539+
const char* message,
540+
const char* trigger,
541+
const std::string& filename,
542+
v8::Local<v8::Value> error);
543+
NODE_EXTERN std::string TriggerNodeReport(Environment* env,
544+
const char* message,
545+
const char* trigger,
546+
const std::string& filename,
547+
v8::Local<v8::Value> error);
548+
NODE_EXTERN void GetNodeReport(v8::Isolate* isolate,
549+
const char* message,
550+
const char* trigger,
551+
v8::Local<v8::Value> error,
552+
std::ostream& out);
553+
NODE_EXTERN void GetNodeReport(Environment* env,
554+
const char* message,
555+
const char* trigger,
556+
v8::Local<v8::Value> error,
557+
std::ostream& out);
558+
531559
// This returns the MultiIsolatePlatform used for an Environment or IsolateData
532560
// instance, if one exists.
533561
NODE_EXTERN MultiIsolatePlatform* GetMultiIsolatePlatform(Environment* env);

‎src/node_errors.cc

+3-14
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,7 @@ static void ReportFatalException(Environment* env,
411411
}
412412

413413
if (env->isolate_data()->options()->report_uncaught_exception) {
414-
report::TriggerNodeReport(
415-
isolate, env, report_message.c_str(), "Exception", "", error);
414+
TriggerNodeReport(env, report_message.c_str(), "Exception", "", error);
416415
}
417416

418417
if (env->options()->trace_uncaught) {
@@ -440,19 +439,14 @@ void OnFatalError(const char* location, const char* message) {
440439
}
441440

442441
Isolate* isolate = Isolate::TryGetCurrent();
443-
Environment* env = nullptr;
444-
if (isolate != nullptr) {
445-
env = Environment::GetCurrent(isolate);
446-
}
447442
bool report_on_fatalerror;
448443
{
449444
Mutex::ScopedLock lock(node::per_process::cli_options_mutex);
450445
report_on_fatalerror = per_process::cli_options->report_on_fatalerror;
451446
}
452447

453448
if (report_on_fatalerror) {
454-
report::TriggerNodeReport(
455-
isolate, env, message, "FatalError", "", Local<Object>());
449+
TriggerNodeReport(isolate, message, "FatalError", "", Local<Object>());
456450
}
457451

458452
fflush(stderr);
@@ -470,19 +464,14 @@ void OOMErrorHandler(const char* location, bool is_heap_oom) {
470464
}
471465

472466
Isolate* isolate = Isolate::TryGetCurrent();
473-
Environment* env = nullptr;
474-
if (isolate != nullptr) {
475-
env = Environment::GetCurrent(isolate);
476-
}
477467
bool report_on_fatalerror;
478468
{
479469
Mutex::ScopedLock lock(node::per_process::cli_options_mutex);
480470
report_on_fatalerror = per_process::cli_options->report_on_fatalerror;
481471
}
482472

483473
if (report_on_fatalerror) {
484-
report::TriggerNodeReport(
485-
isolate, env, message, "OOMError", "", Local<Object>());
474+
TriggerNodeReport(isolate, message, "OOMError", "", Local<Object>());
486475
}
487476

488477
fflush(stderr);

‎src/node_report.cc

+137-106
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
#include "env-inl.h"
2-
#include "json_utils.h"
31
#include "node_report.h"
42
#include "debug_utils-inl.h"
53
#include "diagnosticfilename-inl.h"
4+
#include "env-inl.h"
5+
#include "json_utils.h"
66
#include "node_internals.h"
77
#include "node_metadata.h"
88
#include "node_mutex.h"
@@ -29,8 +29,6 @@ constexpr double SEC_PER_MICROS = 1e-6;
2929
constexpr int MAX_FRAME_COUNT = 10;
3030

3131
namespace node {
32-
namespace report {
33-
3432
using node::worker::Worker;
3533
using v8::Array;
3634
using v8::Context;
@@ -53,6 +51,7 @@ using v8::TryCatch;
5351
using v8::V8;
5452
using v8::Value;
5553

54+
namespace report {
5655
// Internal/static function declarations
5756
static void WriteNodeReport(Isolate* isolate,
5857
Environment* env,
@@ -83,102 +82,6 @@ static void PrintRelease(JSONWriter* writer);
8382
static void PrintCpuInfo(JSONWriter* writer);
8483
static void PrintNetworkInterfaceInfo(JSONWriter* writer);
8584

86-
// External function to trigger a report, writing to file.
87-
std::string TriggerNodeReport(Isolate* isolate,
88-
Environment* env,
89-
const char* message,
90-
const char* trigger,
91-
const std::string& name,
92-
Local<Value> error) {
93-
std::string filename;
94-
95-
// Determine the required report filename. In order of priority:
96-
// 1) supplied on API 2) configured on startup 3) default generated
97-
if (!name.empty()) {
98-
// Filename was specified as API parameter.
99-
filename = name;
100-
} else {
101-
std::string report_filename;
102-
{
103-
Mutex::ScopedLock lock(per_process::cli_options_mutex);
104-
report_filename = per_process::cli_options->report_filename;
105-
}
106-
if (report_filename.length() > 0) {
107-
// File name was supplied via start-up option.
108-
filename = report_filename;
109-
} else {
110-
filename = *DiagnosticFilename(env != nullptr ? env->thread_id() : 0,
111-
"report", "json");
112-
}
113-
}
114-
115-
// Open the report file stream for writing. Supports stdout/err,
116-
// user-specified or (default) generated name
117-
std::ofstream outfile;
118-
std::ostream* outstream;
119-
if (filename == "stdout") {
120-
outstream = &std::cout;
121-
} else if (filename == "stderr") {
122-
outstream = &std::cerr;
123-
} else {
124-
std::string report_directory;
125-
{
126-
Mutex::ScopedLock lock(per_process::cli_options_mutex);
127-
report_directory = per_process::cli_options->report_directory;
128-
}
129-
// Regular file. Append filename to directory path if one was specified
130-
if (report_directory.length() > 0) {
131-
std::string pathname = report_directory;
132-
pathname += kPathSeparator;
133-
pathname += filename;
134-
outfile.open(pathname, std::ios::out | std::ios::binary);
135-
} else {
136-
outfile.open(filename, std::ios::out | std::ios::binary);
137-
}
138-
// Check for errors on the file open
139-
if (!outfile.is_open()) {
140-
std::cerr << "\nFailed to open Node.js report file: " << filename;
141-
142-
if (report_directory.length() > 0)
143-
std::cerr << " directory: " << report_directory;
144-
145-
std::cerr << " (errno: " << errno << ")" << std::endl;
146-
return "";
147-
}
148-
outstream = &outfile;
149-
std::cerr << "\nWriting Node.js report to file: " << filename;
150-
}
151-
152-
bool compact;
153-
{
154-
Mutex::ScopedLock lock(per_process::cli_options_mutex);
155-
compact = per_process::cli_options->report_compact;
156-
}
157-
WriteNodeReport(isolate, env, message, trigger, filename, *outstream,
158-
error, compact);
159-
160-
// Do not close stdout/stderr, only close files we opened.
161-
if (outfile.is_open()) {
162-
outfile.close();
163-
}
164-
165-
// Do not mix JSON and free-form text on stderr.
166-
if (filename != "stderr") {
167-
std::cerr << "\nNode.js report completed" << std::endl;
168-
}
169-
return filename;
170-
}
171-
172-
// External function to trigger a report, writing to a supplied stream.
173-
void GetNodeReport(Isolate* isolate,
174-
Environment* env,
175-
const char* message,
176-
const char* trigger,
177-
Local<Value> error,
178-
std::ostream& out) {
179-
WriteNodeReport(isolate, env, message, trigger, "", out, error, false);
180-
}
181-
18285
// Internal function to coordinate and write the various
18386
// sections of the report to the supplied stream
18487
static void WriteNodeReport(Isolate* isolate,
@@ -319,12 +222,8 @@ static void WriteNodeReport(Isolate* isolate,
319222
expected_results += w->RequestInterrupt([&](Environment* env) {
320223
std::ostringstream os;
321224

322-
GetNodeReport(env->isolate(),
323-
env,
324-
"Worker thread subreport",
325-
trigger,
326-
Local<Value>(),
327-
os);
225+
GetNodeReport(
226+
env, "Worker thread subreport", trigger, Local<Value>(), os);
328227

329228
Mutex::ScopedLock lock(workers_mutex);
330229
worker_infos.emplace_back(os.str());
@@ -884,4 +783,136 @@ static void PrintRelease(JSONWriter* writer) {
884783
}
885784

886785
} // namespace report
786+
787+
// External function to trigger a report, writing to file.
788+
std::string TriggerNodeReport(Isolate* isolate,
789+
const char* message,
790+
const char* trigger,
791+
const std::string& name,
792+
Local<Value> error) {
793+
Environment* env = nullptr;
794+
if (isolate != nullptr) {
795+
env = Environment::GetCurrent(isolate);
796+
}
797+
return TriggerNodeReport(env, message, trigger, name, error);
798+
}
799+
800+
// External function to trigger a report, writing to file.
801+
std::string TriggerNodeReport(Environment* env,
802+
const char* message,
803+
const char* trigger,
804+
const std::string& name,
805+
Local<Value> error) {
806+
std::string filename;
807+
808+
// Determine the required report filename. In order of priority:
809+
// 1) supplied on API 2) configured on startup 3) default generated
810+
if (!name.empty()) {
811+
// Filename was specified as API parameter.
812+
filename = name;
813+
} else {
814+
std::string report_filename;
815+
{
816+
Mutex::ScopedLock lock(per_process::cli_options_mutex);
817+
report_filename = per_process::cli_options->report_filename;
818+
}
819+
if (report_filename.length() > 0) {
820+
// File name was supplied via start-up option.
821+
filename = report_filename;
822+
} else {
823+
filename = *DiagnosticFilename(
824+
env != nullptr ? env->thread_id() : 0, "report", "json");
825+
}
826+
}
827+
828+
// Open the report file stream for writing. Supports stdout/err,
829+
// user-specified or (default) generated name
830+
std::ofstream outfile;
831+
std::ostream* outstream;
832+
if (filename == "stdout") {
833+
outstream = &std::cout;
834+
} else if (filename == "stderr") {
835+
outstream = &std::cerr;
836+
} else {
837+
std::string report_directory;
838+
{
839+
Mutex::ScopedLock lock(per_process::cli_options_mutex);
840+
report_directory = per_process::cli_options->report_directory;
841+
}
842+
// Regular file. Append filename to directory path if one was specified
843+
if (report_directory.length() > 0) {
844+
std::string pathname = report_directory;
845+
pathname += kPathSeparator;
846+
pathname += filename;
847+
outfile.open(pathname, std::ios::out | std::ios::binary);
848+
} else {
849+
outfile.open(filename, std::ios::out | std::ios::binary);
850+
}
851+
// Check for errors on the file open
852+
if (!outfile.is_open()) {
853+
std::cerr << "\nFailed to open Node.js report file: " << filename;
854+
855+
if (report_directory.length() > 0)
856+
std::cerr << " directory: " << report_directory;
857+
858+
std::cerr << " (errno: " << errno << ")" << std::endl;
859+
return "";
860+
}
861+
outstream = &outfile;
862+
std::cerr << "\nWriting Node.js report to file: " << filename;
863+
}
864+
865+
bool compact;
866+
{
867+
Mutex::ScopedLock lock(per_process::cli_options_mutex);
868+
compact = per_process::cli_options->report_compact;
869+
}
870+
871+
Isolate* isolate = nullptr;
872+
if (env != nullptr) {
873+
isolate = env->isolate();
874+
}
875+
report::WriteNodeReport(
876+
isolate, env, message, trigger, filename, *outstream, error, compact);
877+
878+
// Do not close stdout/stderr, only close files we opened.
879+
if (outfile.is_open()) {
880+
outfile.close();
881+
}
882+
883+
// Do not mix JSON and free-form text on stderr.
884+
if (filename != "stderr") {
885+
std::cerr << "\nNode.js report completed" << std::endl;
886+
}
887+
return filename;
888+
}
889+
890+
// External function to trigger a report, writing to a supplied stream.
891+
void GetNodeReport(Isolate* isolate,
892+
const char* message,
893+
const char* trigger,
894+
Local<Value> error,
895+
std::ostream& out) {
896+
Environment* env = nullptr;
897+
if (isolate != nullptr) {
898+
env = Environment::GetCurrent(isolate);
899+
}
900+
report::WriteNodeReport(
901+
isolate, env, message, trigger, "", out, error, false);
902+
}
903+
904+
// External function to trigger a report, writing to a supplied stream.
905+
void GetNodeReport(Environment* env,
906+
const char* message,
907+
const char* trigger,
908+
Local<Value> error,
909+
std::ostream& out) {
910+
Isolate* isolate = nullptr;
911+
if (env != nullptr) {
912+
isolate = env->isolate();
913+
}
914+
report::WriteNodeReport(
915+
isolate, env, message, trigger, "", out, error, false);
916+
}
917+
887918
} // namespace node

‎src/node_report.h

+1-15
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,10 @@
1414
#endif
1515

1616
#include <iomanip>
17+
#include <sstream>
1718

1819
namespace node {
1920
namespace report {
20-
21-
// Function declarations - functions in src/node_report.cc
22-
std::string TriggerNodeReport(v8::Isolate* isolate,
23-
Environment* env,
24-
const char* message,
25-
const char* trigger,
26-
const std::string& name,
27-
v8::Local<v8::Value> error);
28-
void GetNodeReport(v8::Isolate* isolate,
29-
Environment* env,
30-
const char* message,
31-
const char* trigger,
32-
v8::Local<v8::Value> error,
33-
std::ostream& out);
34-
3521
// Function declarations - utility functions in src/node_report_utils.cc
3622
void WalkHandle(uv_handle_t* h, void* arg);
3723

‎src/node_report_module.cc

+2-4
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,7 @@ void WriteReport(const FunctionCallbackInfo<Value>& info) {
4747
else
4848
error = Local<Value>();
4949

50-
filename = TriggerNodeReport(
51-
isolate, env, *message, *trigger, filename, error);
50+
filename = TriggerNodeReport(env, *message, *trigger, filename, error);
5251
// Return value is the report filename
5352
info.GetReturnValue().Set(
5453
String::NewFromUtf8(isolate, filename.c_str()).ToLocalChecked());
@@ -68,8 +67,7 @@ void GetReport(const FunctionCallbackInfo<Value>& info) {
6867
else
6968
error = Local<Object>();
7069

71-
GetNodeReport(
72-
isolate, env, "JavaScript API", __func__, error, out);
70+
GetNodeReport(env, "JavaScript API", __func__, error, out);
7371

7472
// Return value is the contents of a report as a string.
7573
info.GetReturnValue().Set(

‎test/addons/report-api/binding.cc

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <node.h>
2+
#include <v8.h>
3+
4+
using v8::FunctionCallbackInfo;
5+
using v8::Isolate;
6+
using v8::Local;
7+
using v8::Object;
8+
using v8::Value;
9+
10+
void TriggerReport(const FunctionCallbackInfo<Value>& args) {
11+
Isolate* isolate = args.GetIsolate();
12+
13+
node::TriggerNodeReport(
14+
isolate, "FooMessage", "BarTrigger", std::string(), Local<Value>());
15+
}
16+
17+
void TriggerReportNoIsolate(const FunctionCallbackInfo<Value>& args) {
18+
node::TriggerNodeReport(static_cast<Isolate*>(nullptr),
19+
"FooMessage",
20+
"BarTrigger",
21+
std::string(),
22+
Local<Value>());
23+
}
24+
25+
void TriggerReportEnv(const FunctionCallbackInfo<Value>& args) {
26+
Isolate* isolate = args.GetIsolate();
27+
28+
node::TriggerNodeReport(
29+
node::GetCurrentEnvironment(isolate->GetCurrentContext()),
30+
"FooMessage",
31+
"BarTrigger",
32+
std::string(),
33+
Local<Value>());
34+
}
35+
36+
void TriggerReportNoEnv(const FunctionCallbackInfo<Value>& args) {
37+
Isolate* isolate = args.GetIsolate();
38+
39+
node::TriggerNodeReport(static_cast<node::Environment*>(nullptr),
40+
"FooMessage",
41+
"BarTrigger",
42+
std::string(),
43+
Local<Value>());
44+
}
45+
46+
void init(Local<Object> exports) {
47+
NODE_SET_METHOD(exports, "triggerReport", TriggerReport);
48+
NODE_SET_METHOD(exports, "triggerReportNoIsolate", TriggerReportNoIsolate);
49+
NODE_SET_METHOD(exports, "triggerReportEnv", TriggerReportEnv);
50+
NODE_SET_METHOD(exports, "triggerReportNoEnv", TriggerReportNoEnv);
51+
}
52+
53+
NODE_MODULE(NODE_GYP_MODULE_NAME, init)

‎test/addons/report-api/binding.gyp

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
'targets': [
3+
{
4+
'target_name': 'binding',
5+
'sources': [ 'binding.cc' ],
6+
'includes': ['../common.gypi'],
7+
}
8+
]
9+
}

‎test/addons/report-api/test.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const common = require('../../common');
4+
const assert = require('assert');
5+
const path = require('path');
6+
const helper = require('../../common/report.js');
7+
const tmpdir = require('../../common/tmpdir');
8+
9+
const binding = path.resolve(__dirname, `./build/${common.buildType}/binding`);
10+
const addon = require(binding);
11+
12+
function myAddonMain(method, hasJavaScriptFrames) {
13+
tmpdir.refresh();
14+
process.report.directory = tmpdir.path;
15+
16+
addon[method]();
17+
18+
const reports = helper.findReports(process.pid, tmpdir.path);
19+
assert.strictEqual(reports.length, 1);
20+
21+
const report = reports[0];
22+
helper.validate(report);
23+
24+
const content = require(report);
25+
assert.strictEqual(content.header.event, 'FooMessage');
26+
assert.strictEqual(content.header.trigger, 'BarTrigger');
27+
28+
// Check that the javascript stack is present.
29+
if (hasJavaScriptFrames) {
30+
assert.strictEqual(content.javascriptStack.stack.findIndex((frame) => frame.match('myAddonMain')), 0);
31+
} else {
32+
assert.strictEqual(content.javascriptStack, undefined);
33+
}
34+
}
35+
36+
const methods = [
37+
['triggerReport', true],
38+
['triggerReportNoIsolate', false],
39+
['triggerReportEnv', true],
40+
['triggerReportNoEnv', false],
41+
];
42+
for (const [method, hasJavaScriptFrames] of methods) {
43+
myAddonMain(method, hasJavaScriptFrames);
44+
}

‎test/cctest/test_report.cc

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#include "node.h"
2+
3+
#include <string>
4+
#include "gtest/gtest.h"
5+
#include "node_test_fixture.h"
6+
7+
using node::Environment;
8+
using v8::Context;
9+
using v8::Function;
10+
using v8::FunctionCallbackInfo;
11+
using v8::HandleScope;
12+
using v8::Isolate;
13+
using v8::Local;
14+
using v8::SealHandleScope;
15+
using v8::String;
16+
using v8::Value;
17+
18+
bool report_callback_called = false;
19+
20+
class ReportTest : public EnvironmentTestFixture {
21+
private:
22+
void TearDown() override {
23+
NodeTestFixture::TearDown();
24+
report_callback_called = false;
25+
}
26+
};
27+
28+
TEST_F(ReportTest, ReportWithNoIsolate) {
29+
SealHandleScope handle_scope(isolate_);
30+
31+
std::ostringstream oss;
32+
node::GetNodeReport(static_cast<Isolate*>(nullptr),
33+
"FooMessage",
34+
"BarTrigger",
35+
Local<Value>(),
36+
oss);
37+
38+
// Simple checks on the output string contains the message and trigger.
39+
std::string actual = oss.str();
40+
EXPECT_NE(actual.find("FooMessage"), std::string::npos);
41+
EXPECT_NE(actual.find("BarTrigger"), std::string::npos);
42+
}
43+
44+
TEST_F(ReportTest, ReportWithNoEnv) {
45+
SealHandleScope handle_scope(isolate_);
46+
47+
std::ostringstream oss;
48+
node::GetNodeReport(static_cast<Environment*>(nullptr),
49+
"FooMessage",
50+
"BarTrigger",
51+
Local<Value>(),
52+
oss);
53+
54+
// Simple checks on the output string contains the message and trigger.
55+
std::string actual = oss.str();
56+
EXPECT_NE(actual.find("FooMessage"), std::string::npos);
57+
EXPECT_NE(actual.find("BarTrigger"), std::string::npos);
58+
}
59+
60+
TEST_F(ReportTest, ReportWithIsolate) {
61+
const HandleScope handle_scope(isolate_);
62+
const Argv argv;
63+
Env env{handle_scope, argv};
64+
65+
Local<Context> context = isolate_->GetCurrentContext();
66+
Local<Function> fn =
67+
Function::New(context, [](const FunctionCallbackInfo<Value>& args) {
68+
Isolate* isolate = args.GetIsolate();
69+
HandleScope scope(isolate);
70+
71+
std::ostringstream oss;
72+
node::GetNodeReport(isolate, "FooMessage", "BarTrigger", args[0], oss);
73+
74+
// Simple checks on the output string contains the message and trigger.
75+
std::string actual = oss.str();
76+
EXPECT_NE(actual.find("FooMessage"), std::string::npos);
77+
EXPECT_NE(actual.find("BarTrigger"), std::string::npos);
78+
79+
report_callback_called = true;
80+
}).ToLocalChecked();
81+
82+
context->Global()
83+
->Set(context, String::NewFromUtf8(isolate_, "foo").ToLocalChecked(), fn)
84+
.FromJust();
85+
86+
node::LoadEnvironment(*env, "foo()").ToLocalChecked();
87+
88+
EXPECT_TRUE(report_callback_called);
89+
}
90+
91+
TEST_F(ReportTest, ReportWithEnv) {
92+
const HandleScope handle_scope(isolate_);
93+
const Argv argv;
94+
Env env{handle_scope, argv};
95+
96+
Local<Context> context = isolate_->GetCurrentContext();
97+
Local<Function> fn =
98+
Function::New(context, [](const FunctionCallbackInfo<Value>& args) {
99+
Isolate* isolate = args.GetIsolate();
100+
HandleScope scope(isolate);
101+
102+
std::ostringstream oss;
103+
node::GetNodeReport(
104+
node::GetCurrentEnvironment(isolate->GetCurrentContext()),
105+
"FooMessage",
106+
"BarTrigger",
107+
args[0],
108+
oss);
109+
110+
// Simple checks on the output string contains the message and trigger.
111+
std::string actual = oss.str();
112+
EXPECT_NE(actual.find("FooMessage"), std::string::npos);
113+
EXPECT_NE(actual.find("BarTrigger"), std::string::npos);
114+
115+
report_callback_called = true;
116+
}).ToLocalChecked();
117+
118+
context->Global()
119+
->Set(context, String::NewFromUtf8(isolate_, "foo").ToLocalChecked(), fn)
120+
.FromJust();
121+
122+
node::LoadEnvironment(*env, "foo()").ToLocalChecked();
123+
124+
EXPECT_TRUE(report_callback_called);
125+
}

0 commit comments

Comments
 (0)
Please sign in to comment.