Skip to content

Commit

Permalink
bootstrap: print stack trace during environment creation failure
Browse files Browse the repository at this point in the history
#45888 took the environment
creation code out of the scope covered by the v8::TryCatch
that we use to print early failures during environment creation.
So e.g. when adding something that would fail in node.js, we get

```
node:internal/options:554: Uncaught Error: Should not query options before bootstrapping is done
```

This patch restores that by adding another v8::TryCatch for it:

```
node:internal/options:20
    ({ options: optionsMap } = getCLIOptions());
                               ^

Error: Should not query options before bootstrapping is done
    at getCLIOptionsFromBinding (node:internal/options:20:32)
    at getOptionValue (node:internal/options:45:19)
    at node:internal/bootstrap/node:433:29
```

PR-URL: #46533
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
  • Loading branch information
joyeecheung authored and legendecas committed Feb 28, 2023
1 parent 3abbc38 commit bff7be8
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 25 deletions.
12 changes: 11 additions & 1 deletion src/api/embed_helpers.cc
Expand Up @@ -15,6 +15,7 @@ using v8::Maybe;
using v8::Nothing;
using v8::SealHandleScope;
using v8::SnapshotCreator;
using v8::TryCatch;

namespace node {

Expand Down Expand Up @@ -129,12 +130,21 @@ CommonEnvironmentSetup::CommonEnvironmentSetup(
{
Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);

TryCatch bootstrapCatch(isolate);
auto print_Exception = OnScopeLeave([&]() {
if (bootstrapCatch.HasCaught()) {
errors->push_back(FormatCaughtException(
isolate, isolate->GetCurrentContext(), bootstrapCatch));
}
});

impl_->isolate_data.reset(CreateIsolateData(
isolate, loop, platform, impl_->allocator.get(), snapshot_data));
impl_->isolate_data->options()->build_snapshot =
impl_->snapshot_creator.has_value();

HandleScope handle_scope(isolate);
if (snapshot_data) {
impl_->env.reset(make_env(this));
if (impl_->env) {
Expand Down
79 changes: 55 additions & 24 deletions src/node_errors.cc
Expand Up @@ -185,7 +185,8 @@ static std::string GetErrorSource(Isolate* isolate,
return buf + std::string(underline_buf, off);
}

void PrintStackTrace(Isolate* isolate, Local<StackTrace> stack) {
static std::string FormatStackTrace(Isolate* isolate, Local<StackTrace> stack) {
std::string result;
for (int i = 0; i < stack->GetFrameCount(); i++) {
Local<StackFrame> stack_frame = stack->GetFrame(isolate, i);
node::Utf8Value fn_name_s(isolate, stack_frame->GetFunctionName());
Expand All @@ -195,53 +196,82 @@ void PrintStackTrace(Isolate* isolate, Local<StackTrace> stack) {

if (stack_frame->IsEval()) {
if (stack_frame->GetScriptId() == Message::kNoScriptIdInfo) {
FPrintF(stderr, " at [eval]:%i:%i\n", line_number, column);
result += SPrintF(" at [eval]:%i:%i\n", line_number, column);
} else {
FPrintF(stderr,
" at [eval] (%s:%i:%i)\n",
*script_name,
line_number,
column);
std::vector<char> buf(script_name.length() + 64);
snprintf(buf.data(),
buf.size(),
" at [eval] (%s:%i:%i)\n",
*script_name,
line_number,
column);
result += std::string(buf.data());
}
break;
}

if (fn_name_s.length() == 0) {
FPrintF(stderr, " at %s:%i:%i\n", script_name, line_number, column);
std::vector<char> buf(script_name.length() + 64);
snprintf(buf.data(),
buf.size(),
" at %s:%i:%i\n",
*script_name,
line_number,
column);
result += std::string(buf.data());
} else {
FPrintF(stderr,
" at %s (%s:%i:%i)\n",
fn_name_s,
script_name,
line_number,
column);
std::vector<char> buf(fn_name_s.length() + script_name.length() + 64);
snprintf(buf.data(),
buf.size(),
" at %s (%s:%i:%i)\n",
*fn_name_s,
*script_name,
line_number,
column);
result += std::string(buf.data());
}
}
return result;
}

static void PrintToStderrAndFlush(const std::string& str) {
FPrintF(stderr, "%s\n", str);
fflush(stderr);
}

void PrintException(Isolate* isolate,
Local<Context> context,
Local<Value> err,
Local<Message> message) {
void PrintStackTrace(Isolate* isolate, Local<StackTrace> stack) {
PrintToStderrAndFlush(FormatStackTrace(isolate, stack));
}

std::string FormatCaughtException(Isolate* isolate,
Local<Context> context,
Local<Value> err,
Local<Message> message) {
node::Utf8Value reason(isolate,
err->ToDetailString(context)
.FromMaybe(Local<String>()));
bool added_exception_line = false;
std::string source =
GetErrorSource(isolate, context, message, &added_exception_line);
FPrintF(stderr, "%s\n", source);
FPrintF(stderr, "%s\n", reason);
std::string result = source + '\n' + reason.ToString() + '\n';

Local<v8::StackTrace> stack = message->GetStackTrace();
if (!stack.IsEmpty()) PrintStackTrace(isolate, stack);
if (!stack.IsEmpty()) result += FormatStackTrace(isolate, stack);
return result;
}

std::string FormatCaughtException(Isolate* isolate,
Local<Context> context,
const v8::TryCatch& try_catch) {
CHECK(try_catch.HasCaught());
return FormatCaughtException(
isolate, context, try_catch.Exception(), try_catch.Message());
}

void PrintCaughtException(Isolate* isolate,
Local<Context> context,
const v8::TryCatch& try_catch) {
CHECK(try_catch.HasCaught());
PrintException(isolate, context, try_catch.Exception(), try_catch.Message());
PrintToStderrAndFlush(FormatCaughtException(isolate, context, try_catch));
}

void AppendExceptionLine(Environment* env,
Expand Down Expand Up @@ -1089,7 +1119,8 @@ void TriggerUncaughtException(Isolate* isolate,
// error is supposed to be thrown at this point.
// Since we don't have access to Environment here, there is not
// much we can do, so we just print whatever is useful and crash.
PrintException(isolate, context, error, message);
PrintToStderrAndFlush(
FormatCaughtException(isolate, context, error, message));
Abort();
}

Expand Down
3 changes: 3 additions & 0 deletions src/node_internals.h
Expand Up @@ -83,6 +83,9 @@ void PrintStackTrace(v8::Isolate* isolate, v8::Local<v8::StackTrace> stack);
void PrintCaughtException(v8::Isolate* isolate,
v8::Local<v8::Context> context,
const v8::TryCatch& try_catch);
std::string FormatCaughtException(v8::Isolate* isolate,
v8::Local<v8::Context> context,
const v8::TryCatch& try_catch);

void ResetStdio(); // Safe to call more than once and from signal handlers.
#ifdef __POSIX__
Expand Down

0 comments on commit bff7be8

Please sign in to comment.