[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:
Alexey Kozyatinskiy 2017-11-29 14:28:31 -08:00 committed by Commit Bot
parent 0762f35fa7
commit c30472b83e
7 changed files with 140 additions and 33 deletions

View File

@ -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() {

View File

@ -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);

View File

@ -331,11 +331,7 @@ 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;
}
m_taskWithScheduledBreakDebuggerId = debuggerId;
}
Response V8Debugger::continueToLocation(
@ -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(

View File

@ -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>>;

View File

@ -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});

View File

@ -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)

View File

@ -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();
})()