Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/inspector/v8-debugger.h"
6 :
7 : #include "src/inspector/inspected-context.h"
8 : #include "src/inspector/protocol/Protocol.h"
9 : #include "src/inspector/string-util.h"
10 : #include "src/inspector/v8-debugger-agent-impl.h"
11 : #include "src/inspector/v8-inspector-impl.h"
12 : #include "src/inspector/v8-inspector-session-impl.h"
13 : #include "src/inspector/v8-internal-value-type.h"
14 : #include "src/inspector/v8-runtime-agent-impl.h"
15 : #include "src/inspector/v8-stack-trace-impl.h"
16 : #include "src/inspector/v8-value-utils.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 : static const int kNoBreakpointId = 0;
26 :
27 67176 : v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
28 : v8::Local<v8::Value> value) {
29 67176 : v8::Isolate* isolate = context->GetIsolate();
30 : v8::Local<v8::Array> entries;
31 67176 : bool isKeyValue = false;
32 134352 : if (!v8::debug::EntriesPreview(isolate, value, &isKeyValue).ToLocal(&entries))
33 66975 : return v8::MaybeLocal<v8::Array>();
34 :
35 201 : v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
36 201 : CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
37 201 : if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
38 402 : .FromMaybe(false))
39 0 : return v8::MaybeLocal<v8::Array>();
40 10389 : for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
41 : v8::Local<v8::Value> item;
42 20778 : if (!entries->Get(context, i).ToLocal(&item)) continue;
43 : v8::Local<v8::Value> value;
44 15507 : if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
45 10389 : v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
46 20778 : if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
47 : continue;
48 : createDataProperty(
49 : context, wrapper,
50 20778 : toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
51 10389 : if (isKeyValue) {
52 : createDataProperty(context, wrapper,
53 10236 : toV8StringInternalized(isolate, "value"), value);
54 : }
55 10389 : createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
56 10389 : wrapper);
57 : }
58 201 : if (!markArrayEntriesAsInternal(context, wrappedEntries,
59 201 : V8InternalValueType::kEntry)) {
60 0 : return v8::MaybeLocal<v8::Array>();
61 : }
62 201 : return wrappedEntries;
63 : }
64 :
65 75 : v8::MaybeLocal<v8::Object> buildLocation(v8::Local<v8::Context> context,
66 : int scriptId, int lineNumber,
67 : int columnNumber) {
68 75 : if (scriptId == v8::UnboundScript::kNoScriptId)
69 5 : return v8::MaybeLocal<v8::Object>();
70 70 : if (lineNumber == v8::Function::kLineOffsetNotFound ||
71 : columnNumber == v8::Function::kLineOffsetNotFound) {
72 0 : return v8::MaybeLocal<v8::Object>();
73 : }
74 70 : v8::Isolate* isolate = context->GetIsolate();
75 70 : v8::Local<v8::Object> location = v8::Object::New(isolate);
76 140 : if (!location->SetPrototype(context, v8::Null(isolate)).FromMaybe(false)) {
77 0 : return v8::MaybeLocal<v8::Object>();
78 : }
79 70 : if (!createDataProperty(context, location,
80 : toV8StringInternalized(isolate, "scriptId"),
81 280 : toV8String(isolate, String16::fromInteger(scriptId)))
82 140 : .FromMaybe(false)) {
83 0 : return v8::MaybeLocal<v8::Object>();
84 : }
85 70 : if (!createDataProperty(context, location,
86 : toV8StringInternalized(isolate, "lineNumber"),
87 210 : v8::Integer::New(isolate, lineNumber))
88 140 : .FromMaybe(false)) {
89 0 : return v8::MaybeLocal<v8::Object>();
90 : }
91 70 : if (!createDataProperty(context, location,
92 : toV8StringInternalized(isolate, "columnNumber"),
93 210 : v8::Integer::New(isolate, columnNumber))
94 140 : .FromMaybe(false)) {
95 0 : return v8::MaybeLocal<v8::Object>();
96 : }
97 70 : if (!markAsInternal(context, location, V8InternalValueType::kLocation)) {
98 0 : return v8::MaybeLocal<v8::Object>();
99 : }
100 70 : return location;
101 : }
102 :
103 45 : v8::MaybeLocal<v8::Object> generatorObjectLocation(
104 : v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
105 45 : if (!value->IsGeneratorObject()) return v8::MaybeLocal<v8::Object>();
106 : v8::Local<v8::debug::GeneratorObject> generatorObject =
107 45 : v8::debug::GeneratorObject::Cast(value);
108 45 : if (!generatorObject->IsSuspended()) {
109 10 : v8::Local<v8::Function> func = generatorObject->Function();
110 : return buildLocation(context, func->ScriptId(), func->GetScriptLineNumber(),
111 10 : func->GetScriptColumnNumber());
112 : }
113 : v8::Local<v8::debug::Script> script;
114 70 : if (!generatorObject->Script().ToLocal(&script))
115 0 : return v8::MaybeLocal<v8::Object>();
116 35 : v8::debug::Location suspendedLocation = generatorObject->SuspendedLocation();
117 : return buildLocation(context, script->Id(), suspendedLocation.GetLineNumber(),
118 35 : suspendedLocation.GetColumnNumber());
119 : }
120 :
121 : template <typename Map>
122 1410 : void cleanupExpiredWeakPointers(Map& map) {
123 14120 : for (auto it = map.begin(); it != map.end();) {
124 11300 : if (it->second.expired()) {
125 : it = map.erase(it);
126 : } else {
127 : ++it;
128 : }
129 : }
130 1410 : }
131 :
132 65 : class MatchPrototypePredicate : public v8::debug::QueryObjectPredicate {
133 : public:
134 : MatchPrototypePredicate(V8InspectorImpl* inspector,
135 : v8::Local<v8::Context> context,
136 : v8::Local<v8::Object> prototype)
137 65 : : m_inspector(inspector), m_context(context), m_prototype(prototype) {}
138 :
139 221575 : bool Filter(v8::Local<v8::Object> object) override {
140 221575 : v8::Local<v8::Context> objectContext = object->CreationContext();
141 221575 : if (objectContext != m_context) return false;
142 61615 : if (!m_inspector->client()->isInspectableHeapObject(object)) return false;
143 : // Get prototype chain for current object until first visited prototype.
144 179035 : for (v8::Local<v8::Value> prototype = object->GetPrototype();
145 : prototype->IsObject();
146 : prototype = prototype.As<v8::Object>()->GetPrototype()) {
147 117510 : if (m_prototype == prototype) return true;
148 : }
149 : return false;
150 : }
151 :
152 : private:
153 : V8InspectorImpl* m_inspector;
154 : v8::Local<v8::Context> m_context;
155 : v8::Local<v8::Value> m_prototype;
156 : };
157 :
158 : } // namespace
159 :
160 3261 : V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
161 : : m_isolate(isolate),
162 : m_inspector(inspector),
163 : m_enableCount(0),
164 : m_ignoreScriptParsedEventsCounter(0),
165 : m_continueToLocationBreakpointId(kNoBreakpointId),
166 : m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
167 : m_maxAsyncCallStackDepth(0),
168 : m_pauseOnExceptionsState(v8::debug::NoBreakOnException),
169 22827 : m_wasmTranslation(isolate) {}
170 :
171 13044 : V8Debugger::~V8Debugger() {}
172 :
173 3126 : void V8Debugger::enable() {
174 3176 : if (m_enableCount++) return;
175 3076 : v8::HandleScope scope(m_isolate);
176 3076 : v8::debug::SetDebugDelegate(m_isolate, this);
177 : v8::debug::SetOutOfMemoryCallback(m_isolate, &V8Debugger::v8OOMCallback,
178 3076 : this);
179 3076 : v8::debug::ChangeBreakOnException(m_isolate, v8::debug::NoBreakOnException);
180 3076 : m_pauseOnExceptionsState = v8::debug::NoBreakOnException;
181 : }
182 :
183 3126 : void V8Debugger::disable() {
184 6252 : if (--m_enableCount) return;
185 3076 : clearContinueToLocation();
186 3076 : allAsyncTasksCanceled();
187 3076 : m_taskWithScheduledBreak = nullptr;
188 3076 : m_wasmTranslation.Clear();
189 3076 : v8::debug::SetDebugDelegate(m_isolate, nullptr);
190 3076 : v8::debug::SetOutOfMemoryCallback(m_isolate, nullptr, nullptr);
191 3076 : m_isolate->RestoreOriginalHeapLimit();
192 : }
193 :
194 107870 : bool V8Debugger::isPausedInContextGroup(int contextGroupId) const {
195 107870 : return isPaused() && m_pausedContextGroupId == contextGroupId;
196 : }
197 :
198 140636 : bool V8Debugger::enabled() const { return m_enableCount > 0; }
199 :
200 3126 : void V8Debugger::getCompiledScripts(
201 : int contextGroupId,
202 : std::vector<std::unique_ptr<V8DebuggerScript>>& result) {
203 3126 : v8::HandleScope scope(m_isolate);
204 6252 : v8::PersistentValueVector<v8::debug::Script> scripts(m_isolate);
205 3126 : v8::debug::GetLoadedScripts(m_isolate, scripts);
206 22590 : for (size_t i = 0; i < scripts.Size(); ++i) {
207 8169 : v8::Local<v8::debug::Script> script = scripts.Get(i);
208 8169 : if (!script->WasCompiled()) continue;
209 8149 : if (script->IsEmbedded()) {
210 10 : result.push_back(V8DebuggerScript::Create(m_isolate, script, false));
211 5 : continue;
212 : }
213 : int contextId;
214 16288 : if (!script->ContextId().To(&contextId)) continue;
215 8144 : if (m_inspector->contextGroupId(contextId) != contextGroupId) continue;
216 16038 : result.push_back(V8DebuggerScript::Create(m_isolate, script, false));
217 3126 : }
218 3126 : }
219 :
220 6354 : void V8Debugger::setBreakpointsActive(bool active) {
221 6354 : if (!enabled()) {
222 0 : UNREACHABLE();
223 : return;
224 : }
225 6354 : m_breakpointsActiveCount += active ? 1 : -1;
226 6354 : v8::debug::SetBreakPointsActive(m_isolate, m_breakpointsActiveCount);
227 6354 : }
228 :
229 133562 : v8::debug::ExceptionBreakState V8Debugger::getPauseOnExceptionsState() {
230 : DCHECK(enabled());
231 133562 : return m_pauseOnExceptionsState;
232 : }
233 :
234 4909 : void V8Debugger::setPauseOnExceptionsState(
235 : v8::debug::ExceptionBreakState pauseOnExceptionsState) {
236 : DCHECK(enabled());
237 9818 : if (m_pauseOnExceptionsState == pauseOnExceptionsState) return;
238 3885 : v8::debug::ChangeBreakOnException(m_isolate, pauseOnExceptionsState);
239 3885 : m_pauseOnExceptionsState = pauseOnExceptionsState;
240 : }
241 :
242 315 : void V8Debugger::setPauseOnNextStatement(bool pause, int targetContextGroupId) {
243 315 : if (isPaused()) return;
244 : DCHECK(targetContextGroupId);
245 315 : if (!pause && m_targetContextGroupId &&
246 : m_targetContextGroupId != targetContextGroupId) {
247 : return;
248 : }
249 315 : m_targetContextGroupId = targetContextGroupId;
250 315 : m_breakRequested = pause;
251 315 : if (pause)
252 295 : v8::debug::DebugBreak(m_isolate);
253 : else
254 20 : v8::debug::CancelDebugBreak(m_isolate);
255 : }
256 :
257 65 : bool V8Debugger::canBreakProgram() {
258 105 : return !v8::debug::AllFramesOnStackAreBlackboxed(m_isolate);
259 : }
260 :
261 60 : void V8Debugger::breakProgram(int targetContextGroupId) {
262 : DCHECK(canBreakProgram());
263 : // Don't allow nested breaks.
264 120 : if (isPaused()) return;
265 : DCHECK(targetContextGroupId);
266 60 : m_targetContextGroupId = targetContextGroupId;
267 60 : v8::debug::BreakRightNow(m_isolate);
268 : }
269 :
270 1726 : void V8Debugger::continueProgram(int targetContextGroupId) {
271 43173 : if (m_pausedContextGroupId != targetContextGroupId) return;
272 41447 : if (isPaused()) m_inspector->client()->quitMessageLoopOnPause();
273 : }
274 :
275 120 : void V8Debugger::breakProgramOnAssert(int targetContextGroupId) {
276 80 : if (!enabled()) return;
277 60 : if (m_pauseOnExceptionsState == v8::debug::NoBreakOnException) return;
278 : // Don't allow nested breaks.
279 40 : if (isPaused()) return;
280 40 : if (!canBreakProgram()) return;
281 : DCHECK(targetContextGroupId);
282 35 : m_targetContextGroupId = targetContextGroupId;
283 35 : m_scheduledAssertBreak = true;
284 35 : v8::debug::BreakRightNow(m_isolate);
285 : }
286 :
287 31732 : void V8Debugger::stepIntoStatement(int targetContextGroupId) {
288 : DCHECK(isPaused());
289 : DCHECK(targetContextGroupId);
290 31732 : m_targetContextGroupId = targetContextGroupId;
291 31732 : v8::debug::PrepareStep(m_isolate, v8::debug::StepIn);
292 : continueProgram(targetContextGroupId);
293 31732 : }
294 :
295 7489 : void V8Debugger::stepOverStatement(int targetContextGroupId) {
296 : DCHECK(isPaused());
297 : DCHECK(targetContextGroupId);
298 7489 : m_targetContextGroupId = targetContextGroupId;
299 7489 : v8::debug::PrepareStep(m_isolate, v8::debug::StepNext);
300 : continueProgram(targetContextGroupId);
301 7489 : }
302 :
303 440 : void V8Debugger::stepOutOfFunction(int targetContextGroupId) {
304 : DCHECK(isPaused());
305 : DCHECK(targetContextGroupId);
306 440 : m_targetContextGroupId = targetContextGroupId;
307 440 : v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
308 : continueProgram(targetContextGroupId);
309 440 : }
310 :
311 75 : void V8Debugger::scheduleStepIntoAsync(
312 : std::unique_ptr<ScheduleStepIntoAsyncCallback> callback,
313 : int targetContextGroupId) {
314 : DCHECK(isPaused());
315 : DCHECK(targetContextGroupId);
316 75 : if (m_stepIntoAsyncCallback) {
317 : m_stepIntoAsyncCallback->sendFailure(Response::Error(
318 15 : "Current scheduled step into async was overriden with new one."));
319 : }
320 75 : m_targetContextGroupId = targetContextGroupId;
321 : m_stepIntoAsyncCallback = std::move(callback);
322 75 : }
323 :
324 60 : Response V8Debugger::continueToLocation(
325 : int targetContextGroupId, V8DebuggerScript* script,
326 : std::unique_ptr<protocol::Debugger::Location> location,
327 : const String16& targetCallFrames) {
328 : DCHECK(isPaused());
329 : DCHECK(targetContextGroupId);
330 60 : m_targetContextGroupId = targetContextGroupId;
331 : v8::debug::Location v8Location(location->getLineNumber(),
332 60 : location->getColumnNumber(0));
333 120 : if (script->setBreakpoint(String16(), &v8Location,
334 120 : &m_continueToLocationBreakpointId)) {
335 60 : m_continueToLocationTargetCallFrames = targetCallFrames;
336 120 : if (m_continueToLocationTargetCallFrames !=
337 : protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
338 30 : m_continueToLocationStack = captureStackTrace(true);
339 : DCHECK(m_continueToLocationStack);
340 : }
341 : continueProgram(targetContextGroupId);
342 : // TODO(kozyatinskiy): Return actual line and column number.
343 60 : return Response::OK();
344 : } else {
345 0 : return Response::Error("Cannot continue to specified location");
346 : }
347 : }
348 :
349 90 : bool V8Debugger::shouldContinueToCurrentLocation() {
350 180 : if (m_continueToLocationTargetCallFrames ==
351 : protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
352 : return true;
353 : }
354 45 : std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true);
355 90 : if (m_continueToLocationTargetCallFrames ==
356 : protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
357 : return m_continueToLocationStack->isEqualIgnoringTopFrame(
358 45 : currentStack.get());
359 : }
360 : return true;
361 : }
362 :
363 52796 : void V8Debugger::clearContinueToLocation() {
364 105592 : if (m_continueToLocationBreakpointId == kNoBreakpointId) return;
365 60 : v8::debug::RemoveBreakpoint(m_isolate, m_continueToLocationBreakpointId);
366 60 : m_continueToLocationBreakpointId = kNoBreakpointId;
367 120 : m_continueToLocationTargetCallFrames = String16();
368 : m_continueToLocationStack.reset();
369 : }
370 :
371 49780 : void V8Debugger::handleProgramBreak(
372 : v8::Local<v8::Context> pausedContext, v8::Local<v8::Value> exception,
373 49750 : const std::vector<v8::debug::BreakpointId>& breakpointIds,
374 49780 : bool isPromiseRejection, bool isUncaught) {
375 : // Don't allow nested breaks.
376 49840 : if (isPaused()) return;
377 :
378 99500 : int contextGroupId = m_inspector->contextGroupId(pausedContext);
379 49780 : if (m_targetContextGroupId && contextGroupId != m_targetContextGroupId) {
380 20 : v8::debug::PrepareStep(m_isolate, v8::debug::StepOut);
381 20 : return;
382 : }
383 49760 : m_targetContextGroupId = 0;
384 49760 : if (m_stepIntoAsyncCallback) {
385 : m_stepIntoAsyncCallback->sendFailure(
386 30 : Response::Error("No async tasks were scheduled before pause."));
387 : m_stepIntoAsyncCallback.reset();
388 : }
389 49760 : m_breakRequested = false;
390 :
391 49760 : bool scheduledOOMBreak = m_scheduledOOMBreak;
392 49760 : bool scheduledAssertBreak = m_scheduledAssertBreak;
393 49760 : bool hasAgents = false;
394 : m_inspector->forEachSession(
395 : contextGroupId,
396 49870 : [&scheduledOOMBreak, &hasAgents](V8InspectorSessionImpl* session) {
397 99740 : if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak))
398 49845 : hasAgents = true;
399 149390 : });
400 49760 : if (!hasAgents) return;
401 :
402 52028 : if (breakpointIds.size() == 1 &&
403 2278 : breakpointIds[0] == m_continueToLocationBreakpointId) {
404 : v8::Context::Scope contextScope(pausedContext);
405 90 : if (!shouldContinueToCurrentLocation()) return;
406 : }
407 49720 : clearContinueToLocation();
408 :
409 : DCHECK(contextGroupId);
410 49720 : m_pausedContextGroupId = contextGroupId;
411 :
412 : m_inspector->forEachSession(
413 : contextGroupId, [&pausedContext, &exception, &breakpointIds,
414 : &isPromiseRejection, &isUncaught, &scheduledOOMBreak,
415 49825 : &scheduledAssertBreak](V8InspectorSessionImpl* session) {
416 99650 : if (session->debuggerAgent()->acceptsPause(scheduledOOMBreak)) {
417 : session->debuggerAgent()->didPause(
418 : InspectedContext::contextId(pausedContext), exception,
419 : breakpointIds, isPromiseRejection, isUncaught, scheduledOOMBreak,
420 99630 : scheduledAssertBreak);
421 : }
422 149265 : });
423 : {
424 : v8::Context::Scope scope(pausedContext);
425 49720 : v8::Local<v8::Context> context = m_isolate->GetCurrentContext();
426 99440 : CHECK(!context.IsEmpty() &&
427 : context != v8::debug::GetDebugContext(m_isolate));
428 99440 : m_inspector->client()->runMessageLoopOnPause(contextGroupId);
429 49720 : m_pausedContextGroupId = 0;
430 : }
431 : m_inspector->forEachSession(contextGroupId,
432 : [](V8InspectorSessionImpl* session) {
433 49825 : if (session->debuggerAgent()->enabled())
434 49679 : session->debuggerAgent()->didContinue();
435 99440 : });
436 :
437 49720 : if (m_scheduledOOMBreak) m_isolate->RestoreOriginalHeapLimit();
438 49720 : m_scheduledOOMBreak = false;
439 49720 : m_scheduledAssertBreak = false;
440 : }
441 :
442 5 : void V8Debugger::v8OOMCallback(void* data) {
443 : V8Debugger* thisPtr = static_cast<V8Debugger*>(data);
444 5 : thisPtr->m_isolate->IncreaseHeapLimitForDebugging();
445 5 : thisPtr->m_scheduledOOMBreak = true;
446 5 : v8::Local<v8::Context> context = thisPtr->m_isolate->GetEnteredContext();
447 : DCHECK(!context.IsEmpty());
448 : thisPtr->setPauseOnNextStatement(
449 5 : true, thisPtr->m_inspector->contextGroupId(context));
450 5 : }
451 :
452 17872 : void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
453 : bool is_live_edited, bool has_compile_error) {
454 : int contextId;
455 53616 : if (!script->ContextId().To(&contextId)) return;
456 17827 : if (script->IsWasm()) {
457 49 : WasmTranslation* wasmTranslation = &m_wasmTranslation;
458 : m_inspector->forEachSession(
459 : m_inspector->contextGroupId(contextId),
460 49 : [&script, &wasmTranslation](V8InspectorSessionImpl* session) {
461 98 : if (!session->debuggerAgent()->enabled()) return;
462 : wasmTranslation->AddScript(script.As<v8::debug::WasmScript>(),
463 98 : session->debuggerAgent());
464 98 : });
465 17778 : } else if (m_ignoreScriptParsedEventsCounter == 0) {
466 17753 : v8::Isolate* isolate = m_isolate;
467 : m_inspector->forEachSession(
468 : m_inspector->contextGroupId(contextId),
469 : [&isolate, &script, &has_compile_error,
470 17868 : &is_live_edited](V8InspectorSessionImpl* session) {
471 35736 : if (!session->debuggerAgent()->enabled()) return;
472 : session->debuggerAgent()->didParseSource(
473 : V8DebuggerScript::Create(isolate, script, is_live_edited),
474 53589 : !has_compile_error);
475 35506 : });
476 : }
477 : }
478 :
479 48044 : void V8Debugger::BreakProgramRequested(
480 : v8::Local<v8::Context> pausedContext, v8::Local<v8::Object>,
481 : v8::Local<v8::Value>,
482 : const std::vector<v8::debug::BreakpointId>& break_points_hit) {
483 48044 : handleProgramBreak(pausedContext, v8::Local<v8::Value>(), break_points_hit);
484 48044 : }
485 :
486 1736 : void V8Debugger::ExceptionThrown(v8::Local<v8::Context> pausedContext,
487 : v8::Local<v8::Object>,
488 : v8::Local<v8::Value> exception,
489 : v8::Local<v8::Value> promise,
490 : bool isUncaught) {
491 1736 : bool isPromiseRejection = promise->IsPromise();
492 : std::vector<v8::debug::BreakpointId> break_points_hit;
493 : handleProgramBreak(pausedContext, exception, break_points_hit,
494 1736 : isPromiseRejection, isUncaught);
495 1736 : }
496 :
497 11626 : bool V8Debugger::IsFunctionBlackboxed(v8::Local<v8::debug::Script> script,
498 : const v8::debug::Location& start,
499 : const v8::debug::Location& end) {
500 : int contextId;
501 23252 : if (!script->ContextId().To(&contextId)) return false;
502 11621 : bool hasAgents = false;
503 11621 : bool allBlackboxed = true;
504 11621 : String16 scriptId = String16::fromInteger(script->Id());
505 : m_inspector->forEachSession(
506 : m_inspector->contextGroupId(contextId),
507 : [&hasAgents, &allBlackboxed, &scriptId, &start,
508 11686 : &end](V8InspectorSessionImpl* session) {
509 11686 : V8DebuggerAgentImpl* agent = session->debuggerAgent();
510 23372 : if (!agent->enabled()) return;
511 11681 : hasAgents = true;
512 11681 : allBlackboxed &= agent->isFunctionBlackboxed(scriptId, start, end);
513 23242 : });
514 11621 : return hasAgents && allBlackboxed;
515 : }
516 :
517 116959 : void V8Debugger::PromiseEventOccurred(v8::debug::PromiseDebugActionType type,
518 : int id, int parentId,
519 : bool createdByUser) {
520 : // Async task events from Promises are given misaligned pointers to prevent
521 : // from overlapping with other Blink task identifiers.
522 116959 : void* task = reinterpret_cast<void*>(id * 2 + 1);
523 : void* parentTask =
524 116959 : parentId ? reinterpret_cast<void*>(parentId * 2 + 1) : nullptr;
525 116959 : switch (type) {
526 : case v8::debug::kDebugPromiseCreated:
527 42579 : asyncTaskCreatedForStack(task, parentTask);
528 42579 : if (createdByUser && parentTask) asyncTaskCandidateForStepping(task);
529 : break;
530 : case v8::debug::kDebugEnqueueAsyncFunction:
531 9162 : asyncTaskScheduledForStack("async function", task, true);
532 4581 : break;
533 : case v8::debug::kDebugEnqueuePromiseResolve:
534 76334 : asyncTaskScheduledForStack("Promise.resolve", task, true);
535 38167 : break;
536 : case v8::debug::kDebugEnqueuePromiseReject:
537 9302 : asyncTaskScheduledForStack("Promise.reject", task, true);
538 4651 : break;
539 : case v8::debug::kDebugWillHandle:
540 13493 : asyncTaskStartedForStack(task);
541 : asyncTaskStartedForStepping(task);
542 : break;
543 : case v8::debug::kDebugDidHandle:
544 13488 : asyncTaskFinishedForStack(task);
545 : asyncTaskFinishedForStepping(task);
546 : break;
547 : }
548 116959 : }
549 :
550 128194 : std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncParent() {
551 : // TODO(kozyatinskiy): implement creation chain as parent without hack.
552 128194 : if (!m_currentAsyncCreation.empty() && m_currentAsyncCreation.back()) {
553 : return m_currentAsyncCreation.back();
554 : }
555 124499 : return m_currentAsyncParent.empty() ? nullptr : m_currentAsyncParent.back();
556 : }
557 :
558 78575 : std::shared_ptr<AsyncStackTrace> V8Debugger::currentAsyncCreation() {
559 78575 : return nullptr;
560 : }
561 :
562 70 : v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
563 : v8::Local<v8::Context> context, v8::Local<v8::Value> value,
564 : ScopeTargetKind kind) {
565 : v8::Local<v8::Value> scopesValue;
566 : std::unique_ptr<v8::debug::ScopeIterator> iterator;
567 70 : switch (kind) {
568 : case FUNCTION:
569 50 : iterator = v8::debug::ScopeIterator::CreateForFunction(
570 : m_isolate, v8::Local<v8::Function>::Cast(value));
571 85 : break;
572 : case GENERATOR:
573 : v8::Local<v8::debug::GeneratorObject> generatorObject =
574 45 : v8::debug::GeneratorObject::Cast(value);
575 45 : if (!generatorObject->IsSuspended()) return v8::MaybeLocal<v8::Value>();
576 :
577 70 : iterator = v8::debug::ScopeIterator::CreateForGeneratorObject(
578 : m_isolate, v8::Local<v8::Object>::Cast(value));
579 35 : break;
580 : }
581 60 : if (!iterator) return v8::MaybeLocal<v8::Value>();
582 60 : v8::Local<v8::Array> result = v8::Array::New(m_isolate);
583 180 : if (!result->SetPrototype(context, v8::Null(m_isolate)).FromMaybe(false)) {
584 0 : return v8::MaybeLocal<v8::Value>();
585 : }
586 :
587 110 : for (; !iterator->Done(); iterator->Advance()) {
588 110 : v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
589 110 : if (!markAsInternal(context, scope, V8InternalValueType::kScope)) {
590 0 : return v8::MaybeLocal<v8::Value>();
591 : }
592 110 : String16 type = v8_inspector::scopeType(iterator->GetType());
593 110 : String16 name;
594 110 : v8::Local<v8::Function> closure = iterator->GetFunction();
595 110 : if (!closure.IsEmpty()) {
596 100 : name = toProtocolStringWithTypeCheck(closure->GetDebugName());
597 : }
598 110 : v8::Local<v8::Object> object = iterator->GetObject();
599 : createDataProperty(context, scope,
600 : toV8StringInternalized(m_isolate, "type"),
601 330 : toV8String(m_isolate, type));
602 : createDataProperty(context, scope,
603 : toV8StringInternalized(m_isolate, "name"),
604 330 : toV8String(m_isolate, name));
605 : createDataProperty(context, scope,
606 220 : toV8StringInternalized(m_isolate, "object"), object);
607 110 : createDataProperty(context, result, result->Length(), scope);
608 : }
609 60 : if (!markAsInternal(context, v8::Local<v8::Array>::Cast(result),
610 60 : V8InternalValueType::kScopeList))
611 0 : return v8::MaybeLocal<v8::Value>();
612 60 : return result;
613 : }
614 :
615 0 : v8::MaybeLocal<v8::Value> V8Debugger::functionScopes(
616 : v8::Local<v8::Context> context, v8::Local<v8::Function> function) {
617 25 : return getTargetScopes(context, function, FUNCTION);
618 : }
619 :
620 0 : v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
621 : v8::Local<v8::Context> context, v8::Local<v8::Value> generator) {
622 45 : return getTargetScopes(context, generator, GENERATOR);
623 : }
624 :
625 67176 : v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
626 : v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
627 : v8::Local<v8::Array> properties;
628 134352 : if (!v8::debug::GetInternalProperties(m_isolate, value).ToLocal(&properties))
629 0 : return v8::MaybeLocal<v8::Array>();
630 67176 : if (value->IsFunction()) {
631 : v8::Local<v8::Function> function = value.As<v8::Function>();
632 : v8::Local<v8::Object> location;
633 30 : if (buildLocation(context, function->ScriptId(),
634 : function->GetScriptLineNumber(),
635 30 : function->GetScriptColumnNumber())
636 30 : .ToLocal(&location)) {
637 : createDataProperty(
638 25 : context, properties, properties->Length(),
639 50 : toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
640 25 : createDataProperty(context, properties, properties->Length(), location);
641 : }
642 30 : if (function->IsGeneratorFunction()) {
643 5 : createDataProperty(context, properties, properties->Length(),
644 10 : toV8StringInternalized(m_isolate, "[[IsGenerator]]"));
645 5 : createDataProperty(context, properties, properties->Length(),
646 10 : v8::True(m_isolate));
647 : }
648 : }
649 : v8::Local<v8::Array> entries;
650 134352 : if (collectionsEntries(context, value).ToLocal(&entries)) {
651 201 : createDataProperty(context, properties, properties->Length(),
652 402 : toV8StringInternalized(m_isolate, "[[Entries]]"));
653 201 : createDataProperty(context, properties, properties->Length(), entries);
654 : }
655 67176 : if (value->IsGeneratorObject()) {
656 : v8::Local<v8::Object> location;
657 90 : if (generatorObjectLocation(context, value).ToLocal(&location)) {
658 : createDataProperty(
659 45 : context, properties, properties->Length(),
660 90 : toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
661 45 : createDataProperty(context, properties, properties->Length(), location);
662 : }
663 : v8::Local<v8::Value> scopes;
664 45 : if (generatorScopes(context, value).ToLocal(&scopes)) {
665 35 : createDataProperty(context, properties, properties->Length(),
666 70 : toV8StringInternalized(m_isolate, "[[Scopes]]"));
667 35 : createDataProperty(context, properties, properties->Length(), scopes);
668 : }
669 : }
670 67176 : if (value->IsFunction()) {
671 : v8::Local<v8::Function> function = value.As<v8::Function>();
672 30 : v8::Local<v8::Value> boundFunction = function->GetBoundFunction();
673 : v8::Local<v8::Value> scopes;
674 55 : if (boundFunction->IsUndefined() &&
675 : functionScopes(context, function).ToLocal(&scopes)) {
676 25 : createDataProperty(context, properties, properties->Length(),
677 50 : toV8StringInternalized(m_isolate, "[[Scopes]]"));
678 25 : createDataProperty(context, properties, properties->Length(), scopes);
679 : }
680 : }
681 67176 : return properties;
682 : }
683 :
684 65 : v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
685 : v8::Local<v8::Object> prototype) {
686 65 : v8::Isolate* isolate = context->GetIsolate();
687 : v8::PersistentValueVector<v8::Object> v8Objects(isolate);
688 130 : MatchPrototypePredicate predicate(m_inspector, context, prototype);
689 65 : v8::debug::QueryObjects(context, &predicate, &v8Objects);
690 :
691 : v8::MicrotasksScope microtasksScope(isolate,
692 130 : v8::MicrotasksScope::kDoNotRunMicrotasks);
693 : v8::Local<v8::Array> resultArray = v8::Array::New(
694 130 : m_inspector->isolate(), static_cast<int>(v8Objects.Size()));
695 300 : for (size_t i = 0; i < v8Objects.Size(); ++i) {
696 : createDataProperty(context, resultArray, static_cast<int>(i),
697 170 : v8Objects.Get(i));
698 : }
699 130 : return resultArray;
700 : }
701 :
702 275 : std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
703 : v8::Local<v8::StackTrace> v8StackTrace) {
704 : return V8StackTraceImpl::create(this, currentContextGroupId(), v8StackTrace,
705 275 : V8StackTraceImpl::maxCallStackSizeToCapture);
706 : }
707 :
708 3356 : void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
709 3356 : if (depth <= 0)
710 : m_maxAsyncCallStackDepthMap.erase(agent);
711 : else
712 190 : m_maxAsyncCallStackDepthMap[agent] = depth;
713 :
714 : int maxAsyncCallStackDepth = 0;
715 6902 : for (const auto& pair : m_maxAsyncCallStackDepthMap) {
716 190 : if (pair.second > maxAsyncCallStackDepth)
717 : maxAsyncCallStackDepth = pair.second;
718 : }
719 :
720 6712 : if (m_maxAsyncCallStackDepth == maxAsyncCallStackDepth) return;
721 : // TODO(dgozman): ideally, this should be per context group.
722 280 : m_maxAsyncCallStackDepth = maxAsyncCallStackDepth;
723 280 : m_inspector->client()->maxAsyncCallStackDepthChanged(
724 280 : m_maxAsyncCallStackDepth);
725 280 : if (!maxAsyncCallStackDepth) allAsyncTasksCanceled();
726 : }
727 :
728 42579 : void V8Debugger::asyncTaskCreatedForStack(void* task, void* parentTask) {
729 62902 : if (!m_maxAsyncCallStackDepth) return;
730 25966 : if (parentTask) m_parentTask[task] = parentTask;
731 22256 : v8::HandleScope scope(m_isolate);
732 : std::shared_ptr<AsyncStackTrace> asyncCreation =
733 : AsyncStackTrace::capture(this, currentContextGroupId(), String16(),
734 44512 : V8StackTraceImpl::maxCallStackSizeToCapture);
735 : // Passing one as maxStackSize forces no async chain for the new stack.
736 22256 : if (asyncCreation && !asyncCreation->isEmpty()) {
737 : m_asyncTaskCreationStacks[task] = asyncCreation;
738 20106 : m_allAsyncStacks.push_back(std::move(asyncCreation));
739 20106 : ++m_asyncStacksCount;
740 20106 : collectOldAsyncStacksIfNeeded();
741 22256 : }
742 : }
743 :
744 1385 : void V8Debugger::asyncTaskScheduled(const StringView& taskName, void* task,
745 : bool recurring) {
746 2770 : asyncTaskScheduledForStack(toString16(taskName), task, recurring);
747 1385 : asyncTaskCandidateForStepping(task);
748 1385 : }
749 :
750 0 : void V8Debugger::asyncTaskCanceled(void* task) {
751 0 : asyncTaskCanceledForStack(task);
752 : asyncTaskCanceledForStepping(task);
753 0 : }
754 :
755 1385 : void V8Debugger::asyncTaskStarted(void* task) {
756 1385 : asyncTaskStartedForStack(task);
757 : asyncTaskStartedForStepping(task);
758 1385 : }
759 :
760 1385 : void V8Debugger::asyncTaskFinished(void* task) {
761 1385 : asyncTaskFinishedForStack(task);
762 : asyncTaskFinishedForStepping(task);
763 1385 : }
764 :
765 48784 : void V8Debugger::asyncTaskScheduledForStack(const String16& taskName,
766 : void* task, bool recurring) {
767 74372 : if (!m_maxAsyncCallStackDepth) return;
768 23196 : v8::HandleScope scope(m_isolate);
769 : std::shared_ptr<AsyncStackTrace> asyncStack =
770 : AsyncStackTrace::capture(this, currentContextGroupId(), taskName,
771 23196 : V8StackTraceImpl::maxCallStackSizeToCapture);
772 23196 : if (asyncStack) {
773 : m_asyncTaskStacks[task] = asyncStack;
774 23081 : if (recurring) m_recurringTasks.insert(task);
775 23081 : m_allAsyncStacks.push_back(std::move(asyncStack));
776 23081 : ++m_asyncStacksCount;
777 23081 : collectOldAsyncStacksIfNeeded();
778 23196 : }
779 : }
780 :
781 1530 : void V8Debugger::asyncTaskCanceledForStack(void* task) {
782 3060 : if (!m_maxAsyncCallStackDepth) return;
783 : m_asyncTaskStacks.erase(task);
784 : m_recurringTasks.erase(task);
785 : m_parentTask.erase(task);
786 : m_asyncTaskCreationStacks.erase(task);
787 : }
788 :
789 14878 : void V8Debugger::asyncTaskStartedForStack(void* task) {
790 29756 : if (!m_maxAsyncCallStackDepth) return;
791 : // Needs to support following order of events:
792 : // - asyncTaskScheduled
793 : // <-- attached here -->
794 : // - asyncTaskStarted
795 : // - asyncTaskCanceled <-- canceled before finished
796 : // <-- async stack requested here -->
797 : // - asyncTaskFinished
798 4480 : m_currentTasks.push_back(task);
799 : auto parentIt = m_parentTask.find(task);
800 : AsyncTaskToStackTrace::iterator stackIt = m_asyncTaskStacks.find(
801 4480 : parentIt == m_parentTask.end() ? task : parentIt->second);
802 4480 : if (stackIt != m_asyncTaskStacks.end()) {
803 8520 : m_currentAsyncParent.push_back(stackIt->second.lock());
804 : } else {
805 220 : m_currentAsyncParent.emplace_back();
806 : }
807 : auto itCreation = m_asyncTaskCreationStacks.find(task);
808 4480 : if (itCreation != m_asyncTaskCreationStacks.end()) {
809 4260 : m_currentAsyncCreation.push_back(itCreation->second.lock());
810 : // TODO(kozyatinskiy): implement it without hack.
811 2130 : if (m_currentAsyncParent.back()) {
812 : m_currentAsyncCreation.back()->setDescription(
813 4020 : m_currentAsyncParent.back()->description());
814 : m_currentAsyncParent.back().reset();
815 : }
816 : } else {
817 2350 : m_currentAsyncCreation.emplace_back();
818 : }
819 : }
820 :
821 14873 : void V8Debugger::asyncTaskFinishedForStack(void* task) {
822 14873 : if (!m_maxAsyncCallStackDepth) return;
823 : // We could start instrumenting half way and the stack is empty.
824 8970 : if (!m_currentTasks.size()) return;
825 : DCHECK(m_currentTasks.back() == task);
826 : m_currentTasks.pop_back();
827 :
828 : DCHECK(m_currentAsyncParent.size() == m_currentAsyncCreation.size());
829 : m_currentAsyncParent.pop_back();
830 : m_currentAsyncCreation.pop_back();
831 :
832 4475 : if (m_recurringTasks.find(task) == m_recurringTasks.end()) {
833 1530 : asyncTaskCanceledForStack(task);
834 : }
835 : }
836 :
837 15111 : void V8Debugger::asyncTaskCandidateForStepping(void* task) {
838 15111 : if (!m_stepIntoAsyncCallback) return;
839 : DCHECK(m_targetContextGroupId);
840 60 : if (currentContextGroupId() != m_targetContextGroupId) return;
841 60 : m_taskWithScheduledBreak = task;
842 60 : v8::debug::ClearStepping(m_isolate);
843 60 : m_stepIntoAsyncCallback->sendSuccess();
844 : m_stepIntoAsyncCallback.reset();
845 : }
846 :
847 0 : void V8Debugger::asyncTaskStartedForStepping(void* task) {
848 14878 : if (m_breakRequested) return;
849 14878 : if (task != m_taskWithScheduledBreak) return;
850 55 : v8::debug::DebugBreak(m_isolate);
851 : }
852 :
853 0 : void V8Debugger::asyncTaskFinishedForStepping(void* task) {
854 14873 : if (task != m_taskWithScheduledBreak) return;
855 55 : m_taskWithScheduledBreak = nullptr;
856 55 : if (m_breakRequested) return;
857 55 : v8::debug::CancelDebugBreak(m_isolate);
858 : }
859 :
860 0 : void V8Debugger::asyncTaskCanceledForStepping(void* task) {
861 0 : if (task != m_taskWithScheduledBreak) return;
862 0 : m_taskWithScheduledBreak = nullptr;
863 : }
864 :
865 3206 : void V8Debugger::allAsyncTasksCanceled() {
866 : m_asyncTaskStacks.clear();
867 : m_recurringTasks.clear();
868 : m_currentAsyncParent.clear();
869 : m_currentAsyncCreation.clear();
870 : m_currentTasks.clear();
871 : m_parentTask.clear();
872 : m_asyncTaskCreationStacks.clear();
873 :
874 : m_framesCache.clear();
875 : m_allAsyncStacks.clear();
876 3206 : m_asyncStacksCount = 0;
877 3206 : }
878 :
879 25 : void V8Debugger::muteScriptParsedEvents() {
880 25 : ++m_ignoreScriptParsedEventsCounter;
881 25 : }
882 :
883 25 : void V8Debugger::unmuteScriptParsedEvents() {
884 25 : --m_ignoreScriptParsedEventsCounter;
885 : DCHECK_GE(m_ignoreScriptParsedEventsCounter, 0);
886 25 : }
887 :
888 6533 : std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
889 : bool fullStack) {
890 6533 : if (!m_isolate->InContext()) return nullptr;
891 :
892 6533 : v8::HandleScope handles(m_isolate);
893 6533 : int contextGroupId = currentContextGroupId();
894 6533 : if (!contextGroupId) return nullptr;
895 :
896 6533 : int stackSize = 1;
897 6533 : if (fullStack) {
898 90 : stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
899 : } else {
900 : m_inspector->forEachSession(
901 : contextGroupId, [&stackSize](V8InspectorSessionImpl* session) {
902 6498 : if (session->runtimeAgent()->enabled())
903 1150 : stackSize = V8StackTraceImpl::maxCallStackSizeToCapture;
904 12886 : });
905 : }
906 6533 : return V8StackTraceImpl::capture(this, contextGroupId, stackSize);
907 : }
908 :
909 52320 : int V8Debugger::currentContextGroupId() {
910 52320 : if (!m_isolate->InContext()) return 0;
911 52320 : return m_inspector->contextGroupId(m_isolate->GetCurrentContext());
912 : }
913 :
914 43487 : void V8Debugger::collectOldAsyncStacksIfNeeded() {
915 86974 : if (m_asyncStacksCount <= m_maxAsyncCallStacks) return;
916 : int halfOfLimitRoundedUp =
917 470 : m_maxAsyncCallStacks / 2 + m_maxAsyncCallStacks % 2;
918 2095 : while (m_asyncStacksCount > halfOfLimitRoundedUp) {
919 : m_allAsyncStacks.pop_front();
920 1155 : --m_asyncStacksCount;
921 : }
922 470 : cleanupExpiredWeakPointers(m_asyncTaskStacks);
923 470 : cleanupExpiredWeakPointers(m_asyncTaskCreationStacks);
924 1490 : for (auto it = m_recurringTasks.begin(); it != m_recurringTasks.end();) {
925 1100 : if (m_asyncTaskStacks.find(*it) == m_asyncTaskStacks.end()) {
926 : it = m_recurringTasks.erase(it);
927 : } else {
928 : ++it;
929 : }
930 : }
931 1375 : for (auto it = m_parentTask.begin(); it != m_parentTask.end();) {
932 870 : if (m_asyncTaskCreationStacks.find(it->second) ==
933 760 : m_asyncTaskCreationStacks.end() &&
934 : m_asyncTaskStacks.find(it->second) == m_asyncTaskStacks.end()) {
935 : it = m_parentTask.erase(it);
936 : } else {
937 : ++it;
938 : }
939 : }
940 470 : cleanupExpiredWeakPointers(m_framesCache);
941 : }
942 :
943 69884 : std::shared_ptr<StackFrame> V8Debugger::symbolize(
944 : v8::Local<v8::StackFrame> v8Frame) {
945 : auto it = m_framesCache.end();
946 69884 : int frameId = 0;
947 69884 : if (m_maxAsyncCallStackDepth) {
948 41577 : frameId = v8::debug::GetStackFrameId(v8Frame);
949 : it = m_framesCache.find(frameId);
950 : }
951 106996 : if (it != m_framesCache.end() && it->second.lock()) return it->second.lock();
952 65634 : std::shared_ptr<StackFrame> frame(new StackFrame(v8Frame));
953 : // TODO(clemensh): Figure out a way to do this translation only right before
954 : // sending the stack trace over wire.
955 32817 : if (v8Frame->IsWasm()) frame->translate(&m_wasmTranslation);
956 32817 : if (m_maxAsyncCallStackDepth) {
957 4510 : m_framesCache[frameId] = frame;
958 : }
959 : return frame;
960 : }
961 :
962 300 : void V8Debugger::setMaxAsyncTaskStacksForTest(int limit) {
963 300 : m_maxAsyncCallStacks = 0;
964 300 : collectOldAsyncStacksIfNeeded();
965 300 : m_maxAsyncCallStacks = limit;
966 300 : }
967 :
968 30 : void V8Debugger::dumpAsyncTaskStacksStateForTest() {
969 30 : fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount);
970 30 : fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
971 : fprintf(stdout, "Created async tasks: %zu\n",
972 30 : m_asyncTaskCreationStacks.size());
973 30 : fprintf(stdout, "Async tasks with parent: %zu\n", m_parentTask.size());
974 30 : fprintf(stdout, "Recurring async tasks: %zu\n", m_recurringTasks.size());
975 30 : fprintf(stdout, "\n");
976 30 : }
977 :
978 : } // namespace v8_inspector
|