LCOV - code coverage report
Current view: top level - src/inspector - v8-debugger.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 520 557 93.4 %
Date: 2019-02-19 Functions: 80 88 90.9 %

          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       12670 :   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          85 : 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      298942 :   bool Filter(v8::Local<v8::Object> object) override {
      45      298942 :     v8::Local<v8::Context> objectContext = object->CreationContext();
      46      298942 :     if (objectContext != m_context) return false;
      47       66142 :     if (!m_inspector->client()->isInspectableHeapObject(object)) return false;
      48             :     // Get prototype chain for current object until first visited prototype.
      49      192430 :     for (v8::Local<v8::Value> prototype = object->GetPrototype();
      50             :          prototype->IsObject();
      51             :          prototype = prototype.As<v8::Object>()->GetPrototype()) {
      52      126393 :       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             : }  // namespace
      63             : 
      64        3638 : V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
      65             :     : m_isolate(isolate),
      66             :       m_inspector(inspector),
      67             :       m_enableCount(0),
      68             :       m_ignoreScriptParsedEventsCounter(0),
      69             :       m_continueToLocationBreakpointId(kNoBreakpointId),
      70             :       m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
      71             :       m_maxAsyncCallStackDepth(0),
      72             :       m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
      73       40018 :       m_wasmTranslation(isolate) {}
      74             : 
      75       14552 : V8Debugger::~V8Debugger() {
      76             :   m_isolate->RemoveCallCompletedCallback(
      77        3638 :       &V8Debugger::terminateExecutionCompletedCallback);
      78             :   m_isolate->RemoveMicrotasksCompletedCallback(
      79        3638 :       &V8Debugger::terminateExecutionCompletedCallback);
      80        7276 : }
      81             : 
      82        3467 : void V8Debugger::enable() {
      83        3541 :   if (m_enableCount++) return;
      84        3393 :   v8::HandleScope scope(m_isolate);
      85        3393 :   v8::debug::SetDebugDelegate(m_isolate, this);
      86        3393 :   m_isolate->AddNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback, this);
      87        3393 :   v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
      88        3393 :   m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
      89             : }
      90             : 
      91        3467 : void V8Debugger::disable() {
      92        3467 :   if (isPaused()) {
      93         287 :     bool scheduledOOMBreak = m_scheduledOOMBreak;
      94         287 :     bool hasAgentAcceptsPause = false;
      95             :     m_inspector->forEachSession(
      96             :         m_pausedContextGroupId, [&scheduledOOMBreak, &hasAgentAcceptsPause](
      97         297 :                                     V8InspectorSessionImpl* session) {
      98         594 :           if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
      99          15 :             hasAgentAcceptsPause = true;
     100             :           }
     101        1143 :         });
     102         559 :     if (!hasAgentAcceptsPause) m_inspector->client()->quitMessageLoopOnPause();
     103             :   }
     104        6934 :   if (--m_enableCount) return;
     105        3393 :   clearContinueToLocation();
     106        3393 :   m_taskWithScheduledBreak = nullptr;
     107             :   m_taskWithScheduledBreakDebuggerId = String16();
     108        3393 :   m_pauseOnAsyncCall = false;
     109        3393 :   m_wasmTranslation.Clear();
     110        3393 :   v8::debug::SetDebugDelegate(m_isolate, nullptr);
     111             :   m_isolate->RemoveNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback,
     112        3393 :                                          m_originalHeapLimit);
     113        3393 :   m_originalHeapLimit = 0;
     114             : }
     115             : 
     116      117587 : bool V8Debugger::isPausedInContextGroup(int contextGroupId) const {
     117      117587 :   return isPaused() && m_pausedContextGroupId == contextGroupId;
     118             : }
     119             : 
     120      143742 : bool V8Debugger::enabled() const { return m_enableCount > 0; }
     121             : 
     122        3467 : void V8Debugger::getCompiledScripts(
     123             :     int contextGroupId,
     124             :     std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
     125        3467 :   v8::HandleScope scope(m_isolate);
     126        6934 :   v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate);
     127        3467 :   v8::debug::GetLoadedScripts(m_isolate, scripts);
     128       74594 :   for (size_t i = 0; i < scripts.Size(); ++i) {
     129       33830 :     v8::Local<v8::debug::Script> script = scripts.Get(i);
     130       33830 :     if (!script->WasCompiled()) continue;
     131       33727 :     if (script->IsEmbedded()) {
     132             :       result.push_back(V8DebuggerScript::Create(m_isolate, script, false,
     133       33537 :                                                 m_inspector->client()));
     134           5 :       continue;
     135             :     }
     136             :     int contextId;
     137       67444 :     if (!script->ContextId().To(&contextId)) continue;
     138       33722 :     if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
     139             :     result.push_back(V8DebuggerScript::Create(m_isolate, script, false,
     140      100581 :                                               m_inspector->client()));
     141        3467 :   }
     142        3467 : }
     143             : 
     144        7036 : void V8Debugger::setBreakpointsActive(bool active) {
     145        7036 :   if (!enabled()) {
     146           0 :     UNREACHABLE();
     147             :     return;
     148             :   }
     149        7036 :   m_breakpointsActiveCount += active ? 1 : -1;
     150        7036 :   v8::debug::SetBreakPointsActive(m_isolate, m_breakpointsActiveCount);
     151        7036 : }
     152             : 
     153      135826 : v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
     154             :   DCHECK(enabled());
     155      135826 :   return m_pauseOnExceptionsState;
     156             : }
     157             : 
     158        5114 : void V8Debugger::setPauseOnExceptionsState(
     159             :     v8::debug::ExceptionBreakState pauseOnExceptionsState) {
     160             :   DCHECK(enabled());
     161       10228 :   if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
     162        4080 :   v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
     163        4080 :   m_pauseOnExceptionsState = pauseOnExceptionsState;
     164             : }
     165             : 
     166         385 : void V8Debugger::setPauseOnNextCall(bool pause, int targetContextGroupId) {
     167         385 :   if (isPaused()) return;
     168             :   DCHECK(targetContextGroupId);
     169         385 :   if (!pause && m_targetContextGroupId &&
     170             :       m_targetContextGroupId != targetContextGroupId) {
     171             :     return;
     172             :   }
     173         385 :   m_targetContextGroupId = targetContextGroupId;
     174         385 :   m_breakRequested = pause;
     175         385 :   if (pause)
     176         365 :     v8::debug::SetBreakOnNextFunctionCall(m_isolate);
     177             :   else
     178          20 :     v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
     179             : }
     180             : 
     181         330 : bool V8Debugger::canBreakProgram() {
     182         370 :   return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
     183             : }
     184             : 
     185         155 : void V8Debugger::breakProgram(int targetContextGroupId) {
     186             :   DCHECK(canBreakProgram());
     187             :   // Don't allow nested breaks.
     188         215 :   if (isPaused()) return;
     189             :   DCHECK(targetContextGroupId);
     190          60 :   m_targetContextGroupId = targetContextGroupId;
     191         155 :   v8::debug::BreakRightNow(m_isolate);
     192             : }
     193             : 
     194           0 : void V8Debugger::interruptAndBreak(int targetContextGroupId) {
     195             :   // Don't allow nested breaks.
     196           0 :   if (isPaused()) return;
     197             :   DCHECK(targetContextGroupId);
     198           0 :   m_targetContextGroupId = targetContextGroupId;
     199             :   m_isolate->RequestInterrupt(
     200           0 :       [](v8::Isolate* isolate, void*) { v8::debug::BreakRightNow(isolate); },
     201           0 :       nullptr);
     202             : }
     203             : 
     204        2189 : void V8Debugger::continueProgram(int targetContextGroupId) {
     205       48881 :   if (m_pausedContextGroupId != targetContextGroupId) return;
     206       46692 :   if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
     207             : }
     208             : 
     209         120 : void V8Debugger::breakProgramOnAssert(int targetContextGroupId) {
     210          80 :   if (!enabled()) return;
     211          60 :   if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException) return;
     212             :   // Don't allow nested breaks.
     213          40 :   if (isPaused()) return;
     214          40 :   if (!canBreakProgram()) return;
     215             :   DCHECK(targetContextGroupId);
     216          35 :   m_targetContextGroupId = targetContextGroupId;
     217          35 :   m_scheduledAssertBreak = true;
     218          35 :   v8::debug::BreakRightNow(m_isolate);
     219             : }
     220             : 
     221       36289 : void V8Debugger::stepIntoStatement(int targetContextGroupId,
     222             :                                    bool breakOnAsyncCall) {
     223             :   DCHECK(isPaused());
     224             :   DCHECK(targetContextGroupId);
     225       72578 :   if (asyncStepOutOfFunction(targetContextGroupId, true)) return;
     226       36279 :   m_targetContextGroupId = targetContextGroupId;
     227       36279 :   m_pauseOnAsyncCall = breakOnAsyncCall;
     228       36279 :   v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
     229             :   continueProgram(targetContextGroupId);
     230             : }
     231             : 
     232        7626 : void V8Debugger::stepOverStatement(int targetContextGroupId) {
     233             :   DCHECK(isPaused());
     234             :   DCHECK(targetContextGroupId);
     235       15252 :   if (asyncStepOutOfFunction(targetContextGroupId, true)) return;
     236        7616 :   m_targetContextGroupId = targetContextGroupId;
     237        7616 :   v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
     238             :   continueProgram(targetContextGroupId);
     239             : }
     240             : 
     241         528 : void V8Debugger::stepOutOfFunction(int targetContextGroupId) {
     242             :   DCHECK(isPaused());
     243             :   DCHECK(targetContextGroupId);
     244        1056 :   if (asyncStepOutOfFunction(targetContextGroupId, false)) return;
     245         508 :   m_targetContextGroupId = targetContextGroupId;
     246         508 :   v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
     247             :   continueProgram(targetContextGroupId);
     248             : }
     249             : 
     250       44443 : bool V8Debugger::asyncStepOutOfFunction(int targetContextGroupId,
     251             :                                         bool onlyAtReturn) {
     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         652 :   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         100 :       std::shared_ptr<AsyncStackTrace>(parent)->suspendedTaskId();
     275          50 :   if (!parentTask) return false;
     276             :   pauseOnAsyncCall(targetContextGroupId,
     277          80 :                    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          40 :     return;
     299             :   }
     300             :   m_terminateExecutionCallback = std::move(callback);
     301             :   m_isolate->AddCallCompletedCallback(
     302          40 :       &V8Debugger::terminateExecutionCompletedCallback);
     303             :   m_isolate->AddMicrotasksCompletedCallback(
     304          40 :       &V8Debugger::terminateExecutionCompletedCallback);
     305          40 :   m_isolate->TerminateExecution();
     306             : }
     307             : 
     308          66 : void V8Debugger::reportTermination() {
     309         132 :   if (!m_terminateExecutionCallback) return;
     310             :   m_isolate->RemoveCallCompletedCallback(
     311          40 :       &V8Debugger::terminateExecutionCompletedCallback);
     312             :   m_isolate->RemoveMicrotasksCompletedCallback(
     313          40 :       &V8Debugger::terminateExecutionCompletedCallback);
     314          40 :   m_isolate->CancelTerminateExecution();
     315          40 :   m_terminateExecutionCallback->sendSuccess();
     316             :   m_terminateExecutionCallback.reset();
     317             : }
     318             : 
     319          30 : 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          30 : }
     325             : 
     326          60 : Response V8Debugger::continueToLocation(
     327             :     int targetContextGroupId, V8DebuggerScript* script,
     328             :     std::unique_ptr<protocol::Debugger::Location> location,
     329             :     const String16& targetCallFrames) {
     330             :   DCHECK(isPaused());
     331             :   DCHECK(targetContextGroupId);
     332          60 :   m_targetContextGroupId = targetContextGroupId;
     333             :   v8::debug::Location v8Location(location->getLineNumber(),
     334          60 :                                  location->getColumnNumber(0));
     335         120 :   if (script->setBreakpoint(String16(), &v8Location,
     336         120 :                             &m_continueToLocationBreakpointId)) {
     337             :     m_continueToLocationTargetCallFrames = targetCallFrames;
     338         120 :     if (m_continueToLocationTargetCallFrames !=
     339             :         protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
     340          30 :       m_continueToLocationStack = captureStackTrace(true);
     341             :       DCHECK(m_continueToLocationStack);
     342             :     }
     343             :     continueProgram(targetContextGroupId);
     344             :     // TODO(kozyatinskiy): Return actual line and column number.
     345          60 :     return Response::OK();
     346             :   } else {
     347           0 :     return Response::Error("Cannot continue to specified location");
     348             :   }
     349             : }
     350             : 
     351          90 : bool V8Debugger::shouldContinueToCurrentLocation() {
     352         180 :   if (m_continueToLocationTargetCallFrames ==
     353             :       protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
     354             :     return true;
     355             :   }
     356          45 :   std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true);
     357          90 :   if (m_continueToLocationTargetCallFrames ==
     358             :       protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
     359             :     return m_continueToLocationStack->isEqualIgnoringTopFrame(
     360          45 :         currentStack.get());
     361             :   }
     362             :   return true;
     363             : }
     364             : 
     365       58704 : void V8Debugger::clearContinueToLocation() {
     366      117408 :   if (m_continueToLocationBreakpointId == kNoBreakpointId) return;
     367          60 :   v8::debug::RemoveBreakpoint(m_isolate, m_continueToLocationBreakpointId);
     368          60 :   m_continueToLocationBreakpointId = kNoBreakpointId;
     369             :   m_continueToLocationTargetCallFrames = String16();
     370             :   m_continueToLocationStack.reset();
     371             : }
     372             : 
     373       55371 : void V8Debugger::handleProgramBreak(
     374             :     v8::Local<v8::Context> pausedContext, v8::Local<v8::Value> exception,
     375       55341 :     const std::vector<v8::debug::BreakpointId>& breakpointIds,
     376       55371 :     v8::debug::ExceptionType exceptionType, bool isUncaught) {
     377             :   // Don't allow nested breaks.
     378       55431 :   if (isPaused()) return;
     379             : 
     380      110682 :   int contextGroupId = m_inspector->contextGroupId(pausedContext);
     381       55371 :   if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
     382          20 :     v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
     383          20 :     return;
     384             :   }
     385       55351 :   m_targetContextGroupId = 0;
     386       55351 :   m_breakRequested = false;
     387       55351 :   m_pauseOnAsyncCall = false;
     388       55351 :   m_taskWithScheduledBreak = nullptr;
     389             :   m_taskWithScheduledBreakDebuggerId = String16();
     390             : 
     391       55351 :   bool scheduledOOMBreak = m_scheduledOOMBreak;
     392       55351 :   bool scheduledAssertBreak = m_scheduledAssertBreak;
     393       55351 :   bool hasAgents = false;
     394             :   m_inspector->forEachSession(
     395             :       contextGroupId,
     396       55461 :       [&scheduledOOMBreak, &hasAgents](V8InspectorSessionImpl* session) {
     397      110922 :         if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak))
     398       55436 :           hasAgents = true;
     399      166163 :       });
     400       55351 :   if (!hasAgents) return;
     401             : 
     402       57775 :   if (breakpointIds.size() == 1 &&
     403        2434 :       breakpointIds[0] == m_continueToLocationBreakpointId) {
     404             :     v8::Context::Scope contextScope(pausedContext);
     405          90 :     if (!shouldContinueToCurrentLocation()) return;
     406             :   }
     407       55311 :   clearContinueToLocation();
     408             : 
     409             :   DCHECK(contextGroupId);
     410       55311 :   m_pausedContextGroupId = contextGroupId;
     411             : 
     412             :   m_inspector->forEachSession(
     413             :       contextGroupId, [&pausedContext, &exception, &breakpointIds,
     414             :                        &exceptionType, &isUncaught, &scheduledOOMBreak,
     415       55416 :                        &scheduledAssertBreak](V8InspectorSessionImpl* session) {
     416      110832 :         if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
     417             :           session->debuggerAgent()->didPause(
     418             :               InspectedContext::contextId(pausedContext), exception,
     419             :               breakpointIds, exceptionType, isUncaught, scheduledOOMBreak,
     420      110812 :               scheduledAssertBreak);
     421             :         }
     422      166038 :       });
     423             :   {
     424             :     v8::Context::Scope scope(pausedContext);
     425      110622 :     m_inspector->client()->runMessageLoopOnPause(contextGroupId);
     426       55311 :     m_pausedContextGroupId = 0;
     427             :   }
     428             :   m_inspector->forEachSession(contextGroupId,
     429             :                               [](V8InspectorSessionImpl* session) {
     430       55416 :                                 if (session->debuggerAgent()->enabled())
     431       55144 :                                   session->debuggerAgent()->didContinue();
     432      110622 :                               });
     433             : 
     434       55311 :   if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
     435       55311 :   m_scheduledOOMBreak = false;
     436       55311 :   m_scheduledAssertBreak = false;
     437             : }
     438             : 
     439             : namespace {
     440             : 
     441             : size_t HeapLimitForDebugging(size_t initial_heap_limit) {
     442             :   const size_t kDebugHeapSizeFactor = 4;
     443           5 :   size_t max_limit = std::numeric_limits<size_t>::max() / 4;
     444          10 :   return std::min(max_limit, initial_heap_limit * kDebugHeapSizeFactor);
     445             : }
     446             : 
     447             : }  // anonymous namespace
     448             : 
     449           5 : size_t V8Debugger::nearHeapLimitCallback(void* data, size_t current_heap_limit,
     450             :                                          size_t initial_heap_limit) {
     451             :   V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
     452           5 :   thisPtr->m_originalHeapLimit = current_heap_limit;
     453           5 :   thisPtr->m_scheduledOOMBreak = true;
     454             :   v8::Local<v8::Context> context =
     455           5 :       thisPtr->m_isolate->GetEnteredOrMicrotaskContext();
     456             :   thisPtr->m_targetContextGroupId =
     457           5 :       context.IsEmpty() ? 0 : thisPtr->m_inspector->contextGroupId(context);
     458             :   thisPtr->m_isolate->RequestInterrupt(
     459           5 :       [](v8::Isolate* isolate, void*) { v8::debug::BreakRightNow(isolate); },
     460           5 :       nullptr);
     461           5 :   return HeapLimitForDebugging(initial_heap_limit);
     462             : }
     463             : 
     464       26878 : void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
     465             :                                 bool is_live_edited, bool has_compile_error) {
     466             :   int contextId;
     467       80634 :   if (!script->ContextId().To(&contextId)) return;
     468       26990 :   if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
     469          52 :     WasmTranslation* wasmTranslation = &m_wasmTranslation;
     470             :     m_inspector->forEachSession(
     471             :         m_inspector->contextGroupId(contextId),
     472          52 :         [&script, &wasmTranslation](V8InspectorSessionImpl* session) {
     473         104 :           if (!session->debuggerAgent()->enabled()) return;
     474             :           wasmTranslation->AddScript(script.As<v8::debug::WasmScript>(),
     475         104 :                                      session->debuggerAgent());
     476       26905 :         });
     477       26826 :   } else if (m_ignoreScriptParsedEventsCounter == 0) {
     478       26801 :     v8::Isolate* isolate = m_isolate;
     479       53602 :     V8InspectorClient* client = m_inspector->client();
     480             :     m_inspector->forEachSession(
     481             :         m_inspector->contextGroupId(contextId),
     482             :         [&isolate, &script, &has_compile_error, &is_live_edited,
     483       26916 :          &client](V8InspectorSessionImpl* session) {
     484       53832 :           if (!session->debuggerAgent()->enabled()) return;
     485             :           session->debuggerAgent()->didParseSource(
     486             :               V8DebuggerScript::Create(isolate, script, is_live_edited, client),
     487       80733 :               !has_compile_error);
     488       53602 :         });
     489             :   }
     490             : }
     491             : 
     492       53487 : void V8Debugger::BreakProgramRequested(
     493             :     v8::Local<v8::Context> pausedContext,
     494             :     const std::vector<v8::debug::BreakpointId>& break_points_hit) {
     495       53487 :   handleProgramBreak(pausedContext, v8::Local<v8::Value>(), break_points_hit);
     496       53487 : }
     497             : 
     498        1884 : void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
     499             :                                  v8::Local<v8::Value> exception,
     500             :                                  v8::Local<v8::Value> promise, bool isUncaught,
     501             :                                  v8::debug::ExceptionType exceptionType) {
     502             :   std::vector<v8::debug::BreakpointId> break_points_hit;
     503             :   handleProgramBreak(pausedContext, exception, break_points_hit, exceptionType,
     504        1884 :                      isUncaught);
     505        1884 : }
     506             : 
     507       11115 : bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
     508             :                                       const v8::debug::Location& start,
     509             :                                       const v8::debug::Location& end) {
     510             :   int contextId;
     511       22230 :   if (!script->ContextId().To(&contextId)) return false;
     512       11110 :   bool hasAgents = false;
     513       11110 :   bool allBlackboxed = true;
     514       11110 :   String16 scriptId = String16::fromInteger(script->Id());
     515             :   m_inspector->forEachSession(
     516             :       m_inspector->contextGroupId(contextId),
     517             :       [&hasAgents, &allBlackboxed, &scriptId, &start,
     518       11175 :        &end](V8InspectorSessionImpl* session) {
     519       11175 :         V8DebuggerAgentImpl* agent = session->debuggerAgent();
     520       22350 :         if (!agent->enabled()) return;
     521       11170 :         hasAgents = true;
     522       11170 :         allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
     523       22220 :       });
     524       11110 :   return hasAgents && allBlackboxed;
     525             : }
     526             : 
     527        7230 : void V8Debugger::AsyncEventOccurred(v8::debug::DebugAsyncActionType type,
     528             :                                     int id, bool isBlackboxed) {
     529             :   // Async task events from Promises are given misaligned pointers to prevent
     530             :   // from overlapping with other Blink task identifiers.
     531        7230 :   void* task = reinterpret_cast<void*>(id * 2 + 1);
     532        7230 :   switch (type) {
     533             :     case v8::debug::kDebugPromiseThen:
     534        4110 :       asyncTaskScheduledForStack("Promise.then", task, false);
     535        2055 :       if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
     536             :       break;
     537             :     case v8::debug::kDebugPromiseCatch:
     538          60 :       asyncTaskScheduledForStack("Promise.catch", task, false);
     539          30 :       if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
     540             :       break;
     541             :     case v8::debug::kDebugPromiseFinally:
     542          20 :       asyncTaskScheduledForStack("Promise.finally", task, false);
     543          10 :       if (!isBlackboxed) asyncTaskCandidateForStepping(task, true);
     544             :       break;
     545             :     case v8::debug::kDebugWillHandle:
     546        2355 :       asyncTaskStartedForStack(task);
     547        2355 :       asyncTaskStartedForStepping(task);
     548        2355 :       break;
     549             :     case v8::debug::kDebugDidHandle:
     550        2340 :       asyncTaskFinishedForStack(task);
     551        2340 :       asyncTaskFinishedForStepping(task);
     552        2340 :       break;
     553             :     case v8::debug::kAsyncFunctionSuspended: {
     554         275 :       if (m_asyncTaskStacks.find(task) == m_asyncTaskStacks.end()) {
     555         350 :         asyncTaskScheduledForStack("async function", task, true);
     556             :       }
     557             :       auto stackIt = m_asyncTaskStacks.find(task);
     558         550 :       if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
     559             :         std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
     560         275 :         stack->setSuspendedTaskId(task);
     561             :       }
     562             :       break;
     563             :     }
     564             :     case v8::debug::kAsyncFunctionFinished:
     565         165 :       asyncTaskCanceledForStack(task);
     566         165 :       break;
     567             :   }
     568        7230 : }
     569             : 
     570      127365 : std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
     571      254730 :   return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
     572             : }
     573             : 
     574      126713 : V8StackTraceId V8Debugger::currentExternalParent() {
     575             :   return m_currentExternalParent.empty() ? V8StackTraceId()
     576      126713 :                                          : m_currentExternalParent.back();
     577             : }
     578             : 
     579         110 : v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
     580             :     v8::Local<v8::Context> context, v8::Local<v8::Value> value,
     581             :     ScopeTargetKind kind) {
     582             :   v8::Local<v8::Value> scopesValue;
     583             :   std::unique_ptr<v8::debug::ScopeIterator> iterator;
     584         110 :   switch (kind) {
     585             :     case FUNCTION:
     586         120 :       iterator = v8::debug::ScopeIterator::CreateForFunction(
     587             :           m_isolate, v8::Local<v8::Function>::Cast(value));
     588         160 :       break;
     589             :     case GENERATOR:
     590             :       v8::Local<v8::debug::GeneratorObject> generatorObject =
     591          50 :           v8::debug::GeneratorObject::Cast(value);
     592          50 :       if (!generatorObject->IsSuspended()) return v8::MaybeLocal<v8::Value>();
     593             : 
     594          80 :       iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
     595             :           m_isolate, v8::Local<v8::Object>::Cast(value));
     596          40 :       break;
     597             :   }
     598         100 :   if (!iterator) return v8::MaybeLocal<v8::Value>();
     599          95 :   v8::Local<v8::Array> result = v8::Array::New(m_isolate);
     600         285 :   if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) {
     601           0 :     return v8::MaybeLocal<v8::Value>();
     602             :   }
     603             : 
     604         150 :   for (; !iterator->Done(); iterator->Advance()) {
     605         150 :     v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
     606         150 :     if (!addInternalObject(context, scope, V8InternalValueType::kScope))
     607           0 :       return v8::MaybeLocal<v8::Value>();
     608             :     String16 nameSuffix = toProtocolStringWithTypeCheck(
     609         150 :         m_isolate, iterator->GetFunctionDebugName());
     610             :     String16 description;
     611         270 :     if (nameSuffix.length()) nameSuffix = " (" + nameSuffix + ")";
     612         150 :     switch (iterator->GetType()) {
     613             :       case v8::debug::ScopeIterator::ScopeTypeGlobal:
     614         190 :         description = "Global" + nameSuffix;
     615          95 :         break;
     616             :       case v8::debug::ScopeIterator::ScopeTypeLocal:
     617          80 :         description = "Local" + nameSuffix;
     618          40 :         break;
     619             :       case v8::debug::ScopeIterator::ScopeTypeWith:
     620          10 :         description = "With Block" + nameSuffix;
     621           5 :         break;
     622             :       case v8::debug::ScopeIterator::ScopeTypeClosure:
     623          10 :         description = "Closure" + nameSuffix;
     624           5 :         break;
     625             :       case v8::debug::ScopeIterator::ScopeTypeCatch:
     626          10 :         description = "Catch" + nameSuffix;
     627           5 :         break;
     628             :       case v8::debug::ScopeIterator::ScopeTypeBlock:
     629           0 :         description = "Block" + nameSuffix;
     630           0 :         break;
     631             :       case v8::debug::ScopeIterator::ScopeTypeScript:
     632           0 :         description = "Script" + nameSuffix;
     633           0 :         break;
     634             :       case v8::debug::ScopeIterator::ScopeTypeEval:
     635           0 :         description = "Eval" + nameSuffix;
     636           0 :         break;
     637             :       case v8::debug::ScopeIterator::ScopeTypeModule:
     638           0 :         description = "Module" + nameSuffix;
     639           0 :         break;
     640             :     }
     641         150 :     v8::Local<v8::Object> object = iterator->GetObject();
     642             :     createDataProperty(context, scope,
     643             :                        toV8StringInternalized(m_isolate, "description"),
     644         450 :                        toV8String(m_isolate, description));
     645             :     createDataProperty(context, scope,
     646         300 :                        toV8StringInternalized(m_isolate, "object"), object);
     647         150 :     createDataProperty(context, result, result->Length(), scope);
     648             :   }
     649          95 :   if (!addInternalObject(context, result, V8InternalValueType::kScopeList))
     650           0 :     return v8::MaybeLocal<v8::Value>();
     651          95 :   return result;
     652             : }
     653             : 
     654           0 : v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
     655             :     v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
     656          60 :   return getTargetScopes(context, function, FUNCTION);
     657             : }
     658             : 
     659           0 : v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
     660             :     v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
     661          50 :   return getTargetScopes(context, generator, GENERATOR);
     662             : }
     663             : 
     664       79153 : v8::MaybeLocal<v8::Array> V8Debugger::collectionsEntries(
     665             :     v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
     666       79153 :   v8::Isolate* isolate = context->GetIsolate();
     667             :   v8::Local<v8::Array> entries;
     668       79153 :   bool isKeyValue = false;
     669      158306 :   if (!value->IsObject() ||
     670       79153 :       !value.As<v8::Object>()->PreviewEntries(&isKeyValue).ToLocal(&entries)) {
     671       78794 :     return v8::MaybeLocal<v8::Array>();
     672             :   }
     673             : 
     674         359 :   v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
     675         359 :   CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
     676         359 :   if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
     677         718 :            .FromMaybe(false))
     678           0 :     return v8::MaybeLocal<v8::Array>();
     679       10587 :   for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
     680             :     v8::Local<v8::Value> item;
     681       21174 :     if (!entries->Get(context, i).ToLocal(&item)) continue;
     682             :     v8::Local<v8::Value> value;
     683       15807 :     if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
     684       10587 :     v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
     685       21174 :     if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
     686             :       continue;
     687             :     createDataProperty(
     688             :         context, wrapper,
     689       21174 :         toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
     690       10587 :     if (isKeyValue) {
     691             :       createDataProperty(context, wrapper,
     692       10440 :                          toV8StringInternalized(isolate, "value"), value);
     693             :     }
     694       10587 :     if (!addInternalObject(context, wrapper, V8InternalValueType::kEntry))
     695             :       continue;
     696       10587 :     createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
     697       10587 :                        wrapper);
     698             :   }
     699         359 :   return wrappedEntries;
     700             : }
     701             : 
     702       79153 : v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
     703             :     v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
     704             :   v8::Local<v8::Array> properties;
     705      158306 :   if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
     706           0 :     return v8::MaybeLocal<v8::Array>();
     707             :   v8::Local<v8::Array> entries;
     708      158306 :   if (collectionsEntries(context, value).ToLocal(&entries)) {
     709         359 :     createDataProperty(context, properties, properties->Length(),
     710         718 :                        toV8StringInternalized(m_isolate, "[[Entries]]"));
     711         359 :     createDataProperty(context, properties, properties->Length(), entries);
     712             :   }
     713       79153 :   if (value->IsGeneratorObject()) {
     714             :     v8::Local<v8::Value> scopes;
     715          50 :     if (generatorScopes(context, value).ToLocal(&scopes)) {
     716          40 :       createDataProperty(context, properties, properties->Length(),
     717          80 :                          toV8StringInternalized(m_isolate, "[[Scopes]]"));
     718          40 :       createDataProperty(context, properties, properties->Length(), scopes);
     719             :     }
     720             :   }
     721       79153 :   if (value->IsFunction()) {
     722             :     v8::Local<v8::Function> function = value.As<v8::Function>();
     723             :     v8::Local<v8::Value> scopes;
     724          60 :     if (functionScopes(context, function).ToLocal(&scopes)) {
     725          55 :       createDataProperty(context, properties, properties->Length(),
     726         110 :                          toV8StringInternalized(m_isolate, "[[Scopes]]"));
     727          55 :       createDataProperty(context, properties, properties->Length(), scopes);
     728             :     }
     729             :   }
     730       79153 :   return properties;
     731             : }
     732             : 
     733          85 : v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
     734             :                                               v8::Local<v8::Object> prototype) {
     735          85 :   v8::Isolate* isolate = context->GetIsolate();
     736             :   v8::PersistentValueVector<v8::Object> v8Objects(isolate);
     737         170 :   MatchPrototypePredicate predicate(m_inspector, context, prototype);
     738          85 :   v8::debug::QueryObjects(context, &predicate, &v8Objects);
     739             : 
     740             :   v8::MicrotasksScope microtasksScope(isolate,
     741         170 :                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
     742             :   v8::Local<v8::Array> resultArray = v8::Array::New(
     743         170 :       m_inspector->isolate(), static_cast<int>(v8Objects.Size()));
     744         370 :   for (size_t i = 0; i < v8Objects.Size(); ++i) {
     745             :     createDataProperty(context, resultArray, static_cast<int>(i),
     746         200 :                        v8Objects.Get(i));
     747             :   }
     748         170 :   return resultArray;
     749             : }
     750             : 
     751         400 : std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
     752             :     v8::Local<v8::StackTrace> v8StackTrace) {
     753             :   return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
     754         400 :                                   V8StackTraceImpl::maxCallStackSizeToCapture);
     755             : }
     756             : 
     757        3832 : void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
     758        3832 :   if (depth <= 0)
     759             :     m_maxAsyncCallStackDepthMap.erase(agent);
     760             :   else
     761         295 :     m_maxAsyncCallStackDepthMap[agent] = depth;
     762             : 
     763             :   int maxAsyncCallStackDepth = 0;
     764        7979 :   for (const auto& pair : m_maxAsyncCallStackDepthMap) {
     765         315 :     if (pair.second > maxAsyncCallStackDepth)
     766             :       maxAsyncCallStackDepth = pair.second;
     767             :   }
     768             : 
     769        7664 :   if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
     770             :   // TODO(dgozman): ideally, this should be per context group.
     771         460 :   m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
     772         460 :   m_inspector->client()->maxAsyncCallStackDepthChanged(
     773         460 :       m_maxAsyncCallStackDepth);
     774         460 :   if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
     775             :   v8::debug::SetAsyncEventDelegate(m_isolate,
     776         460 :                                    maxAsyncCallStackDepth ? this : nullptr);
     777             : }
     778             : 
     779          35 : std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
     780             :     int contextGroupId, const V8StackTraceId& id) {
     781          70 :   if (debuggerIdFor(contextGroupId) != id.debugger_id) return nullptr;
     782          35 :   auto it = m_storedStackTraces.find(id.id);
     783          35 :   if (it == m_storedStackTraces.end()) return nullptr;
     784          35 :   return it->second.lock();
     785             : }
     786             : 
     787          60 : V8StackTraceId V8Debugger::storeCurrentStackTrace(
     788             :     const StringView& description) {
     789          60 :   if (!m_maxAsyncCallStackDepth) return V8StackTraceId();
     790             : 
     791          60 :   v8::HandleScope scope(m_isolate);
     792          60 :   int contextGroupId = currentContextGroupId();
     793          60 :   if (!contextGroupId) return V8StackTraceId();
     794             : 
     795             :   std::shared_ptr<AsyncStackTrace> asyncStack =
     796             :       AsyncStackTrace::capture(this, contextGroupId, toString16(description),
     797         120 :                                V8StackTraceImpl::maxCallStackSizeToCapture);
     798          60 :   if (!asyncStack) return V8StackTraceId();
     799             : 
     800         120 :   uintptr_t id = AsyncStackTrace::store(this, asyncStack);
     801             : 
     802          60 :   m_allAsyncStacks.push_back(std::move(asyncStack));
     803          60 :   ++m_asyncStacksCount;
     804          60 :   collectOldAsyncStacksIfNeeded();
     805             : 
     806          60 :   asyncTaskCandidateForStepping(reinterpret_cast<void*>(id), false);
     807             : 
     808         120 :   return V8StackTraceId(id, debuggerIdFor(contextGroupId));
     809             : }
     810             : 
     811         140 : uintptr_t V8Debugger::storeStackTrace(
     812             :     std::shared_ptr<AsyncStackTrace> asyncStack) {
     813         140 :   uintptr_t id = ++m_lastStackTraceId;
     814             :   m_storedStackTraces[id] = asyncStack;
     815         140 :   return id;
     816             : }
     817             : 
     818          60 : void V8Debugger::externalAsyncTaskStarted(const V8StackTraceId& parent) {
     819          60 :   if (!m_maxAsyncCallStackDepth || parent.IsInvalid()) return;
     820          55 :   m_currentExternalParent.push_back(parent);
     821          55 :   m_currentAsyncParent.emplace_back();
     822         110 :   m_currentTasks.push_back(reinterpret_cast<void*>(parent.id));
     823             : 
     824          55 :   if (m_breakRequested) return;
     825         110 :   if (!m_taskWithScheduledBreakDebuggerId.isEmpty() &&
     826          65 :       reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) == parent.id &&
     827          75 :       m_taskWithScheduledBreakDebuggerId ==
     828             :           debuggerIdToString(parent.debugger_id)) {
     829          10 :     v8::debug::SetBreakOnNextFunctionCall(m_isolate);
     830             :   }
     831             : }
     832             : 
     833          60 : void V8Debugger::externalAsyncTaskFinished(const V8StackTraceId& parent) {
     834          95 :   if (!m_maxAsyncCallStackDepth || m_currentExternalParent.empty()) return;
     835             :   m_currentExternalParent.pop_back();
     836             :   m_currentAsyncParent.pop_back();
     837             :   DCHECK(m_currentTasks.back() == reinterpret_cast<void*>(parent.id));
     838             :   m_currentTasks.pop_back();
     839             : 
     840          70 :   if (m_taskWithScheduledBreakDebuggerId.isEmpty() ||
     841          35 :       reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) != parent.id ||
     842          35 :       m_taskWithScheduledBreakDebuggerId !=
     843             :           debuggerIdToString(parent.debugger_id)) {
     844             :     return;
     845             :   }
     846           0 :   m_taskWithScheduledBreak = nullptr;
     847             :   m_taskWithScheduledBreakDebuggerId = String16();
     848           0 :   if (m_breakRequested) return;
     849           0 :   v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
     850             : }
     851             : 
     852        1860 : void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
     853             :                                     bool recurring) {
     854        3720 :   asyncTaskScheduledForStack(toString16(taskName), task, recurring);
     855        1860 :   asyncTaskCandidateForStepping(task, true);
     856        1860 : }
     857             : 
     858           0 : void V8Debugger::asyncTaskCanceled(void* task) {
     859           0 :   asyncTaskCanceledForStack(task);
     860             :   asyncTaskCanceledForStepping(task);
     861           0 : }
     862             : 
     863        1860 : void V8Debugger::asyncTaskStarted(void* task) {
     864        1860 :   asyncTaskStartedForStack(task);
     865        1860 :   asyncTaskStartedForStepping(task);
     866        1860 : }
     867             : 
     868        1860 : void V8Debugger::asyncTaskFinished(void* task) {
     869        1860 :   asyncTaskFinishedForStepping(task);
     870        1860 :   asyncTaskFinishedForStack(task);
     871        1860 : }
     872             : 
     873        4130 : void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
     874             :                                             void* task, bool recurring) {
     875        4750 :   if (!m_maxAsyncCallStackDepth) return;
     876        3510 :   v8::HandleScope scope(m_isolate);
     877             :   std::shared_ptr<AsyncStackTrace> asyncStack =
     878             :       AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
     879        3510 :                                V8StackTraceImpl::maxCallStackSizeToCapture);
     880        3510 :   if (asyncStack) {
     881             :     m_asyncTaskStacks[task] = asyncStack;
     882        3435 :     if (recurring) m_recurringTasks.insert(task);
     883        3435 :     m_allAsyncStacks.push_back(std::move(asyncStack));
     884        3435 :     ++m_asyncStacksCount;
     885        3435 :     collectOldAsyncStacksIfNeeded();
     886        3510 :   }
     887             : }
     888             : 
     889        3615 : void V8Debugger::asyncTaskCanceledForStack(void* task) {
     890        7230 :   if (!m_maxAsyncCallStackDepth) return;
     891             :   m_asyncTaskStacks.erase(task);
     892             :   m_recurringTasks.erase(task);
     893             : }
     894             : 
     895        4215 : void V8Debugger::asyncTaskStartedForStack(void* task) {
     896        8430 :   if (!m_maxAsyncCallStackDepth) return;
     897             :   // Needs to support following order of events:
     898             :   // - asyncTaskScheduled
     899             :   //   <-- attached here -->
     900             :   // - asyncTaskStarted
     901             :   // - asyncTaskCanceled <-- canceled before finished
     902             :   //   <-- async stack requested here -->
     903             :   // - asyncTaskFinished
     904        3590 :   m_currentTasks.push_back(task);
     905             :   AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
     906        7055 :   if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
     907             :     std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
     908        3465 :     stack->setSuspendedTaskId(nullptr);
     909        3465 :     m_currentAsyncParent.push_back(stack);
     910             :   } else {
     911         125 :     m_currentAsyncParent.emplace_back();
     912             :   }
     913        3590 :   m_currentExternalParent.emplace_back();
     914             : }
     915             : 
     916        4200 : void V8Debugger::asyncTaskFinishedForStack(void* task) {
     917        4200 :   if (!m_maxAsyncCallStackDepth) return;
     918             :   // We could start instrumenting half way and the stack is empty.
     919        7100 :   if (!m_currentTasks.size()) return;
     920             :   DCHECK(m_currentTasks.back() == task);
     921             :   m_currentTasks.pop_back();
     922             : 
     923             :   m_currentAsyncParent.pop_back();
     924             :   m_currentExternalParent.pop_back();
     925             : 
     926        3550 :   if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
     927        3450 :     asyncTaskCanceledForStack(task);
     928             :   }
     929             : }
     930             : 
     931        3995 : void V8Debugger::asyncTaskCandidateForStepping(void* task, bool isLocal) {
     932        3995 :   if (!m_pauseOnAsyncCall) return;
     933          95 :   int contextGroupId = currentContextGroupId();
     934          95 :   if (contextGroupId != m_targetContextGroupId) return;
     935          95 :   if (isLocal) {
     936         170 :     m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
     937             :         reinterpret_cast<uintptr_t>(task), std::make_pair(0, 0));
     938             :   } else {
     939          20 :     m_scheduledAsyncCall = v8_inspector::V8StackTraceId(
     940             :         reinterpret_cast<uintptr_t>(task), debuggerIdFor(contextGroupId));
     941             :   }
     942             :   breakProgram(m_targetContextGroupId);
     943         190 :   m_scheduledAsyncCall = v8_inspector::V8StackTraceId();
     944             : }
     945             : 
     946        4215 : void V8Debugger::asyncTaskStartedForStepping(void* task) {
     947        8430 :   if (m_breakRequested) return;
     948             :   // TODO(kozyatinskiy): we should search task in async chain to support
     949             :   // blackboxing.
     950        8430 :   if (m_taskWithScheduledBreakDebuggerId.isEmpty() &&
     951        4215 :       task == m_taskWithScheduledBreak) {
     952         110 :     v8::debug::SetBreakOnNextFunctionCall(m_isolate);
     953             :   }
     954             : }
     955             : 
     956        4200 : void V8Debugger::asyncTaskFinishedForStepping(void* task) {
     957        8400 :   if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
     958        4200 :       task != m_taskWithScheduledBreak) {
     959             :     return;
     960             :   }
     961           5 :   m_taskWithScheduledBreak = nullptr;
     962           5 :   if (m_breakRequested) return;
     963           5 :   v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
     964             : }
     965             : 
     966           0 : void V8Debugger::asyncTaskCanceledForStepping(void* task) {
     967           0 :   if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
     968           0 :       task != m_taskWithScheduledBreak)
     969           0 :     return;
     970           0 :   m_taskWithScheduledBreak = nullptr;
     971             : }
     972             : 
     973         215 : void V8Debugger::allAsyncTasksCanceled() {
     974             :   m_asyncTaskStacks.clear();
     975             :   m_recurringTasks.clear();
     976             :   m_currentAsyncParent.clear();
     977             :   m_currentExternalParent.clear();
     978             :   m_currentTasks.clear();
     979             : 
     980             :   m_framesCache.clear();
     981             :   m_allAsyncStacks.clear();
     982         215 :   m_asyncStacksCount = 0;
     983         215 : }
     984             : 
     985          25 : void V8Debugger::muteScriptParsedEvents() {
     986          25 :   ++m_ignoreScriptParsedEventsCounter;
     987          25 : }
     988             : 
     989          25 : void V8Debugger::unmuteScriptParsedEvents() {
     990          25 :   --m_ignoreScriptParsedEventsCounter;
     991             :   DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
     992          25 : }
     993             : 
     994        6760 : std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
     995             :     bool fullStack) {
     996        6760 :   if (!m_isolate->InContext()) return nullptr;
     997             : 
     998        6760 :   v8::HandleScope handles(m_isolate);
     999        6760 :   int contextGroupId = currentContextGroupId();
    1000        6760 :   if (!contextGroupId) return nullptr;
    1001             : 
    1002        6760 :   int stackSize = 1;
    1003        6760 :   if (fullStack) {
    1004          95 :     stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
    1005             :   } else {
    1006             :     m_inspector->forEachSession(
    1007             :         contextGroupId, [&stackSize](V8InspectorSessionImpl* session) {
    1008        6720 :           if (session->runtimeAgent()->enabled())
    1009        1335 :             stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
    1010       13330 :         });
    1011             :   }
    1012        6760 :   return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
    1013             : }
    1014             : 
    1015       10825 : int V8Debugger::currentContextGroupId() {
    1016       10825 :   if (!m_isolate->InContext()) return 0;
    1017       10725 :   return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
    1018             : }
    1019             : 
    1020        3795 : void V8Debugger::collectOldAsyncStacksIfNeeded() {
    1021        7590 :   if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
    1022             :   int halfOfLimitRoundedUp =
    1023         315 :       m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
    1024        1210 :   while (m_asyncStacksCount > halfOfLimitRoundedUp) {
    1025             :     m_allAsyncStacks.pop_front();
    1026         580 :     --m_asyncStacksCount;
    1027             :   }
    1028         315 :   cleanupExpiredWeakPointers(m_asyncTaskStacks);
    1029         315 :   cleanupExpiredWeakPointers(m_storedStackTraces);
    1030         630 :   for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
    1031           0 :     if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
    1032             :       it = m_recurringTasks.erase(it);
    1033             :     } else {
    1034             :       ++it;
    1035             :     }
    1036             :   }
    1037         315 :   cleanupExpiredWeakPointers(m_framesCache);
    1038             : }
    1039             : 
    1040       66009 : std::shared_ptr<StackFrame> V8Debugger::symbolize(
    1041       64429 :     v8::Local<v8::StackFrame> v8Frame) {
    1042             :   auto it = m_framesCache.end();
    1043       66009 :   int frameId = 0;
    1044       66009 :   if (m_maxAsyncCallStackDepth) {
    1045        6025 :     frameId = v8::debug::GetStackFrameId(v8Frame);
    1046             :     it = m_framesCache.find(frameId);
    1047             :   }
    1048       67689 :   if (it != m_framesCache.end() && !it->second.expired()) {
    1049             :     return std::shared_ptr<StackFrame>(it->second);
    1050             :   }
    1051      128858 :   std::shared_ptr<StackFrame> frame(new StackFrame(isolate(), v8Frame));
    1052             :   // TODO(clemensh): Figure out a way to do this translation only right before
    1053             :   // sending the stack trace over wire.
    1054       64429 :   if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation);
    1055       64429 :   if (m_maxAsyncCallStackDepth) {
    1056             :     m_framesCache[frameId] = frame;
    1057             :   }
    1058             :   return frame;
    1059             : }
    1060             : 
    1061         300 : void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
    1062         300 :   m_maxAsyncCallStacks = 0;
    1063         300 :   collectOldAsyncStacksIfNeeded();
    1064         300 :   m_maxAsyncCallStacks = limit;
    1065         300 : }
    1066             : 
    1067        3591 : std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(int contextGroupId) {
    1068             :   auto it = m_contextGroupIdToDebuggerId.find(contextGroupId);
    1069        3591 :   if (it != m_contextGroupIdToDebuggerId.end()) return it->second;
    1070             :   std::pair<int64_t, int64_t> debuggerId(
    1071        3280 :       v8::debug::GetNextRandomInt64(m_isolate),
    1072        3280 :       v8::debug::GetNextRandomInt64(m_isolate));
    1073        3280 :   if (!debuggerId.first && !debuggerId.second) ++debuggerId.first;
    1074             :   m_contextGroupIdToDebuggerId.insert(
    1075        3280 :       it, std::make_pair(contextGroupId, debuggerId));
    1076             :   m_serializedDebuggerIdToDebuggerId.insert(
    1077        9840 :       std::make_pair(debuggerIdToString(debuggerId), debuggerId));
    1078        3280 :   return debuggerId;
    1079             : }
    1080             : 
    1081          25 : std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
    1082             :     const String16& serializedDebuggerId) {
    1083             :   auto it = m_serializedDebuggerIdToDebuggerId.find(serializedDebuggerId);
    1084          25 :   if (it != m_serializedDebuggerIdToDebuggerId.end()) return it->second;
    1085           0 :   return std::make_pair(0, 0);
    1086             : }
    1087             : 
    1088       10832 : bool V8Debugger::addInternalObject(v8::Local<v8::Context> context,
    1089             :                                    v8::Local<v8::Object> object,
    1090             :                                    V8InternalValueType type) {
    1091       10832 :   int contextId = InspectedContext::contextId(context);
    1092       10832 :   InspectedContext* inspectedContext = m_inspector->getContext(contextId);
    1093             :   return inspectedContext ? inspectedContext->addInternalObject(object, type)
    1094       10832 :                           : false;
    1095             : }
    1096             : 
    1097          30 : void V8Debugger::dumpAsyncTaskStacksStateForTest() {
    1098          30 :   fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount);
    1099          30 :   fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
    1100          30 :   fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size());
    1101          30 :   fprintf(stdout, "\n");
    1102          30 : }
    1103             : 
    1104             : }  // namespace v8_inspector

Generated by: LCOV version 1.10