Index: src/inspector/v8-debugger.cc |
diff --git a/src/inspector/v8-debugger.cc b/src/inspector/v8-debugger.cc |
index 0dcdf644677177e2fa48ae4471503b69b63d87b2..ab325d7765bf0de9b97a75fffa2c03766c38466c 100644 |
--- a/src/inspector/v8-debugger.cc |
+++ b/src/inspector/v8-debugger.cc |
@@ -21,10 +21,7 @@ namespace v8_inspector { |
namespace { |
-// 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; |
+static const int kMaxAsyncTaskStacks = 1024 * 1024; |
inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { |
return value ? v8::True(isolate) : v8::False(isolate); |
@@ -167,7 +164,6 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) |
m_runningNestedMessageLoop(false), |
m_ignoreScriptParsedEventsCounter(0), |
m_maxAsyncCallStacks(kMaxAsyncTaskStacks), |
- m_lastTaskId(0), |
m_maxAsyncCallStackDepth(0), |
m_pauseOnExceptionsState(v8::debug::NoBreakOnException), |
m_wasmTranslation(isolate) {} |
@@ -649,13 +645,16 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, |
void* task = reinterpret_cast<void*>(id * 2 + 1); |
void* parentTask = |
parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr; |
+ if (type == v8::debug::kDebugPromiseCollected) { |
+ asyncTaskCanceledForStack(task); |
+ asyncTaskCanceledForStepping(task); |
+ return; |
+ } |
+ v8::Context::Scope contextScope(context); |
dgozman
2017/04/17 17:49:08
Why new scope?
kozy
2017/04/17 21:34:17
It wasn't new - moved from v8::debug::kDebugPromis
|
switch (type) { |
case v8::debug::kDebugPromiseCreated: |
asyncTaskCreatedForStack(task, parentTask); |
- if (createdByUser && parentTask) { |
- v8::Context::Scope contextScope(context); |
- asyncTaskCandidateForStepping(task); |
- } |
+ if (createdByUser && parentTask) asyncTaskCandidateForStepping(task); |
break; |
case v8::debug::kDebugEnqueueAsyncFunction: |
asyncTaskScheduledForStack("async function", task, true); |
@@ -666,10 +665,6 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, |
case v8::debug::kDebugEnqueuePromiseReject: |
asyncTaskScheduledForStack("Promise.reject", task, true); |
break; |
- case v8::debug::kDebugPromiseCollected: |
- asyncTaskCanceledForStack(task); |
- asyncTaskCanceledForStepping(task); |
- break; |
case v8::debug::kDebugWillHandle: |
asyncTaskStartedForStack(task); |
asyncTaskStartedForStepping(task); |
@@ -678,17 +673,19 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, |
asyncTaskFinishedForStack(task); |
asyncTaskFinishedForStepping(task); |
break; |
+ case v8::debug::kDebugPromiseCollected: |
+ UNREACHABLE(); |
+ break; |
} |
} |
-V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { |
- if (!m_currentStacks.size()) return nullptr; |
- return m_currentStacks.back().get(); |
+std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() { |
+ return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back(); |
} |
-V8StackTraceImpl* V8Debugger::currentAsyncTaskCreationStack() { |
- if (!m_currentCreationStacks.size()) return nullptr; |
- return m_currentCreationStacks.back().get(); |
+std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() { |
+ return m_currentAsyncCreation.empty() ? nullptr |
+ : m_currentAsyncCreation.back(); |
} |
void V8Debugger::compileDebuggerScript() { |
@@ -829,8 +826,8 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties( |
} |
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace( |
- v8::Local<v8::StackTrace> stackTrace) { |
- return V8StackTraceImpl::create(this, currentContextGroupId(), stackTrace, |
+ v8::Local<v8::StackTrace> v8StackTrace) { |
+ return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace, |
V8StackTraceImpl::maxCallStackSizeToCapture); |
} |
@@ -851,31 +848,17 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { |
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) { |
if (!m_maxAsyncCallStackDepth) return; |
if (parentTask) m_parentTask[task] = parentTask; |
v8::HandleScope scope(m_isolate); |
- // We don't need to pass context group id here because we get this callback |
- // from V8 for promise events only. |
- // Passing one as maxStackSize forces no async chain for the new stack and |
dgozman
2017/04/17 17:49:08
Leave this comment about passing one.
kozy
2017/04/17 21:34:17
But it's not a true - we don't grow exponentially,
|
- // allows us to not grow exponentially. |
- std::unique_ptr<V8StackTraceImpl> creationStack = |
- V8StackTraceImpl::capture(this, 0, 1, String16()); |
- if (creationStack && !creationStack->isEmpty()) { |
- m_asyncTaskCreationStacks[task] = std::move(creationStack); |
- registerAsyncTaskIfNeeded(task); |
+ std::shared_ptr<AsyncStackTrace> asyncCreation = |
+ AsyncStackTrace::capture(this, currentContextGroupId(), String16(), 1); |
+ if (asyncCreation && !asyncCreation->isEmpty()) { |
+ m_asyncTaskCreationStacks[task] = asyncCreation; |
+ m_allAsyncStacks.push_back(asyncCreation); |
+ ++m_asyncStacksCount; |
+ collectOldAsyncStacksIfNeeded(); |
} |
} |
@@ -904,13 +887,16 @@ void V8Debugger::asyncTaskScheduledForStack(const String16& taskName, |
void* task, bool recurring) { |
if (!m_maxAsyncCallStackDepth) return; |
v8::HandleScope scope(m_isolate); |
- std::unique_ptr<V8StackTraceImpl> chain = V8StackTraceImpl::capture( |
- this, currentContextGroupId(), |
- V8StackTraceImpl::maxCallStackSizeToCapture, taskName); |
+ std::shared_ptr<AsyncStackTrace> chain = |
dgozman
2017/04/17 17:49:08
nit: chain -> asyncStack
kozy
2017/04/17 21:34:17
Done.
|
+ AsyncStackTrace::capture(this, currentContextGroupId(), taskName, |
+ V8StackTraceImpl::maxCallStackSizeToCapture); |
if (chain) { |
- m_asyncTaskStacks[task] = std::move(chain); |
+ m_asyncTaskStacks[task] = chain; |
if (recurring) m_recurringTasks.insert(task); |
- registerAsyncTaskIfNeeded(task); |
+ |
+ m_allAsyncStacks.push_back(chain); |
+ ++m_asyncStacksCount; |
+ collectOldAsyncStacksIfNeeded(); |
} |
} |
@@ -920,10 +906,6 @@ void V8Debugger::asyncTaskCanceledForStack(void* task) { |
m_recurringTasks.erase(task); |
m_parentTask.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) { |
@@ -939,28 +921,28 @@ void V8Debugger::asyncTaskStartedForStack(void* task) { |
// - asyncTaskCanceled <-- canceled before finished |
// <-- async stack requested here --> |
// - asyncTaskFinished |
- std::unique_ptr<V8StackTraceImpl> stack; |
- if (stackIt != m_asyncTaskStacks.end() && stackIt->second) |
- stack = stackIt->second->cloneImpl(); |
+ std::shared_ptr<AsyncStackTrace> asyncParent; |
+ if (stackIt != m_asyncTaskStacks.end()) asyncParent = stackIt->second.lock(); |
auto itCreation = m_asyncTaskCreationStacks.find(task); |
- if (stack && itCreation != m_asyncTaskCreationStacks.end()) { |
- m_currentCreationStacks.push_back(itCreation->second->cloneImpl()); |
+ if (asyncParent && itCreation != m_asyncTaskCreationStacks.end()) { |
+ m_currentAsyncCreation.push_back(itCreation->second.lock()); |
} else { |
- m_currentCreationStacks.push_back(nullptr); |
+ m_currentAsyncCreation.push_back(nullptr); |
} |
- m_currentStacks.push_back(std::move(stack)); |
+ m_currentAsyncParent.push_back(asyncParent); |
} |
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; |
+ if (!m_currentTasks.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(); |
+ DCHECK(m_currentAsyncParent.size() == m_currentAsyncCreation.size()); |
+ m_currentAsyncParent.pop_back(); |
+ m_currentAsyncCreation.pop_back(); |
+ |
if (m_recurringTasks.find(task) == m_recurringTasks.end()) { |
asyncTaskCanceledForStack(task); |
} |
@@ -997,14 +979,14 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) { |
void V8Debugger::allAsyncTasksCanceled() { |
m_asyncTaskStacks.clear(); |
m_recurringTasks.clear(); |
- m_currentStacks.clear(); |
- m_currentCreationStacks.clear(); |
+ m_currentAsyncParent.clear(); |
+ m_currentAsyncCreation.clear(); |
m_currentTasks.clear(); |
m_parentTask.clear(); |
m_asyncTaskCreationStacks.clear(); |
- m_idToTask.clear(); |
- m_taskToId.clear(); |
- m_lastTaskId = 0; |
+ |
+ m_allAsyncStacks.clear(); |
+ m_asyncStacksCount = 0; |
} |
void V8Debugger::muteScriptParsedEvents() { |
@@ -1024,11 +1006,10 @@ std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( |
int contextGroupId = currentContextGroupId(); |
if (!contextGroupId) return nullptr; |
- size_t stackSize = |
- fullStack ? V8StackTraceImpl::maxCallStackSizeToCapture : 1; |
- if (m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) |
+ int stackSize = 1; |
+ if (fullStack || m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) { |
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; |
- |
+ } |
return V8StackTraceImpl::capture(this, contextGroupId, stackSize); |
} |
@@ -1037,4 +1018,28 @@ int V8Debugger::currentContextGroupId() { |
return m_inspector->contextGroupId(m_isolate->GetCurrentContext()); |
} |
+void V8Debugger::collectOldAsyncStacksIfNeeded() { |
+ if (m_asyncStacksCount < m_maxAsyncCallStacks) return; |
+ while (m_asyncStacksCount > m_maxAsyncCallStacks / 2) { |
+ 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 |