Chromium Code Reviews| 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 |