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-stack-trace-impl.h"
6 :
7 : #include <algorithm>
8 :
9 : #include "src/inspector/v8-debugger.h"
10 : #include "src/inspector/v8-inspector-impl.h"
11 : #include "src/inspector/wasm-translation.h"
12 :
13 : namespace v8_inspector {
14 :
15 : int V8StackTraceImpl::maxCallStackSizeToCapture = 200;
16 :
17 : namespace {
18 :
19 : static const v8::StackTrace::StackTraceOptions stackTraceOptions =
20 : static_cast<v8::StackTrace::StackTraceOptions>(
21 : v8::StackTrace::kDetailed |
22 : v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
23 :
24 63454 : std::vector<std::shared_ptr<StackFrame>> toFramesVector(
25 65879 : V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
26 65879 : int maxStackSize) {
27 : DCHECK(debugger->isolate()->InContext());
28 126908 : int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
29 63454 : std::vector<std::shared_ptr<StackFrame>> frames(frameCount);
30 129333 : for (int i = 0; i < frameCount; ++i) {
31 197637 : frames[i] =
32 : debugger->symbolize(v8StackTrace->GetFrame(debugger->isolate(), i));
33 : }
34 63454 : return frames;
35 : }
36 :
37 138746 : void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
38 : std::shared_ptr<AsyncStackTrace>* asyncParent,
39 : V8StackTraceId* externalParent, int* maxAsyncDepth) {
40 142316 : *asyncParent = debugger->currentAsyncParent();
41 142316 : *externalParent = debugger->currentExternalParent();
42 : DCHECK(externalParent->IsInvalid() || !*asyncParent);
43 138746 : if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
44 :
45 : // Do not accidentally append async call chain from another group. This should
46 : // not happen if we have proper instrumentation, but let's double-check to be
47 : // safe.
48 215049 : if (contextGroupId && *asyncParent &&
49 72838 : (*asyncParent)->externalParent().IsInvalid() &&
50 : (*asyncParent)->contextGroupId() != contextGroupId) {
51 : asyncParent->reset();
52 : *externalParent = V8StackTraceId();
53 0 : if (maxAsyncDepth) *maxAsyncDepth = 0;
54 71158 : return;
55 : }
56 :
57 : // Only the top stack in the chain may be empty, so ensure that second stack
58 : // is non-empty (it's the top of appended chain).
59 72858 : if (*asyncParent && (*asyncParent)->isEmpty()) {
60 0 : *asyncParent = (*asyncParent)->parent().lock();
61 : }
62 : }
63 :
64 61704 : std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
65 127198 : V8Debugger* debugger,
66 125303 : const std::vector<std::shared_ptr<StackFrame>>& frames,
67 : const String16& description,
68 : const std::shared_ptr<AsyncStackTrace>& asyncParent,
69 : const V8StackTraceId& externalParent, int maxAsyncDepth) {
70 124643 : if (asyncParent && frames.empty() &&
71 : description == asyncParent->description()) {
72 0 : return asyncParent->buildInspectorObject(debugger, maxAsyncDepth);
73 : }
74 :
75 : std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
76 : inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
77 250606 : for (size_t i = 0; i < frames.size(); i++) {
78 : V8InspectorClient* client = nullptr;
79 127198 : if (debugger && debugger->inspector())
80 63599 : client = debugger->inspector()->client();
81 127198 : inspectorFrames->addItem(frames[i]->buildInspectorObject(client));
82 : }
83 : std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
84 : protocol::Runtime::StackTrace::create()
85 61704 : .setCallFrames(std::move(inspectorFrames))
86 : .build();
87 61704 : if (!description.isEmpty()) stackTrace->setDescription(description);
88 61704 : if (asyncParent) {
89 1220 : if (maxAsyncDepth > 0) {
90 : stackTrace->setParent(
91 2260 : asyncParent->buildInspectorObject(debugger, maxAsyncDepth - 1));
92 90 : } else if (debugger) {
93 : stackTrace->setParentId(
94 : protocol::Runtime::StackTraceId::create()
95 : .setId(stackTraceIdToString(
96 360 : AsyncStackTrace::store(debugger, asyncParent)))
97 : .build());
98 : }
99 : }
100 61704 : if (!externalParent.IsInvalid()) {
101 : stackTrace->setParentId(
102 : protocol::Runtime::StackTraceId::create()
103 75 : .setId(stackTraceIdToString(externalParent.id))
104 50 : .setDebuggerId(debuggerIdToString(externalParent.debugger_id))
105 : .build());
106 : }
107 : return stackTrace;
108 : }
109 :
110 : } // namespace
111 :
112 202744 : V8StackTraceId::V8StackTraceId() : id(0), debugger_id(std::make_pair(0, 0)) {}
113 :
114 190 : V8StackTraceId::V8StackTraceId(uintptr_t id,
115 : const std::pair<int64_t, int64_t> debugger_id)
116 190 : : id(id), debugger_id(debugger_id) {}
117 :
118 182166 : bool V8StackTraceId::IsInvalid() const { return !id; }
119 :
120 64299 : StackFrame::StackFrame(v8::Isolate* isolate, v8::Local<v8::StackFrame> v8Frame)
121 : : m_functionName(toProtocolString(isolate, v8Frame->GetFunctionName())),
122 : m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
123 : m_sourceURL(
124 : toProtocolString(isolate, v8Frame->GetScriptNameOrSourceURL())),
125 64299 : m_lineNumber(v8Frame->GetLineNumber() - 1),
126 64299 : m_columnNumber(v8Frame->GetColumn() - 1),
127 128598 : m_hasSourceURLComment(v8Frame->GetScriptName() !=
128 257196 : v8Frame->GetScriptNameOrSourceURL()) {
129 : DCHECK_NE(v8::Message::kNoLineNumberInfo, m_lineNumber + 1);
130 : DCHECK_NE(v8::Message::kNoColumnInfo, m_columnNumber + 1);
131 64299 : }
132 :
133 420 : void StackFrame::translate(WasmTranslation* wasmTranslation) {
134 : wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
135 420 : &m_scriptId, &m_lineNumber, &m_columnNumber);
136 420 : }
137 :
138 0 : const String16& StackFrame::functionName() const { return m_functionName; }
139 :
140 0 : const String16& StackFrame::scriptId() const { return m_scriptId; }
141 :
142 0 : const String16& StackFrame::sourceURL() const { return m_sourceURL; }
143 :
144 6685 : int StackFrame::lineNumber() const { return m_lineNumber; }
145 :
146 6685 : int StackFrame::columnNumber() const { return m_columnNumber; }
147 :
148 63599 : std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject(
149 : V8InspectorClient* client) const {
150 63599 : String16 frameUrl = m_sourceURL;
151 120648 : if (client && !m_hasSourceURLComment && frameUrl.length() > 0) {
152 : std::unique_ptr<StringBuffer> url =
153 47335 : client->resourceNameToUrl(toStringView(m_sourceURL));
154 47335 : if (url) {
155 40 : frameUrl = toString16(url->string());
156 : }
157 : }
158 : return protocol::Runtime::CallFrame::create()
159 127198 : .setFunctionName(m_functionName)
160 63599 : .setScriptId(m_scriptId)
161 : .setUrl(frameUrl)
162 63599 : .setLineNumber(m_lineNumber)
163 63599 : .setColumnNumber(m_columnNumber)
164 63599 : .build();
165 : }
166 :
167 0 : bool StackFrame::isEqual(StackFrame* frame) const {
168 : return m_scriptId == frame->m_scriptId &&
169 50 : m_lineNumber == frame->m_lineNumber &&
170 0 : m_columnNumber == frame->m_columnNumber;
171 : }
172 :
173 : // static
174 1240 : void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
175 : v8::Isolate* isolate, bool capture) {
176 : isolate->SetCaptureStackTraceForUncaughtExceptions(
177 1240 : capture, V8StackTraceImpl::maxCallStackSizeToCapture);
178 1240 : }
179 :
180 : // static
181 67588 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
182 67588 : V8Debugger* debugger, int contextGroupId,
183 : v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
184 : DCHECK(debugger);
185 :
186 : v8::Isolate* isolate = debugger->isolate();
187 67588 : v8::HandleScope scope(isolate);
188 :
189 67588 : std::vector<std::shared_ptr<StackFrame>> frames;
190 67588 : if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
191 119968 : frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
192 : }
193 :
194 67588 : int maxAsyncDepth = 0;
195 67588 : std::shared_ptr<AsyncStackTrace> asyncParent;
196 : V8StackTraceId externalParent;
197 : calculateAsyncChain(debugger, contextGroupId, &asyncParent, &externalParent,
198 67588 : &maxAsyncDepth);
199 75047 : if (frames.empty() && !asyncParent && externalParent.IsInvalid())
200 : return nullptr;
201 : return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
202 187846 : std::move(frames), maxAsyncDepth, asyncParent, externalParent));
203 : }
204 :
205 : // static
206 67188 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
207 67188 : V8Debugger* debugger, int contextGroupId, int maxStackSize) {
208 : DCHECK(debugger);
209 : v8::Isolate* isolate = debugger->isolate();
210 67188 : v8::HandleScope handleScope(isolate);
211 : v8::Local<v8::StackTrace> v8StackTrace;
212 67188 : if (isolate->InContext()) {
213 : v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
214 65213 : stackTraceOptions);
215 : }
216 : return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
217 67188 : maxStackSize);
218 : }
219 :
220 60129 : V8StackTraceImpl::V8StackTraceImpl(
221 : std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth,
222 : std::shared_ptr<AsyncStackTrace> asyncParent,
223 : const V8StackTraceId& externalParent)
224 : : m_frames(std::move(frames)),
225 : m_maxAsyncDepth(maxAsyncDepth),
226 : m_asyncParent(std::move(asyncParent)),
227 180387 : m_externalParent(externalParent) {}
228 :
229 : V8StackTraceImpl::~V8StackTraceImpl() = default;
230 :
231 0 : std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
232 : return std::unique_ptr<V8StackTrace>(new V8StackTraceImpl(
233 0 : m_frames, 0, std::shared_ptr<AsyncStackTrace>(), V8StackTraceId()));
234 : }
235 :
236 0 : StringView V8StackTraceImpl::firstNonEmptySourceURL() const {
237 0 : StackFrameIterator current(this);
238 0 : while (!current.done()) {
239 0 : if (current.frame()->sourceURL().length()) {
240 0 : return toStringView(current.frame()->sourceURL());
241 : }
242 0 : current.next();
243 : }
244 : return StringView();
245 : }
246 :
247 119798 : bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
248 :
249 6575 : StringView V8StackTraceImpl::topSourceURL() const {
250 6575 : return toStringView(m_frames[0]->sourceURL());
251 : }
252 :
253 6660 : int V8StackTraceImpl::topLineNumber() const {
254 13320 : return m_frames[0]->lineNumber() + 1;
255 : }
256 :
257 6660 : int V8StackTraceImpl::topColumnNumber() const {
258 13320 : return m_frames[0]->columnNumber() + 1;
259 : }
260 :
261 85 : StringView V8StackTraceImpl::topScriptId() const {
262 85 : return toStringView(m_frames[0]->scriptId());
263 : }
264 :
265 0 : StringView V8StackTraceImpl::topFunctionName() const {
266 0 : return toStringView(m_frames[0]->functionName());
267 : }
268 :
269 : std::unique_ptr<protocol::Runtime::StackTrace>
270 6745 : V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger) const {
271 6745 : return buildInspectorObjectImpl(debugger, m_maxAsyncDepth);
272 : }
273 :
274 : std::unique_ptr<protocol::Runtime::StackTrace>
275 59914 : V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger,
276 : int maxAsyncDepth) const {
277 : return buildInspectorObjectCommon(debugger, m_frames, String16(),
278 : m_asyncParent.lock(), m_externalParent,
279 179742 : maxAsyncDepth);
280 : }
281 :
282 : std::unique_ptr<protocol::Runtime::API::StackTrace>
283 0 : V8StackTraceImpl::buildInspectorObject() const {
284 0 : return buildInspectorObjectImpl(nullptr);
285 : }
286 :
287 10 : std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
288 10 : String16Builder stackTrace;
289 70 : for (size_t i = 0; i < m_frames.size(); ++i) {
290 60 : const StackFrame& frame = *m_frames[i];
291 90 : stackTrace.append("\n at " + (frame.functionName().length()
292 15 : ? frame.functionName()
293 25 : : "(anonymous function)"));
294 50 : stackTrace.append(" (");
295 25 : stackTrace.append(frame.sourceURL());
296 25 : stackTrace.append(':');
297 50 : stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
298 25 : stackTrace.append(':');
299 50 : stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
300 25 : stackTrace.append(')');
301 : }
302 10 : String16 string = stackTrace.toString();
303 30 : return StringBufferImpl::adopt(string);
304 : }
305 :
306 45 : bool V8StackTraceImpl::isEqualIgnoringTopFrame(
307 : V8StackTraceImpl* stackTrace) const {
308 45 : StackFrameIterator current(this);
309 45 : StackFrameIterator target(stackTrace);
310 :
311 45 : current.next();
312 45 : target.next();
313 160 : while (!current.done() && !target.done()) {
314 50 : if (!current.frame()->isEqual(target.frame())) {
315 : return false;
316 : }
317 20 : current.next();
318 20 : target.next();
319 : }
320 15 : return current.done() == target.done();
321 : }
322 :
323 90 : V8StackTraceImpl::StackFrameIterator::StackFrameIterator(
324 : const V8StackTraceImpl* stackTrace)
325 180 : : m_currentIt(stackTrace->m_frames.begin()),
326 : m_currentEnd(stackTrace->m_frames.end()),
327 270 : m_parent(stackTrace->m_asyncParent.lock().get()) {}
328 :
329 130 : void V8StackTraceImpl::StackFrameIterator::next() {
330 260 : if (m_currentIt == m_currentEnd) return;
331 : ++m_currentIt;
332 150 : while (m_currentIt == m_currentEnd && m_parent) {
333 40 : const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
334 20 : m_currentIt = frames.begin();
335 40 : if (m_parent->description() == "async function") ++m_currentIt;
336 20 : m_currentEnd = frames.end();
337 80 : m_parent = m_parent->parent().lock().get();
338 : }
339 : }
340 :
341 0 : bool V8StackTraceImpl::StackFrameIterator::done() {
342 0 : return m_currentIt == m_currentEnd;
343 : }
344 :
345 0 : StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
346 50 : return m_currentIt->get();
347 : }
348 :
349 : // static
350 3570 : std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
351 3570 : V8Debugger* debugger, int contextGroupId, const String16& description,
352 : int maxStackSize) {
353 : DCHECK(debugger);
354 :
355 : v8::Isolate* isolate = debugger->isolate();
356 3570 : v8::HandleScope handleScope(isolate);
357 :
358 3570 : std::vector<std::shared_ptr<StackFrame>> frames;
359 3570 : if (isolate->InContext()) {
360 : v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
361 3470 : isolate, maxStackSize, stackTraceOptions);
362 6940 : frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
363 : }
364 :
365 3570 : std::shared_ptr<AsyncStackTrace> asyncParent;
366 : V8StackTraceId externalParent;
367 : calculateAsyncChain(debugger, contextGroupId, &asyncParent, &externalParent,
368 3570 : nullptr);
369 :
370 3655 : if (frames.empty() && !asyncParent && externalParent.IsInvalid())
371 : return nullptr;
372 :
373 : // When async call chain is empty but doesn't contain useful schedule stack
374 : // but doesn't synchronous we can merge them together. e.g. Promise
375 : // ThenableJob.
376 7020 : if (asyncParent && frames.empty() &&
377 15 : (asyncParent->m_description == description || description.isEmpty())) {
378 : return asyncParent;
379 : }
380 :
381 : DCHECK(contextGroupId || asyncParent || !externalParent.IsInvalid());
382 3475 : if (!contextGroupId && asyncParent) {
383 10 : contextGroupId = asyncParent->m_contextGroupId;
384 : }
385 :
386 : return std::shared_ptr<AsyncStackTrace>(
387 : new AsyncStackTrace(contextGroupId, description, std::move(frames),
388 10520 : asyncParent, externalParent));
389 : }
390 :
391 3475 : AsyncStackTrace::AsyncStackTrace(
392 : int contextGroupId, const String16& description,
393 : std::vector<std::shared_ptr<StackFrame>> frames,
394 : std::shared_ptr<AsyncStackTrace> asyncParent,
395 : const V8StackTraceId& externalParent)
396 : : m_contextGroupId(contextGroupId),
397 : m_id(0),
398 : m_suspendedTaskId(nullptr),
399 : m_description(description),
400 : m_frames(std::move(frames)),
401 : m_asyncParent(std::move(asyncParent)),
402 6950 : m_externalParent(externalParent) {
403 : DCHECK(m_contextGroupId || (!externalParent.IsInvalid() && m_frames.empty()));
404 3475 : }
405 :
406 : std::unique_ptr<protocol::Runtime::StackTrace>
407 1790 : AsyncStackTrace::buildInspectorObject(V8Debugger* debugger,
408 : int maxAsyncDepth) const {
409 : return buildInspectorObjectCommon(debugger, m_frames, m_description,
410 : m_asyncParent.lock(), m_externalParent,
411 3580 : maxAsyncDepth);
412 : }
413 :
414 1680 : int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
415 :
416 3740 : void AsyncStackTrace::setSuspendedTaskId(void* task) {
417 3740 : m_suspendedTaskId = task;
418 3740 : }
419 :
420 50 : void* AsyncStackTrace::suspendedTaskId() const { return m_suspendedTaskId; }
421 :
422 150 : uintptr_t AsyncStackTrace::store(V8Debugger* debugger,
423 : std::shared_ptr<AsyncStackTrace> stack) {
424 290 : if (stack->m_id) return stack->m_id;
425 280 : stack->m_id = debugger->storeStackTrace(stack);
426 140 : return stack->m_id;
427 : }
428 :
429 0 : const String16& AsyncStackTrace::description() const { return m_description; }
430 :
431 80 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
432 80 : return m_asyncParent;
433 : }
434 :
435 0 : bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
436 :
437 : } // namespace v8_inspector
|