diff options
author | Myles Borins <mylesborins@google.com> | 2017-08-01 11:36:44 -0500 |
---|---|---|
committer | Myles Borins <mylesborins@google.com> | 2017-08-01 15:23:15 -0500 |
commit | 0a66b223e149a841669bfad5598e4254589730cb (patch) | |
tree | 5ec050f7f78aafbf5b1e0e50d639fb843141e162 /deps/v8/src/inspector/v8-debugger.cc | |
parent | 1782b3836ba58ef0da6b687f2bb970c0bd8199ad (diff) | |
download | android-node-v8-0a66b223e149a841669bfad5598e4254589730cb.tar.gz android-node-v8-0a66b223e149a841669bfad5598e4254589730cb.tar.bz2 android-node-v8-0a66b223e149a841669bfad5598e4254589730cb.zip |
deps: update V8 to 6.0.286.52
PR-URL: https://github.com/nodejs/node/pull/14004
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'deps/v8/src/inspector/v8-debugger.cc')
-rw-r--r-- | deps/v8/src/inspector/v8-debugger.cc | 298 |
1 files changed, 212 insertions, 86 deletions
diff --git a/deps/v8/src/inspector/v8-debugger.cc b/deps/v8/src/inspector/v8-debugger.cc index 87c864cd38..86a48401a6 100644 --- a/deps/v8/src/inspector/v8-debugger.cc +++ b/deps/v8/src/inspector/v8-debugger.cc @@ -21,9 +21,6 @@ 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; inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { @@ -32,11 +29,8 @@ inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) { V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector, v8::Local<v8::debug::Script> script) { - v8::Local<v8::Value> contextData; - if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) { - return nullptr; - } - int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); + int contextId; + if (!script->ContextId().To(&contextId)) return nullptr; int contextGroupId = inspector->contextGroupId(contextId); if (!contextGroupId) return nullptr; return inspector->enabledDebuggerAgentForGroup(contextGroupId); @@ -136,6 +130,17 @@ v8::MaybeLocal<v8::Object> generatorObjectLocation( suspendedLocation.GetColumnNumber()); } +template <typename Map> +void cleanupExpiredWeakPointers(Map& map) { + for (auto it = map.begin(); it != map.end();) { + if (it->second.expired()) { + it = map.erase(it); + } else { + ++it; + } + } +} + } // namespace static bool inLiveEditScope = false; @@ -164,10 +169,8 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) m_inspector(inspector), m_enableCount(0), m_breakpointsActivated(true), - m_runningNestedMessageLoop(false), m_ignoreScriptParsedEventsCounter(0), m_maxAsyncCallStacks(kMaxAsyncTaskStacks), - m_lastTaskId(0), m_maxAsyncCallStackDepth(0), m_pauseOnExceptionsState(v8::debug::NoBreakOnException), m_wasmTranslation(isolate) {} @@ -191,6 +194,7 @@ void V8Debugger::disable() { if (--m_enableCount) return; DCHECK(enabled()); clearBreakpoints(); + clearContinueToLocation(); m_debuggerScript.Reset(); m_debuggerContext.Reset(); allAsyncTasksCanceled(); @@ -212,10 +216,12 @@ void V8Debugger::getCompiledScripts( for (size_t i = 0; i < scripts.Size(); ++i) { v8::Local<v8::debug::Script> script = scripts.Get(i); if (!script->WasCompiled()) continue; - v8::Local<v8::Value> contextData; - if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) + if (script->IsEmbedded()) { + result.push_back(V8DebuggerScript::Create(m_isolate, script, false)); continue; - int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value()); + } + int contextId; + if (!script->ContextId().To(&contextId)) continue; if (m_inspector->contextGroupId(contextId) != contextGroupId) continue; result.push_back(V8DebuggerScript::Create(m_isolate, script, false)); } @@ -358,7 +364,8 @@ bool V8Debugger::breakProgram(int targetContextGroupId) { return m_inspector->enabledDebuggerAgentForGroup(targetContextGroupId); } -void V8Debugger::continueProgram() { +void V8Debugger::continueProgram(int targetContextGroupId) { + if (m_pausedContextGroupId != targetContextGroupId) return; if (isPaused()) m_inspector->client()->quitMessageLoopOnPause(); m_pausedContext.Clear(); m_executionState.Clear(); @@ -370,7 +377,7 @@ void V8Debugger::stepIntoStatement(int targetContextGroupId) { DCHECK(targetContextGroupId); m_targetContextGroupId = targetContextGroupId; v8::debug::PrepareStep(m_isolate, v8::debug::StepIn); - continueProgram(); + continueProgram(targetContextGroupId); } void V8Debugger::stepOverStatement(int targetContextGroupId) { @@ -379,7 +386,7 @@ void V8Debugger::stepOverStatement(int targetContextGroupId) { DCHECK(targetContextGroupId); m_targetContextGroupId = targetContextGroupId; v8::debug::PrepareStep(m_isolate, v8::debug::StepNext); - continueProgram(); + continueProgram(targetContextGroupId); } void V8Debugger::stepOutOfFunction(int targetContextGroupId) { @@ -388,7 +395,7 @@ void V8Debugger::stepOutOfFunction(int targetContextGroupId) { DCHECK(targetContextGroupId); m_targetContextGroupId = targetContextGroupId; v8::debug::PrepareStep(m_isolate, v8::debug::StepOut); - continueProgram(); + continueProgram(targetContextGroupId); } void V8Debugger::scheduleStepIntoAsync( @@ -405,6 +412,58 @@ void V8Debugger::scheduleStepIntoAsync( m_stepIntoAsyncCallback = std::move(callback); } +Response V8Debugger::continueToLocation( + int targetContextGroupId, + std::unique_ptr<protocol::Debugger::Location> location, + const String16& targetCallFrames) { + DCHECK(isPaused()); + DCHECK(!m_executionState.IsEmpty()); + DCHECK(targetContextGroupId); + m_targetContextGroupId = targetContextGroupId; + ScriptBreakpoint breakpoint(location->getScriptId(), + location->getLineNumber(), + location->getColumnNumber(0), String16()); + int lineNumber = 0; + int columnNumber = 0; + m_continueToLocationBreakpointId = + setBreakpoint(breakpoint, &lineNumber, &columnNumber); + if (!m_continueToLocationBreakpointId.isEmpty()) { + m_continueToLocationTargetCallFrames = targetCallFrames; + if (m_continueToLocationTargetCallFrames != + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) { + m_continueToLocationStack = captureStackTrace(true); + DCHECK(m_continueToLocationStack); + } + continueProgram(targetContextGroupId); + // TODO(kozyatinskiy): Return actual line and column number. + return Response::OK(); + } else { + return Response::Error("Cannot continue to specified location"); + } +} + +bool V8Debugger::shouldContinueToCurrentLocation() { + if (m_continueToLocationTargetCallFrames == + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) { + return true; + } + std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true); + if (m_continueToLocationTargetCallFrames == + protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) { + return m_continueToLocationStack->isEqualIgnoringTopFrame( + currentStack.get()); + } + return true; +} + +void V8Debugger::clearContinueToLocation() { + if (m_continueToLocationBreakpointId.isEmpty()) return; + removeBreakpoint(m_continueToLocationBreakpointId); + m_continueToLocationBreakpointId = String16(); + m_continueToLocationTargetCallFrames = String16(); + m_continueToLocationStack.reset(); +} + Response V8Debugger::setScriptSource( const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun, Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails, @@ -560,11 +619,17 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, breakpointIds.push_back(String16::fromInteger( hitBreakpointNumber->Int32Value(debuggerContext()).FromJust())); } + if (breakpointIds.size() == 1 && + breakpointIds[0] == m_continueToLocationBreakpointId) { + v8::Context::Scope contextScope(pausedContext); + if (!shouldContinueToCurrentLocation()) return; + } } + clearContinueToLocation(); m_pausedContext = pausedContext; m_executionState = executionState; - m_runningNestedMessageLoop = true; + m_pausedContextGroupId = contextGroupId; agent->didPause(InspectedContext::contextId(pausedContext), exception, breakpointIds, isPromiseRejection, isUncaught, m_scheduledOOMBreak); @@ -576,7 +641,7 @@ void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext, CHECK(!context.IsEmpty() && context != v8::debug::GetDebugContext(m_isolate)); m_inspector->client()->runMessageLoopOnPause(groupId); - m_runningNestedMessageLoop = false; + m_pausedContextGroupId = 0; } // The agent may have been removed in the nested loop. agent = m_inspector->enabledDebuggerAgentForGroup(groupId); @@ -643,8 +708,7 @@ bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script, end); } -void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, - v8::debug::PromiseDebugActionType type, +void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type, int id, int parentId, bool createdByUser) { // Async task events from Promises are given misaligned pointers to prevent @@ -655,10 +719,7 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, 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); @@ -669,10 +730,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); @@ -684,9 +741,16 @@ void V8Debugger::PromiseEventOccurred(v8::Local<v8::Context> context, } } -V8StackTraceImpl* V8Debugger::currentAsyncCallChain() { - if (!m_currentStacks.size()) return nullptr; - return m_currentStacks.back().get(); +std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() { + // TODO(kozyatinskiy): implement creation chain as parent without hack. + if (!m_currentAsyncCreation.empty() && m_currentAsyncCreation.back()) { + return m_currentAsyncCreation.back(); + } + return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back(); +} + +std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() { + return nullptr; } void V8Debugger::compileDebuggerScript() { @@ -827,8 +891,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); } @@ -849,31 +913,19 @@ 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 - // 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(), + V8StackTraceImpl::maxCallStackSizeToCapture); + // Passing one as maxStackSize forces no async chain for the new stack. + if (asyncCreation && !asyncCreation->isEmpty()) { + m_asyncTaskCreationStacks[task] = asyncCreation; + m_allAsyncStacks.push_back(std::move(asyncCreation)); + ++m_asyncStacksCount; + collectOldAsyncStacksIfNeeded(); } } @@ -902,13 +954,15 @@ 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); - if (chain) { - m_asyncTaskStacks[task] = std::move(chain); + std::shared_ptr<AsyncStackTrace> asyncStack = + AsyncStackTrace::capture(this, currentContextGroupId(), taskName, + V8StackTraceImpl::maxCallStackSizeToCapture); + if (asyncStack) { + m_asyncTaskStacks[task] = asyncStack; if (recurring) m_recurringTasks.insert(task); - registerAsyncTaskIfNeeded(task); + m_allAsyncStacks.push_back(std::move(asyncStack)); + ++m_asyncStacksCount; + collectOldAsyncStacksIfNeeded(); } } @@ -918,18 +972,10 @@ 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) { if (!m_maxAsyncCallStackDepth) return; - m_currentTasks.push_back(task); - auto parentIt = m_parentTask.find(task); - AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find( - parentIt == m_parentTask.end() ? task : parentIt->second); // Needs to support following order of events: // - asyncTaskScheduled // <-- attached here --> @@ -937,25 +983,40 @@ 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(); + m_currentTasks.push_back(task); + auto parentIt = m_parentTask.find(task); + AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find( + parentIt == m_parentTask.end() ? task : parentIt->second); + if (stackIt != m_asyncTaskStacks.end()) { + m_currentAsyncParent.push_back(stackIt->second.lock()); + } else { + m_currentAsyncParent.emplace_back(); + } auto itCreation = m_asyncTaskCreationStacks.find(task); - if (stack && itCreation != m_asyncTaskCreationStacks.end()) { - stack->setCreation(itCreation->second->cloneImpl()); + if (itCreation != m_asyncTaskCreationStacks.end()) { + m_currentAsyncCreation.push_back(itCreation->second.lock()); + // TODO(kozyatinskiy): implement it without hack. + if (m_currentAsyncParent.back()) { + m_currentAsyncCreation.back()->setDescription( + m_currentAsyncParent.back()->description()); + m_currentAsyncParent.back().reset(); + } + } else { + m_currentAsyncCreation.emplace_back(); } - m_currentStacks.push_back(std::move(stack)); } 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(); - m_currentStacks.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); } @@ -992,13 +1053,15 @@ void V8Debugger::asyncTaskCanceledForStepping(void* task) { void V8Debugger::allAsyncTasksCanceled() { m_asyncTaskStacks.clear(); m_recurringTasks.clear(); - m_currentStacks.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_framesCache.clear(); + m_allAsyncStacks.clear(); + m_asyncStacksCount = 0; } void V8Debugger::muteScriptParsedEvents() { @@ -1018,11 +1081,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); } @@ -1031,4 +1093,68 @@ int V8Debugger::currentContextGroupId() { 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; + } + cleanupExpiredWeakPointers(m_asyncTaskStacks); + cleanupExpiredWeakPointers(m_asyncTaskCreationStacks); + for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) { + if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) { + it = m_recurringTasks.erase(it); + } else { + ++it; + } + } + for (auto it = m_parentTask.begin(); it != m_parentTask.end();) { + if (m_asyncTaskCreationStacks.find(it->second) == + m_asyncTaskCreationStacks.end() && + m_asyncTaskStacks.find(it->second) == m_asyncTaskStacks.end()) { + it = m_parentTask.erase(it); + } else { + ++it; + } + } + cleanupExpiredWeakPointers(m_framesCache); +} + +std::shared_ptr<StackFrame> V8Debugger::symbolize( + v8::Local<v8::StackFrame> v8Frame) { + auto it = m_framesCache.end(); + int frameId = 0; + if (m_maxAsyncCallStackDepth) { + frameId = v8::debug::GetStackFrameId(v8Frame); + it = m_framesCache.find(frameId); + } + if (it != m_framesCache.end() && it->second.lock()) return it->second.lock(); + std::shared_ptr<StackFrame> frame(new StackFrame(v8Frame)); + // TODO(clemensh): Figure out a way to do this translation only right before + // sending the stack trace over wire. + if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation); + if (m_maxAsyncCallStackDepth) { + m_framesCache[frameId] = frame; + } + return frame; +} + +void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) { + m_maxAsyncCallStacks = 0; + collectOldAsyncStacksIfNeeded(); + m_maxAsyncCallStacks = limit; +} + +void V8Debugger::dumpAsyncTaskStacksStateForTest() { + fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount); + fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size()); + fprintf(stdout, "Created async tasks: %zu\n", + m_asyncTaskCreationStacks.size()); + fprintf(stdout, "Async tasks with parent: %zu\n", m_parentTask.size()); + fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size()); + fprintf(stdout, "\n"); +} + } // namespace v8_inspector |