5 #include "src/inspector/v8-debugger.h" 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" 17 #include "include/v8-util.h" 23 static const int kMaxAsyncTaskStacks = 128 * 1024;
24 static const int kNoBreakpointId = 0;
26 template <
typename Map>
27 void cleanupExpiredWeakPointers(Map& map) {
28 for (
auto it = map.begin(); it != map.end();) {
29 if (it->second.expired()) {
39 MatchPrototypePredicate(V8InspectorImpl* inspector,
42 : m_inspector(inspector), m_context(context), m_prototype(prototype) {}
46 if (objectContext != m_context)
return false;
47 if (!m_inspector->client()->isInspectableHeapObject(
object))
return false;
50 prototype->IsObject();
51 prototype = prototype.As<
v8::Object>()->GetPrototype()) {
52 if (m_prototype == prototype)
return true;
58 V8InspectorImpl* m_inspector;
64 V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
66 m_inspector(inspector),
68 m_ignoreScriptParsedEventsCounter(0),
69 m_continueToLocationBreakpointId(kNoBreakpointId),
70 m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
71 m_maxAsyncCallStackDepth(0),
72 m_pauseOnExceptionsState(
v8::debug::NoBreakOnException),
73 m_wasmTranslation(isolate) {}
75 V8Debugger::~V8Debugger() {
76 m_isolate->RemoveCallCompletedCallback(
77 &V8Debugger::terminateExecutionCompletedCallback);
78 m_isolate->RemoveMicrotasksCompletedCallback(
79 &V8Debugger::terminateExecutionCompletedCallback);
82 void V8Debugger::enable() {
83 if (m_enableCount++)
return;
85 v8::debug::SetDebugDelegate(m_isolate,
this);
86 m_isolate->AddNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback,
this);
87 v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
88 m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
91 void V8Debugger::disable() {
93 bool scheduledOOMBreak = m_scheduledOOMBreak;
94 bool hasAgentAcceptsPause =
false;
95 m_inspector->forEachSession(
96 m_pausedContextGroupId, [&scheduledOOMBreak, &hasAgentAcceptsPause](
97 V8InspectorSessionImpl* session) {
98 if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
99 hasAgentAcceptsPause =
true;
102 if (!hasAgentAcceptsPause) m_inspector->client()->quitMessageLoopOnPause();
104 if (--m_enableCount)
return;
105 clearContinueToLocation();
106 m_taskWithScheduledBreak =
nullptr;
107 m_taskWithScheduledBreakDebuggerId = String16();
108 m_pauseOnAsyncCall =
false;
109 m_wasmTranslation.Clear();
110 v8::debug::SetDebugDelegate(m_isolate,
nullptr);
111 m_isolate->RemoveNearHeapLimitCallback(&V8Debugger::nearHeapLimitCallback,
112 m_originalHeapLimit);
113 m_originalHeapLimit = 0;
116 bool V8Debugger::isPausedInContextGroup(
int contextGroupId)
const {
117 return isPaused() && m_pausedContextGroupId == contextGroupId;
120 bool V8Debugger::enabled()
const {
return m_enableCount > 0; }
122 void V8Debugger::getCompiledScripts(
124 std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
127 v8::debug::GetLoadedScripts(m_isolate, scripts);
128 for (
size_t i = 0;
i < scripts.Size(); ++
i) {
130 if (!script->WasCompiled())
continue;
131 if (script->IsEmbedded()) {
132 result.push_back(V8DebuggerScript::Create(m_isolate, script,
false,
133 m_inspector->client()));
137 if (!script->ContextId().To(&contextId))
continue;
138 if (m_inspector->contextGroupId(contextId) != contextGroupId)
continue;
139 result.push_back(V8DebuggerScript::Create(m_isolate, script,
false,
140 m_inspector->client()));
144 void V8Debugger::setBreakpointsActive(
bool active) {
149 m_breakpointsActiveCount += active ? 1 : -1;
150 v8::debug::SetBreakPointsActive(m_isolate, m_breakpointsActiveCount);
153 v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
155 return m_pauseOnExceptionsState;
158 void V8Debugger::setPauseOnExceptionsState(
159 v8::debug::ExceptionBreakState pauseOnExceptionsState) {
161 if (m_pauseOnExceptionsState == pauseOnExceptionsState)
return;
162 v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
163 m_pauseOnExceptionsState = pauseOnExceptionsState;
166 void V8Debugger::setPauseOnNextCall(
bool pause,
int targetContextGroupId) {
167 if (isPaused())
return;
168 DCHECK(targetContextGroupId);
169 if (!pause && m_targetContextGroupId &&
170 m_targetContextGroupId != targetContextGroupId) {
173 m_targetContextGroupId = targetContextGroupId;
174 m_breakRequested = pause;
176 v8::debug::SetBreakOnNextFunctionCall(m_isolate);
178 v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
181 bool V8Debugger::canBreakProgram() {
182 return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
185 void V8Debugger::breakProgram(
int targetContextGroupId) {
186 DCHECK(canBreakProgram());
188 if (isPaused())
return;
189 DCHECK(targetContextGroupId);
190 m_targetContextGroupId = targetContextGroupId;
191 v8::debug::BreakRightNow(m_isolate);
194 void V8Debugger::interruptAndBreak(
int targetContextGroupId) {
196 if (isPaused())
return;
197 DCHECK(targetContextGroupId);
198 m_targetContextGroupId = targetContextGroupId;
199 m_isolate->RequestInterrupt(
200 [](v8::Isolate* isolate,
void*) { v8::debug::BreakRightNow(isolate); },
204 void V8Debugger::continueProgram(
int targetContextGroupId) {
205 if (m_pausedContextGroupId != targetContextGroupId)
return;
206 if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
209 void V8Debugger::breakProgramOnAssert(
int targetContextGroupId) {
210 if (!enabled())
return;
211 if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException)
return;
213 if (isPaused())
return;
214 if (!canBreakProgram())
return;
215 DCHECK(targetContextGroupId);
216 m_targetContextGroupId = targetContextGroupId;
217 m_scheduledAssertBreak =
true;
218 v8::debug::BreakRightNow(m_isolate);
221 void V8Debugger::stepIntoStatement(
int targetContextGroupId,
222 bool breakOnAsyncCall) {
224 DCHECK(targetContextGroupId);
225 if (asyncStepOutOfFunction(targetContextGroupId,
true))
return;
226 m_targetContextGroupId = targetContextGroupId;
227 m_pauseOnAsyncCall = breakOnAsyncCall;
228 v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
229 continueProgram(targetContextGroupId);
232 void V8Debugger::stepOverStatement(
int targetContextGroupId) {
234 DCHECK(targetContextGroupId);
235 if (asyncStepOutOfFunction(targetContextGroupId,
true))
return;
236 m_targetContextGroupId = targetContextGroupId;
237 v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
238 continueProgram(targetContextGroupId);
241 void V8Debugger::stepOutOfFunction(
int targetContextGroupId) {
243 DCHECK(targetContextGroupId);
244 if (asyncStepOutOfFunction(targetContextGroupId,
false))
return;
245 m_targetContextGroupId = targetContextGroupId;
246 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
247 continueProgram(targetContextGroupId);
250 bool V8Debugger::asyncStepOutOfFunction(
int targetContextGroupId,
252 auto iterator = v8::debug::StackTraceIterator::Create(m_isolate);
253 DCHECK(!iterator->Done());
254 bool atReturn = !iterator->GetReturnValue().IsEmpty();
257 if (!iterator->Done())
return false;
260 if (onlyAtReturn && !atReturn)
return false;
265 auto current = currentAsyncParent();
266 if (!current)
return false;
268 auto parent = current->parent();
269 if (parent.expired())
return false;
274 std::shared_ptr<AsyncStackTrace>(parent)->suspendedTaskId();
275 if (!parentTask)
return false;
276 pauseOnAsyncCall(targetContextGroupId,
277 reinterpret_cast<uintptr_t>(parentTask), String16());
278 continueProgram(targetContextGroupId);
282 void V8Debugger::scheduleStepIntoAsync(
283 std::unique_ptr<ScheduleStepIntoAsyncCallback> callback,
284 int targetContextGroupId) {
286 DCHECK(targetContextGroupId);
287 if (m_stepIntoAsyncCallback) {
288 m_stepIntoAsyncCallback->sendFailure(Response::Error(
289 "Current scheduled step into async was overriden with new one."));
291 m_targetContextGroupId = targetContextGroupId;
292 m_stepIntoAsyncCallback = std::move(callback);
295 void V8Debugger::pauseOnAsyncCall(
int targetContextGroupId,
uintptr_t task,
296 const String16& debuggerId) {
297 DCHECK(targetContextGroupId);
298 m_targetContextGroupId = targetContextGroupId;
300 m_taskWithScheduledBreak =
reinterpret_cast<void*
>(task);
301 m_taskWithScheduledBreakDebuggerId = debuggerId;
304 void V8Debugger::terminateExecution(
305 std::unique_ptr<TerminateExecutionCallback> callback) {
306 if (m_terminateExecutionCallback) {
308 callback->sendFailure(
309 Response::Error(
"There is current termination request in progress"));
313 m_terminateExecutionCallback = std::move(callback);
314 m_isolate->AddCallCompletedCallback(
315 &V8Debugger::terminateExecutionCompletedCallback);
316 m_isolate->AddMicrotasksCompletedCallback(
317 &V8Debugger::terminateExecutionCompletedCallback);
318 m_isolate->TerminateExecution();
321 void V8Debugger::terminateExecutionCompletedCallback(v8::Isolate* isolate) {
322 isolate->RemoveCallCompletedCallback(
323 &V8Debugger::terminateExecutionCompletedCallback);
324 isolate->RemoveMicrotasksCompletedCallback(
325 &V8Debugger::terminateExecutionCompletedCallback);
326 V8InspectorImpl* inspector =
327 static_cast<V8InspectorImpl*
>(v8::debug::GetInspector(isolate));
328 V8Debugger* debugger = inspector->debugger();
329 debugger->m_isolate->CancelTerminateExecution();
330 if (debugger->m_terminateExecutionCallback) {
331 debugger->m_terminateExecutionCallback->sendSuccess();
332 debugger->m_terminateExecutionCallback.reset();
336 Response V8Debugger::continueToLocation(
337 int targetContextGroupId, V8DebuggerScript* script,
338 std::unique_ptr<protocol::Debugger::Location> location,
339 const String16& targetCallFrames) {
341 DCHECK(targetContextGroupId);
342 m_targetContextGroupId = targetContextGroupId;
344 location->getColumnNumber(0));
345 if (script->setBreakpoint(String16(), &v8Location,
346 &m_continueToLocationBreakpointId)) {
347 m_continueToLocationTargetCallFrames = targetCallFrames;
348 if (m_continueToLocationTargetCallFrames !=
349 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
350 m_continueToLocationStack = captureStackTrace(
true);
351 DCHECK(m_continueToLocationStack);
353 continueProgram(targetContextGroupId);
355 return Response::OK();
357 return Response::Error(
"Cannot continue to specified location");
361 bool V8Debugger::shouldContinueToCurrentLocation() {
362 if (m_continueToLocationTargetCallFrames ==
363 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
366 std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(
true);
367 if (m_continueToLocationTargetCallFrames ==
368 protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
369 return m_continueToLocationStack->isEqualIgnoringTopFrame(
375 void V8Debugger::clearContinueToLocation() {
376 if (m_continueToLocationBreakpointId == kNoBreakpointId)
return;
377 v8::debug::RemoveBreakpoint(m_isolate, m_continueToLocationBreakpointId);
378 m_continueToLocationBreakpointId = kNoBreakpointId;
379 m_continueToLocationTargetCallFrames = String16();
380 m_continueToLocationStack.reset();
383 void V8Debugger::handleProgramBreak(
385 const std::vector<v8::debug::BreakpointId>& breakpointIds,
386 v8::debug::ExceptionType exceptionType,
bool isUncaught) {
388 if (isPaused())
return;
390 int contextGroupId = m_inspector->contextGroupId(pausedContext);
391 if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
392 v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
395 m_targetContextGroupId = 0;
396 if (m_stepIntoAsyncCallback) {
397 m_stepIntoAsyncCallback->sendFailure(
398 Response::Error(
"No async tasks were scheduled before pause."));
399 m_stepIntoAsyncCallback.reset();
401 m_breakRequested =
false;
402 m_pauseOnAsyncCall =
false;
403 m_taskWithScheduledBreak =
nullptr;
404 m_taskWithScheduledBreakDebuggerId = String16();
406 bool scheduledOOMBreak = m_scheduledOOMBreak;
407 bool scheduledAssertBreak = m_scheduledAssertBreak;
408 bool hasAgents =
false;
409 m_inspector->forEachSession(
411 [&scheduledOOMBreak, &hasAgents](V8InspectorSessionImpl* session) {
412 if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak))
415 if (!hasAgents)
return;
417 if (breakpointIds.size() == 1 &&
418 breakpointIds[0] == m_continueToLocationBreakpointId) {
420 if (!shouldContinueToCurrentLocation())
return;
422 clearContinueToLocation();
424 DCHECK(contextGroupId);
425 m_pausedContextGroupId = contextGroupId;
427 m_inspector->forEachSession(
428 contextGroupId, [&pausedContext, &exception, &breakpointIds,
429 &exceptionType, &isUncaught, &scheduledOOMBreak,
430 &scheduledAssertBreak](V8InspectorSessionImpl* session) {
431 if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
432 session->debuggerAgent()->didPause(
433 InspectedContext::contextId(pausedContext), exception,
434 breakpointIds, exceptionType, isUncaught, scheduledOOMBreak,
435 scheduledAssertBreak);
440 m_inspector->client()->runMessageLoopOnPause(contextGroupId);
441 m_pausedContextGroupId = 0;
443 m_inspector->forEachSession(contextGroupId,
444 [](V8InspectorSessionImpl* session) {
445 if (session->debuggerAgent()->enabled())
446 session->debuggerAgent()->didContinue();
449 if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
450 m_scheduledOOMBreak =
false;
451 m_scheduledAssertBreak =
false;
456 size_t HeapLimitForDebugging(
size_t initial_heap_limit) {
457 const size_t kDebugHeapSizeFactor = 4;
458 size_t max_limit = std::numeric_limits<size_t>::max() / 4;
459 return std::min(max_limit, initial_heap_limit * kDebugHeapSizeFactor);
464 size_t V8Debugger::nearHeapLimitCallback(
void* data,
size_t current_heap_limit,
465 size_t initial_heap_limit) {
466 V8Debugger* thisPtr =
static_cast<V8Debugger*
>(data);
467 thisPtr->m_originalHeapLimit = current_heap_limit;
468 thisPtr->m_scheduledOOMBreak =
true;
470 thisPtr->m_isolate->GetEnteredOrMicrotaskContext();
471 thisPtr->m_targetContextGroupId =
472 context.
IsEmpty() ? 0 : thisPtr->m_inspector->contextGroupId(context);
473 thisPtr->m_isolate->RequestInterrupt(
474 [](v8::Isolate* isolate,
void*) { v8::debug::BreakRightNow(isolate); },
476 return HeapLimitForDebugging(initial_heap_limit);
480 bool is_live_edited,
bool has_compile_error) {
482 if (!script->ContextId().To(&contextId))
return;
483 if (script->IsWasm() && script->SourceMappingURL().IsEmpty()) {
484 WasmTranslation* wasmTranslation = &m_wasmTranslation;
485 m_inspector->forEachSession(
486 m_inspector->contextGroupId(contextId),
487 [&script, &wasmTranslation](V8InspectorSessionImpl* session) {
488 if (!session->debuggerAgent()->enabled())
return;
490 session->debuggerAgent());
492 }
else if (m_ignoreScriptParsedEventsCounter == 0) {
493 v8::Isolate* isolate = m_isolate;
494 V8InspectorClient* client = m_inspector->client();
495 m_inspector->forEachSession(
496 m_inspector->contextGroupId(contextId),
497 [&isolate, &script, &has_compile_error, &is_live_edited,
498 &client](V8InspectorSessionImpl* session) {
499 if (!session->debuggerAgent()->enabled())
return;
500 session->debuggerAgent()->didParseSource(
501 V8DebuggerScript::Create(isolate, script, is_live_edited, client),
507 void V8Debugger::BreakProgramRequested(
509 const std::vector<v8::debug::BreakpointId>& break_points_hit) {
516 v8::debug::ExceptionType exceptionType) {
517 std::vector<v8::debug::BreakpointId> break_points_hit;
518 handleProgramBreak(pausedContext, exception, break_points_hit, exceptionType,
526 if (!script->ContextId().To(&contextId))
return false;
527 bool hasAgents =
false;
528 bool allBlackboxed =
true;
529 String16 scriptId = String16::fromInteger(script->Id());
530 m_inspector->forEachSession(
531 m_inspector->contextGroupId(contextId),
532 [&hasAgents, &allBlackboxed, &scriptId, &start,
533 &end](V8InspectorSessionImpl* session) {
534 V8DebuggerAgentImpl* agent = session->debuggerAgent();
535 if (!agent->enabled())
return;
537 allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
539 return hasAgents && allBlackboxed;
542 void V8Debugger::AsyncEventOccurred(v8::debug::DebugAsyncActionType type,
543 int id,
bool isBlackboxed) {
546 void* task =
reinterpret_cast<void*
>(
id * 2 + 1);
548 case v8::debug::kDebugPromiseThen:
549 asyncTaskScheduledForStack(
"Promise.then", task,
false);
550 if (!isBlackboxed) asyncTaskCandidateForStepping(task,
true);
552 case v8::debug::kDebugPromiseCatch:
553 asyncTaskScheduledForStack(
"Promise.catch", task,
false);
554 if (!isBlackboxed) asyncTaskCandidateForStepping(task,
true);
556 case v8::debug::kDebugPromiseFinally:
557 asyncTaskScheduledForStack(
"Promise.finally", task,
false);
558 if (!isBlackboxed) asyncTaskCandidateForStepping(task,
true);
560 case v8::debug::kDebugWillHandle:
561 asyncTaskStartedForStack(task);
562 asyncTaskStartedForStepping(task);
564 case v8::debug::kDebugDidHandle:
565 asyncTaskFinishedForStack(task);
566 asyncTaskFinishedForStepping(task);
568 case v8::debug::kAsyncFunctionSuspended: {
569 if (m_asyncTaskStacks.find(task) == m_asyncTaskStacks.end()) {
570 asyncTaskScheduledForStack(
"async function", task,
true);
572 auto stackIt = m_asyncTaskStacks.find(task);
573 if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
574 std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
575 stack->setSuspendedTaskId(task);
579 case v8::debug::kAsyncFunctionFinished:
580 asyncTaskCanceledForStack(task);
585 std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
586 return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
589 V8StackTraceId V8Debugger::currentExternalParent() {
590 return m_currentExternalParent.empty() ? V8StackTraceId()
591 : m_currentExternalParent.back();
596 ScopeTargetKind kind) {
598 std::unique_ptr<v8::debug::ScopeIterator> iterator;
601 iterator = v8::debug::ScopeIterator::CreateForFunction(
606 v8::debug::GeneratorObject::Cast(value);
609 iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
615 if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(
false)) {
619 for (; !iterator->Done(); iterator->Advance()) {
621 if (!addInternalObject(context, scope, V8InternalValueType::kScope))
623 String16 nameSuffix = toProtocolStringWithTypeCheck(
624 m_isolate, iterator->GetFunctionDebugName());
625 String16 description;
626 if (nameSuffix.length()) nameSuffix =
" (" + nameSuffix +
")";
627 switch (iterator->GetType()) {
628 case v8::debug::ScopeIterator::ScopeTypeGlobal:
629 description =
"Global" + nameSuffix;
631 case v8::debug::ScopeIterator::ScopeTypeLocal:
632 description =
"Local" + nameSuffix;
634 case v8::debug::ScopeIterator::ScopeTypeWith:
635 description =
"With Block" + nameSuffix;
637 case v8::debug::ScopeIterator::ScopeTypeClosure:
638 description =
"Closure" + nameSuffix;
640 case v8::debug::ScopeIterator::ScopeTypeCatch:
641 description =
"Catch" + nameSuffix;
643 case v8::debug::ScopeIterator::ScopeTypeBlock:
644 description =
"Block" + nameSuffix;
646 case v8::debug::ScopeIterator::ScopeTypeScript:
647 description =
"Script" + nameSuffix;
649 case v8::debug::ScopeIterator::ScopeTypeEval:
650 description =
"Eval" + nameSuffix;
652 case v8::debug::ScopeIterator::ScopeTypeModule:
653 description =
"Module" + nameSuffix;
657 createDataProperty(context, scope,
658 toV8StringInternalized(m_isolate,
"description"),
659 toV8String(m_isolate, description));
660 createDataProperty(context, scope,
661 toV8StringInternalized(m_isolate,
"object"),
object);
662 createDataProperty(context, result, result->Length(), scope);
664 if (!addInternalObject(context, result, V8InternalValueType::kScopeList))
671 return getTargetScopes(context,
function, FUNCTION);
676 return getTargetScopes(context, generator, GENERATOR);
681 v8::Isolate* isolate = context->GetIsolate();
683 bool isKeyValue =
false;
684 if (!value->IsObject() ||
685 !value.As<
v8::Object>()->PreviewEntries(&isKeyValue).ToLocal(&entries)) {
690 CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
691 if (!wrappedEntries->
SetPrototype(context, v8::Null(isolate))
694 for (
uint32_t i = 0;
i < entries->Length();
i += isKeyValue ? 2 : 1) {
696 if (!entries->Get(context,
i).
ToLocal(&item))
continue;
698 if (isKeyValue && !entries->Get(context,
i + 1).
ToLocal(&value))
continue;
700 if (!wrapper->
SetPrototype(context, v8::Null(isolate)).FromMaybe(
false))
704 toV8StringInternalized(isolate, isKeyValue ?
"key" :
"value"), item);
706 createDataProperty(context, wrapper,
707 toV8StringInternalized(isolate,
"value"), value);
709 if (!addInternalObject(context, wrapper, V8InternalValueType::kEntry))
711 createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
714 return wrappedEntries;
720 if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
723 if (collectionsEntries(context, value).ToLocal(&entries)) {
724 createDataProperty(context, properties, properties->Length(),
725 toV8StringInternalized(m_isolate,
"[[Entries]]"));
726 createDataProperty(context, properties, properties->Length(), entries);
728 if (value->IsGeneratorObject()) {
730 if (generatorScopes(context, value).ToLocal(&scopes)) {
731 createDataProperty(context, properties, properties->Length(),
732 toV8StringInternalized(m_isolate,
"[[Scopes]]"));
733 createDataProperty(context, properties, properties->Length(), scopes);
736 if (value->IsFunction()) {
739 if (functionScopes(context,
function).ToLocal(&scopes)) {
740 createDataProperty(context, properties, properties->Length(),
741 toV8StringInternalized(m_isolate,
"[[Scopes]]"));
742 createDataProperty(context, properties, properties->Length(), scopes);
750 v8::Isolate* isolate = context->GetIsolate();
752 MatchPrototypePredicate predicate(m_inspector, context, prototype);
753 v8::debug::QueryObjects(context, &predicate, &v8Objects);
755 v8::MicrotasksScope microtasksScope(isolate,
756 v8::MicrotasksScope::kDoNotRunMicrotasks);
758 m_inspector->isolate(),
static_cast<int>(v8Objects.Size()));
759 for (
size_t i = 0;
i < v8Objects.Size(); ++
i) {
760 createDataProperty(context, resultArray, static_cast<int>(
i),
766 std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
768 return V8StackTraceImpl::create(
this, currentContextGroupId(), v8StackTrace,
769 V8StackTraceImpl::maxCallStackSizeToCapture);
772 void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent,
int depth) {
774 m_maxAsyncCallStackDepthMap.erase(agent);
776 m_maxAsyncCallStackDepthMap[agent] = depth;
778 int maxAsyncCallStackDepth = 0;
779 for (
const auto& pair : m_maxAsyncCallStackDepthMap) {
780 if (pair.second > maxAsyncCallStackDepth)
781 maxAsyncCallStackDepth = pair.second;
784 if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth)
return;
786 m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
787 m_inspector->client()->maxAsyncCallStackDepthChanged(
788 m_maxAsyncCallStackDepth);
789 if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
790 v8::debug::SetAsyncEventDelegate(m_isolate,
791 maxAsyncCallStackDepth ?
this :
nullptr);
794 std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
795 int contextGroupId,
const V8StackTraceId&
id) {
796 if (debuggerIdFor(contextGroupId) !=
id.debugger_id)
return nullptr;
797 auto it = m_storedStackTraces.find(
id.
id);
798 if (it == m_storedStackTraces.end())
return nullptr;
799 return it->second.lock();
802 V8StackTraceId V8Debugger::storeCurrentStackTrace(
803 const StringView& description) {
804 if (!m_maxAsyncCallStackDepth)
return V8StackTraceId();
807 int contextGroupId = currentContextGroupId();
808 if (!contextGroupId)
return V8StackTraceId();
810 std::shared_ptr<AsyncStackTrace> asyncStack =
811 AsyncStackTrace::capture(
this, contextGroupId, toString16(description),
812 V8StackTraceImpl::maxCallStackSizeToCapture);
813 if (!asyncStack)
return V8StackTraceId();
815 uintptr_t id = AsyncStackTrace::store(
this, asyncStack);
817 m_allAsyncStacks.push_back(std::move(asyncStack));
818 ++m_asyncStacksCount;
819 collectOldAsyncStacksIfNeeded();
821 asyncTaskCandidateForStepping(reinterpret_cast<void*>(
id),
false);
823 return V8StackTraceId(
id, debuggerIdFor(contextGroupId));
827 std::shared_ptr<AsyncStackTrace> asyncStack) {
829 m_storedStackTraces[id] = asyncStack;
833 void V8Debugger::externalAsyncTaskStarted(
const V8StackTraceId& parent) {
834 if (!m_maxAsyncCallStackDepth || parent.IsInvalid())
return;
835 m_currentExternalParent.push_back(parent);
836 m_currentAsyncParent.emplace_back();
837 m_currentTasks.push_back(reinterpret_cast<void*>(parent.id));
839 if (m_breakRequested)
return;
840 if (!m_taskWithScheduledBreakDebuggerId.isEmpty() &&
841 reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) == parent.id &&
842 m_taskWithScheduledBreakDebuggerId ==
843 debuggerIdToString(parent.debugger_id)) {
844 v8::debug::SetBreakOnNextFunctionCall(m_isolate);
848 void V8Debugger::externalAsyncTaskFinished(
const V8StackTraceId& parent) {
849 if (!m_maxAsyncCallStackDepth || m_currentExternalParent.empty())
return;
850 m_currentExternalParent.pop_back();
851 m_currentAsyncParent.pop_back();
852 DCHECK(m_currentTasks.back() ==
reinterpret_cast<void*
>(parent.id));
853 m_currentTasks.pop_back();
855 if (m_taskWithScheduledBreakDebuggerId.isEmpty() ||
856 reinterpret_cast<uintptr_t>(m_taskWithScheduledBreak) != parent.id ||
857 m_taskWithScheduledBreakDebuggerId !=
858 debuggerIdToString(parent.debugger_id)) {
861 m_taskWithScheduledBreak =
nullptr;
862 m_taskWithScheduledBreakDebuggerId = String16();
863 if (m_breakRequested)
return;
864 v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
867 void V8Debugger::asyncTaskScheduled(
const StringView& taskName,
void* task,
869 asyncTaskScheduledForStack(toString16(taskName), task, recurring);
870 asyncTaskCandidateForStepping(task,
true);
873 void V8Debugger::asyncTaskCanceled(
void* task) {
874 asyncTaskCanceledForStack(task);
875 asyncTaskCanceledForStepping(task);
878 void V8Debugger::asyncTaskStarted(
void* task) {
879 asyncTaskStartedForStack(task);
880 asyncTaskStartedForStepping(task);
883 void V8Debugger::asyncTaskFinished(
void* task) {
884 asyncTaskFinishedForStepping(task);
885 asyncTaskFinishedForStack(task);
888 void V8Debugger::asyncTaskScheduledForStack(
const String16& taskName,
889 void* task,
bool recurring) {
890 if (!m_maxAsyncCallStackDepth)
return;
892 std::shared_ptr<AsyncStackTrace> asyncStack =
893 AsyncStackTrace::capture(
this, currentContextGroupId(), taskName,
894 V8StackTraceImpl::maxCallStackSizeToCapture);
896 m_asyncTaskStacks[task] = asyncStack;
897 if (recurring) m_recurringTasks.insert(task);
898 m_allAsyncStacks.push_back(std::move(asyncStack));
899 ++m_asyncStacksCount;
900 collectOldAsyncStacksIfNeeded();
904 void V8Debugger::asyncTaskCanceledForStack(
void* task) {
905 if (!m_maxAsyncCallStackDepth)
return;
906 m_asyncTaskStacks.erase(task);
907 m_recurringTasks.erase(task);
910 void V8Debugger::asyncTaskStartedForStack(
void* task) {
911 if (!m_maxAsyncCallStackDepth)
return;
919 m_currentTasks.push_back(task);
920 AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(task);
921 if (stackIt != m_asyncTaskStacks.end() && !stackIt->second.expired()) {
922 std::shared_ptr<AsyncStackTrace> stack(stackIt->second);
923 stack->setSuspendedTaskId(
nullptr);
924 m_currentAsyncParent.push_back(stack);
926 m_currentAsyncParent.emplace_back();
928 m_currentExternalParent.emplace_back();
931 void V8Debugger::asyncTaskFinishedForStack(
void* task) {
932 if (!m_maxAsyncCallStackDepth)
return;
934 if (!m_currentTasks.size())
return;
935 DCHECK(m_currentTasks.back() == task);
936 m_currentTasks.pop_back();
938 m_currentAsyncParent.pop_back();
939 m_currentExternalParent.pop_back();
941 if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
942 asyncTaskCanceledForStack(task);
946 void V8Debugger::asyncTaskCandidateForStepping(
void* task,
bool isLocal) {
947 int contextGroupId = currentContextGroupId();
948 if (m_pauseOnAsyncCall && contextGroupId) {
951 reinterpret_cast<uintptr_t>(task), std::make_pair(0, 0));
954 reinterpret_cast<uintptr_t>(task), debuggerIdFor(contextGroupId));
956 breakProgram(m_targetContextGroupId);
960 if (!m_stepIntoAsyncCallback)
return;
961 DCHECK(m_targetContextGroupId);
962 if (contextGroupId != m_targetContextGroupId)
return;
963 m_taskWithScheduledBreak = task;
964 v8::debug::ClearStepping(m_isolate);
965 m_stepIntoAsyncCallback->sendSuccess();
966 m_stepIntoAsyncCallback.reset();
969 void V8Debugger::asyncTaskStartedForStepping(
void* task) {
970 if (m_breakRequested)
return;
973 if (m_taskWithScheduledBreakDebuggerId.isEmpty() &&
974 task == m_taskWithScheduledBreak) {
975 v8::debug::SetBreakOnNextFunctionCall(m_isolate);
979 void V8Debugger::asyncTaskFinishedForStepping(
void* task) {
980 if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
981 task != m_taskWithScheduledBreak) {
984 m_taskWithScheduledBreak =
nullptr;
985 if (m_breakRequested)
return;
986 v8::debug::ClearBreakOnNextFunctionCall(m_isolate);
989 void V8Debugger::asyncTaskCanceledForStepping(
void* task) {
990 if (!m_taskWithScheduledBreakDebuggerId.isEmpty() ||
991 task != m_taskWithScheduledBreak)
993 m_taskWithScheduledBreak =
nullptr;
996 void V8Debugger::allAsyncTasksCanceled() {
997 m_asyncTaskStacks.clear();
998 m_recurringTasks.clear();
999 m_currentAsyncParent.clear();
1000 m_currentExternalParent.clear();
1001 m_currentTasks.clear();
1003 m_framesCache.clear();
1004 m_allAsyncStacks.clear();
1005 m_asyncStacksCount = 0;
1008 void V8Debugger::muteScriptParsedEvents() {
1009 ++m_ignoreScriptParsedEventsCounter;
1012 void V8Debugger::unmuteScriptParsedEvents() {
1013 --m_ignoreScriptParsedEventsCounter;
1014 DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
1017 std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
1019 if (!m_isolate->InContext())
return nullptr;
1022 int contextGroupId = currentContextGroupId();
1023 if (!contextGroupId)
return nullptr;
1027 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
1029 m_inspector->forEachSession(
1030 contextGroupId, [&stackSize](V8InspectorSessionImpl* session) {
1031 if (session->runtimeAgent()->enabled())
1032 stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
1035 return V8StackTraceImpl::capture(
this, contextGroupId, stackSize);
1038 int V8Debugger::currentContextGroupId() {
1039 if (!m_isolate->InContext())
return 0;
1040 return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
1043 void V8Debugger::collectOldAsyncStacksIfNeeded() {
1044 if (m_asyncStacksCount <= m_maxAsyncCallStacks)
return;
1045 int halfOfLimitRoundedUp =
1046 m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
1047 while (m_asyncStacksCount > halfOfLimitRoundedUp) {
1048 m_allAsyncStacks.pop_front();
1049 --m_asyncStacksCount;
1051 cleanupExpiredWeakPointers(m_asyncTaskStacks);
1052 cleanupExpiredWeakPointers(m_storedStackTraces);
1053 for (
auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
1054 if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
1055 it = m_recurringTasks.erase(it);
1060 cleanupExpiredWeakPointers(m_framesCache);
1063 std::shared_ptr<StackFrame> V8Debugger::symbolize(
1065 auto it = m_framesCache.end();
1067 if (m_maxAsyncCallStackDepth) {
1068 frameId = v8::debug::GetStackFrameId(v8Frame);
1069 it = m_framesCache.find(frameId);
1071 if (it != m_framesCache.end() && !it->second.expired()) {
1072 return std::shared_ptr<StackFrame>(it->second);
1074 std::shared_ptr<StackFrame> frame(
new StackFrame(isolate(), v8Frame));
1077 if (v8Frame->
IsWasm()) frame->translate(&m_wasmTranslation);
1078 if (m_maxAsyncCallStackDepth) {
1079 m_framesCache[frameId] = frame;
1084 void V8Debugger::setMaxAsyncTaskStacksForTest(
int limit) {
1085 m_maxAsyncCallStacks = 0;
1086 collectOldAsyncStacksIfNeeded();
1087 m_maxAsyncCallStacks = limit;
1090 std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
int contextGroupId) {
1091 auto it = m_contextGroupIdToDebuggerId.find(contextGroupId);
1092 if (it != m_contextGroupIdToDebuggerId.end())
return it->second;
1093 std::pair<int64_t, int64_t> debuggerId(
1094 v8::debug::GetNextRandomInt64(m_isolate),
1095 v8::debug::GetNextRandomInt64(m_isolate));
1096 if (!debuggerId.first && !debuggerId.second) ++debuggerId.first;
1097 m_contextGroupIdToDebuggerId.insert(
1098 it, std::make_pair(contextGroupId, debuggerId));
1099 m_serializedDebuggerIdToDebuggerId.insert(
1100 std::make_pair(debuggerIdToString(debuggerId), debuggerId));
1104 std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
1105 const String16& serializedDebuggerId) {
1106 auto it = m_serializedDebuggerIdToDebuggerId.find(serializedDebuggerId);
1107 if (it != m_serializedDebuggerIdToDebuggerId.end())
return it->second;
1108 return std::make_pair(0, 0);
1113 V8InternalValueType type) {
1114 int contextId = InspectedContext::contextId(context);
1115 InspectedContext* inspectedContext = m_inspector->getContext(contextId);
1116 return inspectedContext ? inspectedContext->addInternalObject(
object, type)
1120 void V8Debugger::dumpAsyncTaskStacksStateForTest() {
1121 fprintf(stdout,
"Async stacks count: %d\n", m_asyncStacksCount);
1122 fprintf(stdout,
"Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
1123 fprintf(stdout,
"Recurring async tasks: %zu\n", m_recurringTasks.size());
1124 fprintf(stdout,
"\n");
Local< Value > GetPrototype()
V8_INLINE bool IsEmpty() const
static Local< Array > New(Isolate *isolate, int length=0)
V8_INLINE Local< S > As() const
V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local< S > *out) const
V8_WARN_UNUSED_RESULT Maybe< bool > SetPrototype(Local< Context > context, Local< Value > prototype)