Commit 6fac39b3 authored by machenbach's avatar machenbach Committed by Commit bot

Revert of [inspector] avoid cloning of async call chains (patchset #7...

Revert of [inspector] avoid cloning of async call chains (patchset #7 id:120001 of https://codereview.chromium.org/2816043006/ )

Reason for revert:
Speculative revert. Seems to block the roll:
https://codereview.chromium.org/2822983004/

Might require changing a browser test first?

Original issue's description:
> [inspector] avoid cloning of async call chains
>
> - separated V8StackTraceImpl and AsyncStackTrace,
> - V8Debugger owns all AsyncStackTrace and cleanup half of them when limit is reached (first created - first cleaned),
> - V8StackTraceImpl, AsyncStackTrace and async-task-related tables in V8Debugger have weak reference to other async stack traces.
> - async tasks are cleared with related async stacks.
>
> BUG=v8:6189
> R=dgozman@chromium.org
>
> Review-Url: https://codereview.chromium.org/2816043006
> Cr-Commit-Position: refs/heads/master@{#44670}
> Committed: https://chromium.googlesource.com/v8/v8/+/1bca73bc832c645138bd3e0306fcaa8bb44dad04

TBR=dgozman@chromium.org,kozyatinskiy@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:6189

Review-Url: https://codereview.chromium.org/2825713002
Cr-Commit-Position: refs/heads/master@{#44678}
parent 4f3d859f
...@@ -263,7 +263,7 @@ void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { ...@@ -263,7 +263,7 @@ void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
String16 identifier; String16 identifier;
if (title.isEmpty()) { if (title.isEmpty()) {
std::unique_ptr<V8StackTraceImpl> stackTrace = std::unique_ptr<V8StackTraceImpl> stackTrace =
V8StackTraceImpl::capture(m_inspector->debugger(), helper.groupId(), 1); V8StackTraceImpl::capture(m_inspector->debugger(), 0, 1);
if (stackTrace && !stackTrace->isEmpty()) { if (stackTrace && !stackTrace->isEmpty()) {
identifier = toString16(stackTrace->topSourceURL()) + ":" + identifier = toString16(stackTrace->topSourceURL()) + ":" +
String16::fromInteger(stackTrace->topLineNumber()); String16::fromInteger(stackTrace->topLineNumber());
......
...@@ -34,6 +34,7 @@ using protocol::Debugger::BreakpointId; ...@@ -34,6 +34,7 @@ using protocol::Debugger::BreakpointId;
using protocol::Debugger::CallFrame; using protocol::Debugger::CallFrame;
using protocol::Runtime::ExceptionDetails; using protocol::Runtime::ExceptionDetails;
using protocol::Runtime::ScriptId; using protocol::Runtime::ScriptId;
using protocol::Runtime::StackTrace;
using protocol::Runtime::RemoteObject; using protocol::Runtime::RemoteObject;
namespace DebuggerAgentState { namespace DebuggerAgentState {
...@@ -597,8 +598,7 @@ Response V8DebuggerAgentImpl::searchInContent( ...@@ -597,8 +598,7 @@ Response V8DebuggerAgentImpl::searchInContent(
Response V8DebuggerAgentImpl::setScriptSource( Response V8DebuggerAgentImpl::setScriptSource(
const String16& scriptId, const String16& newContent, Maybe<bool> dryRun, const String16& scriptId, const String16& newContent, Maybe<bool> dryRun,
Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames, Maybe<protocol::Array<protocol::Debugger::CallFrame>>* newCallFrames,
Maybe<bool>* stackChanged, Maybe<bool>* stackChanged, Maybe<StackTrace>* asyncStackTrace,
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace,
Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) { Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) {
if (!enabled()) return Response::Error(kDebuggerNotEnabled); if (!enabled()) return Response::Error(kDebuggerNotEnabled);
...@@ -631,7 +631,7 @@ Response V8DebuggerAgentImpl::setScriptSource( ...@@ -631,7 +631,7 @@ Response V8DebuggerAgentImpl::setScriptSource(
Response V8DebuggerAgentImpl::restartFrame( Response V8DebuggerAgentImpl::restartFrame(
const String16& callFrameId, const String16& callFrameId,
std::unique_ptr<Array<CallFrame>>* newCallFrames, std::unique_ptr<Array<CallFrame>>* newCallFrames,
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) { Maybe<StackTrace>* asyncStackTrace) {
if (!isPaused()) return Response::Error(kDebuggerNotPaused); if (!isPaused()) return Response::Error(kDebuggerNotPaused);
InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(), InjectedScript::CallFrameScope scope(m_inspector, m_session->contextGroupId(),
callFrameId); callFrameId);
...@@ -1028,14 +1028,9 @@ Response V8DebuggerAgentImpl::currentCallFrames( ...@@ -1028,14 +1028,9 @@ Response V8DebuggerAgentImpl::currentCallFrames(
return Response::OK(); return Response::OK();
} }
std::unique_ptr<protocol::Runtime::StackTrace> std::unique_ptr<StackTrace> V8DebuggerAgentImpl::currentAsyncStackTrace() {
V8DebuggerAgentImpl::currentAsyncStackTrace() { if (!isPaused()) return nullptr;
std::shared_ptr<AsyncStackTrace> asyncParent = return V8StackTraceImpl::buildInspectorObjectForTail(m_debugger);
m_debugger->currentAsyncParent();
if (!asyncParent) return nullptr;
return asyncParent->buildInspectorObject(
m_debugger->currentAsyncCreation().get(),
m_debugger->maxAsyncCallChainDepth() - 1);
} }
bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); } bool V8DebuggerAgentImpl::isPaused() const { return m_debugger->isPaused(); }
......
...@@ -23,6 +23,7 @@ class V8DebuggerScript; ...@@ -23,6 +23,7 @@ class V8DebuggerScript;
class V8InspectorImpl; class V8InspectorImpl;
class V8InspectorSessionImpl; class V8InspectorSessionImpl;
class V8Regex; class V8Regex;
class V8StackTraceImpl;
using protocol::Maybe; using protocol::Maybe;
using protocol::Response; using protocol::Response;
......
...@@ -21,7 +21,10 @@ namespace v8_inspector { ...@@ -21,7 +21,10 @@ namespace v8_inspector {
namespace { namespace {
static const int kMaxAsyncTaskStacks = 1024 * 1024; // Based on DevTools frontend measurement, with asyncCallStackDepth = 4,
// average async call stack tail requires ~1 Kb. Let's reserve ~ 128 Mb
// for async stacks.
static const int kMaxAsyncTaskStacks = 128 * 1024;
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
return value ? v8::True(isolate) : v8::False(isolate); return value ? v8::True(isolate) : v8::False(isolate);
...@@ -164,6 +167,7 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) ...@@ -164,6 +167,7 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
m_runningNestedMessageLoop(false), m_runningNestedMessageLoop(false),
m_ignoreScriptParsedEventsCounter(0), m_ignoreScriptParsedEventsCounter(0),
m_maxAsyncCallStacks(kMaxAsyncTaskStacks), m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
m_lastTaskId(0),
m_maxAsyncCallStackDepth(0), m_maxAsyncCallStackDepth(0),
m_pauseOnExceptionsState(v8::debug::NoBreakOnException), m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
m_wasmTranslation(isolate) {} m_wasmTranslation(isolate) {}
...@@ -658,6 +662,10 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, ...@@ -658,6 +662,10 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
case v8::debug::kDebugEnqueuePromiseReject: case v8::debug::kDebugEnqueuePromiseReject:
asyncTaskScheduledForStack("Promise.reject", task, true); asyncTaskScheduledForStack("Promise.reject", task, true);
break; break;
case v8::debug::kDebugPromiseCollected:
asyncTaskCanceledForStack(task);
asyncTaskCanceledForStepping(task);
break;
case v8::debug::kDebugWillHandle: case v8::debug::kDebugWillHandle:
asyncTaskStartedForStack(task); asyncTaskStartedForStack(task);
asyncTaskStartedForStepping(task); asyncTaskStartedForStepping(task);
...@@ -666,20 +674,17 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, ...@@ -666,20 +674,17 @@ void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
asyncTaskFinishedForStack(task); asyncTaskFinishedForStack(task);
asyncTaskFinishedForStepping(task); asyncTaskFinishedForStepping(task);
break; break;
case v8::debug::kDebugPromiseCollected:
asyncTaskCanceledForStack(task);
asyncTaskCanceledForStepping(task);
break;
} }
} }
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() { V8StackTraceImpl* V8Debugger::currentAsyncCallChain() {
return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back(); if (!m_currentStacks.size()) return nullptr;
return m_currentStacks.back().get();
} }
std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() { V8StackTraceImpl* V8Debugger::currentAsyncTaskCreationStack() {
return m_currentAsyncCreation.empty() ? nullptr if (!m_currentCreationStacks.size()) return nullptr;
: m_currentAsyncCreation.back(); return m_currentCreationStacks.back().get();
} }
void V8Debugger::compileDebuggerScript() { void V8Debugger::compileDebuggerScript() {
...@@ -820,8 +825,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( ...@@ -820,8 +825,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
} }
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace( std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
v8::Local<v8::StackTrace> v8StackTrace) { v8::Local<v8::StackTrace> stackTrace) {
return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace, return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace,
V8StackTraceImpl::maxCallStackSizeToCapture); V8StackTraceImpl::maxCallStackSizeToCapture);
} }
...@@ -842,18 +847,31 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { ...@@ -842,18 +847,31 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
if (!maxAsyncCallStackDepth) allAsyncTasksCanceled(); if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
} }
void V8Debugger::registerAsyncTaskIfNeeded(void* task) {
if (m_taskToId.find(task) != m_taskToId.end()) return;
int id = ++m_lastTaskId;
m_taskToId[task] = id;
m_idToTask[id] = task;
if (static_cast<int>(m_idToTask.size()) > m_maxAsyncCallStacks) {
void* taskToRemove = m_idToTask.begin()->second;
asyncTaskCanceledForStack(taskToRemove);
}
}
void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) { void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
if (parentTask) m_parentTask[task] = parentTask; if (parentTask) m_parentTask[task] = parentTask;
v8::HandleScope scope(m_isolate); v8::HandleScope scope(m_isolate);
std::shared_ptr<AsyncStackTrace> asyncCreation = // We don't need to pass context group id here because we get this callback
AsyncStackTrace::capture(this, currentContextGroupId(), String16(), 1); // from V8 for promise events only.
// Passing one as maxStackSize forces no async chain for the new stack. // Passing one as maxStackSize forces no async chain for the new stack and
if (asyncCreation && !asyncCreation->isEmpty()) { // allows us to not grow exponentially.
m_asyncTaskCreationStacks[task] = asyncCreation; std::unique_ptr<V8StackTraceImpl> creationStack =
m_allAsyncStacks.push_back(asyncCreation); V8StackTraceImpl::capture(this, 0, 1, String16());
++m_asyncStacksCount; if (creationStack && !creationStack->isEmpty()) {
collectOldAsyncStacksIfNeeded(); m_asyncTaskCreationStacks[task] = std::move(creationStack);
registerAsyncTaskIfNeeded(task);
} }
} }
...@@ -882,16 +900,13 @@ void V8Debugger::asyncTaskScheduledForStack(const String16& taskName, ...@@ -882,16 +900,13 @@ void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
void* task, bool recurring) { void* task, bool recurring) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
v8::HandleScope scope(m_isolate); v8::HandleScope scope(m_isolate);
std::shared_ptr<AsyncStackTrace> asyncStack = std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture(
AsyncStackTrace::capture(this, currentContextGroupId(), taskName, this, currentContextGroupId(),
V8StackTraceImpl::maxCallStackSizeToCapture); V8StackTraceImpl::maxCallStackSizeToCapture, taskName);
if (asyncStack) { if (chain) {
m_asyncTaskStacks[task] = asyncStack; m_asyncTaskStacks[task] = std::move(chain);
if (recurring) m_recurringTasks.insert(task); if (recurring) m_recurringTasks.insert(task);
registerAsyncTaskIfNeeded(task);
m_allAsyncStacks.push_back(asyncStack);
++m_asyncStacksCount;
collectOldAsyncStacksIfNeeded();
} }
} }
...@@ -901,6 +916,10 @@ void V8Debugger::asyncTaskCanceledForStack(void* task) { ...@@ -901,6 +916,10 @@ void V8Debugger::asyncTaskCanceledForStack(void* task) {
m_recurringTasks.erase(task); m_recurringTasks.erase(task);
m_parentTask.erase(task); m_parentTask.erase(task);
m_asyncTaskCreationStacks.erase(task); m_asyncTaskCreationStacks.erase(task);
auto it = m_taskToId.find(task);
if (it == m_taskToId.end()) return;
m_idToTask.erase(it->second);
m_taskToId.erase(it);
} }
void V8Debugger::asyncTaskStartedForStack(void* task) { void V8Debugger::asyncTaskStartedForStack(void* task) {
...@@ -916,28 +935,28 @@ void V8Debugger::asyncTaskStartedForStack(void* task) { ...@@ -916,28 +935,28 @@ void V8Debugger::asyncTaskStartedForStack(void* task) {
// - asyncTaskCanceled <-- canceled before finished // - asyncTaskCanceled <-- canceled before finished
// <-- async stack requested here --> // <-- async stack requested here -->
// - asyncTaskFinished // - asyncTaskFinished
std::shared_ptr<AsyncStackTrace> asyncParent; std::unique_ptr<V8StackTraceImpl> stack;
if (stackIt != m_asyncTaskStacks.end()) asyncParent = stackIt->second.lock(); if (stackIt != m_asyncTaskStacks.end() && stackIt->second)
stack = stackIt->second->cloneImpl();
auto itCreation = m_asyncTaskCreationStacks.find(task); auto itCreation = m_asyncTaskCreationStacks.find(task);
if (asyncParent && itCreation != m_asyncTaskCreationStacks.end()) { if (stack && itCreation != m_asyncTaskCreationStacks.end()) {
m_currentAsyncCreation.push_back(itCreation->second.lock()); m_currentCreationStacks.push_back(itCreation->second->cloneImpl());
} else { } else {
m_currentAsyncCreation.push_back(nullptr); m_currentCreationStacks.push_back(nullptr);
} }
m_currentAsyncParent.push_back(asyncParent); m_currentStacks.push_back(std::move(stack));
} }
void V8Debugger::asyncTaskFinishedForStack(void* task) { 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_currentTasks.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_currentAsyncParent.size() == m_currentAsyncCreation.size()); DCHECK(m_currentStacks.size() == m_currentCreationStacks.size());
m_currentAsyncParent.pop_back(); m_currentStacks.pop_back();
m_currentAsyncCreation.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);
} }
...@@ -974,14 +993,14 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) { ...@@ -974,14 +993,14 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) {
void V8Debugger::allAsyncTasksCanceled() { void V8Debugger::allAsyncTasksCanceled() {
m_asyncTaskStacks.clear(); m_asyncTaskStacks.clear();
m_recurringTasks.clear(); m_recurringTasks.clear();
m_currentAsyncParent.clear(); m_currentStacks.clear();
m_currentAsyncCreation.clear(); m_currentCreationStacks.clear();
m_currentTasks.clear(); m_currentTasks.clear();
m_parentTask.clear(); m_parentTask.clear();
m_asyncTaskCreationStacks.clear(); m_asyncTaskCreationStacks.clear();
m_idToTask.clear();
m_allAsyncStacks.clear(); m_taskToId.clear();
m_asyncStacksCount = 0; m_lastTaskId = 0;
} }
void V8Debugger::muteScriptParsedEvents() { void V8Debugger::muteScriptParsedEvents() {
...@@ -1001,10 +1020,11 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( ...@@ -1001,10 +1020,11 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
int contextGroupId = currentContextGroupId(); int contextGroupId = currentContextGroupId();
if (!contextGroupId) return nullptr; if (!contextGroupId) return nullptr;
int stackSize = 1; size_t stackSize =
if (fullStack || m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) { fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1;
if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId))
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
}
return V8StackTraceImpl::capture(this, contextGroupId, stackSize); return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
} }
...@@ -1013,30 +1033,4 @@ int V8Debugger::currentContextGroupId() { ...@@ -1013,30 +1033,4 @@ int V8Debugger::currentContextGroupId() {
return m_inspector->contextGroupId(m_isolate->GetCurrentContext()); return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
} }
void V8Debugger::collectOldAsyncStacksIfNeeded() {
if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
int halfOfLimitRoundedUp =
m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
while (m_asyncStacksCount > halfOfLimitRoundedUp) {
m_allAsyncStacks.pop_front();
--m_asyncStacksCount;
}
removeOldAsyncTasks(m_asyncTaskStacks);
removeOldAsyncTasks(m_asyncTaskCreationStacks);
}
void V8Debugger::removeOldAsyncTasks(AsyncTaskToStackTrace& map) {
AsyncTaskToStackTrace cleanCopy;
for (auto it : map) {
if (!it.second.expired()) cleanCopy.insert(it);
}
map.swap(cleanCopy);
}
void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
m_maxAsyncCallStacks = 0;
collectOldAsyncStacksIfNeeded();
m_maxAsyncCallStacks = limit;
}
} // namespace v8_inspector } // namespace v8_inspector
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
#ifndef V8_INSPECTOR_V8DEBUGGER_H_ #ifndef V8_INSPECTOR_V8DEBUGGER_H_
#define V8_INSPECTOR_V8DEBUGGER_H_ #define V8_INSPECTOR_V8DEBUGGER_H_
#include <list>
#include <vector> #include <vector>
#include "src/base/macros.h" #include "src/base/macros.h"
...@@ -21,9 +20,7 @@ ...@@ -21,9 +20,7 @@
namespace v8_inspector { namespace v8_inspector {
class AsyncStackTrace;
struct ScriptBreakpoint; struct ScriptBreakpoint;
class V8Debugger;
class V8DebuggerAgentImpl; class V8DebuggerAgentImpl;
class V8InspectorImpl; class V8InspectorImpl;
class V8StackTraceImpl; class V8StackTraceImpl;
...@@ -38,7 +35,6 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -38,7 +35,6 @@ class V8Debugger : public v8::debug::DebugDelegate {
~V8Debugger(); ~V8Debugger();
bool enabled() const; bool enabled() const;
v8::Isolate* isolate() const { return m_isolate; }
String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber, String16 setBreakpoint(const ScriptBreakpoint&, int* actualLineNumber,
int* actualColumnNumber); int* actualColumnNumber);
...@@ -80,11 +76,9 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -80,11 +76,9 @@ class V8Debugger : public v8::debug::DebugDelegate {
v8::Local<v8::Context> pausedContext() { return m_pausedContext; } v8::Local<v8::Context> pausedContext() { return m_pausedContext; }
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
V8StackTraceImpl* currentAsyncCallChain();
V8StackTraceImpl* currentAsyncTaskCreationStack();
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int); void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
std::shared_ptr<AsyncStackTrace> currentAsyncParent();
std::shared_ptr<AsyncStackTrace> currentAsyncCreation();
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);
...@@ -105,7 +99,7 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -105,7 +99,7 @@ class V8Debugger : public v8::debug::DebugDelegate {
WasmTranslation* wasmTranslation() { return &m_wasmTranslation; } WasmTranslation* wasmTranslation() { return &m_wasmTranslation; }
void setMaxAsyncTaskStacksForTest(int limit); void setMaxAsyncTaskStacksForTest(int limit) { m_maxAsyncCallStacks = limit; }
private: private:
void compileDebuggerScript(); void compileDebuggerScript();
...@@ -150,6 +144,8 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -150,6 +144,8 @@ class V8Debugger : public v8::debug::DebugDelegate {
void asyncTaskFinishedForStepping(void* task); void asyncTaskFinishedForStepping(void* task);
void asyncTaskCanceledForStepping(void* task); void asyncTaskCanceledForStepping(void* task);
void registerAsyncTaskIfNeeded(void* task);
// v8::debug::DebugEventListener implementation. // v8::debug::DebugEventListener implementation.
void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, void PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id,
int parentId, bool createdByUser) override; int parentId, bool createdByUser) override;
...@@ -182,24 +178,18 @@ class V8Debugger : public v8::debug::DebugDelegate { ...@@ -182,24 +178,18 @@ class V8Debugger : public v8::debug::DebugDelegate {
int m_targetContextGroupId = 0; int m_targetContextGroupId = 0;
using AsyncTaskToStackTrace = using AsyncTaskToStackTrace =
protocol::HashMap<void*, std::weak_ptr<AsyncStackTrace>>; protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>;
AsyncTaskToStackTrace m_asyncTaskStacks; AsyncTaskToStackTrace m_asyncTaskStacks;
AsyncTaskToStackTrace m_asyncTaskCreationStacks; AsyncTaskToStackTrace m_asyncTaskCreationStacks;
int m_maxAsyncCallStacks; int m_maxAsyncCallStacks;
std::map<int, void*> m_idToTask;
std::unordered_map<void*, int> m_taskToId;
int m_lastTaskId;
protocol::HashSet<void*> m_recurringTasks; protocol::HashSet<void*> m_recurringTasks;
int m_maxAsyncCallStackDepth; int m_maxAsyncCallStackDepth;
std::vector<void*> m_currentTasks; std::vector<void*> m_currentTasks;
std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent; std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks;
std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncCreation; std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentCreationStacks;
void collectOldAsyncStacksIfNeeded();
void removeOldAsyncTasks(AsyncTaskToStackTrace& map);
int m_asyncStacksCount = 0;
// V8Debugger owns all the async stacks, while most of the other references
// are weak, which allows to collect some stacks when there are too many.
std::list<std::shared_ptr<AsyncStackTrace>> m_allAsyncStacks;
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;
......
...@@ -4,10 +4,12 @@ ...@@ -4,10 +4,12 @@
#include "src/inspector/v8-stack-trace-impl.h" #include "src/inspector/v8-stack-trace-impl.h"
#include <algorithm> #include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-debugger.h" #include "src/inspector/v8-debugger.h"
#include "src/inspector/wasm-translation.h" #include "src/inspector/v8-inspector-impl.h"
#include "include/v8-version.h"
namespace v8_inspector { namespace v8_inspector {
...@@ -15,115 +17,77 @@ namespace { ...@@ -15,115 +17,77 @@ namespace {
static const v8::StackTrace::StackTraceOptions stackTraceOptions = static const v8::StackTrace::StackTraceOptions stackTraceOptions =
static_cast<v8::StackTrace::StackTraceOptions>( static_cast<v8::StackTrace::StackTraceOptions>(
v8::StackTrace::kDetailed | v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset |
v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
v8::StackTrace::kFunctionName |
v8::StackTrace::kExposeFramesAcrossSecurityOrigins); v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
std::vector<V8StackTraceImpl::Frame> toFramesVector( V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame,
V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace, WasmTranslation* wasmTranslation) {
int maxStackSize) { String16 scriptId = String16::fromInteger(frame->GetScriptId());
DCHECK(debugger->isolate()->InContext()); String16 sourceName;
int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize); v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
std::vector<V8StackTraceImpl::Frame> frames; if (!sourceNameValue.IsEmpty())
for (int i = 0; i < frameCount; ++i) { sourceName = toProtocolString(sourceNameValue);
v8::Local<v8::StackFrame> v8Frame = v8StackTrace->GetFrame(i);
frames.emplace_back(v8Frame); String16 functionName;
v8::Local<v8::String> functionNameValue(frame->GetFunctionName());
if (!functionNameValue.IsEmpty())
functionName = toProtocolString(functionNameValue);
int sourceLineNumber = frame->GetLineNumber() - 1;
int sourceColumn = frame->GetColumn() - 1;
// TODO(clemensh): Figure out a way to do this translation only right before // TODO(clemensh): Figure out a way to do this translation only right before
// sending the stack trace over wire. // sending the stack trace over wire.
if (v8Frame->IsWasm()) frames.back().translate(debugger->wasmTranslation()); if (frame->IsWasm()) {
} wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
return frames; &scriptId, &sourceLineNumber, &sourceColumn);
}
void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
std::shared_ptr<AsyncStackTrace>* asyncParent,
std::shared_ptr<AsyncStackTrace>* asyncCreation,
int* maxAsyncDepth) {
*asyncParent = debugger->currentAsyncParent();
*asyncCreation = debugger->currentAsyncCreation();
if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
DCHECK(!*asyncParent || !*asyncCreation ||
(*asyncParent)->contextGroupId() ==
(*asyncCreation)->contextGroupId());
// Do not accidentally append async call chain from another group. This should
// not happen if we have proper instrumentation, but let's double-check to be
// safe.
if (contextGroupId && *asyncParent && (*asyncParent)->contextGroupId() &&
(*asyncParent)->contextGroupId() != contextGroupId) {
asyncParent->reset();
asyncCreation->reset();
if (maxAsyncDepth) *maxAsyncDepth = 0;
return;
}
// Only the top stack in the chain may be empty and doesn't contain creation
// stack, so ensure that second stack is non-empty (it's the top of appended
// chain).
if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() &&
(*asyncParent)->isEmpty()) {
*asyncParent = (*asyncParent)->parent().lock();
} }
return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
sourceLineNumber + 1, sourceColumn + 1);
} }
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon( void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
const std::vector<V8StackTraceImpl::Frame>& frames, std::vector<V8StackTraceImpl::Frame>& frames,
const std::shared_ptr<AsyncStackTrace>& asyncParent, size_t maxStackSize, v8::Isolate* isolate,
const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) { V8Debugger* debugger) {
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> DCHECK(isolate->InContext());
inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create(); int frameCount = stackTrace->GetFrameCount();
for (size_t i = 0; i < frames.size(); i++) { if (frameCount > static_cast<int>(maxStackSize))
inspectorFrames->addItem(frames[i].buildInspectorObject()); frameCount = static_cast<int>(maxStackSize);
} WasmTranslation* wasmTranslation = debugger->wasmTranslation();
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = for (int i = 0; i < frameCount; i++) {
protocol::Runtime::StackTrace::create() v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
.setCallFrames(std::move(inspectorFrames)) frames.push_back(toFrame(stackFrame, wasmTranslation));
.build();
if (asyncParent && maxAsyncDepth > 0) {
stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(),
maxAsyncDepth - 1));
} }
return stackTrace;
} }
} // namespace } // namespace
V8StackTraceImpl::Frame::Frame(v8::Local<v8::StackFrame> v8Frame) V8StackTraceImpl::Frame::Frame(const String16& functionName,
: m_functionName(toProtocolString(v8Frame->GetFunctionName())), const String16& scriptId,
m_scriptId(String16::fromInteger(v8Frame->GetScriptId())), const String16& scriptName, int lineNumber,
m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())), int column)
m_lineNumber(v8Frame->GetLineNumber() - 1), : m_functionName(functionName),
m_columnNumber(v8Frame->GetColumn() - 1) { m_scriptId(scriptId),
DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo); m_scriptName(scriptName),
DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo); m_lineNumber(lineNumber),
m_columnNumber(column) {
DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo);
DCHECK(m_columnNumber != v8::Message::kNoColumnInfo);
} }
void V8StackTraceImpl::Frame::translate(WasmTranslation* wasmTranslation) { // buildInspectorObject() and SourceLocation's toTracedValue() should set the
wasmTranslation->TranslateWasmScriptLocationToProtocolLocation( // same fields.
&m_scriptId, &m_lineNumber, &m_columnNumber); // If either of them is modified, the other should be also modified.
}
const String16& V8StackTraceImpl::Frame::functionName() const {
return m_functionName;
}
const String16& V8StackTraceImpl::Frame::scriptId() const { return m_scriptId; }
const String16& V8StackTraceImpl::Frame::sourceURL() const {
return m_sourceURL;
}
int V8StackTraceImpl::Frame::lineNumber() const { return m_lineNumber; }
int V8StackTraceImpl::Frame::columnNumber() const { return m_columnNumber; }
std::unique_ptr<protocol::Runtime::CallFrame> std::unique_ptr<protocol::Runtime::CallFrame>
V8StackTraceImpl::Frame::buildInspectorObject() const { V8StackTraceImpl::Frame::buildInspectorObject() const {
return protocol::Runtime::CallFrame::create() return protocol::Runtime::CallFrame::create()
.setFunctionName(m_functionName) .setFunctionName(m_functionName)
.setScriptId(m_scriptId) .setScriptId(m_scriptId)
.setUrl(m_sourceURL) .setUrl(m_scriptName)
.setLineNumber(m_lineNumber) .setLineNumber(m_lineNumber - 1)
.setColumnNumber(m_columnNumber) .setColumnNumber(m_columnNumber - 1)
.build(); .build();
} }
...@@ -131,96 +95,196 @@ V8StackTraceImpl::Frame::buildInspectorObject() const { ...@@ -131,96 +95,196 @@ V8StackTraceImpl::Frame::buildInspectorObject() const {
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions( void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
v8::Isolate* isolate, bool capture) { v8::Isolate* isolate, bool capture) {
isolate->SetCaptureStackTraceForUncaughtExceptions( isolate->SetCaptureStackTraceForUncaughtExceptions(
capture, V8StackTraceImpl::maxCallStackSizeToCapture); capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions);
} }
// static // static
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
V8Debugger* debugger, int contextGroupId, V8Debugger* debugger, int contextGroupId,
v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) { v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
const String16& description) {
DCHECK(debugger); DCHECK(debugger);
v8::Isolate* isolate = debugger->inspector()->isolate();
v8::Isolate* isolate = debugger->isolate();
v8::HandleScope scope(isolate); v8::HandleScope scope(isolate);
std::vector<V8StackTraceImpl::Frame> frames; std::vector<V8StackTraceImpl::Frame> frames;
if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) { if (!stackTrace.IsEmpty()) {
frames = toFramesVector(debugger, v8StackTrace, maxStackSize); toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger);
}
int maxAsyncCallChainDepth = 1;
V8StackTraceImpl* asyncCallChain = nullptr;
V8StackTraceImpl* creationStack = nullptr;
if (maxStackSize > 1) {
asyncCallChain = debugger->currentAsyncCallChain();
creationStack = debugger->currentAsyncTaskCreationStack();
maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth();
}
// Do not accidentally append async call chain from another group. This should
// not happen if we have proper instrumentation, but let's double-check to be
// safe.
if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId &&
asyncCallChain->m_contextGroupId != contextGroupId) {
asyncCallChain = nullptr;
creationStack = nullptr;
maxAsyncCallChainDepth = 1;
} }
int maxAsyncDepth = 0; // Only the top stack in the chain may be empty and doesn't contain creation
std::shared_ptr<AsyncStackTrace> asyncParent; // stack , so ensure that second stack is non-empty (it's the top of appended
std::shared_ptr<AsyncStackTrace> asyncCreation; // chain).
calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation, if (asyncCallChain && !creationStack && !asyncCallChain->m_creation &&
&maxAsyncDepth); asyncCallChain->isEmpty()) {
if (frames.empty() && !asyncCreation && !asyncParent) return nullptr; asyncCallChain = asyncCallChain->m_parent.get();
return std::unique_ptr<V8StackTraceImpl>( }
new V8StackTraceImpl(frames, maxAsyncDepth, asyncParent, asyncCreation));
if (frames.empty() && !creationStack && !asyncCallChain) return nullptr;
// When async call chain is empty but doesn't contain useful schedule stack
// and parent async call chain contains creationg stack but doesn't
// synchronous we can merge them together.
// e.g. Promise ThenableJob.
if (asyncCallChain && frames.empty() &&
asyncCallChain->m_description == description && !creationStack) {
return asyncCallChain->cloneImpl();
}
std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(
contextGroupId, description, frames,
asyncCallChain ? asyncCallChain->cloneImpl() : nullptr,
creationStack ? creationStack->cloneImpl() : nullptr));
// Crop to not exceed maxAsyncCallChainDepth.
V8StackTraceImpl* deepest = result.get();
while (deepest && maxAsyncCallChainDepth) {
deepest = deepest->m_parent.get();
maxAsyncCallChainDepth--;
}
if (deepest) deepest->m_parent.reset();
return result;
} }
// static // static
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
V8Debugger* debugger, int contextGroupId, int maxStackSize) { V8Debugger* debugger, int contextGroupId, size_t maxStackSize,
const String16& description) {
DCHECK(debugger); DCHECK(debugger);
v8::Isolate* isolate = debugger->isolate(); v8::Isolate* isolate = debugger->inspector()->isolate();
v8::HandleScope handleScope(isolate); v8::HandleScope handleScope(isolate);
v8::Local<v8::StackTrace> v8StackTrace; v8::Local<v8::StackTrace> stackTrace;
if (isolate->InContext()) { if (isolate->InContext()) {
v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, stackTrace = v8::StackTrace::CurrentStackTrace(
stackTraceOptions); isolate, static_cast<int>(maxStackSize), stackTraceOptions);
} }
return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace, return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace,
maxStackSize); maxStackSize, description);
} }
V8StackTraceImpl::V8StackTraceImpl( std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
const std::vector<Frame> frames, int maxAsyncDepth, std::vector<Frame> framesCopy(m_frames);
std::shared_ptr<AsyncStackTrace> asyncParent, std::unique_ptr<V8StackTraceImpl> copy(
std::shared_ptr<AsyncStackTrace> asyncCreation) new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
: m_frames(frames), m_parent ? m_parent->cloneImpl() : nullptr,
m_maxAsyncDepth(maxAsyncDepth), m_creation ? m_creation->cloneImpl() : nullptr));
m_asyncParent(asyncParent), return copy;
m_asyncCreation(asyncCreation) {} }
V8StackTraceImpl::~V8StackTraceImpl() {}
std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
return std::unique_ptr<V8StackTrace>( std::vector<Frame> frames;
new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(), for (size_t i = 0; i < m_frames.size(); i++) {
std::shared_ptr<AsyncStackTrace>())); frames.push_back(m_frames.at(i));
}
return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
m_contextGroupId, m_description, frames, nullptr, nullptr));
}
V8StackTraceImpl::V8StackTraceImpl(int contextGroupId,
const String16& description,
std::vector<Frame>& frames,
std::unique_ptr<V8StackTraceImpl> parent,
std::unique_ptr<V8StackTraceImpl> creation)
: m_contextGroupId(contextGroupId),
m_description(description),
m_parent(std::move(parent)),
m_creation(std::move(creation)) {
m_frames.swap(frames);
} }
bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); } V8StackTraceImpl::~V8StackTraceImpl() {}
StringView V8StackTraceImpl::topSourceURL() const { StringView V8StackTraceImpl::topSourceURL() const {
return toStringView(m_frames[0].sourceURL()); DCHECK(m_frames.size());
return toStringView(m_frames[0].m_scriptName);
} }
int V8StackTraceImpl::topLineNumber() const { int V8StackTraceImpl::topLineNumber() const {
return m_frames[0].lineNumber() + 1; DCHECK(m_frames.size());
return m_frames[0].m_lineNumber;
} }
int V8StackTraceImpl::topColumnNumber() const { int V8StackTraceImpl::topColumnNumber() const {
return m_frames[0].columnNumber() + 1; DCHECK(m_frames.size());
return m_frames[0].m_columnNumber;
} }
StringView V8StackTraceImpl::topScriptId() const { StringView V8StackTraceImpl::topFunctionName() const {
return toStringView(m_frames[0].scriptId()); DCHECK(m_frames.size());
return toStringView(m_frames[0].m_functionName);
} }
StringView V8StackTraceImpl::topFunctionName() const { StringView V8StackTraceImpl::topScriptId() const {
return toStringView(m_frames[0].functionName()); DCHECK(m_frames.size());
return toStringView(m_frames[0].m_scriptId);
} }
std::unique_ptr<protocol::Runtime::StackTrace> std::unique_ptr<protocol::Runtime::StackTrace>
V8StackTraceImpl::buildInspectorObjectImpl() const { V8StackTraceImpl::buildInspectorObjectImpl() const {
return buildInspectorObjectCommon(m_frames, m_asyncParent.lock(), return buildInspectorObjectImpl(nullptr);
m_asyncCreation.lock(), m_maxAsyncDepth); }
std::unique_ptr<protocol::Runtime::StackTrace>
V8StackTraceImpl::buildInspectorObjectImpl(V8StackTraceImpl* creation) const {
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames =
protocol::Array<protocol::Runtime::CallFrame>::create();
for (size_t i = 0; i < m_frames.size(); i++)
frames->addItem(m_frames.at(i).buildInspectorObject());
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
protocol::Runtime::StackTrace::create()
.setCallFrames(std::move(frames))
.build();
if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
if (m_parent) {
stackTrace->setParent(m_parent->buildInspectorObjectImpl(m_creation.get()));
}
if (creation && creation->m_frames.size()) {
stackTrace->setPromiseCreationFrame(
creation->m_frames[0].buildInspectorObject());
}
return stackTrace;
}
// static
std::unique_ptr<protocol::Runtime::StackTrace>
V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) {
DCHECK(debugger);
v8::HandleScope handleScope(debugger->inspector()->isolate());
// Next call collapses possible empty stack and ensures
// maxAsyncCallChainDepth.
V8StackTraceImpl* asyncChain = debugger->currentAsyncCallChain();
if (!asyncChain) return nullptr;
std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(
debugger, asyncChain->m_contextGroupId, v8::Local<v8::StackTrace>(),
V8StackTraceImpl::maxCallStackSizeToCapture);
if (!fullChain || !fullChain->m_parent) return nullptr;
return fullChain->m_parent->buildInspectorObjectImpl(
fullChain->m_creation.get());
} }
std::unique_ptr<protocol::Runtime::API::StackTrace> std::unique_ptr<protocol::Runtime::API::StackTrace>
V8StackTraceImpl::buildInspectorObject() const { V8StackTraceImpl::buildInspectorObject() const {
return buildInspectorObjectImpl(); return buildInspectorObjectImpl(nullptr);
} }
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
...@@ -242,77 +306,4 @@ std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { ...@@ -242,77 +306,4 @@ std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
return StringBufferImpl::adopt(string); return StringBufferImpl::adopt(string);
} }
// static
std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
V8Debugger* debugger, int contextGroupId, const String16& description,
int maxStackSize) {
DCHECK(debugger);
v8::Isolate* isolate = debugger->isolate();
v8::HandleScope handleScope(isolate);
std::vector<V8StackTraceImpl::Frame> frames;
if (isolate->InContext()) {
v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
isolate, maxStackSize, stackTraceOptions);
frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
}
std::shared_ptr<AsyncStackTrace> asyncParent;
std::shared_ptr<AsyncStackTrace> asyncCreation;
calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
nullptr);
if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
// When async call chain is empty but doesn't contain useful schedule stack
// and parent async call chain contains creationg stack but doesn't
// synchronous we can merge them together.
// e.g. Promise ThenableJob.
if (asyncParent && frames.empty() &&
asyncParent->m_description == description && !asyncCreation) {
return asyncParent;
}
return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace(
contextGroupId, description, frames, asyncParent, asyncCreation));
}
AsyncStackTrace::AsyncStackTrace(
int contextGroupId, const String16& description,
const std::vector<V8StackTraceImpl::Frame>& frames,
std::shared_ptr<AsyncStackTrace> asyncParent,
std::shared_ptr<AsyncStackTrace> asyncCreation)
: m_contextGroupId(contextGroupId),
m_description(description),
m_frames(frames),
m_asyncParent(asyncParent),
m_asyncCreation(asyncCreation) {}
std::unique_ptr<protocol::Runtime::StackTrace>
AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation,
int maxAsyncDepth) const {
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
m_asyncCreation.lock(), maxAsyncDepth);
if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
if (asyncCreation && !asyncCreation->isEmpty()) {
stackTrace->setPromiseCreationFrame(
asyncCreation->m_frames[0].buildInspectorObject());
}
return stackTrace;
}
int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
return m_asyncParent;
}
std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const {
return m_asyncCreation;
}
bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
} // namespace v8_inspector } // namespace v8_inspector
...@@ -5,115 +5,93 @@ ...@@ -5,115 +5,93 @@
#ifndef V8_INSPECTOR_V8STACKTRACEIMPL_H_ #ifndef V8_INSPECTOR_V8STACKTRACEIMPL_H_
#define V8_INSPECTOR_V8STACKTRACEIMPL_H_ #define V8_INSPECTOR_V8STACKTRACEIMPL_H_
#include <memory>
#include <vector> #include <vector>
#include "include/v8-inspector.h"
#include "include/v8.h"
#include "src/base/macros.h" #include "src/base/macros.h"
#include "src/inspector/protocol/Forward.h"
#include "src/inspector/protocol/Runtime.h" #include "src/inspector/protocol/Runtime.h"
#include "src/inspector/string-16.h"
#include "include/v8-inspector.h"
namespace v8_inspector { namespace v8_inspector {
class AsyncStackTrace;
class V8Debugger; class V8Debugger;
class WasmTranslation;
class V8StackTraceImpl : public V8StackTrace { // Note: async stack trace may have empty top stack with non-empty tail to
// 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: public:
static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*, static const size_t maxCallStackSizeToCapture = 200;
bool capture);
static const int maxCallStackSizeToCapture = 200;
static std::unique_ptr<V8StackTraceImpl> create(V8Debugger*,
int contextGroupId,
v8::Local<v8::StackTrace>,
int maxStackSize);
static std::unique_ptr<V8StackTraceImpl> capture(V8Debugger*,
int contextGroupId,
int maxStackSize);
~V8StackTraceImpl() override;
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
const;
// V8StackTrace implementation.
// This method drops the async stack trace.
std::unique_ptr<V8StackTrace> clone() override;
bool isEmpty() const override;
StringView topSourceURL() const override;
int topLineNumber() const override; // 1-based.
int topColumnNumber() const override; // 1-based.
StringView topScriptId() const override;
StringView topFunctionName() const override;
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject()
const override;
std::unique_ptr<StringBuffer> toString() const override;
class Frame { class Frame {
public: public:
explicit Frame(v8::Local<v8::StackFrame> frame); Frame(const String16& functionName, const String16& scriptId,
const String16& scriptName, int lineNumber, int column = 0);
~Frame() = default; ~Frame() = default;
void translate(WasmTranslation* wasmTranslation); 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; }
const String16& functionName() const; private:
const String16& scriptId() const; friend class V8StackTraceImpl;
const String16& sourceURL() const;
int lineNumber() const; // 0-based.
int columnNumber() const; // 0-based.
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const; std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const;
private:
String16 m_functionName; String16 m_functionName;
String16 m_scriptId; String16 m_scriptId;
String16 m_sourceURL; String16 m_scriptName;
int m_lineNumber; // 0-based. int m_lineNumber;
int m_columnNumber; // 0-based. int m_columnNumber;
}; };
private: static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*,
V8StackTraceImpl(const std::vector<Frame> frames, int maxAsyncDepth, bool capture);
std::shared_ptr<AsyncStackTrace> asyncParent, static std::unique_ptr<V8StackTraceImpl> create(
std::shared_ptr<AsyncStackTrace> asyncCreation); V8Debugger*, int contextGroupId, v8::Local<v8::StackTrace>,
size_t maxStackSize, const String16& description = String16());
std::vector<Frame> m_frames; static std::unique_ptr<V8StackTraceImpl> capture(
int m_maxAsyncDepth; V8Debugger*, int contextGroupId, size_t maxStackSize,
std::weak_ptr<AsyncStackTrace> m_asyncParent; const String16& description = String16());
std::weak_ptr<AsyncStackTrace> m_asyncCreation;
// This method drops the async chain. Use cloneImpl() instead.
DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl); std::unique_ptr<V8StackTrace> clone() override;
}; std::unique_ptr<V8StackTraceImpl> cloneImpl();
static std::unique_ptr<protocol::Runtime::StackTrace>
class AsyncStackTrace { buildInspectorObjectForTail(V8Debugger*);
public: std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl()
static std::shared_ptr<AsyncStackTrace> capture(V8Debugger*, const;
int contextGroupId, ~V8StackTraceImpl() override;
const String16& description,
int maxStackSize);
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObject(
AsyncStackTrace* asyncCreation, int maxAsyncDepth) const;
int contextGroupId() const; // V8StackTrace implementation.
std::weak_ptr<AsyncStackTrace> parent() const; bool isEmpty() const override { return !m_frames.size(); };
std::weak_ptr<AsyncStackTrace> creation() const; StringView topSourceURL() const override;
bool isEmpty() const; int topLineNumber() const override;
int topColumnNumber() const override;
StringView topScriptId() const override;
StringView topFunctionName() const override;
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject()
const override;
std::unique_ptr<StringBuffer> toString() const override;
private: private:
AsyncStackTrace(int contextGroupId, const String16& description, V8StackTraceImpl(int contextGroupId, const String16& description,
const std::vector<V8StackTraceImpl::Frame>& frames, std::vector<Frame>& frames,
std::shared_ptr<AsyncStackTrace> asyncParent, std::unique_ptr<V8StackTraceImpl> parent,
std::shared_ptr<AsyncStackTrace> asyncCreation); 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;
std::vector<Frame> m_frames;
std::unique_ptr<V8StackTraceImpl> m_parent;
std::unique_ptr<V8StackTraceImpl> m_creation;
std::vector<V8StackTraceImpl::Frame> m_frames; DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl);
std::weak_ptr<AsyncStackTrace> m_asyncParent;
std::weak_ptr<AsyncStackTrace> m_asyncCreation;
DISALLOW_COPY_AND_ASSIGN(AsyncStackTrace);
}; };
} // namespace v8_inspector } // namespace v8_inspector
......
Checks that async stacks works good with different limits
Running test: testZeroLimit
foo1 (test.js:11:2)
Running test: testTwoLimit
foo1 (test.js:11:2)
-- Promise.resolve --
promise (test.js:23:2)
(anonymous) (expr.js:0:0)
Running test: testOneLimitTwoPromises
foo1 (test.js:11:2)
foo2 (test.js:15:2)
Running test: testFourLimitTwoPromises
foo1 (test.js:11:2)
foo2 (test.js:15:2)
Running test: testSixLimitTwoPromises
foo1 (test.js:11:2)
foo2 (test.js:15:2)
-- Promise.resolve --
twoPromises (test.js:35:2)
(anonymous) (expr.js:0:0)
Running test: testTwoLimitTwoSetTimeouts
foo1 (test.js:11:2)
foo2 (test.js:15:2)
-- setTimeout --
twoSetTimeout (test.js:41:2)
(anonymous) (expr.js:0:0)
Running test: testThreeLimitTwoSetTimeouts
foo1 (test.js:11:2)
-- setTimeout --
twoSetTimeout (test.js:40:2)
(anonymous) (expr.js:0:0)
foo2 (test.js:15:2)
-- setTimeout --
twoSetTimeout (test.js:41:2)
(anonymous) (expr.js:0:0)
Running test: testTenLimitTwentySetTimeouts
foo1 (:0:17)
(anonymous) (:0:28)
foo2 (:0:17)
(anonymous) (:0:28)
foo3 (:0:17)
(anonymous) (:0:28)
foo4 (:0:17)
(anonymous) (:0:28)
foo5 (:0:17)
(anonymous) (:0:28)
foo6 (:0:17)
(anonymous) (:0:28)
foo7 (:0:17)
(anonymous) (:0:28)
foo8 (:0:17)
(anonymous) (:0:28)
foo9 (:0:17)
(anonymous) (:0:28)
foo10 (:0:18)
(anonymous) (:0:29)
foo11 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo12 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo13 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo14 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo15 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo16 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo17 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo18 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
foo19 (:0:18)
(anonymous) (:0:29)
-- setTimeout --
twentySetTimeout (test.js:49:4)
(anonymous) (expr.js:0:0)
// Copyright 2016 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 async stacks works good with different limits');
InspectorTest.addScript(`
var resolveTest;
function foo1() {
debugger;
}
function foo2() {
debugger;
if (resolveTest) resolveTest();
}
function promise() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = p1.then(foo1);
resolve1();
return p2;
}
function twoPromises() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = p1.then(foo1);
var p4 = p2.then(foo2);
resolve1();
resolve2();
return Promise.all([p3, p4]);
}
function twoSetTimeout() {
setTimeout(foo1, 0);
setTimeout(foo2, 0);
return new Promise(resolve => resolveTest = resolve);
}
function twentySetTimeout() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
for (var i = 1; i <= 19; ++i)
setTimeout('(function foo' + i + '(){debugger;})()',0);
setTimeout(resolve1, 0);
return p1;
}
//# sourceURL=test.js`, 7, 26);
InspectorTest.setupScriptMap();
Protocol.Debugger.onPaused(message => {
InspectorTest.logCallFrames(message.params.callFrames);
var asyncStackTrace = message.params.asyncStackTrace;
while (asyncStackTrace) {
InspectorTest.log(`-- ${asyncStackTrace.description} --`);
InspectorTest.logCallFrames(asyncStackTrace.callFrames);
asyncStackTrace = asyncStackTrace.parent;
}
InspectorTest.log('');
Protocol.Debugger.resume();
});
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
InspectorTest.runTestSuite([
function testZeroLimit(next) {
Protocol.Runtime.evaluate({
expression: 'setMaxAsyncTaskStacks(0)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'promise()//# sourceURL=expr.js', awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testTwoLimit(next) {
// we need one stack for parent task and one for next task.
Protocol.Runtime
.evaluate({expression: 'setMaxAsyncTaskStacks(2)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'promise()//# sourceURL=expr.js',
awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testOneLimitTwoPromises(next) {
// Should be no async stacks because when first microtask is finished
// it will resolve and schedule p3 - will remove async stack for scheduled
// p2.
Protocol.Runtime.evaluate({
expression: 'setMaxAsyncTaskStacks(1)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'twoPromises()//# sourceURL=expr.js', awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testFourLimitTwoPromises(next) {
Protocol.Runtime
.evaluate({expression: 'setMaxAsyncTaskStacks(4)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'twoPromises()//# sourceURL=expr.js',
awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testSixLimitTwoPromises(next) {
Protocol.Runtime
.evaluate({expression: 'setMaxAsyncTaskStacks(6)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'twoPromises()//# sourceURL=expr.js',
awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testTwoLimitTwoSetTimeouts(next) {
Protocol.Runtime.evaluate({
expression: 'setMaxAsyncTaskStacks(2)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'twoSetTimeout()//# sourceURL=expr.js', awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testThreeLimitTwoSetTimeouts(next) {
Protocol.Runtime.evaluate({
expression: 'setMaxAsyncTaskStacks(3)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'twoSetTimeout()//# sourceURL=expr.js', awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
},
function testTenLimitTwentySetTimeouts(next) {
Protocol.Runtime.evaluate({
expression: 'setMaxAsyncTaskStacks(10)//# sourceURL=expr.js'})
.then(() => Protocol.Runtime.evaluate({
expression: 'twentySetTimeout()//# sourceURL=expr.js',
awaitPromise: true
}))
.then(() => cancelAllAsyncTasks())
.then(next);
}
]);
function cancelAllAsyncTasks() {
return Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 0 })
.then(() => Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 }));
}
Checks that we drop old async call chains.
Running test: testInfrastructure
setMaxAsyncTaskStacks(1024)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(1024)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(1024)
Run expression 'console.trace(42)' with async chain len: 5
actual async chain len: 5
setMaxAsyncTaskStacks(1024)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(1024)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(1024)
Run expression 'console.trace(42)' with async chain len: 5
actual async chain len: 5
Running test: testZeroLimit
setMaxAsyncTaskStacks(0)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 0
setMaxAsyncTaskStacks(0)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 0
setMaxAsyncTaskStacks(0)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 0
setMaxAsyncTaskStacks(0)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 0
Running test: testOneLimit
setMaxAsyncTaskStacks(1)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 0
setMaxAsyncTaskStacks(1)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 0
setMaxAsyncTaskStacks(1)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(1)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 1
Running test: testTwoLimit
setMaxAsyncTaskStacks(2)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 0
setMaxAsyncTaskStacks(2)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 0
setMaxAsyncTaskStacks(2)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 0
setMaxAsyncTaskStacks(2)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(2)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(2)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 1
Running test: testMoreThanTwoLimit
setMaxAsyncTaskStacks(3)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(3)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 0
setMaxAsyncTaskStacks(3)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 0
setMaxAsyncTaskStacks(3)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(3)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(3)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 3
setMaxAsyncTaskStacks(4)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(4)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 1
setMaxAsyncTaskStacks(4)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 0
setMaxAsyncTaskStacks(4)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(4)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(4)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 3
setMaxAsyncTaskStacks(5)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(5)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(5)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 2
setMaxAsyncTaskStacks(5)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(5)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(5)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 3
setMaxAsyncTaskStacks(6)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(6)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(6)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 2
setMaxAsyncTaskStacks(6)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(6)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(6)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 3
setMaxAsyncTaskStacks(7)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(7)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(7)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 3
setMaxAsyncTaskStacks(7)
Run expression 'console.trace(42)' with async chain len: 1
actual async chain len: 1
setMaxAsyncTaskStacks(7)
Run expression 'console.trace(42)' with async chain len: 2
actual async chain len: 2
setMaxAsyncTaskStacks(7)
Run expression 'console.trace(42)' with async chain len: 3
actual async chain len: 3
// 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 drop old async call chains.');
Protocol.Debugger.enable();
Protocol.Runtime.enable();
InspectorTest.runAsyncTestSuite([
async function testInfrastructure() {
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
await setMaxAsyncTaskStacks(1024);
runWithAsyncChainPromise(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(1024);
runWithAsyncChainPromise(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(1024);
runWithAsyncChainPromise(5, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(1024);
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(1024);
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(1024);
runWithAsyncChainSetTimeout(5, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function testZeroLimit() {
const limit = 0;
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function testOneLimit() {
const limit = 1;
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function testTwoLimit() {
const limit = 2;
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(3, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(3, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
},
async function testMoreThanTwoLimit() {
for (let limit = 3; limit <= 7; ++limit) {
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: 128});
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainPromise(3, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(1, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(2, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
await setMaxAsyncTaskStacks(limit);
runWithAsyncChainSetTimeout(3, 'console.trace(42)');
dumpAsyncChainLength(await Protocol.Runtime.onceConsoleAPICalled());
}
},
]);
function runWithAsyncChainPromise(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 setTimeoutPrefix = '() => setTimeout(';
let setTimeoutSuffix = ', 0)';
Protocol.Runtime.evaluate({
expression: `setTimeout(${setTimeoutPrefix.repeat(len - 1)}'${source}'${setTimeoutSuffix.repeat(len - 1)}, 0)`
});
}
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}\n`);
}
async function setMaxAsyncTaskStacks(max) {
let expression = `setMaxAsyncTaskStacks(${max})`;
InspectorTest.log(expression);
await Protocol.Runtime.evaluate({expression});
}
Checks that we report not more then maxDepth call chains.
Running test: testPaused
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 16
reported: 8
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 8
reported: 8
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 7
reported: 7
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 0
reported: 0
Running test: testConsoleTrace
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 16
reported: 8
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 8
reported: 8
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 7
reported: 7
Actual call chain length: 8
setAsyncCallStackDepth(maxDepth): 0
reported: 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.
InspectorTest.log('Checks that we report not more then maxDepth call chains.');
InspectorTest.addScript(`
function promisesChain(num) {
var p = Promise.resolve();
for (var i = 0; i < num - 1; ++i) {
p = p.then(() => 42);
}
return p;
}
`);
Protocol.Debugger.enable();
InspectorTest.runAsyncTestSuite([
async function testPaused() {
let callback = '() => { debugger; }';
startTest({ generated: 8, limit: 16, callback});
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
await Protocol.Debugger.resume();
startTest({ generated: 8, limit: 8, callback});
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
await Protocol.Debugger.resume();
startTest({ generated: 8, limit: 7, callback});
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
await Protocol.Debugger.resume();
startTest({ generated: 8, limit: 0, callback});
dumpCaptured((await Protocol.Debugger.oncePaused()).params.asyncStackTrace);
await Protocol.Debugger.resume();
},
async function testConsoleTrace() {
await Protocol.Runtime.enable();
let callback = '() => { console.trace(42); }';
startTest({ generated: 8, limit: 16, callback});
let msg = await Protocol.Runtime.onceConsoleAPICalled();
dumpCaptured(msg.params.stackTrace.parent);
startTest({ generated: 8, limit: 8, callback});
msg = await Protocol.Runtime.onceConsoleAPICalled();
dumpCaptured(msg.params.stackTrace.parent);
startTest({ generated: 8, limit: 7, callback});
msg = await Protocol.Runtime.onceConsoleAPICalled();
dumpCaptured(msg.params.stackTrace.parent);
startTest({ generated: 8, limit: 0, callback});
msg = await Protocol.Runtime.onceConsoleAPICalled();
dumpCaptured(msg.params.stackTrace.parent);
await Protocol.Runtime.disable();
}
]);
function startTest(params) {
InspectorTest.log('Actual call chain length: ' + params.generated);
InspectorTest.log('setAsyncCallStackDepth(maxDepth): ' + params.limit);
Protocol.Debugger.setAsyncCallStackDepth({maxDepth: params.limit});
Protocol.Runtime.evaluate({expression:
`promisesChain(${params.generated}).then(${params.callback})`});
}
function dumpCaptured(stack) {
let count = 0;
while (stack) {
++count;
stack = stack.parent;
}
InspectorTest.log('reported: ' + count + '\n');
}
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