Commit aee49387 authored by kozyatinskiy's avatar kozyatinskiy Committed by Commit bot

[inspector] store creation stack in current V8StackTraceImpl

We currently store it in parent stack trace but stacks with the same parent can have different creations stacks.

BUG=v8:6189
R=dgozman@chromium.org

Review-Url: https://codereview.chromium.org/2807273002
Cr-Commit-Position: refs/heads/master@{#44624}
parent b2dc9230
......@@ -1030,9 +1030,7 @@ Response V8DebuggerAgentImpl::currentCallFrames(
std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
if (!isPaused()) return nullptr;
V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain();
return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
: nullptr;
return V8StackTraceImpl::buildInspectorObjectForTail(m_debugger);
}
bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
......
......@@ -686,6 +686,11 @@ V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
return m_currentStacks.back().get();
}
V8StackTraceImpl* V8Debugger::currentAsyncTaskCreationStack() {
if (!m_currentCreationStacks.size()) return nullptr;
return m_currentCreationStacks.back().get();
}
void V8Debugger::compileDebuggerScript() {
if (!m_debuggerScript.IsEmpty()) {
UNREACHABLE();
......@@ -939,7 +944,9 @@ void V8Debugger::asyncTaskStartedForStack(void* task) {
stack = stackIt->second->cloneImpl();
auto itCreation = m_asyncTaskCreationStacks.find(task);
if (stack && itCreation != m_asyncTaskCreationStacks.end()) {
stack->setCreation(itCreation->second->cloneImpl());
m_currentCreationStacks.push_back(itCreation->second->cloneImpl());
} else {
m_currentCreationStacks.push_back(nullptr);
}
m_currentStacks.push_back(std::move(stack));
}
......@@ -948,11 +955,12 @@ void V8Debugger::asyncTaskFinishedForStack(void* task) {
if (!m_maxAsyncCallStackDepth) return;
// We could start instrumenting half way and the stack is empty.
if (!m_currentStacks.size()) return;
DCHECK(m_currentTasks.back() == task);
m_currentTasks.pop_back();
DCHECK(m_currentStacks.size() == m_currentCreationStacks.size());
m_currentStacks.pop_back();
m_currentCreationStacks.pop_back();
if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
asyncTaskCanceledForStack(task);
}
......@@ -990,6 +998,7 @@ void V8Debugger::allAsyncTasksCanceled() {
m_asyncTaskStacks.clear();
m_recurringTasks.clear();
m_currentStacks.clear();
m_currentCreationStacks.clear();
m_currentTasks.clear();
m_parentTask.clear();
m_asyncTaskCreationStacks.clear();
......
......@@ -77,6 +77,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
V8StackTraceImpl* currentAsyncCallChain();
V8StackTraceImpl* currentAsyncTaskCreationStack();
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>);
std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack);
......@@ -189,6 +190,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
int m_maxAsyncCallStackDepth;
std::vector<void*> m_currentTasks;
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentCreationStacks;
protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
protocol::HashMap<void*, void*> m_parentTask;
protocol::HashMap<void*, void*> m_firstNextTask;
......
This diff is collapsed.
......@@ -15,12 +15,10 @@
namespace v8_inspector {
class TracedValue;
class V8Debugger;
// Note: async stack trace may have empty top stack with non-empty tail to
// indicate
// that current native-only state had some async story.
// indicate that current native-only state had some async story.
// On the other hand, any non-top async stack is guaranteed to be non-empty.
class V8StackTraceImpl final : public V8StackTrace {
public:
......@@ -28,22 +26,19 @@ class V8StackTraceImpl final : public V8StackTrace {
class Frame {
public:
Frame();
Frame(const String16& functionName, const String16& scriptId,
const String16& scriptName, int lineNumber, int column = 0);
~Frame();
~Frame() = default;
const String16& functionName() const { return m_functionName; }
const String16& scriptId() const { return m_scriptId; }
const String16& sourceURL() const { return m_scriptName; }
int lineNumber() const { return m_lineNumber; }
int columnNumber() const { return m_columnNumber; }
Frame clone() const;
private:
friend class V8StackTraceImpl;
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
void toTracedValue(TracedValue*) const;
String16 m_functionName;
String16 m_scriptId;
......@@ -64,8 +59,8 @@ class V8StackTraceImpl final : public V8StackTrace {
// This method drops the async chain. Use cloneImpl() instead.
std::unique_ptr<V8StackTrace> clone() override;
std::unique_ptr<V8StackTraceImpl> cloneImpl();
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail(
V8Debugger*) const;
static std::unique_ptr<protocol::Runtime::StackTrace>
buildInspectorObjectForTail(V8Debugger*);
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
const;
~V8StackTraceImpl() override;
......@@ -81,12 +76,14 @@ class V8StackTraceImpl final : public V8StackTrace {
const override;
std::unique_ptr<StringBuffer> toString() const override;
void setCreation(std::unique_ptr<V8StackTraceImpl> creation);
private:
V8StackTraceImpl(int contextGroupId, const String16& description,
std::vector<Frame>& frames,
std::unique_ptr<V8StackTraceImpl> parent);
std::unique_ptr<V8StackTraceImpl> parent,
std::unique_ptr<V8StackTraceImpl> creation);
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl(
V8StackTraceImpl* creation) const;
int m_contextGroupId;
String16 m_description;
......
Checks that we trim async call chains correctly.
set async chain depth to 8
Running test: testDebuggerPaused
Run expression 'debugger;' with async chain len: 4
actual async chain len: 4
Run expression 'debugger;' with async chain len: 8
actual async chain len: 8
Run expression 'debugger;' with async chain len: 9
actual async chain len: 8
Run expression 'debugger;' with async chain len: 32
actual async chain len: 8
Running test: testConsoleTrace
Run expression 'console.trace(42);' with async chain len: 4
actual async chain len: 4
Run expression 'console.trace(42);' with async chain len: 8
actual async chain len: 8
Run expression 'console.trace(42);' with async chain len: 9
actual async chain len: 8
Run expression 'console.trace(42);' with async chain len: 32
actual async chain len: 8
Running test: testDebuggerPausedSetTimeout
Run expression 'debugger;' with async chain len: 4
actual async chain len: 4
Run expression 'debugger;' with async chain len: 8
actual async chain len: 8
Run expression 'debugger;' with async chain len: 9
actual async chain len: 8
Run expression 'debugger;' with async chain len: 32
actual async chain len: 8
Running test: testConsoleTraceSetTimeout
Run expression 'console.trace(42);' with async chain len: 4
actual async chain len: 4
Run expression 'console.trace(42);' with async chain len: 8
actual async chain len: 8
Run expression 'console.trace(42);' with async chain len: 9
actual async chain len: 8
Run expression 'console.trace(42);' with async chain len: 32
actual async chain len: 8
Running test: testConsoleTraceWithEmptySync
{
callFrames : [
[0] : {
columnNumber : 66
functionName : Promise.then
lineNumber : 0
scriptId : <scriptId>
url :
}
]
parent : {
callFrames : [
]
description : Promise.resolve
parent : {
callFrames : [
[0] : {
columnNumber : 23
functionName : resolve
lineNumber : 0
scriptId : <scriptId>
url :
}
[1] : {
columnNumber : 0
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
]
description : setTimeout
}
promiseCreationFrame : {
columnNumber : 47
functionName :
lineNumber : 0
scriptId : <scriptId>
url :
}
}
}
Running test: testDebuggerPausedThenableJob
Run expression 'debugger;' with async chain len: 4
actual async chain len: 4
Run expression 'debugger;' with async chain len: 8
actual async chain len: 8
Run expression 'debugger;' with async chain len: 9
actual async chain len: 8
Run expression 'debugger;' with async chain len: 32
actual async chain len: 8
Running test: testConsoleTraceThenableJob
Run expression 'console.trace(42);' with async chain len: 4
actual async chain len: 4
Run expression 'console.trace(42);' with async chain len: 8
actual async chain len: 8
Run expression 'console.trace(42);' with async chain len: 9
actual async chain len: 8
Run expression 'console.trace(42);' with async chain len: 32
actual async chain len: 8
Running test: twoConsoleAssert
actual async chain len: 1
actual async chain len: 2
// 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.
InspectorTest.log('Checks that we trim async call chains correctly.');
Protocol.Debugger.enable();
InspectorTest.log('set async chain depth to 8');
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 8});
InspectorTest.runAsyncTestSuite([
async function testDebuggerPaused() {
runWithAsyncChain(4, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithAsyncChain(8, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithAsyncChain(9, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithAsyncChain(32, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
},
async function testConsoleTrace() {
Protocol.Runtime.enable();
runWithAsyncChain(4, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithAsyncChain(8, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithAsyncChain(9, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithAsyncChain(32, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function testDebuggerPausedSetTimeout() {
runWithAsyncChainSetTimeout(4, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithAsyncChainSetTimeout(8, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithAsyncChainSetTimeout(9, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithAsyncChainSetTimeout(32, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
},
async function testConsoleTraceSetTimeout() {
runWithAsyncChainSetTimeout(4, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithAsyncChainSetTimeout(8, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithAsyncChainSetTimeout(9, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithAsyncChainSetTimeout(32, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function testConsoleTraceWithEmptySync() {
Protocol.Runtime.evaluate({
expression: 'new Promise(resolve => setTimeout(resolve, 0)).then(() => console.trace(42))'
});
InspectorTest.logMessage((await Protocol.Runtime.onceConsoleAPICalled()).params.stackTrace);
},
async function testDebuggerPausedThenableJob() {
runWithThenableJob(4, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithThenableJob(8, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithThenableJob(9, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
runWithThenableJob(32, 'debugger;');
dumpAsyncChainLength(await Protocol.Debugger.oncePaused());
await Protocol.Debugger.resume();
},
async function testConsoleTraceThenableJob() {
runWithThenableJob(4, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithThenableJob(8, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithThenableJob(9, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
runWithThenableJob(32, 'console.trace(42);');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function twoConsoleAssert() {
Protocol.Runtime.evaluate({
expression: 'setTimeout(' +
'setTimeout.bind(null, ' +
'setTimeout.bind(null, () => { console.assert(); setTimeout(console.assert, 0) }, 0), 0), 0)'
});
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
}
]);
function runWithAsyncChain(len, source) {
InspectorTest.log(`Run expression '${source}' with async chain len: ${len}`);
let then = '.then(() => 1)';
let pause = `.then(() => { ${source} })`;
Protocol.Runtime.evaluate({
expression: `Promise.resolve()${then.repeat(len - 1)}${pause}`
});
}
function runWithAsyncChainSetTimeout(len, source) {
InspectorTest.log(`Run expression '${source}' with async chain len: ${len}`);
let setTimeout = 'setTimeout(() => {';
let suffix = '}, 0)';
Protocol.Runtime.evaluate({
expression: `${setTimeout.repeat(len)}${source}${suffix.repeat(len)}`
});
}
function runWithThenableJob(len, source) {
InspectorTest.log(`Run expression '${source}' with async chain len: ${len}`);
let then = '.then(Promise.resolve.bind(Promise, 0))';
let pause = `.then(() => { ${source} })`;
Protocol.Runtime.evaluate({
expression: `Promise.resolve()${then.repeat(len - 1)}${pause}`
});
}
function dumpAsyncChainLength(message) {
let stackTrace = message.params.asyncStackTrace || message.params.stackTrace.parent;
let asyncChainCount = 0;
while (stackTrace) {
++asyncChainCount;
stackTrace = stackTrace.parent;
}
InspectorTest.log(`actual async chain len: ${asyncChainCount}`);
}
......@@ -125,10 +125,6 @@ Running test: testFunctionReturnRejectedPromise
}
exceptionId : <exceptionId>
lineNumber : 0
stackTrace : {
callFrames : [
]
}
text : Uncaught (in promise)
}
result : {
......
......@@ -232,10 +232,6 @@ console.log(239)
executionContextId : <executionContextId>
lineNumber : 0
scriptId : <scriptId>
stackTrace : {
callFrames : [
]
}
text : Uncaught SyntaxError: Unexpected token }
url : module4
}
......
......@@ -25,10 +25,6 @@ Running test: testRejectedPromise
}
exceptionId : <exceptionId>
lineNumber : 0
stackTrace : {
callFrames : [
]
}
text : Uncaught (in promise)
}
result : {
......
......@@ -77,10 +77,6 @@ Check that exceptionThrown is supported by test runner.
executionContextId : <executionContextId>
lineNumber : 0
scriptId : <scriptId>
stackTrace : {
callFrames : [
]
}
text : Uncaught SyntaxError: Unexpected token }
}
timestamp : <timestamp>
......
......@@ -175,10 +175,6 @@ Running test: testAwaitRejectedPromise
}
exceptionId : <exceptionId>
lineNumber : 0
stackTrace : {
callFrames : [
]
}
text : Uncaught (in promise)
}
result : {
......
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