[inspector] external stack intrumentation can be called on one debugger
Some embedders primitive can trigger execution in current JavaScript instance or in another (e.g. MessageChannel). With this CL external async task can be local as well. R=dgozman@chromium.org Bug: chromium:661705 Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel Change-Id: I82c68a021c2c25bc67a706c4bfed8c1a2b2388c5 Reviewed-on: https://chromium-review.googlesource.com/792015 Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Cr-Commit-Position: refs/heads/master@{#49728}
This commit is contained in:
parent
0762f35fa7
commit
c30472b83e
@ -1330,6 +1330,24 @@ V8DebuggerAgentImpl::currentExternalStackTrace() {
|
||||
.build();
|
||||
}
|
||||
|
||||
std::unique_ptr<protocol::Runtime::StackTraceId>
|
||||
V8DebuggerAgentImpl::currentScheduledAsyncCall() {
|
||||
v8_inspector::V8StackTraceId scheduledAsyncCall =
|
||||
m_debugger->scheduledAsyncCall();
|
||||
if (scheduledAsyncCall.IsInvalid()) return nullptr;
|
||||
std::unique_ptr<protocol::Runtime::StackTraceId> asyncCallStackTrace =
|
||||
protocol::Runtime::StackTraceId::create()
|
||||
.setId(stackTraceIdToString(scheduledAsyncCall.id))
|
||||
.build();
|
||||
// TODO(kozyatinskiy): extract this check to IsLocal function.
|
||||
if (scheduledAsyncCall.debugger_id.first ||
|
||||
scheduledAsyncCall.debugger_id.second) {
|
||||
asyncCallStackTrace->setDebuggerId(
|
||||
debuggerIdToString(scheduledAsyncCall.debugger_id));
|
||||
}
|
||||
return asyncCallStackTrace;
|
||||
}
|
||||
|
||||
bool V8DebuggerAgentImpl::isPaused() const {
|
||||
return m_debugger->isPausedInContextGroup(m_session->contextGroupId());
|
||||
}
|
||||
@ -1532,22 +1550,10 @@ void V8DebuggerAgentImpl::didPause(
|
||||
Response response = currentCallFrames(&protocolCallFrames);
|
||||
if (!response.isSuccess()) protocolCallFrames = Array<CallFrame>::create();
|
||||
|
||||
Maybe<protocol::Runtime::StackTraceId> asyncCallStackTrace;
|
||||
void* rawScheduledAsyncTask = m_debugger->scheduledAsyncTask();
|
||||
if (rawScheduledAsyncTask) {
|
||||
asyncCallStackTrace =
|
||||
protocol::Runtime::StackTraceId::create()
|
||||
.setId(stackTraceIdToString(
|
||||
reinterpret_cast<uintptr_t>(rawScheduledAsyncTask)))
|
||||
.setDebuggerId(debuggerIdToString(
|
||||
m_debugger->debuggerIdFor(m_session->contextGroupId())))
|
||||
.build();
|
||||
}
|
||||
|
||||
m_frontend.paused(std::move(protocolCallFrames), breakReason,
|
||||
std::move(breakAuxData), std::move(hitBreakpointIds),
|
||||
currentAsyncStackTrace(), currentExternalStackTrace(),
|
||||
std::move(asyncCallStackTrace));
|
||||
currentScheduledAsyncCall());
|
||||
}
|
||||
|
||||
void V8DebuggerAgentImpl::didContinue() {
|
||||
|
@ -156,6 +156,7 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
|
||||
std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>*);
|
||||
std::unique_ptr<protocol::Runtime::StackTrace> currentAsyncStackTrace();
|
||||
std::unique_ptr<protocol::Runtime::StackTraceId> currentExternalStackTrace();
|
||||
std::unique_ptr<protocol::Runtime::StackTraceId> currentScheduledAsyncCall();
|
||||
|
||||
void setPauseOnExceptionsImpl(int);
|
||||
|
||||
|
@ -331,12 +331,8 @@ void V8Debugger::pauseOnAsyncCall(int targetContextGroupId, uintptr_t task,
|
||||
m_targetContextGroupId = targetContextGroupId;
|
||||
|
||||
m_taskWithScheduledBreak = reinterpret_cast<void*>(task);
|
||||
String16 currentDebuggerId =
|
||||
debuggerIdToString(debuggerIdFor(targetContextGroupId));
|
||||
if (currentDebuggerId != debuggerId) {
|
||||
m_taskWithScheduledBreakDebuggerId = debuggerId;
|
||||
}
|
||||
}
|
||||
|
||||
Response V8Debugger::continueToLocation(
|
||||
int targetContextGroupId, V8DebuggerScript* script,
|
||||
@ -542,19 +538,19 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
|
||||
switch (type) {
|
||||
case v8::debug::kDebugAsyncFunctionPromiseCreated:
|
||||
asyncTaskScheduledForStack("async function", task, true);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
|
||||
break;
|
||||
case v8::debug::kDebugPromiseThen:
|
||||
asyncTaskScheduledForStack("Promise.then", task, false);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
|
||||
break;
|
||||
case v8::debug::kDebugPromiseCatch:
|
||||
asyncTaskScheduledForStack("Promise.catch", task, false);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
|
||||
break;
|
||||
case v8::debug::kDebugPromiseFinally:
|
||||
asyncTaskScheduledForStack("Promise.finally", task, false);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task);
|
||||
if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
|
||||
break;
|
||||
case v8::debug::kDebugWillHandle:
|
||||
asyncTaskStartedForStack(task);
|
||||
@ -767,7 +763,7 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace(
|
||||
++m_asyncStacksCount;
|
||||
collectOldAsyncStacksIfNeeded();
|
||||
|
||||
asyncTaskCandidateForStepping(reinterpret_cast<void*>(id));
|
||||
asyncTaskCandidateForStepping(reinterpret_cast<void*>(id), false);
|
||||
|
||||
return V8StackTraceId(id, debuggerIdFor(contextGroupId));
|
||||
}
|
||||
@ -816,7 +812,7 @@ void V8Debugger::externalAsyncTaskFinished(const V8StackTraceId& parent) {
|
||||
void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
|
||||
bool recurring) {
|
||||
asyncTaskScheduledForStack(toString16(taskName), task, recurring);
|
||||
asyncTaskCandidateForStepping(task);
|
||||
asyncTaskCandidateForStepping(task, true);
|
||||
}
|
||||
|
||||
void V8Debugger::asyncTaskCanceled(void* task) {
|
||||
@ -890,16 +886,23 @@ void V8Debugger::asyncTaskFinishedForStack(void* task) {
|
||||
}
|
||||
}
|
||||
|
||||
void V8Debugger::asyncTaskCandidateForStepping(void* task) {
|
||||
if (m_pauseOnAsyncCall) {
|
||||
m_scheduledAsyncTask = task;
|
||||
void V8Debugger::asyncTaskCandidateForStepping(void* task, bool isLocal) {
|
||||
int contextGroupId = currentContextGroupId();
|
||||
if (m_pauseOnAsyncCall && contextGroupId) {
|
||||
if (isLocal) {
|
||||
m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
|
||||
reinterpret_cast<uintptr_t>(task), std::make_pair(0, 0));
|
||||
} else {
|
||||
m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
|
||||
reinterpret_cast<uintptr_t>(task), debuggerIdFor(contextGroupId));
|
||||
}
|
||||
breakProgram(m_targetContextGroupId);
|
||||
m_scheduledAsyncTask = nullptr;
|
||||
m_scheduledAsyncCall = v8_inspector::V8StackTraceId();
|
||||
return;
|
||||
}
|
||||
if (!m_stepIntoAsyncCallback) return;
|
||||
DCHECK(m_targetContextGroupId);
|
||||
if (currentContextGroupId() != m_targetContextGroupId) return;
|
||||
if (contextGroupId != m_targetContextGroupId) return;
|
||||
m_taskWithScheduledBreak = task;
|
||||
v8::debug::ClearStepping(m_isolate);
|
||||
m_stepIntoAsyncCallback->sendSuccess();
|
||||
@ -1031,6 +1034,7 @@ std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(int contextGroupId) {
|
||||
std::pair<int64_t, int64_t> debuggerId(
|
||||
v8::debug::GetNextRandomInt64(m_isolate),
|
||||
v8::debug::GetNextRandomInt64(m_isolate));
|
||||
if (!debuggerId.first && !debuggerId.second) ++debuggerId.first;
|
||||
m_contextGroupIdToDebuggerId.insert(
|
||||
it, std::make_pair(contextGroupId, debuggerId));
|
||||
m_serializedDebuggerIdToDebuggerId.insert(
|
||||
|
@ -117,7 +117,9 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
void setMaxAsyncTaskStacksForTest(int limit);
|
||||
void dumpAsyncTaskStacksStateForTest();
|
||||
|
||||
void* scheduledAsyncTask() { return m_scheduledAsyncTask; }
|
||||
v8_inspector::V8StackTraceId scheduledAsyncCall() {
|
||||
return m_scheduledAsyncCall;
|
||||
}
|
||||
|
||||
std::pair<int64_t, int64_t> debuggerIdFor(int contextGroupId);
|
||||
std::pair<int64_t, int64_t> debuggerIdFor(
|
||||
@ -155,7 +157,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
void asyncTaskStartedForStack(void* task);
|
||||
void asyncTaskFinishedForStack(void* task);
|
||||
|
||||
void asyncTaskCandidateForStepping(void* task);
|
||||
void asyncTaskCandidateForStepping(void* task, bool isLocal);
|
||||
void asyncTaskStartedForStepping(void* task);
|
||||
void asyncTaskFinishedForStepping(void* task);
|
||||
void asyncTaskCanceledForStepping(void* task);
|
||||
@ -219,7 +221,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
|
||||
|
||||
v8::debug::ExceptionBreakState m_pauseOnExceptionsState;
|
||||
bool m_pauseOnAsyncCall = false;
|
||||
void* m_scheduledAsyncTask = nullptr;
|
||||
v8_inspector::V8StackTraceId m_scheduledAsyncCall;
|
||||
|
||||
using StackTraceIdToStackTrace =
|
||||
protocol::HashMap<uintptr_t, std::weak_ptr<AsyncStackTrace>>;
|
||||
|
@ -119,7 +119,6 @@ InspectorTest.runAsyncTestSuite([
|
||||
},
|
||||
|
||||
async function testExternalStacks() {
|
||||
|
||||
let debuggerId1 = (await Protocol1.Debugger.enable()).result.debuggerId;
|
||||
let debuggerId2 = (await Protocol2.Debugger.enable()).result.debuggerId;
|
||||
Protocol1.Debugger.setAsyncCallStackDepth({maxDepth: 32});
|
||||
|
@ -0,0 +1,14 @@
|
||||
Test for step-into remote async task.
|
||||
Setup debugger agents..
|
||||
Pause before stack trace is captured..
|
||||
Run stepInto with breakOnAsyncCall flag
|
||||
Call pauseOnAsyncCall
|
||||
Trigger external async task on another context group
|
||||
Dump stack trace
|
||||
boo (target.js:1:18)
|
||||
call (framework.js:3:2)
|
||||
(anonymous) (target.js:0:0)
|
||||
-- remote-task --
|
||||
store (utils.js:2:25)
|
||||
foo (source.js:1:13)
|
||||
(anonymous) (source.js:2:6)
|
@ -0,0 +1,81 @@
|
||||
// Copyright 2017 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
let {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Test for step-into remote async task.');
|
||||
|
||||
contextGroup.addScript(`
|
||||
function store(description) {
|
||||
let buffer = inspector.storeCurrentStackTrace(description);
|
||||
return '[' + new Int32Array(buffer).join(',') + ']';
|
||||
}
|
||||
//# sourceURL=utils.js`);
|
||||
|
||||
contextGroup.addScript(`
|
||||
function call(id, f) {
|
||||
inspector.externalAsyncTaskStarted(Int32Array.from(JSON.parse(id)).buffer);
|
||||
f();
|
||||
inspector.externalAsyncTaskFinished(Int32Array.from(JSON.parse(id)).buffer);
|
||||
}
|
||||
//# sourceURL=framework.js`);
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
(async function test() {
|
||||
InspectorTest.log('Setup debugger agents..');
|
||||
let debuggerId = (await Protocol.Debugger.enable()).result.debuggerId;
|
||||
|
||||
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
|
||||
Protocol.Debugger.setBlackboxPatterns({patterns: ['framework\.js']});
|
||||
|
||||
InspectorTest.log('Pause before stack trace is captured..');
|
||||
Protocol.Debugger.setBreakpointByUrl(
|
||||
{lineNumber: 2, columnNumber: 25, url: 'utils.js'});
|
||||
let evaluatePromise = Protocol.Runtime.evaluate({
|
||||
expression: `(function foo() {
|
||||
return store('remote-task');
|
||||
})()
|
||||
//# sourceURL=source.js`
|
||||
});
|
||||
await Protocol.Debugger.oncePaused();
|
||||
|
||||
InspectorTest.log('Run stepInto with breakOnAsyncCall flag');
|
||||
Protocol.Debugger.stepInto({breakOnAsyncCall: true});
|
||||
let {params: {asyncCallStackTraceId}} = await Protocol.Debugger.oncePaused();
|
||||
|
||||
InspectorTest.log('Call pauseOnAsyncCall');
|
||||
Protocol.Debugger.pauseOnAsyncCall({
|
||||
parentStackTraceId: asyncCallStackTraceId,
|
||||
});
|
||||
Protocol.Debugger.resume();
|
||||
|
||||
InspectorTest.log('Trigger external async task on another context group');
|
||||
let stackTraceId = (await evaluatePromise).result.result.value;
|
||||
Protocol.Runtime.evaluate({
|
||||
expression: `call('${stackTraceId}',
|
||||
function boo() {})
|
||||
//# sourceURL=target.js`
|
||||
});
|
||||
|
||||
InspectorTest.log('Dump stack trace');
|
||||
let {params: {callFrames, asyncStackTraceId}} =
|
||||
await Protocol.Debugger.oncePaused();
|
||||
while (true) {
|
||||
session.logCallFrames(callFrames);
|
||||
if (asyncStackTraceId) {
|
||||
let {result: {stackTrace}} = await Protocol.Debugger.getStackTrace(
|
||||
{stackTraceId: asyncStackTraceId});
|
||||
InspectorTest.log(`-- ${stackTrace.description} --`);
|
||||
callFrames = stackTrace.callFrames;
|
||||
asyncStackTraceId = stackTrace.parentId;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 0});
|
||||
await Protocol.Debugger.disable();
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})()
|
Loading…
Reference in New Issue
Block a user