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/wasm-translation.h"
11 :
12 : namespace v8_inspector {
13 :
14 : namespace {
15 :
16 : static const v8::StackTrace::StackTraceOptions stackTraceOptions =
17 : static_cast<v8::StackTrace::StackTraceOptions>(
18 : v8::StackTrace::kDetailed |
19 : v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
20 :
21 73889 : std::vector<std::shared_ptr<StackFrame>> toFramesVector(
22 : V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
23 : int maxStackSize) {
24 : DCHECK(debugger->isolate()->InContext());
25 147778 : int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
26 : std::vector<std::shared_ptr<StackFrame>> frames;
27 143773 : for (int i = 0; i < frameCount; ++i) {
28 139768 : frames.push_back(debugger->symbolize(v8StackTrace->GetFrame(i)));
29 : }
30 73889 : return frames;
31 : }
32 :
33 111048 : void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
34 : std::shared_ptr<AsyncStackTrace>* asyncParent,
35 : std::shared_ptr<AsyncStackTrace>* asyncCreation,
36 : int* maxAsyncDepth) {
37 156500 : *asyncParent = debugger->currentAsyncParent();
38 156500 : *asyncCreation = debugger->currentAsyncCreation();
39 111048 : if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
40 :
41 : DCHECK(!*asyncParent || !*asyncCreation ||
42 : (*asyncParent)->contextGroupId() ==
43 : (*asyncCreation)->contextGroupId());
44 : // Do not accidentally append async call chain from another group. This should
45 : // not happen if we have proper instrumentation, but let's double-check to be
46 : // safe.
47 170300 : if (contextGroupId && *asyncParent &&
48 : (*asyncParent)->contextGroupId() != contextGroupId) {
49 : asyncParent->reset();
50 6730 : asyncCreation->reset();
51 0 : if (maxAsyncDepth) *maxAsyncDepth = 0;
52 78250 : return;
53 : }
54 :
55 : // Only the top stack in the chain may be empty and doesn't contain creation
56 : // stack, so ensure that second stack is non-empty (it's the top of appended
57 : // chain).
58 111900 : if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() &&
59 : (*asyncParent)->isEmpty()) {
60 1020 : *asyncParent = (*asyncParent)->parent().lock();
61 : }
62 : }
63 :
64 29439 : std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
65 59658 : const std::vector<std::shared_ptr<StackFrame>>& frames,
66 : const String16& description,
67 : const std::shared_ptr<AsyncStackTrace>& asyncParent,
68 : const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) {
69 89097 : if (asyncParent && frames.empty() &&
70 30179 : description == asyncParent->description() && !asyncCreation) {
71 0 : return asyncParent->buildInspectorObject(nullptr, maxAsyncDepth);
72 : }
73 :
74 : std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
75 : inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
76 119316 : for (size_t i = 0; i < frames.size(); i++) {
77 60438 : inspectorFrames->addItem(frames[i]->buildInspectorObject());
78 : }
79 : std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
80 : protocol::Runtime::StackTrace::create()
81 29439 : .setCallFrames(std::move(inspectorFrames))
82 : .build();
83 29439 : if (!description.isEmpty()) stackTrace->setDescription(description);
84 29439 : if (asyncParent && maxAsyncDepth > 0) {
85 : stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(),
86 2220 : maxAsyncDepth - 1));
87 : }
88 : return stackTrace;
89 : }
90 :
91 : } // namespace
92 :
93 32817 : StackFrame::StackFrame(v8::Local<v8::StackFrame> v8Frame)
94 : : m_functionName(toProtocolString(v8Frame->GetFunctionName())),
95 : m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
96 : m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())),
97 32817 : m_lineNumber(v8Frame->GetLineNumber() - 1),
98 65634 : m_columnNumber(v8Frame->GetColumn() - 1) {
99 : DCHECK_NE(v8::Message::kNoLineNumberInfo, m_lineNumber + 1);
100 : DCHECK_NE(v8::Message::kNoColumnInfo, m_columnNumber + 1);
101 32817 : }
102 :
103 275 : void StackFrame::translate(WasmTranslation* wasmTranslation) {
104 : wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
105 275 : &m_scriptId, &m_lineNumber, &m_columnNumber);
106 275 : }
107 :
108 0 : const String16& StackFrame::functionName() const { return m_functionName; }
109 :
110 0 : const String16& StackFrame::scriptId() const { return m_scriptId; }
111 :
112 0 : const String16& StackFrame::sourceURL() const { return m_sourceURL; }
113 :
114 6483 : int StackFrame::lineNumber() const { return m_lineNumber; }
115 :
116 6468 : int StackFrame::columnNumber() const { return m_columnNumber; }
117 :
118 30219 : std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject()
119 : const {
120 : return protocol::Runtime::CallFrame::create()
121 60438 : .setFunctionName(m_functionName)
122 30219 : .setScriptId(m_scriptId)
123 30219 : .setUrl(m_sourceURL)
124 30219 : .setLineNumber(m_lineNumber)
125 30219 : .setColumnNumber(m_columnNumber)
126 30219 : .build();
127 : }
128 :
129 0 : bool StackFrame::isEqual(StackFrame* frame) const {
130 : return m_scriptId == frame->m_scriptId &&
131 50 : m_lineNumber == frame->m_lineNumber &&
132 0 : m_columnNumber == frame->m_columnNumber;
133 : }
134 :
135 : // static
136 1090 : void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
137 : v8::Isolate* isolate, bool capture) {
138 : isolate->SetCaptureStackTraceForUncaughtExceptions(
139 1090 : capture, V8StackTraceImpl::maxCallStackSizeToCapture);
140 1090 : }
141 :
142 : // static
143 32798 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
144 32798 : V8Debugger* debugger, int contextGroupId,
145 : v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
146 : DCHECK(debugger);
147 :
148 : v8::Isolate* isolate = debugger->isolate();
149 32798 : v8::HandleScope scope(isolate);
150 :
151 32798 : std::vector<std::shared_ptr<StackFrame>> frames;
152 32798 : if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
153 56874 : frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
154 : }
155 :
156 32798 : int maxAsyncDepth = 0;
157 32798 : std::shared_ptr<AsyncStackTrace> asyncParent;
158 32798 : std::shared_ptr<AsyncStackTrace> asyncCreation;
159 : calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
160 32798 : &maxAsyncDepth);
161 32798 : if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
162 : return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
163 118679 : std::move(frames), maxAsyncDepth, asyncParent, asyncCreation));
164 : }
165 :
166 : // static
167 32523 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
168 32523 : V8Debugger* debugger, int contextGroupId, int maxStackSize) {
169 : DCHECK(debugger);
170 : v8::Isolate* isolate = debugger->isolate();
171 32523 : v8::HandleScope handleScope(isolate);
172 : v8::Local<v8::StackTrace> v8StackTrace;
173 32523 : if (isolate->InContext()) {
174 : v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
175 31919 : stackTraceOptions);
176 : }
177 : return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
178 32523 : maxStackSize);
179 : }
180 :
181 28627 : V8StackTraceImpl::V8StackTraceImpl(
182 : std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth,
183 : std::shared_ptr<AsyncStackTrace> asyncParent,
184 : std::shared_ptr<AsyncStackTrace> asyncCreation)
185 : : m_frames(std::move(frames)),
186 : m_maxAsyncDepth(maxAsyncDepth),
187 : m_asyncParent(asyncParent),
188 85881 : m_asyncCreation(asyncCreation) {}
189 :
190 114508 : V8StackTraceImpl::~V8StackTraceImpl() {}
191 :
192 0 : std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
193 : return std::unique_ptr<V8StackTrace>(
194 : new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(),
195 0 : std::shared_ptr<AsyncStackTrace>()));
196 : }
197 :
198 56808 : bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
199 :
200 6405 : StringView V8StackTraceImpl::topSourceURL() const {
201 6405 : return toStringView(m_frames[0]->sourceURL());
202 : }
203 :
204 6458 : int V8StackTraceImpl::topLineNumber() const {
205 12916 : return m_frames[0]->lineNumber() + 1;
206 : }
207 :
208 6443 : int V8StackTraceImpl::topColumnNumber() const {
209 12886 : return m_frames[0]->columnNumber() + 1;
210 : }
211 :
212 53 : StringView V8StackTraceImpl::topScriptId() const {
213 53 : return toStringView(m_frames[0]->scriptId());
214 : }
215 :
216 0 : StringView V8StackTraceImpl::topFunctionName() const {
217 0 : return toStringView(m_frames[0]->functionName());
218 : }
219 :
220 : std::unique_ptr<protocol::Runtime::StackTrace>
221 28374 : V8StackTraceImpl::buildInspectorObjectImpl() const {
222 : return buildInspectorObjectCommon(m_frames, String16(), m_asyncParent.lock(),
223 113496 : m_asyncCreation.lock(), m_maxAsyncDepth);
224 : }
225 :
226 : std::unique_ptr<protocol::Runtime::API::StackTrace>
227 0 : V8StackTraceImpl::buildInspectorObject() const {
228 0 : return buildInspectorObjectImpl();
229 : }
230 :
231 10 : std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
232 10 : String16Builder stackTrace;
233 70 : for (size_t i = 0; i < m_frames.size(); ++i) {
234 60 : const StackFrame& frame = *m_frames[i];
235 90 : stackTrace.append("\n at " + (frame.functionName().length()
236 15 : ? frame.functionName()
237 25 : : "(anonymous function)"));
238 50 : stackTrace.append(" (");
239 25 : stackTrace.append(frame.sourceURL());
240 25 : stackTrace.append(':');
241 50 : stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
242 25 : stackTrace.append(':');
243 50 : stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
244 25 : stackTrace.append(')');
245 : }
246 10 : String16 string = stackTrace.toString();
247 30 : return StringBufferImpl::adopt(string);
248 : }
249 :
250 45 : bool V8StackTraceImpl::isEqualIgnoringTopFrame(
251 : V8StackTraceImpl* stackTrace) const {
252 45 : StackFrameIterator current(this);
253 45 : StackFrameIterator target(stackTrace);
254 :
255 45 : current.next();
256 45 : target.next();
257 160 : while (!current.done() && !target.done()) {
258 50 : if (!current.frame()->isEqual(target.frame())) {
259 : return false;
260 : }
261 20 : current.next();
262 20 : target.next();
263 : }
264 15 : return current.done() == target.done();
265 : }
266 :
267 90 : V8StackTraceImpl::StackFrameIterator::StackFrameIterator(
268 : const V8StackTraceImpl* stackTrace)
269 180 : : m_currentIt(stackTrace->m_frames.begin()),
270 : m_currentEnd(stackTrace->m_frames.end()),
271 270 : m_parent(stackTrace->m_asyncParent.lock().get()) {}
272 :
273 130 : void V8StackTraceImpl::StackFrameIterator::next() {
274 260 : if (m_currentIt == m_currentEnd) return;
275 : ++m_currentIt;
276 150 : while (m_currentIt == m_currentEnd && m_parent) {
277 40 : const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
278 20 : m_currentIt = frames.begin();
279 40 : if (m_parent->description() == "async function") ++m_currentIt;
280 20 : m_currentEnd = frames.end();
281 80 : m_parent = m_parent->parent().lock().get();
282 : }
283 : }
284 :
285 0 : bool V8StackTraceImpl::StackFrameIterator::done() {
286 0 : return m_currentIt == m_currentEnd;
287 : }
288 :
289 0 : StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
290 50 : return m_currentIt->get();
291 : }
292 :
293 : // static
294 45452 : std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
295 45452 : V8Debugger* debugger, int contextGroupId, const String16& description,
296 : int maxStackSize) {
297 : DCHECK(debugger);
298 :
299 : v8::Isolate* isolate = debugger->isolate();
300 45452 : v8::HandleScope handleScope(isolate);
301 :
302 45452 : std::vector<std::shared_ptr<StackFrame>> frames;
303 45452 : if (isolate->InContext()) {
304 : v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
305 45452 : isolate, maxStackSize, stackTraceOptions);
306 90904 : frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
307 : }
308 :
309 45452 : std::shared_ptr<AsyncStackTrace> asyncParent;
310 45452 : std::shared_ptr<AsyncStackTrace> asyncCreation;
311 : calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
312 45452 : nullptr);
313 :
314 45452 : if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
315 :
316 : // When async call chain is empty but doesn't contain useful schedule stack
317 : // and parent async call chain contains creationg stack but doesn't
318 : // synchronous we can merge them together.
319 : // e.g. Promise ThenableJob.
320 98459 : if (asyncParent && frames.empty() &&
321 47417 : asyncParent->m_description == description && !asyncCreation) {
322 : return asyncParent;
323 : }
324 :
325 : DCHECK(contextGroupId || asyncParent);
326 39947 : if (!contextGroupId && asyncParent) {
327 0 : contextGroupId = asyncParent->m_contextGroupId;
328 : }
329 : return std::shared_ptr<AsyncStackTrace>(
330 : new AsyncStackTrace(contextGroupId, description, std::move(frames),
331 165293 : asyncParent, asyncCreation));
332 : }
333 :
334 39947 : AsyncStackTrace::AsyncStackTrace(
335 : int contextGroupId, const String16& description,
336 : std::vector<std::shared_ptr<StackFrame>> frames,
337 : std::shared_ptr<AsyncStackTrace> asyncParent,
338 : std::shared_ptr<AsyncStackTrace> asyncCreation)
339 : : m_contextGroupId(contextGroupId),
340 : m_description(description),
341 : m_frames(std::move(frames)),
342 : m_asyncParent(asyncParent),
343 39947 : m_asyncCreation(asyncCreation) {
344 : DCHECK(m_contextGroupId);
345 39947 : }
346 :
347 : std::unique_ptr<protocol::Runtime::StackTrace>
348 1065 : AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation,
349 : int maxAsyncDepth) const {
350 : return buildInspectorObjectCommon(m_frames, m_description,
351 : m_asyncParent.lock(),
352 3195 : m_asyncCreation.lock(), maxAsyncDepth);
353 : }
354 :
355 6730 : int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
356 :
357 2010 : const String16& AsyncStackTrace::description() const { return m_description; }
358 :
359 0 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
360 0 : return m_asyncParent;
361 : }
362 :
363 0 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const {
364 0 : return m_asyncCreation;
365 : }
366 :
367 41202 : bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
368 :
369 : } // namespace v8_inspector
|