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/debugger-script.h"
8 : #include "src/inspector/inspected-context.h"
9 : #include "src/inspector/protocol/Protocol.h"
10 : #include "src/inspector/script-breakpoint.h"
11 : #include "src/inspector/string-util.h"
12 : #include "src/inspector/v8-debugger-agent-impl.h"
13 : #include "src/inspector/v8-inspector-impl.h"
14 : #include "src/inspector/v8-internal-value-type.h"
15 : #include "src/inspector/v8-stack-trace-impl.h"
16 : #include "src/inspector/v8-value-copier.h"
17 :
18 : #include "include/v8-util.h"
19 :
20 : namespace v8_inspector {
21 :
22 : namespace {
23 :
24 : static const int kMaxAsyncTaskStacks = 128 * 1024;
25 :
26 : inline v8::Local<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate) {
27 18 : return value ? v8::True(isolate) : v8::False(isolate);
28 : }
29 :
30 41548 : V8DebuggerAgentImpl* agentForScript(V8InspectorImpl* inspector,
31 : v8::Local<v8::debug::Script> script) {
32 : v8::Local<v8::Value> contextData;
33 83096 : if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32()) {
34 : return nullptr;
35 : }
36 41484 : int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value());
37 41484 : int contextGroupId = inspector->contextGroupId(contextId);
38 41484 : if (!contextGroupId) return nullptr;
39 41484 : return inspector->enabledDebuggerAgentForGroup(contextGroupId);
40 : }
41 :
42 106344 : v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
43 : v8::Local<v8::Value> value) {
44 106344 : v8::Isolate* isolate = context->GetIsolate();
45 : v8::Local<v8::Array> entries;
46 106344 : bool isKeyValue = false;
47 212688 : if (!v8::debug::EntriesPreview(isolate, value, &isKeyValue).ToLocal(&entries))
48 106090 : return v8::MaybeLocal<v8::Array>();
49 :
50 254 : v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
51 254 : CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
52 254 : if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
53 508 : .FromMaybe(false))
54 0 : return v8::MaybeLocal<v8::Array>();
55 12486 : for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
56 : v8::Local<v8::Value> item;
57 24972 : if (!entries->Get(context, i).ToLocal(&item)) continue;
58 : v8::Local<v8::Value> value;
59 18634 : if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
60 12486 : v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
61 24972 : if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
62 : continue;
63 : createDataProperty(
64 : context, wrapper,
65 24972 : toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
66 12486 : if (isKeyValue) {
67 : createDataProperty(context, wrapper,
68 12296 : toV8StringInternalized(isolate, "value"), value);
69 : }
70 12486 : createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
71 12486 : wrapper);
72 : }
73 254 : if (!markArrayEntriesAsInternal(context, wrappedEntries,
74 254 : V8InternalValueType::kEntry)) {
75 0 : return v8::MaybeLocal<v8::Array>();
76 : }
77 254 : return wrappedEntries;
78 : }
79 :
80 84 : v8::MaybeLocal<v8::Object> buildLocation(v8::Local<v8::Context> context,
81 : int scriptId, int lineNumber,
82 : int columnNumber) {
83 84 : if (scriptId == v8::UnboundScript::kNoScriptId)
84 6 : return v8::MaybeLocal<v8::Object>();
85 78 : if (lineNumber == v8::Function::kLineOffsetNotFound ||
86 : columnNumber == v8::Function::kLineOffsetNotFound) {
87 0 : return v8::MaybeLocal<v8::Object>();
88 : }
89 78 : v8::Isolate* isolate = context->GetIsolate();
90 78 : v8::Local<v8::Object> location = v8::Object::New(isolate);
91 156 : if (!location->SetPrototype(context, v8::Null(isolate)).FromMaybe(false)) {
92 0 : return v8::MaybeLocal<v8::Object>();
93 : }
94 78 : if (!createDataProperty(context, location,
95 : toV8StringInternalized(isolate, "scriptId"),
96 312 : toV8String(isolate, String16::fromInteger(scriptId)))
97 156 : .FromMaybe(false)) {
98 0 : return v8::MaybeLocal<v8::Object>();
99 : }
100 78 : if (!createDataProperty(context, location,
101 : toV8StringInternalized(isolate, "lineNumber"),
102 234 : v8::Integer::New(isolate, lineNumber))
103 156 : .FromMaybe(false)) {
104 0 : return v8::MaybeLocal<v8::Object>();
105 : }
106 78 : if (!createDataProperty(context, location,
107 : toV8StringInternalized(isolate, "columnNumber"),
108 234 : v8::Integer::New(isolate, columnNumber))
109 156 : .FromMaybe(false)) {
110 0 : return v8::MaybeLocal<v8::Object>();
111 : }
112 78 : if (!markAsInternal(context, location, V8InternalValueType::kLocation)) {
113 0 : return v8::MaybeLocal<v8::Object>();
114 : }
115 78 : return location;
116 : }
117 :
118 54 : v8::MaybeLocal<v8::Object> generatorObjectLocation(
119 : v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
120 54 : if (!value->IsGeneratorObject()) return v8::MaybeLocal<v8::Object>();
121 : v8::Local<v8::debug::GeneratorObject> generatorObject =
122 54 : v8::debug::GeneratorObject::Cast(value);
123 54 : if (!generatorObject->IsSuspended()) {
124 12 : v8::Local<v8::Function> func = generatorObject->Function();
125 : return buildLocation(context, func->ScriptId(), func->GetScriptLineNumber(),
126 12 : func->GetScriptColumnNumber());
127 : }
128 : v8::Local<v8::debug::Script> script;
129 84 : if (!generatorObject->Script().ToLocal(&script))
130 0 : return v8::MaybeLocal<v8::Object>();
131 42 : v8::debug::Location suspendedLocation = generatorObject->SuspendedLocation();
132 : return buildLocation(context, script->Id(), suspendedLocation.GetLineNumber(),
133 42 : suspendedLocation.GetColumnNumber());
134 : }
135 :
136 : template <typename Map>
137 1404 : void cleanupExpiredWeakPointers(Map& map) {
138 14880 : for (auto it = map.begin(); it != map.end();) {
139 12072 : if (it->second.expired()) {
140 : it = map.erase(it);
141 : } else {
142 : ++it;
143 : }
144 : }
145 1404 : }
146 :
147 : } // namespace
148 :
149 : static bool inLiveEditScope = false;
150 :
151 151606 : v8::MaybeLocal<v8::Value> V8Debugger::callDebuggerMethod(
152 : const char* functionName, int argc, v8::Local<v8::Value> argv[],
153 : bool catchExceptions) {
154 : v8::MicrotasksScope microtasks(m_isolate,
155 151606 : v8::MicrotasksScope::kDoNotRunMicrotasks);
156 : DCHECK(m_isolate->InContext());
157 151606 : v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
158 151606 : v8::Local<v8::Object> debuggerScript = m_debuggerScript.Get(m_isolate);
159 : v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(
160 : debuggerScript
161 303212 : ->Get(context, toV8StringInternalized(m_isolate, functionName))
162 151606 : .ToLocalChecked());
163 151606 : if (catchExceptions) {
164 151588 : v8::TryCatch try_catch(m_isolate);
165 151588 : return function->Call(context, debuggerScript, argc, argv);
166 : }
167 18 : return function->Call(context, debuggerScript, argc, argv);
168 : }
169 :
170 4537 : V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
171 : : m_isolate(isolate),
172 : m_inspector(inspector),
173 : m_enableCount(0),
174 : m_breakpointsActivated(true),
175 : m_ignoreScriptParsedEventsCounter(0),
176 : m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
177 : m_maxAsyncCallStackDepth(0),
178 : m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
179 36296 : m_wasmTranslation(isolate) {}
180 :
181 18148 : V8Debugger::~V8Debugger() {}
182 :
183 4333 : void V8Debugger::enable() {
184 4363 : if (m_enableCount++) return;
185 : DCHECK(!enabled());
186 4303 : v8::HandleScope scope(m_isolate);
187 4303 : v8::debug::SetDebugDelegate(m_isolate, this);
188 : v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback,
189 4303 : this);
190 8606 : m_debuggerContext.Reset(m_isolate, v8::debug::GetDebugContext(m_isolate));
191 4303 : v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
192 4303 : m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
193 4303 : compileDebuggerScript();
194 : }
195 :
196 4333 : void V8Debugger::disable() {
197 8666 : if (--m_enableCount) return;
198 : DCHECK(enabled());
199 4303 : clearBreakpoints();
200 : m_debuggerScript.Reset();
201 : m_debuggerContext.Reset();
202 4303 : allAsyncTasksCanceled();
203 4303 : m_taskWithScheduledBreak = nullptr;
204 4303 : m_wasmTranslation.Clear();
205 4303 : v8::debug::SetDebugDelegate(m_isolate, nullptr);
206 4303 : v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
207 4303 : m_isolate->RestoreOriginalHeapLimit();
208 : }
209 :
210 425016 : bool V8Debugger::enabled() const { return !m_debuggerScript.IsEmpty(); }
211 :
212 4333 : void V8Debugger::getCompiledScripts(
213 : int contextGroupId,
214 : std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
215 4333 : v8::HandleScope scope(m_isolate);
216 8666 : v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate);
217 4333 : v8::debug::GetLoadedScripts(m_isolate, scripts);
218 32842 : for (size_t i = 0; i < scripts.Size(); ++i) {
219 12088 : v8::Local<v8::debug::Script> script = scripts.Get(i);
220 12088 : if (!script->WasCompiled()) continue;
221 : v8::Local<v8::Value> contextData;
222 24128 : if (!script->ContextData().ToLocal(&contextData) || !contextData->IsInt32())
223 : continue;
224 12058 : int contextId = static_cast<int>(contextData.As<v8::Int32>()->Value());
225 12058 : if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
226 24008 : result.push_back(V8DebuggerScript::Create(m_isolate, script, false));
227 4333 : }
228 4333 : }
229 :
230 2458 : String16 V8Debugger::setBreakpoint(const ScriptBreakpoint& breakpoint,
231 : int* actualLineNumber,
232 : int* actualColumnNumber) {
233 2458 : v8::HandleScope scope(m_isolate);
234 2458 : v8::Local<v8::Context> context = debuggerContext();
235 : v8::Context::Scope contextScope(context);
236 :
237 2458 : v8::Local<v8::Object> info = v8::Object::New(m_isolate);
238 : bool success = false;
239 : success = info->Set(context, toV8StringInternalized(m_isolate, "sourceID"),
240 7374 : toV8String(m_isolate, breakpoint.script_id))
241 2458 : .FromMaybe(false);
242 : DCHECK(success);
243 : success = info->Set(context, toV8StringInternalized(m_isolate, "lineNumber"),
244 7374 : v8::Integer::New(m_isolate, breakpoint.line_number))
245 2458 : .FromMaybe(false);
246 : DCHECK(success);
247 : success =
248 : info->Set(context, toV8StringInternalized(m_isolate, "columnNumber"),
249 7374 : v8::Integer::New(m_isolate, breakpoint.column_number))
250 2458 : .FromMaybe(false);
251 : DCHECK(success);
252 : success = info->Set(context, toV8StringInternalized(m_isolate, "condition"),
253 7374 : toV8String(m_isolate, breakpoint.condition))
254 2458 : .FromMaybe(false);
255 : DCHECK(success);
256 : USE(success);
257 :
258 : v8::Local<v8::Function> setBreakpointFunction = v8::Local<v8::Function>::Cast(
259 2458 : m_debuggerScript.Get(m_isolate)
260 4916 : ->Get(context, toV8StringInternalized(m_isolate, "setBreakpoint"))
261 4916 : .ToLocalChecked());
262 : v8::Local<v8::Value> breakpointId =
263 4916 : v8::debug::Call(debuggerContext(), setBreakpointFunction, info)
264 2458 : .ToLocalChecked();
265 2458 : if (!breakpointId->IsString()) return "";
266 : *actualLineNumber =
267 4916 : info->Get(context, toV8StringInternalized(m_isolate, "lineNumber"))
268 2458 : .ToLocalChecked()
269 : ->Int32Value(context)
270 4916 : .FromJust();
271 : *actualColumnNumber =
272 4916 : info->Get(context, toV8StringInternalized(m_isolate, "columnNumber"))
273 2458 : .ToLocalChecked()
274 : ->Int32Value(context)
275 4916 : .FromJust();
276 4916 : return toProtocolString(breakpointId.As<v8::String>());
277 : }
278 :
279 822 : void V8Debugger::removeBreakpoint(const String16& breakpointId) {
280 822 : v8::HandleScope scope(m_isolate);
281 822 : v8::Local<v8::Context> context = debuggerContext();
282 : v8::Context::Scope contextScope(context);
283 :
284 822 : v8::Local<v8::Object> info = v8::Object::New(m_isolate);
285 : bool success = false;
286 : success =
287 : info->Set(context, toV8StringInternalized(m_isolate, "breakpointId"),
288 2466 : toV8String(m_isolate, breakpointId))
289 822 : .FromMaybe(false);
290 : DCHECK(success);
291 : USE(success);
292 :
293 : v8::Local<v8::Function> removeBreakpointFunction =
294 : v8::Local<v8::Function>::Cast(
295 822 : m_debuggerScript.Get(m_isolate)
296 : ->Get(context,
297 1644 : toV8StringInternalized(m_isolate, "removeBreakpoint"))
298 1644 : .ToLocalChecked());
299 1644 : v8::debug::Call(debuggerContext(), removeBreakpointFunction, info)
300 1644 : .ToLocalChecked();
301 822 : }
302 :
303 4303 : void V8Debugger::clearBreakpoints() {
304 4303 : v8::HandleScope scope(m_isolate);
305 4303 : v8::Local<v8::Context> context = debuggerContext();
306 : v8::Context::Scope contextScope(context);
307 :
308 : v8::Local<v8::Function> clearBreakpoints = v8::Local<v8::Function>::Cast(
309 4303 : m_debuggerScript.Get(m_isolate)
310 8606 : ->Get(context, toV8StringInternalized(m_isolate, "clearBreakpoints"))
311 8606 : .ToLocalChecked());
312 12909 : v8::debug::Call(debuggerContext(), clearBreakpoints).ToLocalChecked();
313 4303 : }
314 :
315 4513 : void V8Debugger::setBreakpointsActivated(bool activated) {
316 4513 : if (!enabled()) {
317 0 : UNREACHABLE();
318 : return;
319 : }
320 4513 : v8::debug::SetBreakPointsActive(m_isolate, activated);
321 4513 : m_breakpointsActivated = activated;
322 4513 : }
323 :
324 211932 : v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
325 : DCHECK(enabled());
326 211932 : return m_pauseOnExceptionsState;
327 : }
328 :
329 7532 : void V8Debugger::setPauseOnExceptionsState(
330 : v8::debug::ExceptionBreakState pauseOnExceptionsState) {
331 : DCHECK(enabled());
332 15064 : if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
333 5960 : v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
334 5960 : m_pauseOnExceptionsState = pauseOnExceptionsState;
335 : }
336 :
337 282 : void V8Debugger::setPauseOnNextStatement(bool pause, int targetContextGroupId) {
338 282 : if (isPaused()) return;
339 : DCHECK(targetContextGroupId);
340 282 : if (!pause && m_targetContextGroupId &&
341 : m_targetContextGroupId != targetContextGroupId) {
342 : return;
343 : }
344 282 : m_targetContextGroupId = targetContextGroupId;
345 282 : m_breakRequested = pause;
346 282 : if (pause)
347 261 : v8::debug::DebugBreak(m_isolate);
348 : else
349 21 : v8::debug::CancelDebugBreak(m_isolate);
350 : }
351 :
352 78 : bool V8Debugger::canBreakProgram() {
353 144 : if (!m_breakpointsActivated) return false;
354 144 : return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
355 : }
356 :
357 66 : bool V8Debugger::breakProgram(int targetContextGroupId) {
358 : // Don't allow nested breaks.
359 66 : if (isPaused()) return true;
360 66 : if (!canBreakProgram()) return true;
361 : DCHECK(targetContextGroupId);
362 66 : m_targetContextGroupId = targetContextGroupId;
363 66 : v8::debug::BreakRightNow(m_isolate);
364 66 : return m_inspector->enabledDebuggerAgentForGroup(targetContextGroupId);
365 : }
366 :
367 1725 : void V8Debugger::continueProgram(int targetContextGroupId) {
368 66268 : if (m_pausedContextGroupId != targetContextGroupId) return;
369 64531 : if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
370 : m_pausedContext.Clear();
371 : m_executionState.Clear();
372 : }
373 :
374 50541 : void V8Debugger::stepIntoStatement(int targetContextGroupId) {
375 : DCHECK(isPaused());
376 : DCHECK(!m_executionState.IsEmpty());
377 : DCHECK(targetContextGroupId);
378 50541 : m_targetContextGroupId = targetContextGroupId;
379 50541 : v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
380 : continueProgram(targetContextGroupId);
381 50541 : }
382 :
383 11616 : void V8Debugger::stepOverStatement(int targetContextGroupId) {
384 : DCHECK(isPaused());
385 : DCHECK(!m_executionState.IsEmpty());
386 : DCHECK(targetContextGroupId);
387 11616 : m_targetContextGroupId = targetContextGroupId;
388 11616 : v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
389 : continueProgram(targetContextGroupId);
390 11616 : }
391 :
392 661 : void V8Debugger::stepOutOfFunction(int targetContextGroupId) {
393 : DCHECK(isPaused());
394 : DCHECK(!m_executionState.IsEmpty());
395 : DCHECK(targetContextGroupId);
396 661 : m_targetContextGroupId = targetContextGroupId;
397 661 : v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
398 : continueProgram(targetContextGroupId);
399 661 : }
400 :
401 90 : void V8Debugger::scheduleStepIntoAsync(
402 : std::unique_ptr<ScheduleStepIntoAsyncCallback> callback,
403 : int targetContextGroupId) {
404 : DCHECK(isPaused());
405 : DCHECK(!m_executionState.IsEmpty());
406 : DCHECK(targetContextGroupId);
407 90 : if (m_stepIntoAsyncCallback) {
408 : m_stepIntoAsyncCallback->sendFailure(Response::Error(
409 18 : "Current scheduled step into async was overriden with new one."));
410 : }
411 90 : m_targetContextGroupId = targetContextGroupId;
412 : m_stepIntoAsyncCallback = std::move(callback);
413 90 : }
414 :
415 18 : Response V8Debugger::setScriptSource(
416 : const String16& sourceID, v8::Local<v8::String> newSource, bool dryRun,
417 : Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails,
418 : JavaScriptCallFrames* newCallFrames, Maybe<bool>* stackChanged,
419 24 : bool* compileError) {
420 : class EnableLiveEditScope {
421 : public:
422 : explicit EnableLiveEditScope(v8::Isolate* isolate) : m_isolate(isolate) {
423 18 : v8::debug::SetLiveEditEnabled(m_isolate, true);
424 18 : inLiveEditScope = true;
425 : }
426 : ~EnableLiveEditScope() {
427 18 : v8::debug::SetLiveEditEnabled(m_isolate, false);
428 18 : inLiveEditScope = false;
429 : }
430 :
431 : private:
432 : v8::Isolate* m_isolate;
433 : };
434 :
435 18 : *compileError = false;
436 : DCHECK(enabled());
437 18 : v8::HandleScope scope(m_isolate);
438 :
439 : std::unique_ptr<v8::Context::Scope> contextScope;
440 18 : if (!isPaused())
441 36 : contextScope.reset(new v8::Context::Scope(debuggerContext()));
442 :
443 : v8::Local<v8::Value> argv[] = {toV8String(m_isolate, sourceID), newSource,
444 36 : v8Boolean(dryRun, m_isolate)};
445 :
446 : v8::Local<v8::Value> v8result;
447 : {
448 : EnableLiveEditScope enableLiveEditScope(m_isolate);
449 36 : v8::TryCatch tryCatch(m_isolate);
450 18 : tryCatch.SetVerbose(false);
451 : v8::MaybeLocal<v8::Value> maybeResult =
452 18 : callDebuggerMethod("liveEditScriptSource", 3, argv, false);
453 18 : if (tryCatch.HasCaught()) {
454 0 : v8::Local<v8::Message> message = tryCatch.Message();
455 0 : if (!message.IsEmpty())
456 0 : return Response::Error(toProtocolStringWithTypeCheck(message->Get()));
457 : else
458 0 : return Response::InternalError();
459 : }
460 : v8result = maybeResult.ToLocalChecked();
461 : }
462 : DCHECK(!v8result.IsEmpty());
463 18 : v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
464 : v8::Local<v8::Object> resultTuple =
465 18 : v8result->ToObject(context).ToLocalChecked();
466 : int code = static_cast<int>(resultTuple->Get(context, 0)
467 18 : .ToLocalChecked()
468 : ->ToInteger(context)
469 18 : .ToLocalChecked()
470 18 : ->Value());
471 18 : switch (code) {
472 : case 0: {
473 : *stackChanged = resultTuple->Get(context, 1)
474 6 : .ToLocalChecked()
475 : ->BooleanValue(context)
476 12 : .FromJust();
477 : // Call stack may have changed after if the edited function was on the
478 : // stack.
479 12 : if (!dryRun && isPaused()) {
480 0 : JavaScriptCallFrames frames = currentCallFrames();
481 0 : newCallFrames->swap(frames);
482 : }
483 6 : return Response::OK();
484 : }
485 : // Compile error.
486 : case 1: {
487 : *exceptionDetails =
488 : protocol::Runtime::ExceptionDetails::create()
489 36 : .setExceptionId(m_inspector->nextExceptionId())
490 : .setText(toProtocolStringWithTypeCheck(
491 36 : resultTuple->Get(context, 2).ToLocalChecked()))
492 : .setLineNumber(static_cast<int>(resultTuple->Get(context, 3)
493 12 : .ToLocalChecked()
494 : ->ToInteger(context)
495 12 : .ToLocalChecked()
496 12 : ->Value()) -
497 12 : 1)
498 : .setColumnNumber(static_cast<int>(resultTuple->Get(context, 4)
499 12 : .ToLocalChecked()
500 : ->ToInteger(context)
501 12 : .ToLocalChecked()
502 12 : ->Value()) -
503 12 : 1)
504 : .build();
505 12 : *compileError = true;
506 12 : return Response::OK();
507 : }
508 : }
509 18 : return Response::InternalError();
510 : }
511 :
512 77166 : JavaScriptCallFrames V8Debugger::currentCallFrames(int limit) {
513 77166 : if (!isPaused()) return JavaScriptCallFrames();
514 : v8::Local<v8::Value> currentCallFramesV8;
515 : v8::Local<v8::Value> argv[] = {m_executionState,
516 77166 : v8::Integer::New(m_isolate, limit)};
517 77166 : if (!callDebuggerMethod("currentCallFrames", arraysize(argv), argv, true)
518 154332 : .ToLocal(¤tCallFramesV8)) {
519 : return JavaScriptCallFrames();
520 : }
521 77160 : if (!currentCallFramesV8->IsArray()) return JavaScriptCallFrames();
522 : v8::Local<v8::Array> callFramesArray = currentCallFramesV8.As<v8::Array>();
523 : JavaScriptCallFrames callFrames;
524 268828 : for (uint32_t i = 0; i < callFramesArray->Length(); ++i) {
525 : v8::Local<v8::Value> callFrameValue;
526 383336 : if (!callFramesArray->Get(debuggerContext(), i).ToLocal(&callFrameValue))
527 : return JavaScriptCallFrames();
528 191668 : if (!callFrameValue->IsObject()) return JavaScriptCallFrames();
529 : v8::Local<v8::Object> callFrameObject = callFrameValue.As<v8::Object>();
530 : callFrames.push_back(JavaScriptCallFrame::create(
531 383336 : debuggerContext(), v8::Local<v8::Object>::Cast(callFrameObject)));
532 : }
533 77160 : return callFrames;
534 : }
535 :
536 77036 : void V8Debugger::handleProgramBreak(v8::Local<v8::Context> pausedContext,
537 : v8::Local<v8::Object> executionState,
538 : v8::Local<v8::Value> exception,
539 : v8::Local<v8::Array> hitBreakpointNumbers,
540 77036 : bool isPromiseRejection, bool isUncaught) {
541 : // Don't allow nested breaks.
542 77060 : if (isPaused()) return;
543 :
544 154048 : int contextGroupId = m_inspector->contextGroupId(pausedContext);
545 77036 : if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
546 24 : v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
547 24 : return;
548 : }
549 77012 : m_targetContextGroupId = 0;
550 77012 : if (m_stepIntoAsyncCallback) {
551 : m_stepIntoAsyncCallback->sendFailure(
552 36 : Response::Error("No async tasks were scheduled before pause."));
553 : m_stepIntoAsyncCallback.reset();
554 : }
555 77012 : m_breakRequested = false;
556 77012 : V8DebuggerAgentImpl* agent = m_inspector->enabledDebuggerAgentForGroup(
557 77012 : m_inspector->contextGroupId(pausedContext));
558 154024 : if (!agent || (agent->skipAllPauses() && !m_scheduledOOMBreak)) return;
559 :
560 : std::vector<String16> breakpointIds;
561 77012 : if (!hitBreakpointNumbers.IsEmpty()) {
562 74338 : breakpointIds.reserve(hitBreakpointNumbers->Length());
563 77181 : for (uint32_t i = 0; i < hitBreakpointNumbers->Length(); i++) {
564 : v8::Local<v8::Value> hitBreakpointNumber =
565 2843 : hitBreakpointNumbers->Get(debuggerContext(), i).ToLocalChecked();
566 : DCHECK(hitBreakpointNumber->IsInt32());
567 : breakpointIds.push_back(String16::fromInteger(
568 8529 : hitBreakpointNumber->Int32Value(debuggerContext()).FromJust()));
569 : }
570 : }
571 :
572 77012 : m_pausedContext = pausedContext;
573 77012 : m_executionState = executionState;
574 77012 : m_pausedContextGroupId = contextGroupId;
575 : agent->didPause(InspectedContext::contextId(pausedContext), exception,
576 : breakpointIds, isPromiseRejection, isUncaught,
577 77012 : m_scheduledOOMBreak);
578 77012 : int groupId = m_inspector->contextGroupId(pausedContext);
579 : DCHECK(groupId);
580 : {
581 : v8::Context::Scope scope(pausedContext);
582 77012 : v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
583 154024 : CHECK(!context.IsEmpty() &&
584 : context != v8::debug::GetDebugContext(m_isolate));
585 154024 : m_inspector->client()->runMessageLoopOnPause(groupId);
586 77012 : m_pausedContextGroupId = 0;
587 : }
588 : // The agent may have been removed in the nested loop.
589 77012 : agent = m_inspector->enabledDebuggerAgentForGroup(groupId);
590 77012 : if (agent) agent->didContinue();
591 77012 : if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
592 77012 : m_scheduledOOMBreak = false;
593 : m_pausedContext.Clear();
594 77012 : m_executionState.Clear();
595 : }
596 :
597 6 : void V8Debugger::v8OOMCallback(void* data) {
598 : V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
599 6 : thisPtr->m_isolate->IncreaseHeapLimitForDebugging();
600 6 : thisPtr->m_scheduledOOMBreak = true;
601 6 : v8::Local<v8::Context> context = thisPtr->m_isolate->GetEnteredContext();
602 : DCHECK(!context.IsEmpty());
603 : thisPtr->setPauseOnNextStatement(
604 6 : true, thisPtr->m_inspector->contextGroupId(context));
605 6 : }
606 :
607 24704 : void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
608 : bool has_compile_error) {
609 24704 : V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script);
610 49408 : if (!agent) return;
611 24640 : if (script->IsWasm()) {
612 56 : m_wasmTranslation.AddScript(script.As<v8::debug::WasmScript>(), agent);
613 24584 : } else if (m_ignoreScriptParsedEventsCounter == 0) {
614 : agent->didParseSource(
615 : V8DebuggerScript::Create(m_isolate, script, inLiveEditScope),
616 49108 : !has_compile_error);
617 : }
618 : }
619 :
620 74362 : void V8Debugger::BreakProgramRequested(v8::Local<v8::Context> pausedContext,
621 : v8::Local<v8::Object> execState,
622 : v8::Local<v8::Value> breakPointsHit) {
623 74362 : v8::Local<v8::Value> argv[] = {breakPointsHit};
624 : v8::Local<v8::Value> hitBreakpoints;
625 74362 : if (!callDebuggerMethod("getBreakpointNumbers", 1, argv, true)
626 148724 : .ToLocal(&hitBreakpoints)) {
627 0 : return;
628 : }
629 : DCHECK(hitBreakpoints->IsArray());
630 : handleProgramBreak(pausedContext, execState, v8::Local<v8::Value>(),
631 74362 : hitBreakpoints.As<v8::Array>());
632 : }
633 :
634 2674 : void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
635 : v8::Local<v8::Object> execState,
636 : v8::Local<v8::Value> exception,
637 : v8::Local<v8::Value> promise,
638 : bool isUncaught) {
639 2674 : bool isPromiseRejection = promise->IsPromise();
640 : handleProgramBreak(pausedContext, execState, exception,
641 5348 : v8::Local<v8::Array>(), isPromiseRejection, isUncaught);
642 2674 : }
643 :
644 16844 : bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
645 : const v8::debug::Location& start,
646 : const v8::debug::Location& end) {
647 16844 : V8DebuggerAgentImpl* agent = agentForScript(m_inspector, script);
648 16844 : if (!agent) return false;
649 : return agent->isFunctionBlackboxed(String16::fromInteger(script->Id()), start,
650 33688 : end);
651 : }
652 :
653 119578 : void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
654 : int id, int parentId,
655 : bool createdByUser) {
656 : // Async task events from Promises are given misaligned pointers to prevent
657 : // from overlapping with other Blink task identifiers.
658 119578 : void* task = reinterpret_cast<void*>(id * 2 + 1);
659 : void* parentTask =
660 119578 : parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr;
661 119578 : switch (type) {
662 : case v8::debug::kDebugPromiseCreated:
663 36980 : asyncTaskCreatedForStack(task, parentTask);
664 36980 : if (createdByUser && parentTask) asyncTaskCandidateForStepping(task);
665 : break;
666 : case v8::debug::kDebugEnqueueAsyncFunction:
667 14648 : asyncTaskScheduledForStack("async function", task, true);
668 7324 : break;
669 : case v8::debug::kDebugEnqueuePromiseResolve:
670 60208 : asyncTaskScheduledForStack("Promise.resolve", task, true);
671 30104 : break;
672 : case v8::debug::kDebugEnqueuePromiseReject:
673 14428 : asyncTaskScheduledForStack("Promise.reject", task, true);
674 7214 : break;
675 : case v8::debug::kDebugWillHandle:
676 18978 : asyncTaskStartedForStack(task);
677 : asyncTaskStartedForStepping(task);
678 : break;
679 : case v8::debug::kDebugDidHandle:
680 18978 : asyncTaskFinishedForStack(task);
681 : asyncTaskFinishedForStepping(task);
682 : break;
683 : }
684 119578 : }
685 :
686 133410 : std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
687 266820 : return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
688 : }
689 :
690 56616 : std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() {
691 : return m_currentAsyncCreation.empty() ? nullptr
692 113232 : : m_currentAsyncCreation.back();
693 : }
694 :
695 4303 : void V8Debugger::compileDebuggerScript() {
696 4303 : if (!m_debuggerScript.IsEmpty()) {
697 0 : UNREACHABLE();
698 : return;
699 : }
700 :
701 4303 : v8::HandleScope scope(m_isolate);
702 4303 : v8::Context::Scope contextScope(debuggerContext());
703 :
704 : v8::Local<v8::String> scriptValue =
705 : v8::String::NewFromUtf8(m_isolate, DebuggerScript_js,
706 : v8::NewStringType::kInternalized,
707 4303 : sizeof(DebuggerScript_js))
708 8606 : .ToLocalChecked();
709 : v8::Local<v8::Value> value;
710 8606 : if (!m_inspector->compileAndRunInternalScript(debuggerContext(), scriptValue)
711 8606 : .ToLocal(&value)) {
712 0 : UNREACHABLE();
713 : return;
714 : }
715 : DCHECK(value->IsObject());
716 8606 : m_debuggerScript.Reset(m_isolate, value.As<v8::Object>());
717 4303 : }
718 :
719 412872 : v8::Local<v8::Context> V8Debugger::debuggerContext() const {
720 : DCHECK(!m_debuggerContext.IsEmpty());
721 825744 : return m_debuggerContext.Get(m_isolate);
722 : }
723 :
724 60 : v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
725 : v8::Local<v8::Context> context, v8::Local<v8::Value> value,
726 : ScopeTargetKind kind) {
727 60 : if (!enabled()) {
728 0 : UNREACHABLE();
729 : return v8::Local<v8::Value>::New(m_isolate, v8::Undefined(m_isolate));
730 : }
731 60 : v8::Local<v8::Value> argv[] = {value};
732 : v8::Local<v8::Value> scopesValue;
733 :
734 : const char* debuggerMethod = nullptr;
735 60 : switch (kind) {
736 : case FUNCTION:
737 : debuggerMethod = "getFunctionScopes";
738 24 : break;
739 : case GENERATOR:
740 : debuggerMethod = "getGeneratorScopes";
741 36 : break;
742 : }
743 :
744 120 : if (!callDebuggerMethod(debuggerMethod, 1, argv, true).ToLocal(&scopesValue))
745 0 : return v8::MaybeLocal<v8::Value>();
746 : v8::Local<v8::Value> copied;
747 60 : if (!copyValueFromDebuggerContext(m_isolate, debuggerContext(), context,
748 60 : scopesValue)
749 180 : .ToLocal(&copied) ||
750 60 : !copied->IsArray())
751 0 : return v8::MaybeLocal<v8::Value>();
752 60 : if (!markAsInternal(context, v8::Local<v8::Array>::Cast(copied),
753 60 : V8InternalValueType::kScopeList))
754 0 : return v8::MaybeLocal<v8::Value>();
755 60 : if (!markArrayEntriesAsInternal(context, v8::Local<v8::Array>::Cast(copied),
756 60 : V8InternalValueType::kScope))
757 0 : return v8::MaybeLocal<v8::Value>();
758 60 : return copied;
759 : }
760 :
761 0 : v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
762 : v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
763 24 : return getTargetScopes(context, function, FUNCTION);
764 : }
765 :
766 0 : v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
767 : v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
768 36 : return getTargetScopes(context, generator, GENERATOR);
769 : }
770 :
771 106344 : v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
772 : v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
773 : v8::Local<v8::Array> properties;
774 212688 : if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
775 0 : return v8::MaybeLocal<v8::Array>();
776 106344 : if (value->IsFunction()) {
777 : v8::Local<v8::Function> function = value.As<v8::Function>();
778 : v8::Local<v8::Object> location;
779 30 : if (buildLocation(context, function->ScriptId(),
780 : function->GetScriptLineNumber(),
781 30 : function->GetScriptColumnNumber())
782 60 : .ToLocal(&location)) {
783 : createDataProperty(
784 24 : context, properties, properties->Length(),
785 48 : toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
786 24 : createDataProperty(context, properties, properties->Length(), location);
787 : }
788 30 : if (function->IsGeneratorFunction()) {
789 6 : createDataProperty(context, properties, properties->Length(),
790 12 : toV8StringInternalized(m_isolate, "[[IsGenerator]]"));
791 6 : createDataProperty(context, properties, properties->Length(),
792 12 : v8::True(m_isolate));
793 : }
794 : }
795 : v8::Local<v8::Array> entries;
796 212688 : if (collectionsEntries(context, value).ToLocal(&entries)) {
797 254 : createDataProperty(context, properties, properties->Length(),
798 508 : toV8StringInternalized(m_isolate, "[[Entries]]"));
799 254 : createDataProperty(context, properties, properties->Length(), entries);
800 : }
801 106344 : if (value->IsGeneratorObject()) {
802 : v8::Local<v8::Object> location;
803 108 : if (generatorObjectLocation(context, value).ToLocal(&location)) {
804 : createDataProperty(
805 54 : context, properties, properties->Length(),
806 108 : toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
807 54 : createDataProperty(context, properties, properties->Length(), location);
808 : }
809 54 : if (!enabled()) return properties;
810 : v8::Local<v8::Value> scopes;
811 36 : if (generatorScopes(context, value).ToLocal(&scopes)) {
812 36 : createDataProperty(context, properties, properties->Length(),
813 72 : toV8StringInternalized(m_isolate, "[[Scopes]]"));
814 36 : createDataProperty(context, properties, properties->Length(), scopes);
815 : }
816 : }
817 106326 : if (!enabled()) return properties;
818 106032 : if (value->IsFunction()) {
819 : v8::Local<v8::Function> function = value.As<v8::Function>();
820 24 : v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
821 : v8::Local<v8::Value> scopes;
822 48 : if (boundFunction->IsUndefined() &&
823 : functionScopes(context, function).ToLocal(&scopes)) {
824 24 : createDataProperty(context, properties, properties->Length(),
825 48 : toV8StringInternalized(m_isolate, "[[Scopes]]"));
826 24 : createDataProperty(context, properties, properties->Length(), scopes);
827 : }
828 : }
829 106032 : return properties;
830 : }
831 :
832 120 : std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
833 : v8::Local<v8::StackTrace> v8StackTrace) {
834 : return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
835 120 : V8StackTraceImpl::maxCallStackSizeToCapture);
836 : }
837 :
838 4543 : void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
839 4543 : if (depth <= 0)
840 : m_maxAsyncCallStackDepthMap.erase(agent);
841 : else
842 168 : m_maxAsyncCallStackDepthMap[agent] = depth;
843 :
844 : int maxAsyncCallStackDepth = 0;
845 9254 : for (const auto& pair : m_maxAsyncCallStackDepthMap) {
846 168 : if (pair.second > maxAsyncCallStackDepth)
847 : maxAsyncCallStackDepth = pair.second;
848 : }
849 :
850 9086 : if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
851 216 : m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
852 216 : if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
853 : }
854 :
855 36980 : void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) {
856 68488 : if (!m_maxAsyncCallStackDepth) return;
857 9264 : if (parentTask) m_parentTask[task] = parentTask;
858 5472 : v8::HandleScope scope(m_isolate);
859 : std::shared_ptr<AsyncStackTrace> asyncCreation =
860 10944 : AsyncStackTrace::capture(this, currentContextGroupId(), String16(), 1);
861 : // Passing one as maxStackSize forces no async chain for the new stack.
862 5472 : if (asyncCreation && !asyncCreation->isEmpty()) {
863 : m_asyncTaskCreationStacks[task] = asyncCreation;
864 3516 : m_allAsyncStacks.push_back(std::move(asyncCreation));
865 3516 : ++m_asyncStacksCount;
866 3516 : collectOldAsyncStacksIfNeeded();
867 5472 : }
868 : }
869 :
870 1482 : void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
871 : bool recurring) {
872 2964 : asyncTaskScheduledForStack(toString16(taskName), task, recurring);
873 1482 : asyncTaskCandidateForStepping(task);
874 1482 : }
875 :
876 0 : void V8Debugger::asyncTaskCanceled(void* task) {
877 0 : asyncTaskCanceledForStack(task);
878 : asyncTaskCanceledForStepping(task);
879 0 : }
880 :
881 1482 : void V8Debugger::asyncTaskStarted(void* task) {
882 1482 : asyncTaskStartedForStack(task);
883 : asyncTaskStartedForStepping(task);
884 1482 : }
885 :
886 1482 : void V8Debugger::asyncTaskFinished(void* task) {
887 1482 : asyncTaskFinishedForStack(task);
888 : asyncTaskFinishedForStepping(task);
889 1482 : }
890 :
891 46124 : void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
892 : void* task, bool recurring) {
893 85612 : if (!m_maxAsyncCallStackDepth) return;
894 6636 : v8::HandleScope scope(m_isolate);
895 : std::shared_ptr<AsyncStackTrace> asyncStack =
896 : AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
897 6636 : V8StackTraceImpl::maxCallStackSizeToCapture);
898 6636 : if (asyncStack) {
899 : m_asyncTaskStacks[task] = asyncStack;
900 6252 : if (recurring) m_recurringTasks.insert(task);
901 6252 : m_allAsyncStacks.push_back(std::move(asyncStack));
902 6252 : ++m_asyncStacksCount;
903 6252 : collectOldAsyncStacksIfNeeded();
904 6636 : }
905 : }
906 :
907 2022 : void V8Debugger::asyncTaskCanceledForStack(void* task) {
908 4044 : if (!m_maxAsyncCallStackDepth) return;
909 : m_asyncTaskStacks.erase(task);
910 : m_recurringTasks.erase(task);
911 : m_parentTask.erase(task);
912 : m_asyncTaskCreationStacks.erase(task);
913 : }
914 :
915 20460 : void V8Debugger::asyncTaskStartedForStack(void* task) {
916 36000 : if (!m_maxAsyncCallStackDepth) return;
917 4920 : m_currentTasks.push_back(task);
918 : auto parentIt = m_parentTask.find(task);
919 : AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(
920 4920 : parentIt == m_parentTask.end() ? task : parentIt->second);
921 : // Needs to support following order of events:
922 : // - asyncTaskScheduled
923 : // <-- attached here -->
924 : // - asyncTaskStarted
925 : // - asyncTaskCanceled <-- canceled before finished
926 : // <-- async stack requested here -->
927 : // - asyncTaskFinished
928 4920 : std::weak_ptr<AsyncStackTrace> asyncParent;
929 4920 : if (stackIt != m_asyncTaskStacks.end()) asyncParent = stackIt->second;
930 : auto itCreation = m_asyncTaskCreationStacks.find(task);
931 9840 : if (asyncParent.lock() && itCreation != m_asyncTaskCreationStacks.end()) {
932 4584 : m_currentAsyncCreation.push_back(itCreation->second.lock());
933 : } else {
934 2628 : m_currentAsyncCreation.emplace_back();
935 : }
936 9840 : m_currentAsyncParent.push_back(asyncParent.lock());
937 : }
938 :
939 20460 : void V8Debugger::asyncTaskFinishedForStack(void* task) {
940 20460 : if (!m_maxAsyncCallStackDepth) return;
941 : // We could start instrumenting half way and the stack is empty.
942 9864 : if (!m_currentTasks.size()) return;
943 : DCHECK(m_currentTasks.back() == task);
944 : m_currentTasks.pop_back();
945 :
946 : DCHECK(m_currentAsyncParent.size() == m_currentAsyncCreation.size());
947 : m_currentAsyncParent.pop_back();
948 : m_currentAsyncCreation.pop_back();
949 :
950 4920 : if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
951 2022 : asyncTaskCanceledForStack(task);
952 : }
953 : }
954 :
955 15458 : void V8Debugger::asyncTaskCandidateForStepping(void* task) {
956 15458 : if (!m_stepIntoAsyncCallback) return;
957 : DCHECK(m_targetContextGroupId);
958 72 : if (currentContextGroupId() != m_targetContextGroupId) return;
959 72 : m_taskWithScheduledBreak = task;
960 72 : v8::debug::ClearStepping(m_isolate);
961 72 : m_stepIntoAsyncCallback->sendSuccess();
962 : m_stepIntoAsyncCallback.reset();
963 : }
964 :
965 0 : void V8Debugger::asyncTaskStartedForStepping(void* task) {
966 20460 : if (m_breakRequested) return;
967 20460 : if (task != m_taskWithScheduledBreak) return;
968 66 : v8::debug::DebugBreak(m_isolate);
969 : }
970 :
971 0 : void V8Debugger::asyncTaskFinishedForStepping(void* task) {
972 20460 : if (task != m_taskWithScheduledBreak) return;
973 66 : m_taskWithScheduledBreak = nullptr;
974 66 : if (m_breakRequested) return;
975 66 : v8::debug::CancelDebugBreak(m_isolate);
976 : }
977 :
978 0 : void V8Debugger::asyncTaskCanceledForStepping(void* task) {
979 0 : if (task != m_taskWithScheduledBreak) return;
980 0 : m_taskWithScheduledBreak = nullptr;
981 : }
982 :
983 4399 : void V8Debugger::allAsyncTasksCanceled() {
984 : m_asyncTaskStacks.clear();
985 : m_recurringTasks.clear();
986 : m_currentAsyncParent.clear();
987 : m_currentAsyncCreation.clear();
988 : m_currentTasks.clear();
989 : m_parentTask.clear();
990 : m_asyncTaskCreationStacks.clear();
991 :
992 : m_framesCache.clear();
993 : m_allAsyncStacks.clear();
994 4399 : m_asyncStacksCount = 0;
995 4399 : }
996 :
997 30 : void V8Debugger::muteScriptParsedEvents() {
998 30 : ++m_ignoreScriptParsedEventsCounter;
999 30 : }
1000 :
1001 30 : void V8Debugger::unmuteScriptParsedEvents() {
1002 30 : --m_ignoreScriptParsedEventsCounter;
1003 : DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
1004 30 : }
1005 :
1006 7334 : std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
1007 : bool fullStack) {
1008 7334 : if (!m_isolate->InContext()) return nullptr;
1009 :
1010 7334 : v8::HandleScope handles(m_isolate);
1011 7334 : int contextGroupId = currentContextGroupId();
1012 7334 : if (!contextGroupId) return nullptr;
1013 :
1014 : int stackSize = 1;
1015 7334 : if (fullStack || m_inspector->enabledRuntimeAgentForGroup(contextGroupId)) {
1016 : stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
1017 : }
1018 7334 : return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
1019 : }
1020 :
1021 19634 : int V8Debugger::currentContextGroupId() {
1022 19634 : if (!m_isolate->InContext()) return 0;
1023 19634 : return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
1024 : }
1025 :
1026 10092 : void V8Debugger::collectOldAsyncStacksIfNeeded() {
1027 20184 : if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
1028 : int halfOfLimitRoundedUp =
1029 468 : m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
1030 2028 : while (m_asyncStacksCount > halfOfLimitRoundedUp) {
1031 : m_allAsyncStacks.pop_front();
1032 1092 : --m_asyncStacksCount;
1033 : }
1034 468 : cleanupExpiredWeakPointers(m_asyncTaskStacks);
1035 468 : cleanupExpiredWeakPointers(m_asyncTaskCreationStacks);
1036 1362 : for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
1037 852 : if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
1038 : it = m_recurringTasks.erase(it);
1039 : } else {
1040 : ++it;
1041 : }
1042 : }
1043 1260 : for (auto it = m_parentTask.begin(); it != m_parentTask.end();) {
1044 648 : if (m_asyncTaskCreationStacks.find(it->second) ==
1045 : m_asyncTaskCreationStacks.end()) {
1046 : it = m_parentTask.erase(it);
1047 : } else {
1048 : ++it;
1049 : }
1050 : }
1051 468 : cleanupExpiredWeakPointers(m_framesCache);
1052 : }
1053 :
1054 46529 : std::shared_ptr<StackFrame> V8Debugger::symbolize(
1055 : v8::Local<v8::StackFrame> v8Frame) {
1056 : auto it = m_framesCache.end();
1057 46529 : int frameId = 0;
1058 46529 : if (m_maxAsyncCallStackDepth) {
1059 6378 : frameId = v8::debug::GetStackFrameId(v8Frame);
1060 : it = m_framesCache.find(frameId);
1061 : }
1062 47927 : if (it != m_framesCache.end() && it->second.lock()) return it->second.lock();
1063 90262 : std::shared_ptr<StackFrame> frame(new StackFrame(v8Frame));
1064 : // TODO(clemensh): Figure out a way to do this translation only right before
1065 : // sending the stack trace over wire.
1066 45131 : if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation);
1067 45131 : if (m_maxAsyncCallStackDepth) {
1068 4980 : m_framesCache[frameId] = frame;
1069 : }
1070 : return frame;
1071 : }
1072 :
1073 324 : void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
1074 324 : m_maxAsyncCallStacks = 0;
1075 324 : collectOldAsyncStacksIfNeeded();
1076 324 : m_maxAsyncCallStacks = limit;
1077 324 : }
1078 :
1079 36 : void V8Debugger::dumpAsyncTaskStacksStateForTest() {
1080 36 : fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount);
1081 36 : fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
1082 : fprintf(stdout, "Created async tasks: %zu\n",
1083 36 : m_asyncTaskCreationStacks.size());
1084 36 : fprintf(stdout, "Async tasks with parent: %zu\n", m_parentTask.size());
1085 36 : fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size());
1086 36 : fprintf(stdout, "\n");
1087 36 : }
1088 :
1089 : } // namespace v8_inspector
|