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( ...@@ -1030,9 +1030,7 @@ Response V8DebuggerAgentImpl::currentCallFrames(
std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() { std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
if (!isPaused()) return nullptr; if (!isPaused()) return nullptr;
V8StackTraceImpl* stackTrace = m_debugger->currentAsyncCallChain(); return V8StackTraceImpl::buildInspectorObjectForTail(m_debugger);
return stackTrace ? stackTrace->buildInspectorObjectForTail(m_debugger)
: nullptr;
} }
bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); } bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
......
...@@ -686,6 +686,11 @@ V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { ...@@ -686,6 +686,11 @@ V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
return m_currentStacks.back().get(); return m_currentStacks.back().get();
} }
V8StackTraceImpl* V8Debugger::currentAsyncTaskCreationStack() {
if (!m_currentCreationStacks.size()) return nullptr;
return m_currentCreationStacks.back().get();
}
void V8Debugger::compileDebuggerScript() { void V8Debugger::compileDebuggerScript() {
if (!m_debuggerScript.IsEmpty()) { if (!m_debuggerScript.IsEmpty()) {
UNREACHABLE(); UNREACHABLE();
...@@ -939,7 +944,9 @@ void V8Debugger::asyncTaskStartedForStack(void* task) { ...@@ -939,7 +944,9 @@ void V8Debugger::asyncTaskStartedForStack(void* task) {
stack = stackIt->second->cloneImpl(); stack = stackIt->second->cloneImpl();
auto itCreation = m_asyncTaskCreationStacks.find(task); auto itCreation = m_asyncTaskCreationStacks.find(task);
if (stack && itCreation != m_asyncTaskCreationStacks.end()) { 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)); m_currentStacks.push_back(std::move(stack));
} }
...@@ -948,11 +955,12 @@ void V8Debugger::asyncTaskFinishedForStack(void* task) { ...@@ -948,11 +955,12 @@ void V8Debugger::asyncTaskFinishedForStack(void* task) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
// We could start instrumenting half way and the stack is empty. // We could start instrumenting half way and the stack is empty.
if (!m_currentStacks.size()) return; if (!m_currentStacks.size()) return;
DCHECK(m_currentTasks.back() == task); DCHECK(m_currentTasks.back() == task);
m_currentTasks.pop_back(); m_currentTasks.pop_back();
DCHECK(m_currentStacks.size() == m_currentCreationStacks.size());
m_currentStacks.pop_back(); m_currentStacks.pop_back();
m_currentCreationStacks.pop_back();
if (m_recurringTasks.find(task) == m_recurringTasks.end()) { if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
asyncTaskCanceledForStack(task); asyncTaskCanceledForStack(task);
} }
...@@ -990,6 +998,7 @@ void V8Debugger::allAsyncTasksCanceled() { ...@@ -990,6 +998,7 @@ void V8Debugger::allAsyncTasksCanceled() {
m_asyncTaskStacks.clear(); m_asyncTaskStacks.clear();
m_recurringTasks.clear(); m_recurringTasks.clear();
m_currentStacks.clear(); m_currentStacks.clear();
m_currentCreationStacks.clear();
m_currentTasks.clear(); m_currentTasks.clear();
m_parentTask.clear(); m_parentTask.clear();
m_asyncTaskCreationStacks.clear(); m_asyncTaskCreationStacks.clear();
......
...@@ -77,6 +77,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -77,6 +77,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
V8StackTraceImpl* currentAsyncCallChain(); V8StackTraceImpl* currentAsyncCallChain();
V8StackTraceImpl* currentAsyncTaskCreationStack();
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int); void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>); std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>);
std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack); std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack);
...@@ -189,6 +190,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -189,6 +190,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
int m_maxAsyncCallStackDepth; int m_maxAsyncCallStackDepth;
std::vector<void*> m_currentTasks; std::vector<void*> m_currentTasks;
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks; std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentCreationStacks;
protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap; protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
protocol::HashMap<void*, void*> m_parentTask; protocol::HashMap<void*, void*> m_parentTask;
protocol::HashMap<void*, void*> m_firstNextTask; protocol::HashMap<void*, void*> m_firstNextTask;
......
This diff is collapsed.
...@@ -15,12 +15,10 @@ ...@@ -15,12 +15,10 @@
namespace v8_inspector { namespace v8_inspector {
class TracedValue;
class V8Debugger; class V8Debugger;
// Note: async stack trace may have empty top stack with non-empty tail to // Note: async stack trace may have empty top stack with non-empty tail to
// indicate // indicate that current native-only state had some async story.
// that current native-only state had some async story.
// On the other hand, any non-top async stack is guaranteed to be non-empty. // On the other hand, any non-top async stack is guaranteed to be non-empty.
class V8StackTraceImpl final : public V8StackTrace { class V8StackTraceImpl final : public V8StackTrace {
public: public:
...@@ -28,22 +26,19 @@ class V8StackTraceImpl final : public V8StackTrace { ...@@ -28,22 +26,19 @@ class V8StackTraceImpl final : public V8StackTrace {
class Frame { class Frame {
public: public:
Frame();
Frame(const String16& functionName, const String16& scriptId, Frame(const String16& functionName, const String16& scriptId,
const String16& scriptName, int lineNumber, int column = 0); const String16& scriptName, int lineNumber, int column = 0);
~Frame(); ~Frame() = default;
const String16& functionName() const { return m_functionName; } const String16& functionName() const { return m_functionName; }
const String16& scriptId() const { return m_scriptId; } const String16& scriptId() const { return m_scriptId; }
const String16& sourceURL() const { return m_scriptName; } const String16& sourceURL() const { return m_scriptName; }
int lineNumber() const { return m_lineNumber; } int lineNumber() const { return m_lineNumber; }
int columnNumber() const { return m_columnNumber; } int columnNumber() const { return m_columnNumber; }
Frame clone() const;
private: private:
friend class V8StackTraceImpl; friend class V8StackTraceImpl;
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const; std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
void toTracedValue(TracedValue*) const;
String16 m_functionName; String16 m_functionName;
String16 m_scriptId; String16 m_scriptId;
...@@ -64,8 +59,8 @@ class V8StackTraceImpl final : public V8StackTrace { ...@@ -64,8 +59,8 @@ class V8StackTraceImpl final : public V8StackTrace {
// This method drops the async chain. Use cloneImpl() instead. // This method drops the async chain. Use cloneImpl() instead.
std::unique_ptr<V8StackTrace> clone() override; std::unique_ptr<V8StackTrace> clone() override;
std::unique_ptr<V8StackTraceImpl> cloneImpl(); std::unique_ptr<V8StackTraceImpl> cloneImpl();
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail( static std::unique_ptr<protocol::Runtime::StackTrace>
V8Debugger*) const; buildInspectorObjectForTail(V8Debugger*);
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl() std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
const; const;
~V8StackTraceImpl() override; ~V8StackTraceImpl() override;
...@@ -81,12 +76,14 @@ class V8StackTraceImpl final : public V8StackTrace { ...@@ -81,12 +76,14 @@ class V8StackTraceImpl final : public V8StackTrace {
const override; const override;
std::unique_ptr<StringBuffer> toString() const override; std::unique_ptr<StringBuffer> toString() const override;
void setCreation(std::unique_ptr<V8StackTraceImpl> creation);
private: private:
V8StackTraceImpl(int contextGroupId, const String16& description, V8StackTraceImpl(int contextGroupId, const String16& description,
std::vector<Frame>& frames, 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; int m_contextGroupId;
String16 m_description; 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 ...@@ -125,10 +125,6 @@ Running test: testFunctionReturnRejectedPromise
} }
exceptionId : <exceptionId> exceptionId : <exceptionId>
lineNumber : 0 lineNumber : 0
stackTrace : {
callFrames : [
]
}
text : Uncaught (in promise) text : Uncaught (in promise)
} }
result : { result : {
......
...@@ -232,10 +232,6 @@ console.log(239) ...@@ -232,10 +232,6 @@ console.log(239)
executionContextId : <executionContextId> executionContextId : <executionContextId>
lineNumber : 0 lineNumber : 0
scriptId : <scriptId> scriptId : <scriptId>
stackTrace : {
callFrames : [
]
}
text : Uncaught SyntaxError: Unexpected token } text : Uncaught SyntaxError: Unexpected token }
url : module4 url : module4
} }
......
...@@ -25,10 +25,6 @@ Running test: testRejectedPromise ...@@ -25,10 +25,6 @@ Running test: testRejectedPromise
} }
exceptionId : <exceptionId> exceptionId : <exceptionId>
lineNumber : 0 lineNumber : 0
stackTrace : {
callFrames : [
]
}
text : Uncaught (in promise) text : Uncaught (in promise)
} }
result : { result : {
......
...@@ -77,10 +77,6 @@ Check that exceptionThrown is supported by test runner. ...@@ -77,10 +77,6 @@ Check that exceptionThrown is supported by test runner.
executionContextId : <executionContextId> executionContextId : <executionContextId>
lineNumber : 0 lineNumber : 0
scriptId : <scriptId> scriptId : <scriptId>
stackTrace : {
callFrames : [
]
}
text : Uncaught SyntaxError: Unexpected token } text : Uncaught SyntaxError: Unexpected token }
} }
timestamp : <timestamp> timestamp : <timestamp>
......
...@@ -175,10 +175,6 @@ Running test: testAwaitRejectedPromise ...@@ -175,10 +175,6 @@ Running test: testAwaitRejectedPromise
} }
exceptionId : <exceptionId> exceptionId : <exceptionId>
lineNumber : 0 lineNumber : 0
stackTrace : {
callFrames : [
]
}
text : Uncaught (in promise) text : Uncaught (in promise)
} }
result : { 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