LCOV - code coverage report
Current view: top level - src/inspector - v8-debugger.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 546 584 93.5 %
Date: 2019-03-21 Functions: 79 87 90.8 %

          Line data    Source code
       1             : // Copyright 2016 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/inspector/v8-debugger.h"
       6             : 
       7             : #include "src/inspector/inspected-context.h"
       8             : #include "src/inspector/protocol/Protocol.h"
       9             : #include "src/inspector/string-util.h"
      10             : #include "src/inspector/v8-debugger-agent-impl.h"
      11             : #include "src/inspector/v8-inspector-impl.h"
      12             : #include "src/inspector/v8-inspector-session-impl.h"
      13             : #include "src/inspector/v8-runtime-agent-impl.h"
      14             : #include "src/inspector/v8-stack-trace-impl.h"
      15             : #include "src/inspector/v8-value-utils.h"
      16             : 
      17             : #include "include/v8-util.h"
      18             : 
      19             : namespace v8_inspector {
      20             : 
      21             : namespace {
      22             : 
      23             : static const int kMaxAsyncTaskStacks = 128 * 1024;
      24             : static const int kNoBreakpointId = 0;
      25             : 
      26             : template <typename Map>
      27         945 : void cleanupExpiredWeakPointers(Map& map) {
      28       11725 :   for (auto it = map.begin(); it != map.end();) {
      29       10780 :     if (it->second.expired()) {
      30             :       it = map.erase(it);
      31             :     } else {
      32             :       ++it;
      33             :     }
      34             :   }
      35         945 : }
      36             : 
      37         170 : class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate {
      38             :  public:
      39             :   MatchPrototypePredicate(V8InspectorImpl* inspector,
      40             :                           v8::Local<v8::Context> context,
      41             :                           v8::Local<v8::Object> prototype)
      42          85 :       : m_inspector(inspector), m_context(context), m_prototype(prototype) {}
      43             : 
      44      304717 :   bool Filter(v8::Local<v8::Object> object) override {
      45      304717 :     v8::Local<v8::Context> objectContext = object->CreationContext();
      46      304717 :     if (objectContext != m_context) return false;
      47       67417 :     if (!m_inspector->client()->isInspectableHeapObject(object)) return false;
      48             :     // Get prototype chain for current object until first visited prototype.
      49      196000 :     for (v8::Local<v8::Value> prototype = object->GetPrototype();
      50             :          prototype->IsObject();
      51             :          prototype = prototype.As<v8::Object>()->GetPrototype()) {
      52      128688 :       if (m_prototype == prototype) return true;
      53             :     }
      54             :     return false;
      55             :   }
      56             : 
      57             :  private:
      58             :   V8InspectorImpl* m_inspector;
      59             :   v8::Local<v8::Context> m_context;
      60             :   v8::Local<v8::Value> m_prototype;
      61             : };
      62             : 
      63             : }  // namespace
      64             : 
      65        3672 : V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
      66             :     : m_isolate(isolate),
      67             :       m_inspector(inspector),
      68             :       m_enableCount(0),
      69             :       m_ignoreScriptParsedEventsCounter(0),
      70             :       m_continueToLocationBreakpointId(kNoBreakpointId),
      71             :       m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
      72             :       m_maxAsyncCallStackDepth(0),
      73             :       m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
      74       25704 :       m_wasmTranslation(isolate) {}
      75             : 
      76       18360 : V8Debugger::~V8Debugger() {
      77        3672 :   m_isolate->RemoveCallCompletedCallback(
      78        3672 :       &V8Debugger::terminateExecutionCompletedCallback);
      79        3672 :   m_isolate->RemoveMicrotasksCompletedCallback(
      80        3672 :       &V8Debugger::terminateExecutionCompletedCallbackIgnoringData);
      81        7344 : }
      82             : 
      83        3487 : void V8Debugger::enable() {
      84        3561 :   if (m_enableCount++) return;
      85        6826 :   v8::HandleScope scope(m_isolate);
      86        3413 :   v8::debug::SetDebugDelegate(m_isolate, this);
      87        3413 :   m_isolate->AddNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback, this);
      88        3413 :   v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
      89        3413 :   m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
      90             : }
      91             : 
      92        3487 : void V8Debugger::disable() {
      93        3487 :   if (isPaused()) {
      94         302 :     bool scheduledOOMBreak = m_scheduledOOMBreak;
      95         302 :     bool hasAgentAcceptsPause = false;
      96         604 :     m_inspector->forEachSession(
      97             :         m_pausedContextGroupId, [&scheduledOOMBreak, &hasAgentAcceptsPause](
      98         327 :                                     V8InspectorSessionImpl* session) {
      99         624 :           if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
     100          15 :             hasAgentAcceptsPause = true;
     101             :           }
     102         302 :         });
     103         302 :     if (!hasAgentAcceptsPause) m_inspector->client()->quitMessageLoopOnPause();
     104             :   }
     105        3487 :   if (--m_enableCount) return;
     106        3413 :   clearContinueToLocation();
     107        3413 :   m_taskWithScheduledBreak = nullptr;
     108        6826 :   m_taskWithScheduledBreakDebuggerId = String16();
     109        3413 :   m_pauseOnAsyncCall = false;
     110        3413 :   m_wasmTranslation.Clear();
     111        3413 :   v8::debug::SetDebugDelegate(m_isolate, nullptr);
     112        3413 :   m_isolate->RemoveNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback,
     113        3413 :                                          m_originalHeapLimit);
     114        3413 :   m_originalHeapLimit = 0;
     115             : }
     116             : 
     117      117901 : bool V8Debugger::isPausedInContextGroup(int contextGroupId) const {
     118      117901 :   return isPaused() && m_pausedContextGroupId == contextGroupId;
     119             : }
     120             : 
     121      143972 : bool V8Debugger::enabled() const { return m_enableCount > 0; }
     122             : 
     123        3487 : std::vector<std::unique_ptr<V8DebuggerScript>> V8Debugger::getCompiledScripts(
     124             :     int contextGroupId, V8DebuggerAgentImpl* agent) {
     125             :   std::vector<std::unique_ptr<V8DebuggerScript>> result;
     126        6974 :   v8::HandleScope scope(m_isolate);
     127        6974 :   v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate);
     128        3487 :   v8::debug::GetLoadedScripts(m_isolate, scripts);
     129       71147 :   for (size_t i = 0; i < scripts.Size(); ++i) {
     130       33830 :     v8::Local<v8::debug::Script> script = scripts.Get(i);
     131       33830 :     if (!script->WasCompiled()) continue;
     132       33727 :     if (!script->IsEmbedded()) {
     133             :       int contextId;
     134       67444 :       if (!script->ContextId().To(&contextId)) continue;
     135       33722 :       if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
     136             :     }
     137       67064 :     result.push_back(V8DebuggerScript::Create(m_isolate, script, false, agent,
     138       33532 :                                               m_inspector->client()));
     139             :   }
     140        3487 :   return result;
     141             : }
     142             : 
     143        7076 : void V8Debugger::setBreakpointsActive(bool active) {
     144        7076 :   if (!enabled()) {
     145           0 :     UNREACHABLE();
     146             :     return;
     147             :   }
     148        7076 :   m_breakpointsActiveCount += active ? 1 : -1;
     149        7076 :   v8::debug::SetBreakPointsActive(m_isolate, m_breakpointsActiveCount);
     150        7076 : }
     151             : 
     152      135986 : v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
     153             :   DCHECK(enabled());
     154      135986 :   return m_pauseOnExceptionsState;
     155             : }
     156             : 
     157        5114 : void V8Debugger::setPauseOnExceptionsState(
     158             :     v8::debug::ExceptionBreakState pauseOnExceptionsState) {
     159             :   DCHECK(enabled());
     160        5114 :   if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
     161        4080 :   v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
     162        4080 :   m_pauseOnExceptionsState = pauseOnExceptionsState;
     163             : }
     164             : 
     165         385 : void V8Debugger::setPauseOnNextCall(bool pause, int targetContextGroupId) {
     166         385 :   if (isPaused()) return;
     167             :   DCHECK(targetContextGroupId);
     168         385 :   if (!pause && m_targetContextGroupId &&
     169             :       m_targetContextGroupId != targetContextGroupId) {
     170             :     return;
     171             :   }
     172         385 :   m_targetContextGroupId = targetContextGroupId;
     173         385 :   m_breakRequested = pause;
     174         385 :   if (pause)
     175         365 :     v8::debug::SetBreakOnNextFunctionCall(m_isolate);
     176             :   else
     177          20 :     v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
     178             : }
     179             : 
     180         330 : bool V8Debugger::canBreakProgram() {
     181         370 :   return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
     182             : }
     183             : 
     184          60 : void V8Debugger::breakProgram(int targetContextGroupId) {
     185             :   DCHECK(canBreakProgram());
     186             :   // Don't allow nested breaks.
     187         155 :   if (isPaused()) return;
     188             :   DCHECK(targetContextGroupId);
     189          60 :   m_targetContextGroupId = targetContextGroupId;
     190         155 :   v8::debug::BreakRightNow(m_isolate);
     191             : }
     192             : 
     193           0 : void V8Debugger::interruptAndBreak(int targetContextGroupId) {
     194             :   // Don't allow nested breaks.
     195           0 :   if (isPaused()) return;
     196             :   DCHECK(targetContextGroupId);
     197           0 :   m_targetContextGroupId = targetContextGroupId;
     198           0 :   m_isolate->RequestInterrupt(
     199           0 :       [](v8::Isolate* isolate, void*) { v8::debug::BreakRightNow(isolate); },
     200           0 :       nullptr);
     201             : }
     202             : 
     203        2199 : void V8Debugger::continueProgram(int targetContextGroupId) {
     204       46702 :   if (m_pausedContextGroupId != targetContextGroupId) return;
     205       46702 :   if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
     206             : }
     207             : 
     208          80 : void V8Debugger::breakProgramOnAssert(int targetContextGroupId) {
     209          80 :   if (!enabled()) return;
     210          60 :   if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException) return;
     211             :   // Don't allow nested breaks.
     212          40 :   if (isPaused()) return;
     213          40 :   if (!canBreakProgram()) return;
     214             :   DCHECK(targetContextGroupId);
     215          35 :   m_targetContextGroupId = targetContextGroupId;
     216          35 :   m_scheduledAssertBreak = true;
     217          35 :   v8::debug::BreakRightNow(m_isolate);
     218             : }
     219             : 
     220       36289 : void V8Debugger::stepIntoStatement(int targetContextGroupId,
     221             :                                    bool breakOnAsyncCall) {
     222             :   DCHECK(isPaused());
     223             :   DCHECK(targetContextGroupId);
     224       36289 :   if (asyncStepOutOfFunction(targetContextGroupId, true)) return;
     225       36279 :   m_targetContextGroupId = targetContextGroupId;
     226       36279 :   m_pauseOnAsyncCall = breakOnAsyncCall;
     227       36279 :   v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
     228             :   continueProgram(targetContextGroupId);
     229             : }
     230             : 
     231        7626 : void V8Debugger::stepOverStatement(int targetContextGroupId) {
     232             :   DCHECK(isPaused());
     233             :   DCHECK(targetContextGroupId);
     234        7626 :   if (asyncStepOutOfFunction(targetContextGroupId, true)) return;
     235        7616 :   m_targetContextGroupId = targetContextGroupId;
     236        7616 :   v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
     237             :   continueProgram(targetContextGroupId);
     238             : }
     239             : 
     240         528 : void V8Debugger::stepOutOfFunction(int targetContextGroupId) {
     241             :   DCHECK(isPaused());
     242             :   DCHECK(targetContextGroupId);
     243         528 :   if (asyncStepOutOfFunction(targetContextGroupId, false)) return;
     244         508 :   m_targetContextGroupId = targetContextGroupId;
     245         508 :   v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
     246             :   continueProgram(targetContextGroupId);
     247             : }
     248             : 
     249       44443 : bool V8Debugger::asyncStepOutOfFunction(int targetContextGroupId,
     250             :                                         bool onlyAtReturn) {
     251       88886 :   v8::HandleScope handleScope(m_isolate);
     252       44443 :   auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
     253             :   DCHECK(!iterator->Done());
     254       88886 :   bool atReturn = !iterator->GetReturnValue().IsEmpty();
     255       44443 :   iterator->Advance();
     256             :   // Synchronous stack has more then one frame.
     257       44443 :   if (!iterator->Done()) return false;
     258             :   // There is only one synchronous frame but we are not at return position and
     259             :   // user requests stepOver or stepInto.
     260        7014 :   if (onlyAtReturn && !atReturn) return false;
     261             :   // If we are inside async function, current async parent was captured when
     262             :   // async function was suspended first time and we install that stack as
     263             :   // current before resume async function. So it represents current async
     264             :   // function.
     265             :   auto current = currentAsyncParent();
     266         652 :   if (!current) return false;
     267             :   // Lookup for parent async function.
     268          80 :   auto parent = current->parent();
     269          80 :   if (parent.expired()) return false;
     270             :   // Parent async stack will have suspended task id iff callee async function
     271             :   // is awaiting current async function. We can make stepOut there only in this
     272             :   // case.
     273             :   void* parentTask =
     274          50 :       std::shared_ptr<AsyncStackTrace>(parent)->suspendedTaskId();
     275          50 :   if (!parentTask) return false;
     276          80 :   pauseOnAsyncCall(targetContextGroupId,
     277             :                    reinterpret_cast<uintptr_t>(parentTask), String16());
     278             :   continueProgram(targetContextGroupId);
     279             :   return true;
     280             : }
     281             : 
     282          95 : void V8Debugger::pauseOnAsyncCall(int targetContextGroupId, uintptr_t task,
     283             :                                   const String16& debuggerId) {
     284             :   DCHECK(targetContextGroupId);
     285         135 :   m_targetContextGroupId = targetContextGroupId;
     286             : 
     287         135 :   m_taskWithScheduledBreak = reinterpret_cast<void*>(task);
     288             :   m_taskWithScheduledBreakDebuggerId = debuggerId;
     289          95 : }
     290             : 
     291          40 : void V8Debugger::terminateExecution(
     292             :     std::unique_ptr<TerminateExecutionCallback> callback) {
     293          40 :   if (m_terminateExecutionCallback) {
     294           0 :     if (callback) {
     295             :       callback->sendFailure(
     296           0 :           Response::Error("There is current termination request in progress"));
     297             :     }
     298             :     return;
     299             :   }
     300             :   m_terminateExecutionCallback = std::move(callback);
     301          40 :   m_isolate->AddCallCompletedCallback(
     302          40 :       &V8Debugger::terminateExecutionCompletedCallback);
     303          40 :   m_isolate->AddMicrotasksCompletedCallback(
     304          40 :       &V8Debugger::terminateExecutionCompletedCallbackIgnoringData);
     305          40 :   m_isolate->TerminateExecution();
     306             : }
     307             : 
     308          68 : void V8Debugger::reportTermination() {
     309          68 :   if (!m_terminateExecutionCallback) return;
     310          40 :   m_isolate->RemoveCallCompletedCallback(
     311          40 :       &V8Debugger::terminateExecutionCompletedCallback);
     312          40 :   m_isolate->RemoveMicrotasksCompletedCallback(
     313          40 :       &V8Debugger::terminateExecutionCompletedCallbackIgnoringData);
     314          40 :   m_isolate->CancelTerminateExecution();
     315          40 :   m_terminateExecutionCallback->sendSuccess();
     316             :   m_terminateExecutionCallback.reset();
     317             : }
     318             : 
     319           5 : void V8Debugger::terminateExecutionCompletedCallback(v8::Isolate* isolate) {
     320             :   V8InspectorImpl* inspector =
     321          30 :       static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
     322             :   V8Debugger* debugger = inspector->debugger();
     323          30 :   debugger->reportTermination();
     324           5 : }
     325             : 
     326          25 : void V8Debugger::terminateExecutionCompletedCallbackIgnoringData(
     327             :     v8::Isolate* isolate, void*) {
     328             :   terminateExecutionCompletedCallback(isolate);
     329          25 : }
     330             : 
     331          60 : Response V8Debugger::continueToLocation(
     332             :     int targetContextGroupId, V8DebuggerScript* script,
     333             :     std::unique_ptr<protocol::Debugger::Location> location,
     334             :     const String16& targetCallFrames) {
     335             :   DCHECK(isPaused());
     336             :   DCHECK(targetContextGroupId);
     337          60 :   m_targetContextGroupId = targetContextGroupId;
     338             :   v8::debug::Location v8Location(location->getLineNumber(),
     339          60 :                                  location->getColumnNumber(0));
     340         180 :   if (script->setBreakpoint(String16(), &v8Location,
     341          60 :                             &m_continueToLocationBreakpointId)) {
     342             :     m_continueToLocationTargetCallFrames = targetCallFrames;
     343         120 :     if (m_continueToLocationTargetCallFrames !=
     344             :         protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
     345          30 :       m_continueToLocationStack = captureStackTrace(true);
     346             :       DCHECK(m_continueToLocationStack);
     347             :     }
     348             :     continueProgram(targetContextGroupId);
     349             :     // TODO(kozyatinskiy): Return actual line and column number.
     350          60 :     return Response::OK();
     351             :   } else {
     352           0 :     return Response::Error("Cannot continue to specified location");
     353             :   }
     354             : }
     355             : 
     356          90 : bool V8Debugger::shouldContinueToCurrentLocation() {
     357         180 :   if (m_continueToLocationTargetCallFrames ==
     358             :       protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
     359             :     return true;
     360             :   }
     361          45 :   std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true);
     362          90 :   if (m_continueToLocationTargetCallFrames ==
     363             :       protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
     364             :     return m_continueToLocationStack->isEqualIgnoringTopFrame(
     365          45 :         currentStack.get());
     366             :   }
     367             :   return true;
     368             : }
     369             : 
     370       58753 : void V8Debugger::clearContinueToLocation() {
     371       58753 :   if (m_continueToLocationBreakpointId == kNoBreakpointId) return;
     372          60 :   v8::debug::RemoveBreakpoint(m_isolate, m_continueToLocationBreakpointId);
     373          60 :   m_continueToLocationBreakpointId = kNoBreakpointId;
     374         120 :   m_continueToLocationTargetCallFrames = String16();
     375             :   m_continueToLocationStack.reset();
     376             : }
     377             : 
     378       55400 : void V8Debugger::handleProgramBreak(
     379             :     v8::Local<v8::Context> pausedContext, v8::Local<v8::Value> exception,
     380             :     const std::vector<v8::debug::BreakpointId>& breakpointIds,
     381             :     v8::debug::ExceptionType exceptionType, bool isUncaught) {
     382             :   // Don't allow nested breaks.
     383       55460 :   if (isPaused()) return;
     384             : 
     385       55400 :   int contextGroupId = m_inspector->contextGroupId(pausedContext);
     386       55400 :   if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
     387          20 :     v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
     388          20 :     return;
     389             :   }
     390       55380 :   m_targetContextGroupId = 0;
     391       55380 :   m_breakRequested = false;
     392       55380 :   m_pauseOnAsyncCall = false;
     393       55380 :   m_taskWithScheduledBreak = nullptr;
     394      110760 :   m_taskWithScheduledBreakDebuggerId = String16();
     395             : 
     396       55380 :   bool scheduledOOMBreak = m_scheduledOOMBreak;
     397       55380 :   bool scheduledAssertBreak = m_scheduledAssertBreak;
     398       55380 :   bool hasAgents = false;
     399      110760 :   m_inspector->forEachSession(
     400             :       contextGroupId,
     401      110955 :       [&scheduledOOMBreak, &hasAgents](V8InspectorSessionImpl* session) {
     402      110980 :         if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak))
     403       55465 :           hasAgents = true;
     404       55380 :       });
     405       55380 :   if (!hasAgents) return;
     406             : 
     407       57818 :   if (breakpointIds.size() == 1 &&
     408        2448 :       breakpointIds[0] == m_continueToLocationBreakpointId) {
     409             :     v8::Context::Scope contextScope(pausedContext);
     410          90 :     if (!shouldContinueToCurrentLocation()) return;
     411             :   }
     412       55340 :   clearContinueToLocation();
     413             : 
     414             :   DCHECK(contextGroupId);
     415       55340 :   m_pausedContextGroupId = contextGroupId;
     416             : 
     417      110680 :   m_inspector->forEachSession(
     418             :       contextGroupId, [&pausedContext, &exception, &breakpointIds,
     419             :                        &exceptionType, &isUncaught, &scheduledOOMBreak,
     420      332620 :                        &scheduledAssertBreak](V8InspectorSessionImpl* session) {
     421      110890 :         if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
     422      277175 :           session->debuggerAgent()->didPause(
     423             :               InspectedContext::contextId(pausedContext), exception,
     424             :               breakpointIds, exceptionType, isUncaught, scheduledOOMBreak,
     425       55435 :               scheduledAssertBreak);
     426             :         }
     427      110785 :       });
     428             :   {
     429             :     v8::Context::Scope scope(pausedContext);
     430       55340 :     m_inspector->client()->runMessageLoopOnPause(contextGroupId);
     431       55340 :     m_pausedContextGroupId = 0;
     432             :   }
     433      110680 :   m_inspector->forEachSession(contextGroupId,
     434             :                               [](V8InspectorSessionImpl* session) {
     435       55445 :                                 if (session->debuggerAgent()->enabled())
     436       55158 :                                   session->debuggerAgent()->didContinue();
     437       55340 :                               });
     438             : 
     439       55340 :   if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
     440       55340 :   m_scheduledOOMBreak = false;
     441       55340 :   m_scheduledAssertBreak = false;
     442             : }
     443             : 
     444             : namespace {
     445             : 
     446             : size_t HeapLimitForDebugging(size_t initial_heap_limit) {
     447             :   const size_t kDebugHeapSizeFactor = 4;
     448           5 :   size_t max_limit = std::numeric_limits<size_t>::max() / 4;
     449          10 :   return std::min(max_limit, initial_heap_limit * kDebugHeapSizeFactor);
     450             : }
     451             : 
     452             : }  // anonymous namespace
     453             : 
     454           5 : size_t V8Debugger::nearHeapLimitCallback(void* data, size_t current_heap_limit,
     455             :                                          size_t initial_heap_limit) {
     456             :   V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
     457           5 :   thisPtr->m_originalHeapLimit = current_heap_limit;
     458           5 :   thisPtr->m_scheduledOOMBreak = true;
     459             :   v8::Local<v8::Context> context =
     460           5 :       thisPtr->m_isolate->GetEnteredOrMicrotaskContext();
     461             :   thisPtr->m_targetContextGroupId =
     462           5 :       context.IsEmpty() ? 0 : thisPtr->m_inspector->contextGroupId(context);
     463           5 :   thisPtr->m_isolate->RequestInterrupt(
     464           5 :       [](v8::Isolate* isolate, void*) { v8::debug::BreakRightNow(isolate); },
     465           5 :       nullptr);
     466           5 :   return HeapLimitForDebugging(initial_heap_limit);
     467             : }
     468             : 
     469       22322 : void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
     470             :                                 bool is_live_edited, bool has_compile_error) {
     471             :   int contextId;
     472       44644 :   if (!script->ContextId().To(&contextId)) return;
     473       22434 :   if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
     474          52 :     WasmTranslation* wasmTranslation = &m_wasmTranslation;
     475         104 :     m_inspector->forEachSession(
     476          52 :         m_inspector->contextGroupId(contextId),
     477         156 :         [&script, &wasmTranslation](V8InspectorSessionImpl* session) {
     478          52 :           if (!session->debuggerAgent()->enabled()) return;
     479          52 :           wasmTranslation->AddScript(script.As<v8::debug::WasmScript>(),
     480          52 :                                      session->debuggerAgent());
     481          52 :         });
     482       22270 :   } else if (m_ignoreScriptParsedEventsCounter == 0) {
     483       22245 :     v8::Isolate* isolate = m_isolate;
     484       22245 :     V8InspectorClient* client = m_inspector->client();
     485       44490 :     m_inspector->forEachSession(
     486       22245 :         m_inspector->contextGroupId(contextId),
     487             :         [&isolate, &script, &has_compile_error, &is_live_edited,
     488      111780 :          &client](V8InspectorSessionImpl* session) {
     489       22360 :           if (!session->debuggerAgent()->enabled()) return;
     490       22355 :           session->debuggerAgent()->didParseSource(
     491       89420 :               V8DebuggerScript::Create(isolate, script, is_live_edited,
     492             :                                        session->debuggerAgent(), client),
     493       44710 :               !has_compile_error);
     494       22245 :         });
     495             :   }
     496             : }
     497             : 
     498       53516 : void V8Debugger::BreakProgramRequested(
     499             :     v8::Local<v8::Context> pausedContext,
     500             :     const std::vector<v8::debug::BreakpointId>& break_points_hit) {
     501       53516 :   handleProgramBreak(pausedContext, v8::Local<v8::Value>(), break_points_hit);
     502       53516 : }
     503             : 
     504        1884 : void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
     505             :                                  v8::Local<v8::Value> exception,
     506             :                                  v8::Local<v8::Value> promise, bool isUncaught,
     507             :                                  v8::debug::ExceptionType exceptionType) {
     508             :   std::vector<v8::debug::BreakpointId> break_points_hit;
     509        1884 :   handleProgramBreak(pausedContext, exception, break_points_hit, exceptionType,
     510        1884 :                      isUncaught);
     511        1884 : }
     512             : 
     513       11080 : bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
     514             :                                       const v8::debug::Location& start,
     515             :                                       const v8::debug::Location& end) {
     516             :   int contextId;
     517       22160 :   if (!script->ContextId().To(&contextId)) return false;
     518       11075 :   bool hasAgents = false;
     519       11075 :   bool allBlackboxed = true;
     520       11075 :   String16 scriptId = String16::fromInteger(script->Id());
     521       22150 :   m_inspector->forEachSession(
     522       11075 :       m_inspector->contextGroupId(contextId),
     523             :       [&hasAgents, &allBlackboxed, &scriptId, &start,
     524       44545 :        &end](V8InspectorSessionImpl* session) {
     525             :         V8DebuggerAgentImpl* agent = session->debuggerAgent();
     526       11140 :         if (!agent->enabled()) return;
     527       11135 :         hasAgents = true;
     528       22270 :         allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
     529       11075 :       });
     530       11075 :   return hasAgents && allBlackboxed;
     531             : }
     532             : 
     533        6510 : void V8Debugger::AsyncEventOccurred(v8::debug::DebugAsyncActionType type,
     534             :                                     int id, bool isBlackboxed) {
     535             :   // Async task events from Promises are given misaligned pointers to prevent
     536             :   // from overlapping with other Blink task identifiers.
     537        6510 :   void* task = reinterpret_cast<void*>(id * 2 + 1);
     538        6510 :   switch (type) {
     539             :     case v8::debug::kDebugPromiseThen:
     540        3630 :       asyncTaskScheduledForStack("Promise.then", task, false);
     541        1815 :       if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
     542             :       break;
     543             :     case v8::debug::kDebugPromiseCatch:
     544          60 :       asyncTaskScheduledForStack("Promise.catch", task, false);
     545          30 :       if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
     546             :       break;
     547             :     case v8::debug::kDebugPromiseFinally:
     548          20 :       asyncTaskScheduledForStack("Promise.finally", task, false);
     549          10 :       if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
     550             :       break;
     551             :     case v8::debug::kDebugWillHandle:
     552        2115 :       asyncTaskStartedForStack(task);
     553        2115 :       asyncTaskStartedForStepping(task);
     554        2115 :       break;
     555             :     case v8::debug::kDebugDidHandle:
     556        2100 :       asyncTaskFinishedForStack(task);
     557        2100 :       asyncTaskFinishedForStepping(task);
     558        2100 :       break;
     559             :     case v8::debug::kAsyncFunctionSuspended: {
     560         275 :       if (m_asyncTaskStacks.find(task) == m_asyncTaskStacks.end()) {
     561         350 :         asyncTaskScheduledForStack("async function", task, true);
     562             :       }
     563             :       auto stackIt = m_asyncTaskStacks.find(task);
     564         550 :       if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
     565             :         std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
     566         275 :         stack->setSuspendedTaskId(task);
     567             :       }
     568             :       break;
     569             :     }
     570             :     case v8::debug::kAsyncFunctionFinished:
     571         165 :       asyncTaskCanceledForStack(task);
     572         165 :       break;
     573             :   }
     574        6510 : }
     575             : 
     576      122296 : std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
     577      245244 :   return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
     578             : }
     579             : 
     580      122296 : V8StackTraceId V8Debugger::currentExternalParent() {
     581             :   return m_currentExternalParent.empty() ? V8StackTraceId()
     582      122296 :                                          : m_currentExternalParent.back();
     583             : }
     584             : 
     585         115 : v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
     586             :     v8::Local<v8::Context> context, v8::Local<v8::Value> value,
     587             :     ScopeTargetKind kind) {
     588             :   v8::Local<v8::Value> scopesValue;
     589             :   std::unique_ptr<v8::debug::ScopeIterator> iterator;
     590         115 :   switch (kind) {
     591             :     case FUNCTION:
     592         130 :       iterator = v8::debug::ScopeIterator::CreateForFunction(
     593             :           m_isolate, v8::Local<v8::Function>::Cast(value));
     594         170 :       break;
     595             :     case GENERATOR:
     596             :       v8::Local<v8::debug::GeneratorObject> generatorObject =
     597          50 :           v8::debug::GeneratorObject::Cast(value);
     598          50 :       if (!generatorObject->IsSuspended()) return v8::MaybeLocal<v8::Value>();
     599             : 
     600          80 :       iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
     601             :           m_isolate, v8::Local<v8::Object>::Cast(value));
     602          40 :       break;
     603             :   }
     604         105 :   if (!iterator) return v8::MaybeLocal<v8::Value>();
     605          95 :   v8::Local<v8::Array> result = v8::Array::New(m_isolate);
     606         285 :   if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) {
     607           0 :     return v8::MaybeLocal<v8::Value>();
     608             :   }
     609             : 
     610         395 :   for (; !iterator->Done(); iterator->Advance()) {
     611         150 :     v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
     612         150 :     if (!addInternalObject(context, scope, V8InternalValueType::kScope))
     613           0 :       return v8::MaybeLocal<v8::Value>();
     614             :     String16 nameSuffix = toProtocolStringWithTypeCheck(
     615         150 :         m_isolate, iterator->GetFunctionDebugName());
     616             :     String16 description;
     617         270 :     if (nameSuffix.length()) nameSuffix = " (" + nameSuffix + ")";
     618         150 :     switch (iterator->GetType()) {
     619             :       case v8::debug::ScopeIterator::ScopeTypeGlobal:
     620         190 :         description = "Global" + nameSuffix;
     621          95 :         break;
     622             :       case v8::debug::ScopeIterator::ScopeTypeLocal:
     623          80 :         description = "Local" + nameSuffix;
     624          40 :         break;
     625             :       case v8::debug::ScopeIterator::ScopeTypeWith:
     626          10 :         description = "With Block" + nameSuffix;
     627           5 :         break;
     628             :       case v8::debug::ScopeIterator::ScopeTypeClosure:
     629          10 :         description = "Closure" + nameSuffix;
     630           5 :         break;
     631             :       case v8::debug::ScopeIterator::ScopeTypeCatch:
     632          10 :         description = "Catch" + nameSuffix;
     633           5 :         break;
     634             :       case v8::debug::ScopeIterator::ScopeTypeBlock:
     635           0 :         description = "Block" + nameSuffix;
     636           0 :         break;
     637             :       case v8::debug::ScopeIterator::ScopeTypeScript:
     638           0 :         description = "Script" + nameSuffix;
     639           0 :         break;
     640             :       case v8::debug::ScopeIterator::ScopeTypeEval:
     641           0 :         description = "Eval" + nameSuffix;
     642           0 :         break;
     643             :       case v8::debug::ScopeIterator::ScopeTypeModule:
     644           0 :         description = "Module" + nameSuffix;
     645           0 :         break;
     646             :     }
     647         150 :     v8::Local<v8::Object> object = iterator->GetObject();
     648             :     createDataProperty(context, scope,
     649             :                        toV8StringInternalized(m_isolate, "description"),
     650         450 :                        toV8String(m_isolate, description));
     651             :     createDataProperty(context, scope,
     652         300 :                        toV8StringInternalized(m_isolate, "object"), object);
     653         150 :     createDataProperty(context, result, result->Length(), scope);
     654             :   }
     655          95 :   if (!addInternalObject(context, result, V8InternalValueType::kScopeList))
     656           0 :     return v8::MaybeLocal<v8::Value>();
     657          95 :   return result;
     658             : }
     659             : 
     660           0 : v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
     661             :     v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
     662          65 :   return getTargetScopes(context, function, FUNCTION);
     663             : }
     664             : 
     665           0 : v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
     666             :     v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
     667          50 :   return getTargetScopes(context, generator, GENERATOR);
     668             : }
     669             : 
     670       79258 : v8::MaybeLocal<v8::Array> V8Debugger::collectionsEntries(
     671             :     v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
     672       79258 :   v8::Isolate* isolate = context->GetIsolate();
     673             :   v8::Local<v8::Array> entries;
     674       79258 :   bool isKeyValue = false;
     675      158516 :   if (!value->IsObject() ||
     676       79258 :       !value.As<v8::Object>()->PreviewEntries(&isKeyValue).ToLocal(&entries)) {
     677       78899 :     return v8::MaybeLocal<v8::Array>();
     678             :   }
     679             : 
     680         359 :   v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
     681         359 :   CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
     682         718 :   if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
     683             :            .FromMaybe(false))
     684           0 :     return v8::MaybeLocal<v8::Array>();
     685       21533 :   for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
     686             :     v8::Local<v8::Value> item;
     687       21174 :     if (!entries->Get(context, i).ToLocal(&item)) continue;
     688             :     v8::Local<v8::Value> value;
     689       15807 :     if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
     690       10587 :     v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
     691       21174 :     if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
     692             :       continue;
     693             :     createDataProperty(
     694             :         context, wrapper,
     695       21174 :         toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
     696       10587 :     if (isKeyValue) {
     697             :       createDataProperty(context, wrapper,
     698       10440 :                          toV8StringInternalized(isolate, "value"), value);
     699             :     }
     700       10587 :     if (!addInternalObject(context, wrapper, V8InternalValueType::kEntry))
     701             :       continue;
     702       10587 :     createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
     703       10587 :                        wrapper);
     704             :   }
     705         359 :   return wrappedEntries;
     706             : }
     707             : 
     708       79258 : v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
     709             :     v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
     710             :   v8::Local<v8::Array> properties;
     711      158516 :   if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
     712           0 :     return v8::MaybeLocal<v8::Array>();
     713             :   v8::Local<v8::Array> entries;
     714      158516 :   if (collectionsEntries(context, value).ToLocal(&entries)) {
     715         359 :     createDataProperty(context, properties, properties->Length(),
     716         718 :                        toV8StringInternalized(m_isolate, "[[Entries]]"));
     717         359 :     createDataProperty(context, properties, properties->Length(), entries);
     718             :   }
     719       79258 :   if (value->IsGeneratorObject()) {
     720             :     v8::Local<v8::Value> scopes;
     721          50 :     if (generatorScopes(context, value).ToLocal(&scopes)) {
     722          40 :       createDataProperty(context, properties, properties->Length(),
     723          80 :                          toV8StringInternalized(m_isolate, "[[Scopes]]"));
     724          40 :       createDataProperty(context, properties, properties->Length(), scopes);
     725             :     }
     726             :   }
     727       79258 :   if (value->IsFunction()) {
     728             :     v8::Local<v8::Function> function = value.As<v8::Function>();
     729             :     v8::Local<v8::Value> scopes;
     730          65 :     if (functionScopes(context, function).ToLocal(&scopes)) {
     731          55 :       createDataProperty(context, properties, properties->Length(),
     732         110 :                          toV8StringInternalized(m_isolate, "[[Scopes]]"));
     733          55 :       createDataProperty(context, properties, properties->Length(), scopes);
     734             :     }
     735             :   }
     736       79258 :   return properties;
     737             : }
     738             : 
     739          85 : v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
     740             :                                               v8::Local<v8::Object> prototype) {
     741          85 :   v8::Isolate* isolate = context->GetIsolate();
     742          85 :   v8::PersistentValueVector<v8::Object> v8Objects(isolate);
     743          85 :   MatchPrototypePredicate predicate(m_inspector, context, prototype);
     744          85 :   v8::debug::QueryObjects(context, &predicate, &v8Objects);
     745             : 
     746             :   v8::MicrotasksScope microtasksScope(isolate,
     747         170 :                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
     748             :   v8::Local<v8::Array> resultArray = v8::Array::New(
     749          85 :       m_inspector->isolate(), static_cast<int>(v8Objects.Size()));
     750         285 :   for (size_t i = 0; i < v8Objects.Size(); ++i) {
     751             :     createDataProperty(context, resultArray, static_cast<int>(i),
     752         200 :                        v8Objects.Get(i));
     753             :   }
     754         170 :   return resultArray;
     755             : }
     756             : 
     757         420 : std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
     758             :     v8::Local<v8::StackTrace> v8StackTrace) {
     759             :   return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
     760         420 :                                   V8StackTraceImpl::maxCallStackSizeToCapture);
     761             : }
     762             : 
     763        3852 : void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
     764        3852 :   if (depth <= 0)
     765             :     m_maxAsyncCallStackDepthMap.erase(agent);
     766             :   else
     767         295 :     m_maxAsyncCallStackDepthMap[agent] = depth;
     768             : 
     769             :   int maxAsyncCallStackDepth = 0;
     770        4167 :   for (const auto& pair : m_maxAsyncCallStackDepthMap) {
     771         315 :     if (pair.second > maxAsyncCallStackDepth)
     772             :       maxAsyncCallStackDepth = pair.second;
     773             :   }
     774             : 
     775        3852 :   if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
     776             :   // TODO(dgozman): ideally, this should be per context group.
     777         460 :   m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
     778         460 :   m_inspector->client()->maxAsyncCallStackDepthChanged(
     779         460 :       m_maxAsyncCallStackDepth);
     780         460 :   if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
     781         460 :   v8::debug::SetAsyncEventDelegate(m_isolate,
     782         920 :                                    maxAsyncCallStackDepth ? this : nullptr);
     783             : }
     784             : 
     785          35 : std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
     786             :     int contextGroupId, const V8StackTraceId& id) {
     787          70 :   if (debuggerIdFor(contextGroupId) != id.debugger_id) return nullptr;
     788          35 :   auto it = m_storedStackTraces.find(id.id);
     789          35 :   if (it == m_storedStackTraces.end()) return nullptr;
     790             :   return it->second.lock();
     791             : }
     792             : 
     793          60 : V8StackTraceId V8Debugger::storeCurrentStackTrace(
     794             :     const StringView& description) {
     795          60 :   if (!m_maxAsyncCallStackDepth) return V8StackTraceId();
     796             : 
     797         120 :   v8::HandleScope scope(m_isolate);
     798          60 :   int contextGroupId = currentContextGroupId();
     799          60 :   if (!contextGroupId) return V8StackTraceId();
     800             : 
     801             :   std::shared_ptr<AsyncStackTrace> asyncStack =
     802         120 :       AsyncStackTrace::capture(this, contextGroupId, toString16(description),
     803         120 :                                V8StackTraceImpl::maxCallStackSizeToCapture);
     804          60 :   if (!asyncStack) return V8StackTraceId();
     805             : 
     806         120 :   uintptr_t id = AsyncStackTrace::store(this, asyncStack);
     807             : 
     808          60 :   m_allAsyncStacks.push_back(std::move(asyncStack));
     809          60 :   ++m_asyncStacksCount;
     810          60 :   collectOldAsyncStacksIfNeeded();
     811             : 
     812          60 :   asyncTaskCandidateForStepping(reinterpret_cast<void*>(id), false);
     813             : 
     814          60 :   return V8StackTraceId(id, debuggerIdFor(contextGroupId));
     815             : }
     816             : 
     817         150 : uintptr_t V8Debugger::storeStackTrace(
     818             :     std::shared_ptr<AsyncStackTrace> asyncStack) {
     819         150 :   uintptr_t id = ++m_lastStackTraceId;
     820             :   m_storedStackTraces[id] = asyncStack;
     821         150 :   return id;
     822             : }
     823             : 
     824          60 : void V8Debugger::externalAsyncTaskStarted(const V8StackTraceId& parent) {
     825          60 :   if (!m_maxAsyncCallStackDepth || parent.IsInvalid()) return;
     826          55 :   m_currentExternalParent.push_back(parent);
     827          55 :   m_currentAsyncParent.emplace_back();
     828         110 :   m_currentTasks.push_back(reinterpret_cast<void*>(parent.id));
     829             : 
     830          55 :   if (m_breakRequested) return;
     831         110 :   if (!m_taskWithScheduledBreakDebuggerId.isEmpty() &&
     832          65 :       reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) == parent.id &&
     833             :       m_taskWithScheduledBreakDebuggerId ==
     834          75 :           debuggerIdToString(parent.debugger_id)) {
     835          10 :     v8::debug::SetBreakOnNextFunctionCall(m_isolate);
     836             :   }
     837             : }
     838             : 
     839          60 : void V8Debugger::externalAsyncTaskFinished(const V8StackTraceId& parent) {
     840          60 :   if (!m_maxAsyncCallStackDepth || m_currentExternalParent.empty()) return;
     841             :   m_currentExternalParent.pop_back();
     842             :   m_currentAsyncParent.pop_back();
     843             :   DCHECK(m_currentTasks.back() == reinterpret_cast<void*>(parent.id));
     844             :   m_currentTasks.pop_back();
     845             : 
     846          70 :   if (m_taskWithScheduledBreakDebuggerId.isEmpty() ||
     847          35 :       reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) != parent.id ||
     848             :       m_taskWithScheduledBreakDebuggerId !=
     849          35 :           debuggerIdToString(parent.debugger_id)) {
     850             :     return;
     851             :   }
     852           0 :   m_taskWithScheduledBreak = nullptr;
     853           0 :   m_taskWithScheduledBreakDebuggerId = String16();
     854           0 :   if (m_breakRequested) return;
     855           0 :   v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
     856             : }
     857             : 
     858        2020 : void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
     859             :                                     bool recurring) {
     860        4040 :   asyncTaskScheduledForStack(toString16(taskName), task, recurring);
     861        2020 :   asyncTaskCandidateForStepping(task, true);
     862        2020 : }
     863             : 
     864           0 : void V8Debugger::asyncTaskCanceled(void* task) {
     865           0 :   asyncTaskCanceledForStack(task);
     866             :   asyncTaskCanceledForStepping(task);
     867           0 : }
     868             : 
     869        2020 : void V8Debugger::asyncTaskStarted(void* task) {
     870        2020 :   asyncTaskStartedForStack(task);
     871        2020 :   asyncTaskStartedForStepping(task);
     872        2020 : }
     873             : 
     874        2020 : void V8Debugger::asyncTaskFinished(void* task) {
     875        2020 :   asyncTaskFinishedForStepping(task);
     876        2020 :   asyncTaskFinishedForStack(task);
     877        2020 : }
     878             : 
     879        4050 : void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
     880             :                                             void* task, bool recurring) {
     881        4710 :   if (!m_maxAsyncCallStackDepth) return;
     882        6780 :   v8::HandleScope scope(m_isolate);
     883             :   std::shared_ptr<AsyncStackTrace> asyncStack =
     884             :       AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
     885        3390 :                                V8StackTraceImpl::maxCallStackSizeToCapture);
     886        3390 :   if (asyncStack) {
     887             :     m_asyncTaskStacks[task] = asyncStack;
     888        3315 :     if (recurring) m_recurringTasks.insert(task);
     889        3315 :     m_allAsyncStacks.push_back(std::move(asyncStack));
     890        3315 :     ++m_asyncStacksCount;
     891        3315 :     collectOldAsyncStacksIfNeeded();
     892             :   }
     893             : }
     894             : 
     895        3495 : void V8Debugger::asyncTaskCanceledForStack(void* task) {
     896        3495 :   if (!m_maxAsyncCallStackDepth) return;
     897             :   m_asyncTaskStacks.erase(task);
     898             :   m_recurringTasks.erase(task);
     899             : }
     900             : 
     901        4135 : void V8Debugger::asyncTaskStartedForStack(void* task) {
     902        4135 :   if (!m_maxAsyncCallStackDepth) return;
     903             :   // Needs to support following order of events:
     904             :   // - asyncTaskScheduled
     905             :   //   <-- attached here -->
     906             :   // - asyncTaskStarted
     907             :   // - asyncTaskCanceled <-- canceled before finished
     908             :   //   <-- async stack requested here -->
     909             :   // - asyncTaskFinished
     910        3470 :   m_currentTasks.push_back(task);
     911             :   AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
     912        6815 :   if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
     913             :     std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
     914        3345 :     stack->setSuspendedTaskId(nullptr);
     915        3345 :     m_currentAsyncParent.push_back(stack);
     916             :   } else {
     917         125 :     m_currentAsyncParent.emplace_back();
     918             :   }
     919        3470 :   m_currentExternalParent.emplace_back();
     920             : }
     921             : 
     922        4120 : void V8Debugger::asyncTaskFinishedForStack(void* task) {
     923        4120 :   if (!m_maxAsyncCallStackDepth) return;
     924             :   // We could start instrumenting half way and the stack is empty.
     925        3430 :   if (!m_currentTasks.size()) return;
     926             :   DCHECK(m_currentTasks.back() == task);
     927             :   m_currentTasks.pop_back();
     928             : 
     929             :   m_currentAsyncParent.pop_back();
     930             :   m_currentExternalParent.pop_back();
     931             : 
     932        3430 :   if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
     933        3330 :     asyncTaskCanceledForStack(task);
     934             :   }
     935             : }
     936             : 
     937        3915 : void V8Debugger::asyncTaskCandidateForStepping(void* task, bool isLocal) {
     938        3915 :   if (!m_pauseOnAsyncCall) return;
     939          95 :   int contextGroupId = currentContextGroupId();
     940          95 :   if (contextGroupId != m_targetContextGroupId) return;
     941          95 :   if (isLocal) {
     942         170 :     m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
     943             :         reinterpret_cast<uintptr_t>(task), std::make_pair(0, 0));
     944             :   } else {
     945          20 :     m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
     946             :         reinterpret_cast<uintptr_t>(task), debuggerIdFor(contextGroupId));
     947             :   }
     948             :   breakProgram(m_targetContextGroupId);
     949         190 :   m_scheduledAsyncCall = v8_inspector::V8StackTraceId();
     950             : }
     951             : 
     952        4135 : void V8Debugger::asyncTaskStartedForStepping(void* task) {
     953        4135 :   if (m_breakRequested) return;
     954             :   // TODO(kozyatinskiy): we should search task in async chain to support
     955             :   // blackboxing.
     956        8270 :   if (m_taskWithScheduledBreakDebuggerId.isEmpty() &&
     957        4135 :       task == m_taskWithScheduledBreak) {
     958         110 :     v8::debug::SetBreakOnNextFunctionCall(m_isolate);
     959             :   }
     960             : }
     961             : 
     962        4120 : void V8Debugger::asyncTaskFinishedForStepping(void* task) {
     963        8240 :   if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
     964        4120 :       task != m_taskWithScheduledBreak) {
     965             :     return;
     966             :   }
     967           5 :   m_taskWithScheduledBreak = nullptr;
     968           5 :   if (m_breakRequested) return;
     969           5 :   v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
     970             : }
     971             : 
     972           0 : void V8Debugger::asyncTaskCanceledForStepping(void* task) {
     973           0 :   if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
     974           0 :       task != m_taskWithScheduledBreak)
     975             :     return;
     976           0 :   m_taskWithScheduledBreak = nullptr;
     977             : }
     978             : 
     979         215 : void V8Debugger::allAsyncTasksCanceled() {
     980             :   m_asyncTaskStacks.clear();
     981             :   m_recurringTasks.clear();
     982         215 :   m_currentAsyncParent.clear();
     983             :   m_currentExternalParent.clear();
     984             :   m_currentTasks.clear();
     985             : 
     986             :   m_framesCache.clear();
     987             :   m_allAsyncStacks.clear();
     988         215 :   m_asyncStacksCount = 0;
     989         215 : }
     990             : 
     991          25 : void V8Debugger::muteScriptParsedEvents() {
     992          25 :   ++m_ignoreScriptParsedEventsCounter;
     993          25 : }
     994             : 
     995          25 : void V8Debugger::unmuteScriptParsedEvents() {
     996          25 :   --m_ignoreScriptParsedEventsCounter;
     997             :   DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
     998          25 : }
     999             : 
    1000        6970 : std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
    1001             :     bool fullStack) {
    1002        6970 :   if (!m_isolate->InContext()) return nullptr;
    1003             : 
    1004       13940 :   v8::HandleScope handles(m_isolate);
    1005        6970 :   int contextGroupId = currentContextGroupId();
    1006        6970 :   if (!contextGroupId) return nullptr;
    1007             : 
    1008        6970 :   int stackSize = 1;
    1009        6970 :   if (fullStack) {
    1010          95 :     stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
    1011             :   } else {
    1012       13750 :     m_inspector->forEachSession(
    1013        1355 :         contextGroupId, [&stackSize](V8InspectorSessionImpl* session) {
    1014        6930 :           if (session->runtimeAgent()->enabled())
    1015        1355 :             stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
    1016        6875 :         });
    1017             :   }
    1018        6970 :   return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
    1019             : }
    1020             : 
    1021       10935 : int V8Debugger::currentContextGroupId() {
    1022       10935 :   if (!m_isolate->InContext()) return 0;
    1023       21670 :   v8::HandleScope handleScope(m_isolate);
    1024       10835 :   return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
    1025             : }
    1026             : 
    1027        3675 : void V8Debugger::collectOldAsyncStacksIfNeeded() {
    1028        3675 :   if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
    1029             :   int halfOfLimitRoundedUp =
    1030         315 :       m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
    1031        1475 :   while (m_asyncStacksCount > halfOfLimitRoundedUp) {
    1032         580 :     m_allAsyncStacks.pop_front();
    1033         580 :     --m_asyncStacksCount;
    1034             :   }
    1035         315 :   cleanupExpiredWeakPointers(m_asyncTaskStacks);
    1036         315 :   cleanupExpiredWeakPointers(m_storedStackTraces);
    1037         315 :   for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
    1038           0 :     if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
    1039             :       it = m_recurringTasks.erase(it);
    1040             :     } else {
    1041             :       ++it;
    1042             :     }
    1043             :   }
    1044         315 :   cleanupExpiredWeakPointers(m_framesCache);
    1045             : }
    1046             : 
    1047       61368 : std::shared_ptr<StackFrame> V8Debugger::symbolize(
    1048             :     v8::Local<v8::StackFrame> v8Frame) {
    1049             :   auto it = m_framesCache.end();
    1050       61368 :   int frameId = 0;
    1051       61368 :   if (m_maxAsyncCallStackDepth) {
    1052        5755 :     frameId = v8::debug::GetStackFrameId(v8Frame);
    1053             :     it = m_framesCache.find(frameId);
    1054             :   }
    1055       62778 :   if (it != m_framesCache.end() && !it->second.expired()) {
    1056             :     return std::shared_ptr<StackFrame>(it->second);
    1057             :   }
    1058      120116 :   std::shared_ptr<StackFrame> frame(new StackFrame(isolate(), v8Frame));
    1059             :   // TODO(clemensh): Figure out a way to do this translation only right before
    1060             :   // sending the stack trace over wire.
    1061       60058 :   if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation);
    1062       60058 :   if (m_maxAsyncCallStackDepth) {
    1063             :     m_framesCache[frameId] = frame;
    1064             :   }
    1065             :   return frame;
    1066             : }
    1067             : 
    1068         300 : void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
    1069         300 :   m_maxAsyncCallStacks = 0;
    1070         300 :   collectOldAsyncStacksIfNeeded();
    1071         300 :   m_maxAsyncCallStacks = limit;
    1072         300 : }
    1073             : 
    1074        3611 : std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(int contextGroupId) {
    1075             :   auto it = m_contextGroupIdToDebuggerId.find(contextGroupId);
    1076        3611 :   if (it != m_contextGroupIdToDebuggerId.end()) return it->second;
    1077             :   std::pair<int64_t, int64_t> debuggerId(
    1078        3300 :       v8::debug::GetNextRandomInt64(m_isolate),
    1079        3300 :       v8::debug::GetNextRandomInt64(m_isolate));
    1080        3300 :   if (!debuggerId.first && !debuggerId.second) ++debuggerId.first;
    1081             :   m_contextGroupIdToDebuggerId.insert(
    1082        3300 :       it, std::make_pair(contextGroupId, debuggerId));
    1083             :   m_serializedDebuggerIdToDebuggerId.insert(
    1084        9900 :       std::make_pair(debuggerIdToString(debuggerId), debuggerId));
    1085        3300 :   return debuggerId;
    1086             : }
    1087             : 
    1088          25 : std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
    1089             :     const String16& serializedDebuggerId) {
    1090             :   auto it = m_serializedDebuggerIdToDebuggerId.find(serializedDebuggerId);
    1091          25 :   if (it != m_serializedDebuggerIdToDebuggerId.end()) return it->second;
    1092           0 :   return std::make_pair(0, 0);
    1093             : }
    1094             : 
    1095       10832 : bool V8Debugger::addInternalObject(v8::Local<v8::Context> context,
    1096             :                                    v8::Local<v8::Object> object,
    1097             :                                    V8InternalValueType type) {
    1098       10832 :   int contextId = InspectedContext::contextId(context);
    1099       10832 :   InspectedContext* inspectedContext = m_inspector->getContext(contextId);
    1100             :   return inspectedContext ? inspectedContext->addInternalObject(object, type)
    1101       10832 :                           : false;
    1102             : }
    1103             : 
    1104          30 : void V8Debugger::dumpAsyncTaskStacksStateForTest() {
    1105          30 :   fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount);
    1106          30 :   fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
    1107          30 :   fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size());
    1108          30 :   fprintf(stdout, "\n");
    1109          30 : }
    1110             : 
    1111             : }  // namespace v8_inspector

Generated by: LCOV version 1.10