diff --git a/src/execution/isolate.cc b/src/execution/isolate.cc index 6f92d8ffe4..386850771b 100644 --- a/src/execution/isolate.cc +++ b/src/execution/isolate.cc @@ -44,6 +44,7 @@ #include "src/diagnostics/basic-block-profiler.h" #include "src/diagnostics/compilation-statistics.h" #include "src/execution/frames-inl.h" +#include "src/execution/frames.h" #include "src/execution/isolate-inl.h" #include "src/execution/local-isolate.h" #include "src/execution/messages.h" @@ -683,12 +684,16 @@ StackTraceFailureMessage::StackTraceFailureMessage( } } -class StackTraceBuilder { +bool NoExtension(const v8::FunctionCallbackInfo&) { return false; } + +namespace { + +class CallSiteBuilder { public: enum FrameFilterMode { ALL, CURRENT_SECURITY_CONTEXT }; - StackTraceBuilder(Isolate* isolate, FrameSkipMode mode, int limit, - Handle caller, FrameFilterMode filter_mode) + CallSiteBuilder(Isolate* isolate, FrameSkipMode mode, int limit, + Handle caller, FrameFilterMode filter_mode) : isolate_(isolate), mode_(mode), limit_(limit), @@ -703,6 +708,18 @@ class StackTraceBuilder { elements_ = isolate->factory()->NewFixedArray(std::min(64, limit)); } + bool Visit(FrameSummary const& summary) { + if (Full()) return false; +#if V8_ENABLE_WEBASSEMBLY + if (summary.IsWasm()) { + AppendWasmFrame(summary.AsWasm()); + return true; + } +#endif // V8_ENABLE_WEBASSEMBLY + AppendJavaScriptFrame(summary.AsJavaScript()); + return true; + } + void AppendAsyncFrame(Handle generator_object) { Handle function(generator_object->function(), isolate_); if (!IsVisibleInStackTrace(function)) return; @@ -905,10 +922,6 @@ bool GetStackTraceLimit(Isolate* isolate, int* result) { return true; } -bool NoExtension(const v8::FunctionCallbackInfo&) { return false; } - -namespace { - bool IsBuiltinFunction(Isolate* isolate, HeapObject object, Builtin builtin) { if (!object.IsJSFunction()) return false; JSFunction const function = JSFunction::cast(object); @@ -916,7 +929,7 @@ bool IsBuiltinFunction(Isolate* isolate, HeapObject object, Builtin builtin) { } void CaptureAsyncStackTrace(Isolate* isolate, Handle promise, - StackTraceBuilder* builder) { + CallSiteBuilder* builder) { while (!builder->Full()) { // Check that the {promise} is not settled. if (promise->status() != Promise::kPending) return; @@ -1026,13 +1039,110 @@ void CaptureAsyncStackTrace(Isolate* isolate, Handle promise, } } +void CaptureAsyncStackTrace(Isolate* isolate, CallSiteBuilder* builder) { + Handle current_microtask = isolate->factory()->current_microtask(); + if (current_microtask->IsPromiseReactionJobTask()) { + Handle promise_reaction_job_task = + Handle::cast(current_microtask); + // Check if the {reaction} has one of the known async function or + // async generator continuations as its fulfill handler. + if (IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), + Builtin::kAsyncFunctionAwaitResolveClosure) || + IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), + Builtin::kAsyncGeneratorAwaitResolveClosure) || + IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), + Builtin::kAsyncGeneratorYieldResolveClosure) || + IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), + Builtin::kAsyncFunctionAwaitRejectClosure) || + IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), + Builtin::kAsyncGeneratorAwaitRejectClosure)) { + // Now peek into the handlers' AwaitContext to get to + // the JSGeneratorObject for the async function. + Handle context( + JSFunction::cast(promise_reaction_job_task->handler()).context(), + isolate); + Handle generator_object( + JSGeneratorObject::cast(context->extension()), isolate); + if (generator_object->is_executing()) { + if (generator_object->IsJSAsyncFunctionObject()) { + Handle async_function_object = + Handle::cast(generator_object); + Handle promise(async_function_object->promise(), isolate); + CaptureAsyncStackTrace(isolate, promise, builder); + } else { + Handle async_generator_object = + Handle::cast(generator_object); + Handle queue(async_generator_object->queue(), isolate); + if (!queue->IsUndefined(isolate)) { + Handle async_generator_request = + Handle::cast(queue); + Handle promise( + JSPromise::cast(async_generator_request->promise()), isolate); + CaptureAsyncStackTrace(isolate, promise, builder); + } + } + } + } else { + // The {promise_reaction_job_task} doesn't belong to an await (or + // yield inside an async generator), but we might still be able to + // find an async frame if we follow along the chain of promises on + // the {promise_reaction_job_task}. + Handle promise_or_capability( + promise_reaction_job_task->promise_or_capability(), isolate); + if (promise_or_capability->IsJSPromise()) { + Handle promise = + Handle::cast(promise_or_capability); + CaptureAsyncStackTrace(isolate, promise, builder); + } + } + } +} + +template +void VisitStack(Isolate* isolate, Visitor* visitor, Options const& options) { + DisallowJavascriptExecution no_js(isolate); + for (StackFrameIterator it(isolate); !it.done(); it.Advance()) { + StackFrame* frame = it.frame(); + switch (frame->type()) { + case StackFrame::BUILTIN_EXIT: + case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION: + case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: + case StackFrame::OPTIMIZED: + case StackFrame::INTERPRETED: + case StackFrame::BASELINE: + case StackFrame::BUILTIN: +#if V8_ENABLE_WEBASSEMBLY + case StackFrame::WASM: +#endif // V8_ENABLE_WEBASSEMBLY + { + // A standard frame may include many summarized frames (due to + // inlining). + std::vector summaries; + CommonFrame::cast(frame)->Summarize(&summaries); + for (auto rit = summaries.rbegin(); rit != summaries.rend(); ++rit) { + FrameSummary& summary = *rit; + if (options.capture_only_frames_subject_to_debugging && + !summary.is_subject_to_debugging()) { + continue; + } + if (!visitor->Visit(summary)) return; + } + break; + } + + default: + break; + } + } +} + struct CaptureStackTraceOptions { int limit; // 'filter_mode' and 'skip_mode' are somewhat orthogonal. 'filter_mode' // specifies whether to capture all frames, or just frames in the same // security context. While 'skip_mode' allows skipping the first frame. FrameSkipMode skip_mode; - StackTraceBuilder::FrameFilterMode filter_mode; + CallSiteBuilder::FrameFilterMode filter_mode; bool capture_only_frames_subject_to_debugging; bool async_stack_trace; @@ -1049,123 +1159,15 @@ Handle CaptureStackTrace(Isolate* isolate, Handle caller, wasm::WasmCodeRefScope code_ref_scope; #endif // V8_ENABLE_WEBASSEMBLY - StackTraceBuilder builder(isolate, options.skip_mode, options.limit, caller, - options.filter_mode); - - // Build the regular stack trace, and remember the last relevant - // frame ID and inlined index (for the async stack trace handling - // below, which starts from this last frame). - for (StackFrameIterator it(isolate); !it.done() && !builder.Full(); - it.Advance()) { - StackFrame* const frame = it.frame(); - switch (frame->type()) { - case StackFrame::BUILTIN_EXIT: - case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION: - case StackFrame::JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH: - case StackFrame::OPTIMIZED: - case StackFrame::INTERPRETED: - case StackFrame::BASELINE: - case StackFrame::BUILTIN: -#if V8_ENABLE_WEBASSEMBLY - case StackFrame::WASM: -#endif // V8_ENABLE_WEBASSEMBLY - { - // A standard frame may include many summarized frames (due to - // inlining). - std::vector frames; - CommonFrame::cast(frame)->Summarize(&frames); - for (size_t i = frames.size(); i-- != 0 && !builder.Full();) { - auto& summary = frames[i]; - if (options.capture_only_frames_subject_to_debugging && - !summary.is_subject_to_debugging()) { - continue; - } - - if (summary.IsJavaScript()) { - //========================================================= - // Handle a JavaScript frame. - //========================================================= - auto const& java_script = summary.AsJavaScript(); - builder.AppendJavaScriptFrame(java_script); -#if V8_ENABLE_WEBASSEMBLY - } else if (summary.IsWasm()) { - //========================================================= - // Handle a Wasm frame. - //========================================================= - auto const& wasm = summary.AsWasm(); - builder.AppendWasmFrame(wasm); -#endif // V8_ENABLE_WEBASSEMBLY - } - } - break; - } - - default: - break; - } - } + CallSiteBuilder builder(isolate, options.skip_mode, options.limit, caller, + options.filter_mode); + VisitStack(isolate, &builder, options); // If --async-stack-traces are enabled and the "current microtask" is a // PromiseReactionJobTask, we try to enrich the stack trace with async // frames. if (options.async_stack_trace) { - Handle current_microtask = isolate->factory()->current_microtask(); - if (current_microtask->IsPromiseReactionJobTask()) { - Handle promise_reaction_job_task = - Handle::cast(current_microtask); - // Check if the {reaction} has one of the known async function or - // async generator continuations as its fulfill handler. - if (IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), - Builtin::kAsyncFunctionAwaitResolveClosure) || - IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), - Builtin::kAsyncGeneratorAwaitResolveClosure) || - IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), - Builtin::kAsyncGeneratorYieldResolveClosure) || - IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), - Builtin::kAsyncFunctionAwaitRejectClosure) || - IsBuiltinFunction(isolate, promise_reaction_job_task->handler(), - Builtin::kAsyncGeneratorAwaitRejectClosure)) { - // Now peek into the handlers' AwaitContext to get to - // the JSGeneratorObject for the async function. - Handle context( - JSFunction::cast(promise_reaction_job_task->handler()).context(), - isolate); - Handle generator_object( - JSGeneratorObject::cast(context->extension()), isolate); - if (generator_object->is_executing()) { - if (generator_object->IsJSAsyncFunctionObject()) { - Handle async_function_object = - Handle::cast(generator_object); - Handle promise(async_function_object->promise(), - isolate); - CaptureAsyncStackTrace(isolate, promise, &builder); - } else { - Handle async_generator_object = - Handle::cast(generator_object); - Handle queue(async_generator_object->queue(), isolate); - if (!queue->IsUndefined(isolate)) { - Handle async_generator_request = - Handle::cast(queue); - Handle promise( - JSPromise::cast(async_generator_request->promise()), isolate); - CaptureAsyncStackTrace(isolate, promise, &builder); - } - } - } - } else { - // The {promise_reaction_job_task} doesn't belong to an await (or - // yield inside an async generator), but we might still be able to - // find an async frame if we follow along the chain of promises on - // the {promise_reaction_job_task}. - Handle promise_or_capability( - promise_reaction_job_task->promise_or_capability(), isolate); - if (promise_or_capability->IsJSPromise()) { - Handle promise = - Handle::cast(promise_or_capability); - CaptureAsyncStackTrace(isolate, promise, &builder); - } - } - } + CaptureAsyncStackTrace(isolate, &builder); } Handle stack_trace = builder.Build(); @@ -1189,7 +1191,7 @@ Handle Isolate::CaptureSimpleStackTrace(Handle error_object, options.limit = limit; options.skip_mode = mode; options.async_stack_trace = FLAG_async_stack_traces; - options.filter_mode = StackTraceBuilder::CURRENT_SECURITY_CONTEXT; + options.filter_mode = CallSiteBuilder::CURRENT_SECURITY_CONTEXT; options.capture_only_frames_subject_to_debugging = false; return CaptureStackTrace(this, caller, options); @@ -1289,8 +1291,8 @@ Handle Isolate::CaptureDetailedStackTrace( options.async_stack_trace = false; options.filter_mode = (stack_trace_options & StackTrace::kExposeFramesAcrossSecurityOrigins) - ? StackTraceBuilder::ALL - : StackTraceBuilder::CURRENT_SECURITY_CONTEXT; + ? CallSiteBuilder::ALL + : CallSiteBuilder::CURRENT_SECURITY_CONTEXT; options.capture_only_frames_subject_to_debugging = true; return CaptureStackTrace(this, factory()->undefined_value(), options); @@ -2203,7 +2205,7 @@ void Isolate::PrintCurrentStackTrace(std::ostream& out) { options.limit = 0; options.skip_mode = SKIP_NONE; options.async_stack_trace = FLAG_async_stack_traces; - options.filter_mode = StackTraceBuilder::CURRENT_SECURITY_CONTEXT; + options.filter_mode = CallSiteBuilder::CURRENT_SECURITY_CONTEXT; options.capture_only_frames_subject_to_debugging = false; Handle frames =