Commit 8205786a authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] decouple debugger delegate and async stacks delegate

Currently we enable instrumentation if debugger is active. With this
approach we can not:
- capture async stack when debugger is disabled,
- avoid async instrumentation overhead when debugger is enabled and
  async stacks are disabled.

R=dgozman@chromium.org,yangguo@chromium.org

Bug: none
Cq-Include-Trybots: luci.chromium.try:linux_chromium_headless_rel;luci.chromium.try:linux_chromium_rel_ng;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I19400c4c4e12b6c9b5a980fb6bd3293bac6e6a64
Reviewed-on: https://chromium-review.googlesource.com/1081494
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53530}
parent 85bcc400
...@@ -9555,6 +9555,11 @@ void debug::SetDebugDelegate(Isolate* v8_isolate, ...@@ -9555,6 +9555,11 @@ void debug::SetDebugDelegate(Isolate* v8_isolate,
isolate->debug()->SetDebugDelegate(delegate, false); isolate->debug()->SetDebugDelegate(delegate, false);
} }
void debug::SetAsyncEventDelegate(Isolate* v8_isolate,
debug::AsyncEventDelegate* delegate) {
reinterpret_cast<i::Isolate*>(v8_isolate)->set_async_event_delegate(delegate);
}
void debug::ResetBlackboxedStateCache(Isolate* v8_isolate, void debug::ResetBlackboxedStateCache(Isolate* v8_isolate,
v8::Local<debug::Script> script) { v8::Local<debug::Script> script) {
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
......
...@@ -121,7 +121,7 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait( ...@@ -121,7 +121,7 @@ void AsyncFunctionBuiltinsAssembler::AsyncFunctionAwait(
// InternalPerformPromiseThen. // InternalPerformPromiseThen.
Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred); Label after_debug_hook(this), call_debug_hook(this, Label::kDeferred);
GotoIf(IsDebugActive(), &call_debug_hook); GotoIf(HasAsyncEventDelegate(), &call_debug_hook);
Goto(&after_debug_hook); Goto(&after_debug_hook);
BIND(&after_debug_hook); BIND(&after_debug_hook);
...@@ -196,13 +196,14 @@ TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) { ...@@ -196,13 +196,14 @@ TF_BUILTIN(AsyncFunctionPromiseRelease, AsyncFunctionBuiltinsAssembler) {
Node* const promise = Parameter(Descriptor::kPromise); Node* const promise = Parameter(Descriptor::kPromise);
Node* const context = Parameter(Descriptor::kContext); Node* const context = Parameter(Descriptor::kContext);
Label if_is_debug_active(this, Label::kDeferred); Label call_debug_instrumentation(this, Label::kDeferred);
GotoIf(IsDebugActive(), &if_is_debug_active); GotoIf(HasAsyncEventDelegate(), &call_debug_instrumentation);
GotoIf(IsDebugActive(), &call_debug_instrumentation);
// Early exit if debug is not active. // Early exit if debug is not active.
Return(UndefinedConstant()); Return(UndefinedConstant());
BIND(&if_is_debug_active); BIND(&call_debug_instrumentation);
{ {
// Pop the Promise under construction in an async function on // Pop the Promise under construction in an async function on
// from catch prediction stack. // from catch prediction stack.
......
...@@ -98,7 +98,7 @@ Node* AsyncBuiltinsAssembler::Await( ...@@ -98,7 +98,7 @@ Node* AsyncBuiltinsAssembler::Await(
{ {
// Add PromiseHooks if needed // Add PromiseHooks if needed
Label next(this); Label next(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &next); GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &next);
CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value, CallRuntime(Runtime::kAwaitPromisesInit, context, wrapped_value,
outer_promise, throwaway); outer_promise, throwaway);
Goto(&next); Goto(&next);
......
...@@ -792,7 +792,8 @@ void InternalBuiltinsAssembler::RunPromiseHook( ...@@ -792,7 +792,8 @@ void InternalBuiltinsAssembler::RunPromiseHook(
Runtime::FunctionId id, TNode<Context> context, Runtime::FunctionId id, TNode<Context> context,
SloppyTNode<HeapObject> promise_or_capability) { SloppyTNode<HeapObject> promise_or_capability) {
Label hook(this, Label::kDeferred), done_hook(this); Label hook(this, Label::kDeferred), done_hook(this);
Branch(IsPromiseHookEnabledOrDebugIsActive(), &hook, &done_hook); GotoIf(IsDebugActive(), &hook);
Branch(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &hook, &done_hook);
BIND(&hook); BIND(&hook);
{ {
// Get to the underlying JSPromise instance. // Get to the underlying JSPromise instance.
......
...@@ -56,7 +56,7 @@ Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context, ...@@ -56,7 +56,7 @@ Node* PromiseBuiltinsAssembler::AllocateAndInitJSPromise(Node* context,
PromiseInit(instance); PromiseInit(instance);
Label out(this); Label out(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
CallRuntime(Runtime::kPromiseHookInit, context, instance, parent); CallRuntime(Runtime::kPromiseHookInit, context, instance, parent);
Goto(&out); Goto(&out);
...@@ -80,7 +80,7 @@ Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise( ...@@ -80,7 +80,7 @@ Node* PromiseBuiltinsAssembler::AllocateAndSetJSPromise(
} }
Label out(this); Label out(this);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &out); GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &out);
CallRuntime(Runtime::kPromiseHookInit, context, instance, CallRuntime(Runtime::kPromiseHookInit, context, instance,
UndefinedConstant()); UndefinedConstant());
Goto(&out); Goto(&out);
...@@ -839,7 +839,7 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) { ...@@ -839,7 +839,7 @@ TF_BUILTIN(PromiseConstructor, PromiseBuiltinsAssembler) {
PromiseInit(instance); PromiseInit(instance);
var_result.Bind(instance); var_result.Bind(instance);
GotoIfNot(IsPromiseHookEnabledOrDebugIsActive(), &debug_push); GotoIfNot(IsPromiseHookEnabledOrHasAsyncEventDelegate(), &debug_push);
CallRuntime(Runtime::kPromiseHookInit, context, instance, CallRuntime(Runtime::kPromiseHookInit, context, instance,
UndefinedConstant()); UndefinedConstant());
Goto(&debug_push); Goto(&debug_push);
...@@ -1059,7 +1059,8 @@ TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) { ...@@ -1059,7 +1059,8 @@ TF_BUILTIN(PromiseResolveThenableJob, PromiseBuiltinsAssembler) {
GotoIfNot(WordEqual(then, promise_then), &if_slow); GotoIfNot(WordEqual(then, promise_then), &if_slow);
Node* const thenable_map = LoadMap(thenable); Node* const thenable_map = LoadMap(thenable);
GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow); GotoIfNot(IsJSPromiseMap(thenable_map), &if_slow);
GotoIf(IsPromiseHookEnabledOrDebugIsActive(), &if_slow); GotoIf(IsPromiseHookEnabled(), &if_slow);
GotoIf(IsDebugActive(), &if_slow);
BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map, BranchIfPromiseSpeciesLookupChainIntact(native_context, thenable_map,
&if_fast, &if_slow); &if_fast, &if_slow);
...@@ -1665,7 +1666,8 @@ TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) { ...@@ -1665,7 +1666,8 @@ TF_BUILTIN(RejectPromise, PromiseBuiltinsAssembler) {
// the runtime handle this operation, which greatly reduces // the runtime handle this operation, which greatly reduces
// the complexity here and also avoids a couple of back and // the complexity here and also avoids a couple of back and
// forth between JavaScript and C++ land. // forth between JavaScript and C++ land.
GotoIf(IsPromiseHookEnabledOrDebugIsActive(), &if_runtime); GotoIf(IsPromiseHookEnabled(), &if_runtime);
GotoIf(IsDebugActive(), &if_runtime);
// 7. If promise.[[PromiseIsHandled]] is false, perform // 7. If promise.[[PromiseIsHandled]] is false, perform
// HostPromiseRejectionTracker(promise, "reject"). // HostPromiseRejectionTracker(promise, "reject").
...@@ -1712,7 +1714,8 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) { ...@@ -1712,7 +1714,8 @@ TF_BUILTIN(ResolvePromise, PromiseBuiltinsAssembler) {
// the runtime handle this operation, which greatly reduces // the runtime handle this operation, which greatly reduces
// the complexity here and also avoids a couple of back and // the complexity here and also avoids a couple of back and
// forth between JavaScript and C++ land. // forth between JavaScript and C++ land.
GotoIf(IsPromiseHookEnabledOrDebugIsActive(), &if_runtime); GotoIf(IsPromiseHookEnabled(), &if_runtime);
GotoIf(IsDebugActive(), &if_runtime);
// 6. If SameValue(resolution, promise) is true, then // 6. If SameValue(resolution, promise) is true, then
// We can use pointer comparison here, since the {promise} is guaranteed // We can use pointer comparison here, since the {promise} is guaranteed
......
...@@ -11588,13 +11588,28 @@ Node* CodeStubAssembler::IsDebugActive() { ...@@ -11588,13 +11588,28 @@ Node* CodeStubAssembler::IsDebugActive() {
return Word32NotEqual(is_debug_active, Int32Constant(0)); return Word32NotEqual(is_debug_active, Int32Constant(0));
} }
Node* CodeStubAssembler::IsPromiseHookEnabledOrDebugIsActive() { Node* CodeStubAssembler::IsPromiseHookEnabled() {
Node* const promise_hook_or_debug_is_active = Node* const promise_hook = Load(
MachineType::Pointer(),
ExternalConstant(ExternalReference::promise_hook_address(isolate())));
return WordNotEqual(promise_hook, IntPtrConstant(0));
}
Node* CodeStubAssembler::HasAsyncEventDelegate() {
Node* const async_event_delegate =
Load(MachineType::Pointer(),
ExternalConstant(
ExternalReference::async_event_delegate_address(isolate())));
return WordNotEqual(async_event_delegate, IntPtrConstant(0));
}
Node* CodeStubAssembler::IsPromiseHookEnabledOrHasAsyncEventDelegate() {
Node* const promise_hook_or_async_event_delegate =
Load(MachineType::Uint8(), Load(MachineType::Uint8(),
ExternalConstant( ExternalConstant(
ExternalReference::promise_hook_or_debug_is_active_address( ExternalReference::promise_hook_or_async_event_delegate_address(
isolate()))); isolate())));
return Word32NotEqual(promise_hook_or_debug_is_active, Int32Constant(0)); return Word32NotEqual(promise_hook_or_async_event_delegate, Int32Constant(0));
} }
TNode<Code> CodeStubAssembler::LoadBuiltin(TNode<Smi> builtin_id) { TNode<Code> CodeStubAssembler::LoadBuiltin(TNode<Smi> builtin_id) {
......
...@@ -2465,7 +2465,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -2465,7 +2465,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Node* context); Node* context);
// Promise helpers // Promise helpers
Node* IsPromiseHookEnabledOrDebugIsActive(); Node* IsPromiseHookEnabled();
Node* HasAsyncEventDelegate();
Node* IsPromiseHookEnabledOrHasAsyncEventDelegate();
// Helpers for StackFrame markers. // Helpers for StackFrame markers.
Node* MarkerIsFrameType(Node* marker_or_function, Node* MarkerIsFrameType(Node* marker_or_function,
......
...@@ -140,8 +140,6 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate, ...@@ -140,8 +140,6 @@ MaybeLocal<UnboundScript> CompileInspectorScript(Isolate* isolate,
class DebugDelegate { class DebugDelegate {
public: public:
virtual ~DebugDelegate() {} virtual ~DebugDelegate() {}
virtual void AsyncEventOccurred(debug::DebugAsyncActionType type, int id,
bool is_blackboxed) {}
virtual void ScriptCompiled(v8::Local<Script> script, bool is_live_edited, virtual void ScriptCompiled(v8::Local<Script> script, bool is_live_edited,
bool has_compile_error) {} bool has_compile_error) {}
// |inspector_break_points_hit| contains id of breakpoints installed with // |inspector_break_points_hit| contains id of breakpoints installed with
...@@ -162,6 +160,15 @@ class DebugDelegate { ...@@ -162,6 +160,15 @@ class DebugDelegate {
void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener); void SetDebugDelegate(Isolate* isolate, DebugDelegate* listener);
class AsyncEventDelegate {
public:
virtual ~AsyncEventDelegate() {}
virtual void AsyncEventOccurred(debug::DebugAsyncActionType type, int id,
bool is_blackboxed) = 0;
};
void SetAsyncEventDelegate(Isolate* isolate, AsyncEventDelegate* delegate);
void ResetBlackboxedStateCache(Isolate* isolate, void ResetBlackboxedStateCache(Isolate* isolate,
v8::Local<debug::Script> script); v8::Local<debug::Script> script);
......
...@@ -345,7 +345,6 @@ void Debug::ThreadInit() { ...@@ -345,7 +345,6 @@ void Debug::ThreadInit() {
thread_local_.ignore_step_into_function_ = Smi::kZero; thread_local_.ignore_step_into_function_ = Smi::kZero;
thread_local_.target_frame_count_ = -1; thread_local_.target_frame_count_ = -1;
thread_local_.return_value_ = Smi::kZero; thread_local_.return_value_ = Smi::kZero;
thread_local_.async_task_count_ = 0;
thread_local_.last_breakpoint_id_ = 0; thread_local_.last_breakpoint_id_ = 0;
clear_suspended_generator(); clear_suspended_generator();
thread_local_.restart_fp_ = kNullAddress; thread_local_.restart_fp_ = kNullAddress;
...@@ -1733,15 +1732,6 @@ void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) { ...@@ -1733,15 +1732,6 @@ void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) {
} }
} }
void Debug::OnAsyncFunctionStateChanged(Handle<JSPromise> promise,
debug::DebugAsyncActionType event) {
if (in_debug_scope() || ignore_events()) return;
if (!debug_delegate_) return;
PostponeInterruptsScope no_interrupts(isolate_);
int id = NextAsyncTaskId(promise);
debug_delegate_->AsyncEventOccurred(event, id, false);
}
namespace { namespace {
v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) { v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) {
Handle<Context> context = handle(isolate->context()); Handle<Context> context = handle(isolate->context());
...@@ -1855,66 +1845,6 @@ void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit) { ...@@ -1855,66 +1845,6 @@ void Debug::OnDebugBreak(Handle<FixedArray> break_points_hit) {
inspector_break_points_hit); inspector_break_points_hit);
} }
void Debug::RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
Handle<Object> parent) {
if (hook_type == PromiseHookType::kResolve) return;
if (in_debug_scope() || ignore_events()) return;
if (!debug_delegate_) return;
PostponeInterruptsScope no_interrupts(isolate_);
if (hook_type == PromiseHookType::kBefore) {
if (!promise->async_task_id()) return;
debug_delegate_->AsyncEventOccurred(debug::kDebugWillHandle,
promise->async_task_id(), false);
} else if (hook_type == PromiseHookType::kAfter) {
if (!promise->async_task_id()) return;
debug_delegate_->AsyncEventOccurred(debug::kDebugDidHandle,
promise->async_task_id(), false);
} else {
DCHECK(hook_type == PromiseHookType::kInit);
debug::DebugAsyncActionType type = debug::kDebugPromiseThen;
bool last_frame_was_promise_builtin = false;
JavaScriptFrameIterator it(isolate_);
while (!it.done()) {
std::vector<Handle<SharedFunctionInfo>> infos;
it.frame()->GetFunctions(&infos);
for (size_t i = 1; i <= infos.size(); ++i) {
Handle<SharedFunctionInfo> info = infos[infos.size() - i];
if (info->IsUserJavaScript()) {
// We should not report PromiseThen and PromiseCatch which is called
// indirectly, e.g. Promise.all calls Promise.then internally.
if (last_frame_was_promise_builtin) {
int id = NextAsyncTaskId(promise);
debug_delegate_->AsyncEventOccurred(type, id, IsBlackboxed(info));
}
return;
}
last_frame_was_promise_builtin = false;
if (info->HasBuiltinId()) {
if (info->builtin_id() == Builtins::kPromisePrototypeThen) {
type = debug::kDebugPromiseThen;
last_frame_was_promise_builtin = true;
} else if (info->builtin_id() == Builtins::kPromisePrototypeCatch) {
type = debug::kDebugPromiseCatch;
last_frame_was_promise_builtin = true;
} else if (info->builtin_id() == Builtins::kPromisePrototypeFinally) {
type = debug::kDebugPromiseFinally;
last_frame_was_promise_builtin = true;
}
}
}
it.Advance();
}
}
}
int Debug::NextAsyncTaskId(Handle<JSPromise> promise) {
if (!promise->async_task_id()) {
promise->set_async_task_id(++thread_local_.async_task_count_);
}
return promise->async_task_id();
}
namespace { namespace {
debug::Location GetDebugLocation(Handle<Script> script, int source_position) { debug::Location GetDebugLocation(Handle<Script> script, int source_position) {
Script::PositionInfo info; Script::PositionInfo info;
...@@ -2086,7 +2016,9 @@ void Debug::UpdateState() { ...@@ -2086,7 +2016,9 @@ void Debug::UpdateState() {
Unload(); Unload();
} }
is_active_ = is_active; is_active_ = is_active;
isolate_->DebugStateUpdated(); if (is_active && isolate_->IsPromiseHookProtectorIntact()) {
isolate_->InvalidatePromiseHookProtector();
}
} }
void Debug::UpdateHookOnFunctionCall() { void Debug::UpdateHookOnFunctionCall() {
......
...@@ -224,9 +224,6 @@ class Debug { ...@@ -224,9 +224,6 @@ class Debug {
void OnCompileError(Handle<Script> script); void OnCompileError(Handle<Script> script);
void OnAfterCompile(Handle<Script> script); void OnAfterCompile(Handle<Script> script);
void OnAsyncFunctionStateChanged(Handle<JSPromise> promise,
debug::DebugAsyncActionType);
Handle<Context> GetDebugContext(); Handle<Context> GetDebugContext();
void HandleDebugBreak(IgnoreBreakMode ignore_break_mode); void HandleDebugBreak(IgnoreBreakMode ignore_break_mode);
...@@ -275,11 +272,6 @@ class Debug { ...@@ -275,11 +272,6 @@ class Debug {
int end_position, bool restrict_to_function, int end_position, bool restrict_to_function,
std::vector<BreakLocation>* locations); std::vector<BreakLocation>* locations);
void RunPromiseHook(PromiseHookType hook_type, Handle<JSPromise> promise,
Handle<Object> parent);
int NextAsyncTaskId(Handle<JSPromise> promise);
bool IsBlackboxed(Handle<SharedFunctionInfo> shared); bool IsBlackboxed(Handle<SharedFunctionInfo> shared);
bool CanBreakAtEntry(Handle<SharedFunctionInfo> shared); bool CanBreakAtEntry(Handle<SharedFunctionInfo> shared);
...@@ -575,8 +567,6 @@ class Debug { ...@@ -575,8 +567,6 @@ class Debug {
// The new frame pointer to drop to when restarting a frame. // The new frame pointer to drop to when restarting a frame.
Address restart_fp_; Address restart_fp_;
int async_task_count_;
// Last used inspector breakpoint id. // Last used inspector breakpoint id.
int last_breakpoint_id_; int last_breakpoint_id_;
......
...@@ -839,9 +839,20 @@ ExternalReference ExternalReference::cpu_features() { ...@@ -839,9 +839,20 @@ ExternalReference ExternalReference::cpu_features() {
return ExternalReference(&CpuFeatures::supported_); return ExternalReference(&CpuFeatures::supported_);
} }
ExternalReference ExternalReference::promise_hook_or_debug_is_active_address( ExternalReference ExternalReference::promise_hook_address(Isolate* isolate) {
return ExternalReference(isolate->promise_hook_address());
}
ExternalReference ExternalReference::async_event_delegate_address(
Isolate* isolate) { Isolate* isolate) {
return ExternalReference(isolate->promise_hook_or_debug_is_active_address()); return ExternalReference(isolate->async_event_delegate_address());
}
ExternalReference
ExternalReference::promise_hook_or_async_event_delegate_address(
Isolate* isolate) {
return ExternalReference(
isolate->promise_hook_or_async_event_delegate_address());
} }
ExternalReference ExternalReference::debug_is_active_address(Isolate* isolate) { ExternalReference ExternalReference::debug_is_active_address(Isolate* isolate) {
......
...@@ -53,8 +53,10 @@ class StatsCounter; ...@@ -53,8 +53,10 @@ class StatsCounter;
V(address_of_pending_message_obj, "address_of_pending_message_obj") \ V(address_of_pending_message_obj, "address_of_pending_message_obj") \
V(get_or_create_hash_raw, "get_or_create_hash_raw") \ V(get_or_create_hash_raw, "get_or_create_hash_raw") \
V(jsreceiver_create_identity_hash, "jsreceiver_create_identity_hash") \ V(jsreceiver_create_identity_hash, "jsreceiver_create_identity_hash") \
V(promise_hook_or_debug_is_active_address, \ V(promise_hook_address, "Isolate::promise_hook_address()") \
"Isolate::promise_hook_or_debug_is_active_address()") \ V(async_event_delegate_address, "Isolate::async_event_delegate_address()") \
V(promise_hook_or_async_event_delegate_address, \
"Isolate::promise_hook_or_async_event_delegate_address()") \
V(debug_is_active_address, "Debug::is_active_address()") \ V(debug_is_active_address, "Debug::is_active_address()") \
V(debug_hook_on_function_call_address, \ V(debug_hook_on_function_call_address, \
"Debug::hook_on_function_call_address()") \ "Debug::hook_on_function_call_address()") \
......
...@@ -200,7 +200,6 @@ void V8Debugger::disable() { ...@@ -200,7 +200,6 @@ void V8Debugger::disable() {
} }
if (--m_enableCount) return; if (--m_enableCount) return;
clearContinueToLocation(); clearContinueToLocation();
allAsyncTasksCanceled();
m_taskWithScheduledBreak = nullptr; m_taskWithScheduledBreak = nullptr;
m_taskWithScheduledBreakDebuggerId = String16(); m_taskWithScheduledBreakDebuggerId = String16();
m_pauseOnAsyncCall = false; m_pauseOnAsyncCall = false;
...@@ -854,6 +853,8 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { ...@@ -854,6 +853,8 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
m_inspector->client()->maxAsyncCallStackDepthChanged( m_inspector->client()->maxAsyncCallStackDepthChanged(
m_maxAsyncCallStackDepth); m_maxAsyncCallStackDepth);
if (!maxAsyncCallStackDepth) allAsyncTasksCanceled(); if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
v8::debug::SetAsyncEventDelegate(m_isolate,
maxAsyncCallStackDepth ? this : nullptr);
} }
std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor( std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
......
...@@ -35,7 +35,8 @@ using ScheduleStepIntoAsyncCallback = ...@@ -35,7 +35,8 @@ using ScheduleStepIntoAsyncCallback =
using TerminateExecutionCallback = using TerminateExecutionCallback =
protocol::Runtime::Backend::TerminateExecutionCallback; protocol::Runtime::Backend::TerminateExecutionCallback;
class V8Debugger : public v8::debug::DebugDelegate { class V8Debugger : public v8::debug::DebugDelegate,
public v8::debug::AsyncEventDelegate {
public: public:
V8Debugger(v8::Isolate*, V8InspectorImpl*); V8Debugger(v8::Isolate*, V8InspectorImpl*);
~V8Debugger(); ~V8Debugger();
......
...@@ -2513,7 +2513,6 @@ Isolate::Isolate() ...@@ -2513,7 +2513,6 @@ Isolate::Isolate()
random_number_generator_(nullptr), random_number_generator_(nullptr),
fuzzer_rng_(nullptr), fuzzer_rng_(nullptr),
rail_mode_(PERFORMANCE_ANIMATION), rail_mode_(PERFORMANCE_ANIMATION),
promise_hook_or_debug_is_active_(false),
atomics_wait_callback_(nullptr), atomics_wait_callback_(nullptr),
atomics_wait_callback_data_(nullptr), atomics_wait_callback_data_(nullptr),
promise_hook_(nullptr), promise_hook_(nullptr),
...@@ -3471,7 +3470,7 @@ bool Isolate::IsPromiseHookProtectorIntact() { ...@@ -3471,7 +3470,7 @@ bool Isolate::IsPromiseHookProtectorIntact() {
bool is_promise_hook_protector_intact = bool is_promise_hook_protector_intact =
Smi::ToInt(promise_hook_cell->value()) == kProtectorValid; Smi::ToInt(promise_hook_cell->value()) == kProtectorValid;
DCHECK_IMPLIES(is_promise_hook_protector_intact, DCHECK_IMPLIES(is_promise_hook_protector_intact,
!promise_hook_or_debug_is_active_); !promise_hook_or_async_event_delegate_);
return is_promise_hook_protector_intact; return is_promise_hook_protector_intact;
} }
...@@ -3762,12 +3761,13 @@ void Isolate::FireCallCompletedCallback() { ...@@ -3762,12 +3761,13 @@ void Isolate::FireCallCompletedCallback() {
} }
} }
void Isolate::DebugStateUpdated() { void Isolate::PromiseHookStateUpdated() {
bool promise_hook_or_debug_is_active = promise_hook_ || debug()->is_active(); bool is_active = promise_hook_ || async_event_delegate_;
if (promise_hook_or_debug_is_active && IsPromiseHookProtectorIntact()) { if (is_active && IsPromiseHookProtectorIntact()) {
HandleScope scope(this);
InvalidatePromiseHookProtector(); InvalidatePromiseHookProtector();
} }
promise_hook_or_debug_is_active_ = promise_hook_or_debug_is_active; promise_hook_or_async_event_delegate_ = is_active;
} }
namespace { namespace {
...@@ -3869,17 +3869,81 @@ void Isolate::RunAtomicsWaitCallback(v8::Isolate::AtomicsWaitEvent event, ...@@ -3869,17 +3869,81 @@ void Isolate::RunAtomicsWaitCallback(v8::Isolate::AtomicsWaitEvent event,
void Isolate::SetPromiseHook(PromiseHook hook) { void Isolate::SetPromiseHook(PromiseHook hook) {
promise_hook_ = hook; promise_hook_ = hook;
DebugStateUpdated(); PromiseHookStateUpdated();
} }
void Isolate::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise, void Isolate::RunPromiseHook(PromiseHookType type, Handle<JSPromise> promise,
Handle<Object> parent) { Handle<Object> parent) {
if (debug()->is_active()) debug()->RunPromiseHook(type, promise, parent); RunPromiseHookForAsyncEventDelegate(type, promise);
if (promise_hook_ == nullptr) return; if (promise_hook_ == nullptr) return;
promise_hook_(type, v8::Utils::PromiseToLocal(promise), promise_hook_(type, v8::Utils::PromiseToLocal(promise),
v8::Utils::ToLocal(parent)); v8::Utils::ToLocal(parent));
} }
void Isolate::RunPromiseHookForAsyncEventDelegate(PromiseHookType type,
Handle<JSPromise> promise) {
if (!async_event_delegate_) return;
if (type == PromiseHookType::kResolve) return;
if (type == PromiseHookType::kBefore) {
if (!promise->async_task_id()) return;
async_event_delegate_->AsyncEventOccurred(debug::kDebugWillHandle,
promise->async_task_id(), false);
} else if (type == PromiseHookType::kAfter) {
if (!promise->async_task_id()) return;
async_event_delegate_->AsyncEventOccurred(debug::kDebugDidHandle,
promise->async_task_id(), false);
} else {
DCHECK(type == PromiseHookType::kInit);
debug::DebugAsyncActionType type = debug::kDebugPromiseThen;
bool last_frame_was_promise_builtin = false;
JavaScriptFrameIterator it(this);
while (!it.done()) {
std::vector<Handle<SharedFunctionInfo>> infos;
it.frame()->GetFunctions(&infos);
for (size_t i = 1; i <= infos.size(); ++i) {
Handle<SharedFunctionInfo> info = infos[infos.size() - i];
if (info->IsUserJavaScript()) {
// We should not report PromiseThen and PromiseCatch which is called
// indirectly, e.g. Promise.all calls Promise.then internally.
if (last_frame_was_promise_builtin) {
if (!promise->async_task_id()) {
promise->set_async_task_id(++async_task_count_);
}
async_event_delegate_->AsyncEventOccurred(
type, promise->async_task_id(), debug()->IsBlackboxed(info));
}
return;
}
last_frame_was_promise_builtin = false;
if (info->HasBuiltinId()) {
if (info->builtin_id() == Builtins::kPromisePrototypeThen) {
type = debug::kDebugPromiseThen;
last_frame_was_promise_builtin = true;
} else if (info->builtin_id() == Builtins::kPromisePrototypeCatch) {
type = debug::kDebugPromiseCatch;
last_frame_was_promise_builtin = true;
} else if (info->builtin_id() == Builtins::kPromisePrototypeFinally) {
type = debug::kDebugPromiseFinally;
last_frame_was_promise_builtin = true;
}
}
}
it.Advance();
}
}
}
void Isolate::OnAsyncFunctionStateChanged(Handle<JSPromise> promise,
debug::DebugAsyncActionType event) {
if (!async_event_delegate_) return;
if (!promise->async_task_id()) {
promise->set_async_task_id(++async_task_count_);
}
async_event_delegate_->AsyncEventOccurred(event, promise->async_task_id(),
false);
}
void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) { void Isolate::SetPromiseRejectCallback(PromiseRejectCallback callback) {
promise_reject_callback_ = callback; promise_reject_callback_ = callback;
} }
......
...@@ -765,6 +765,13 @@ class Isolate : private HiddenFactory { ...@@ -765,6 +765,13 @@ class Isolate : private HiddenFactory {
} }
debug::ConsoleDelegate* console_delegate() { return console_delegate_; } debug::ConsoleDelegate* console_delegate() { return console_delegate_; }
void set_async_event_delegate(debug::AsyncEventDelegate* delegate) {
async_event_delegate_ = delegate;
PromiseHookStateUpdated();
}
void OnAsyncFunctionStateChanged(Handle<JSPromise> promise,
debug::DebugAsyncActionType);
// Re-throw an exception. This involves no error reporting since error // Re-throw an exception. This involves no error reporting since error
// reporting was handled when the exception was thrown originally. // reporting was handled when the exception was thrown originally.
Object* ReThrow(Object* exception); Object* ReThrow(Object* exception);
...@@ -1237,8 +1244,16 @@ class Isolate : private HiddenFactory { ...@@ -1237,8 +1244,16 @@ class Isolate : private HiddenFactory {
int GetNextUniqueSharedFunctionInfoId() { return next_unique_sfi_id_++; } int GetNextUniqueSharedFunctionInfoId() { return next_unique_sfi_id_++; }
#endif #endif
Address promise_hook_or_debug_is_active_address() { Address promise_hook_address() {
return reinterpret_cast<Address>(&promise_hook_or_debug_is_active_); return reinterpret_cast<Address>(&promise_hook_);
}
Address async_event_delegate_address() {
return reinterpret_cast<Address>(&async_event_delegate_);
}
Address promise_hook_or_async_event_delegate_address() {
return reinterpret_cast<Address>(&promise_hook_or_async_event_delegate_);
} }
Address pending_microtask_count_address() { Address pending_microtask_count_address() {
...@@ -1253,8 +1268,6 @@ class Isolate : private HiddenFactory { ...@@ -1253,8 +1268,6 @@ class Isolate : private HiddenFactory {
return reinterpret_cast<Address>(&debug_execution_mode_); return reinterpret_cast<Address>(&debug_execution_mode_);
} }
void DebugStateUpdated();
void SetAtomicsWaitCallback(v8::Isolate::AtomicsWaitCallback callback, void SetAtomicsWaitCallback(v8::Isolate::AtomicsWaitCallback callback,
void* data); void* data);
void RunAtomicsWaitCallback(v8::Isolate::AtomicsWaitEvent event, void RunAtomicsWaitCallback(v8::Isolate::AtomicsWaitEvent event,
...@@ -1490,6 +1503,10 @@ class Isolate : private HiddenFactory { ...@@ -1490,6 +1503,10 @@ class Isolate : private HiddenFactory {
void SetTerminationOnExternalTryCatch(); void SetTerminationOnExternalTryCatch();
void PromiseHookStateUpdated();
void RunPromiseHookForAsyncEventDelegate(PromiseHookType type,
Handle<JSPromise> promise);
const char* RAILModeName(RAILMode rail_mode) const { const char* RAILModeName(RAILMode rail_mode) const {
switch (rail_mode) { switch (rail_mode) {
case PERFORMANCE_RESPONSE: case PERFORMANCE_RESPONSE:
...@@ -1549,7 +1566,6 @@ class Isolate : private HiddenFactory { ...@@ -1549,7 +1566,6 @@ class Isolate : private HiddenFactory {
base::RandomNumberGenerator* random_number_generator_; base::RandomNumberGenerator* random_number_generator_;
base::RandomNumberGenerator* fuzzer_rng_; base::RandomNumberGenerator* fuzzer_rng_;
base::AtomicValue<RAILMode> rail_mode_; base::AtomicValue<RAILMode> rail_mode_;
bool promise_hook_or_debug_is_active_;
v8::Isolate::AtomicsWaitCallback atomics_wait_callback_; v8::Isolate::AtomicsWaitCallback atomics_wait_callback_;
void* atomics_wait_callback_data_; void* atomics_wait_callback_data_;
PromiseHook promise_hook_; PromiseHook promise_hook_;
...@@ -1671,6 +1687,10 @@ class Isolate : private HiddenFactory { ...@@ -1671,6 +1687,10 @@ class Isolate : private HiddenFactory {
debug::ConsoleDelegate* console_delegate_ = nullptr; debug::ConsoleDelegate* console_delegate_ = nullptr;
debug::AsyncEventDelegate* async_event_delegate_ = nullptr;
bool promise_hook_or_async_event_delegate_ = false;
int async_task_count_ = 0;
v8::Isolate::AbortOnUncaughtExceptionCallback v8::Isolate::AbortOnUncaughtExceptionCallback
abort_on_uncaught_exception_callback_; abort_on_uncaught_exception_callback_;
......
...@@ -880,8 +880,7 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionSuspended) { ...@@ -880,8 +880,7 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionSuspended) {
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
HandleScope scope(isolate); HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0); CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 0);
isolate->debug()->OnAsyncFunctionStateChanged(promise, isolate->OnAsyncFunctionStateChanged(promise, debug::kAsyncFunctionSuspended);
debug::kAsyncFunctionSuspended);
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
...@@ -892,8 +891,8 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionFinished) { ...@@ -892,8 +891,8 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionFinished) {
CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 1); CONVERT_ARG_HANDLE_CHECKED(JSPromise, promise, 1);
isolate->PopPromise(); isolate->PopPromise();
if (has_suspend) { if (has_suspend) {
isolate->debug()->OnAsyncFunctionStateChanged( isolate->OnAsyncFunctionStateChanged(promise,
promise, debug::kAsyncFunctionFinished); debug::kAsyncFunctionFinished);
} }
return isolate->heap()->undefined_value(); return isolate->heap()->undefined_value();
} }
......
...@@ -2207,7 +2207,8 @@ TEST(IsPromiseHookEnabled) { ...@@ -2207,7 +2207,8 @@ TEST(IsPromiseHookEnabled) {
CodeAssemblerTester asm_tester(isolate, kNumParams); CodeAssemblerTester asm_tester(isolate, kNumParams);
CodeStubAssembler m(asm_tester.state()); CodeStubAssembler m(asm_tester.state());
m.Return(m.SelectBooleanConstant(m.IsPromiseHookEnabledOrDebugIsActive())); m.Return(
m.SelectBooleanConstant(m.IsPromiseHookEnabledOrHasAsyncEventDelegate()));
FunctionTester ft(asm_tester.GenerateCode(), kNumParams); FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
Handle<Object> result = Handle<Object> result =
......
...@@ -72,6 +72,7 @@ function createPromise() { ...@@ -72,6 +72,7 @@ function createPromise() {
session.setupScriptMap(); session.setupScriptMap();
Protocol.Debugger.enable(); Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
InspectorTest.runAsyncTestSuite([ InspectorTest.runAsyncTestSuite([
async function testScheduleErrors() { async function testScheduleErrors() {
Protocol.Runtime.evaluate({ expression: 'testNoScheduledTask()' }); Protocol.Runtime.evaluate({ expression: 'testNoScheduledTask()' });
......
...@@ -58,6 +58,7 @@ function createPromise() { ...@@ -58,6 +58,7 @@ function createPromise() {
session.setupScriptMap(); session.setupScriptMap();
Protocol.Debugger.enable(); Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
InspectorTest.runAsyncTestSuite([ InspectorTest.runAsyncTestSuite([
async function testScheduleErrors() { async function testScheduleErrors() {
Protocol.Runtime.evaluate({ expression: 'testNoScheduledTask()' }); Protocol.Runtime.evaluate({ expression: 'testNoScheduledTask()' });
......
...@@ -9,6 +9,8 @@ InspectorTest.runAsyncTestSuite([ ...@@ -9,6 +9,8 @@ InspectorTest.runAsyncTestSuite([
async function testSetTimeout() { async function testSetTimeout() {
Protocol.Debugger.enable(); Protocol.Debugger.enable();
Protocol.Debugger.pause(); Protocol.Debugger.pause();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
let pausedPromise = Protocol.Debugger.oncePaused(); let pausedPromise = Protocol.Debugger.oncePaused();
Protocol.Runtime.evaluate({ Protocol.Runtime.evaluate({
expression: 'setTimeout(() => 42, 0)//# sourceURL=test.js' expression: 'setTimeout(() => 42, 0)//# sourceURL=test.js'
...@@ -35,6 +37,8 @@ InspectorTest.runAsyncTestSuite([ ...@@ -35,6 +37,8 @@ InspectorTest.runAsyncTestSuite([
async function testPromiseThen() { async function testPromiseThen() {
Protocol.Debugger.enable(); Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
Protocol.Runtime.evaluate({expression: 'var p = Promise.resolve()'}); Protocol.Runtime.evaluate({expression: 'var p = Promise.resolve()'});
Protocol.Debugger.pause(); Protocol.Debugger.pause();
let pausedPromise = Protocol.Debugger.oncePaused(); let pausedPromise = Protocol.Debugger.oncePaused();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment