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