Commit 48c1cb97 authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[inspector] introduce way to get full stored async stack

If async stack is longer then max depth, we add externalParent as id,
client can fetch next max depth async stacks by Debugger.getStackTrace.

R=dgozman@chromium.org

Bug: chromium:778796
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I89d461e672251f03fb239f4f16ae3b0374fce766
Reviewed-on: https://chromium-review.googlesource.com/776242
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49595}
parent 653a9e2b
......@@ -222,7 +222,8 @@ class InjectedScript::ProtocolPromiseHandler {
.setException(wrappedValue->clone())
.build();
if (stack)
exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl());
exceptionDetails->setStackTrace(
stack->buildInspectorObjectImpl(m_inspector->debugger()));
if (stack && !stack->isEmpty())
exceptionDetails->setScriptId(toString16(stack->topScriptId()));
callback->sendSuccess(std::move(wrappedValue), std::move(exceptionDetails));
......@@ -570,10 +571,11 @@ Response InjectedScript::createExceptionDetails(
static_cast<int>(message->GetScriptOrigin().ScriptID()->Value())));
v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace();
if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
exceptionDetails->setStackTrace(m_context->inspector()
exceptionDetails->setStackTrace(
m_context->inspector()
->debugger()
->createStackTrace(stackTrace)
->buildInspectorObjectImpl());
->buildInspectorObjectImpl(m_context->inspector()->debugger()));
}
if (!exception.IsEmpty()) {
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped;
......
......@@ -214,10 +214,10 @@
{
"id": "StackTraceId",
"type": "object",
"description": "External stack trace comes from another debugger and can be resolved there. This allows to track cross-debugger calls. See <code>Runtime.StackTrace</code> and <code>Debugger.paused</code> for usages.",
"description": "If <code>debuggerId</code> is set stack trace comes from another debugger and can be resolved there. This allows to track cross-debugger calls. See <code>Runtime.StackTrace</code> and <code>Debugger.paused</code> for usages.",
"properties": [
{ "name": "id", "type": "string" },
{ "name": "debuggerId", "$ref": "UniqueDebuggerId" }
{ "name": "debuggerId", "$ref": "UniqueDebuggerId", "optional": true }
],
"experimental": true
}
......
......@@ -295,8 +295,10 @@ void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
if (m_scriptId)
exceptionDetails->setScriptId(String16::fromInteger(m_scriptId));
if (!m_url.isEmpty()) exceptionDetails->setUrl(m_url);
if (m_stackTrace)
exceptionDetails->setStackTrace(m_stackTrace->buildInspectorObjectImpl());
if (m_stackTrace) {
exceptionDetails->setStackTrace(
m_stackTrace->buildInspectorObjectImpl(inspector->debugger()));
}
if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId);
if (exception) exceptionDetails->setException(std::move(exception));
frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails));
......@@ -326,7 +328,9 @@ void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend,
frontend->consoleAPICalled(
consoleAPITypeValue(m_type), std::move(arguments), m_contextId,
m_timestamp,
m_stackTrace ? m_stackTrace->buildInspectorObjectImpl() : nullptr,
m_stackTrace
? m_stackTrace->buildInspectorObjectImpl(inspector->debugger())
: nullptr,
std::move(consoleContext));
return;
}
......
......@@ -722,8 +722,13 @@ Response V8DebuggerAgentImpl::getStackTrace(
std::unique_ptr<protocol::Runtime::StackTrace>* outStackTrace) {
bool isOk = false;
int64_t id = inStackTraceId->getId().toInteger64(&isOk);
std::pair<int64_t, int64_t> debuggerId =
m_debugger->debuggerIdFor(inStackTraceId->getDebuggerId());
std::pair<int64_t, int64_t> debuggerId;
if (inStackTraceId->hasDebuggerId()) {
debuggerId =
m_debugger->debuggerIdFor(inStackTraceId->getDebuggerId(String16()));
} else {
debuggerId = m_debugger->debuggerIdFor(m_session->contextGroupId());
}
V8StackTraceId v8StackTraceId(id, debuggerId);
if (!isOk || v8StackTraceId.IsInvalid()) {
return Response::Error("Invalid stack trace id");
......@@ -733,8 +738,8 @@ Response V8DebuggerAgentImpl::getStackTrace(
if (!stack) {
return Response::Error("Stack trace with given id is not found");
}
*outStackTrace =
stack->buildInspectorObject(m_debugger->maxAsyncCallChainDepth());
*outStackTrace = stack->buildInspectorObject(
m_debugger, m_debugger->maxAsyncCallChainDepth());
return Response::OK();
}
......@@ -1011,7 +1016,7 @@ Response V8DebuggerAgentImpl::pauseOnAsyncCall(
return Response::Error("Invalid stack trace id");
}
m_debugger->pauseOnAsyncCall(m_session->contextGroupId(), stackTraceId,
inParentStackTraceId->getDebuggerId());
inParentStackTraceId->getDebuggerId(String16()));
return Response::OK();
}
......@@ -1312,7 +1317,7 @@ V8DebuggerAgentImpl::currentAsyncStackTrace() {
m_debugger->currentAsyncParent();
if (!asyncParent) return nullptr;
return asyncParent->buildInspectorObject(
m_debugger->maxAsyncCallChainDepth() - 1);
m_debugger, m_debugger->maxAsyncCallChainDepth() - 1);
}
std::unique_ptr<protocol::Runtime::StackTraceId>
......@@ -1374,7 +1379,8 @@ void V8DebuggerAgentImpl::didParseSource(
std::unique_ptr<V8StackTraceImpl> stack =
V8StackTraceImpl::capture(m_inspector->debugger(), contextGroupId, 1);
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
stack && !stack->isEmpty() ? stack->buildInspectorObjectImpl() : nullptr;
stack && !stack->isEmpty() ? stack->buildInspectorObjectImpl(m_debugger)
: nullptr;
if (success) {
m_frontend.scriptParsed(
scriptId, scriptURL, scriptRef->startLine(), scriptRef->startColumn(),
......
......@@ -761,8 +761,8 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace(
V8StackTraceImpl::maxCallStackSizeToCapture);
if (!asyncStack) return V8StackTraceId();
uintptr_t id = ++m_lastStackTraceId;
m_storedStackTraces[id] = asyncStack;
uintptr_t id = AsyncStackTrace::store(this, asyncStack);
m_allAsyncStacks.push_back(std::move(asyncStack));
++m_asyncStacksCount;
collectOldAsyncStacksIfNeeded();
......@@ -772,6 +772,13 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace(
return V8StackTraceId(id, debuggerIdFor(contextGroupId));
}
uintptr_t V8Debugger::storeStackTrace(
std::shared_ptr<AsyncStackTrace> asyncStack) {
uintptr_t id = ++m_lastStackTraceId;
m_storedStackTraces[id] = asyncStack;
return id;
}
void V8Debugger::externalAsyncTaskStarted(const V8StackTraceId& parent) {
if (!m_maxAsyncCallStackDepth || parent.IsInvalid()) return;
m_currentExternalParent.push_back(parent);
......
......@@ -105,6 +105,8 @@ class V8Debugger : public v8::debug::DebugDelegate {
void externalAsyncTaskStarted(const V8StackTraceId& parent);
void externalAsyncTaskFinished(const V8StackTraceId& parent);
uintptr_t storeStackTrace(std::shared_ptr<AsyncStackTrace> stack);
void muteScriptParsedEvents();
void unmuteScriptParsedEvents();
......
......@@ -57,13 +57,14 @@ void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
}
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
V8Debugger* debugger,
const std::vector<std::shared_ptr<StackFrame>>& frames,
const String16& description,
const std::shared_ptr<AsyncStackTrace>& asyncParent,
const V8StackTraceId& externalParent, int maxAsyncDepth) {
if (asyncParent && frames.empty() &&
description == asyncParent->description()) {
return asyncParent->buildInspectorObject(maxAsyncDepth);
return asyncParent->buildInspectorObject(debugger, maxAsyncDepth);
}
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
......@@ -76,10 +77,19 @@ std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
.setCallFrames(std::move(inspectorFrames))
.build();
if (!description.isEmpty()) stackTrace->setDescription(description);
if (asyncParent && maxAsyncDepth > 0) {
stackTrace->setParent(asyncParent->buildInspectorObject(maxAsyncDepth - 1));
if (asyncParent) {
if (maxAsyncDepth > 0) {
stackTrace->setParent(
asyncParent->buildInspectorObject(debugger, maxAsyncDepth - 1));
} else if (debugger) {
stackTrace->setParentId(
protocol::Runtime::StackTraceId::create()
.setId(stackTraceIdToString(
AsyncStackTrace::store(debugger, asyncParent)))
.build());
}
}
if (!externalParent.IsInvalid() && maxAsyncDepth > 0) {
if (!externalParent.IsInvalid()) {
stackTrace->setParentId(
protocol::Runtime::StackTraceId::create()
.setId(stackTraceIdToString(externalParent.id))
......@@ -227,14 +237,15 @@ StringView V8StackTraceImpl::topFunctionName() const {
}
std::unique_ptr<protocol::Runtime::StackTrace>
V8StackTraceImpl::buildInspectorObjectImpl() const {
return buildInspectorObjectCommon(m_frames, String16(), m_asyncParent.lock(),
m_externalParent, m_maxAsyncDepth);
V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger) const {
return buildInspectorObjectCommon(debugger, m_frames, String16(),
m_asyncParent.lock(), m_externalParent,
m_maxAsyncDepth);
}
std::unique_ptr<protocol::Runtime::API::StackTrace>
V8StackTraceImpl::buildInspectorObject() const {
return buildInspectorObjectImpl();
return buildInspectorObjectImpl(nullptr);
}
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
......@@ -346,6 +357,7 @@ AsyncStackTrace::AsyncStackTrace(
std::shared_ptr<AsyncStackTrace> asyncParent,
const V8StackTraceId& externalParent)
: m_contextGroupId(contextGroupId),
m_id(0),
m_description(description),
m_frames(std::move(frames)),
m_asyncParent(asyncParent),
......@@ -354,14 +366,22 @@ AsyncStackTrace::AsyncStackTrace(
}
std::unique_ptr<protocol::Runtime::StackTrace>
AsyncStackTrace::buildInspectorObject(int maxAsyncDepth) const {
return buildInspectorObjectCommon(m_frames, m_description,
AsyncStackTrace::buildInspectorObject(V8Debugger* debugger,
int maxAsyncDepth) const {
return buildInspectorObjectCommon(debugger, m_frames, m_description,
m_asyncParent.lock(), m_externalParent,
maxAsyncDepth);
}
int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
uintptr_t AsyncStackTrace::store(V8Debugger* debugger,
std::shared_ptr<AsyncStackTrace> stack) {
if (stack->m_id) return stack->m_id;
stack->m_id = debugger->storeStackTrace(stack);
return stack->m_id;
}
const String16& AsyncStackTrace::description() const { return m_description; }
std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
......
......@@ -58,8 +58,8 @@ class V8StackTraceImpl : public V8StackTrace {
int maxStackSize);
~V8StackTraceImpl() override;
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
const;
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl(
V8Debugger* debugger) const;
// V8StackTrace implementation.
// This method drops the async stack trace.
......@@ -110,9 +110,11 @@ class AsyncStackTrace {
int contextGroupId,
const String16& description,
int maxStackSize);
static uintptr_t store(V8Debugger* debugger,
std::shared_ptr<AsyncStackTrace> stack);
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject(
int maxAsyncDepth) const;
V8Debugger* debugger, int maxAsyncDepth) const;
int contextGroupId() const;
const String16& description() const;
......@@ -130,6 +132,7 @@ class AsyncStackTrace {
const V8StackTraceId& externalParent);
int m_contextGroupId;
uintptr_t m_id;
String16 m_description;
std::vector<std::shared_ptr<StackFrame>> m_frames;
......
Tests super long async stacks.
callWithAsyncStack (expr.js:0:26)
callWithAsyncStack (utils.js:3:4)
call1 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call2 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call3 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call4 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call5 (wrapper.js:0:20)
(fetch parent..)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call6 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call7 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call8 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call9 (wrapper.js:0:20)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call10 (wrapper.js:0:21)
(fetch parent..)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call11 (wrapper.js:0:21)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
call12 (wrapper.js:0:21)
--Promise.then--
callWithAsyncStack (utils.js:7:20)
(anonymous) (expr.js:0:0)
// 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('Tests super long async stacks.');
contextGroup.addScript(`
function callWithAsyncStack(f, depth) {
if (depth === 0) {
f();
return;
}
wrapper = eval('(function call' + depth + '() { callWithAsyncStack(f, depth - 1) }) //# sourceURL=wrapper.js');
Promise.resolve().then(wrapper);
}
//# sourceURL=utils.js`);
(async function test() {
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 4});
Protocol.Runtime.evaluate({
expression: 'callWithAsyncStack(() => {debugger}, 12)//# sourceURL=expr.js'
});
let {params} = await Protocol.Debugger.oncePaused();
let {callFrames, asyncStackTrace, externalAsyncStackTrace} = params;
while (true) {
session.logCallFrames(callFrames);
if (externalAsyncStackTrace) {
InspectorTest.log('(fetch parent..)');
asyncStackTrace = (await Protocol.Debugger.getStackTrace({
stackTraceId: externalAsyncStackTrace
})).result.stackTrace;
}
if (asyncStackTrace) {
InspectorTest.log('--' + asyncStackTrace.description + '--');
callFrames = asyncStackTrace.callFrames;
externalAsyncStackTrace = asyncStackTrace.parentId;
asyncStackTrace = asyncStackTrace.parent;
} else {
break;
}
}
InspectorTest.completeTest();
})()
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