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 52253 : std::vector<std::shared_ptr<StackFrame>> toFramesVector(
22 : V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
23 : int maxStackSize) {
24 : DCHECK(debugger->isolate()->InContext());
25 104506 : int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
26 : std::vector<std::shared_ptr<StackFrame>> frames;
27 98782 : for (int i = 0; i < frameCount; ++i) {
28 93058 : frames.push_back(debugger->symbolize(v8StackTrace->GetFrame(i)));
29 : }
30 52253 : return frames;
31 : }
32 :
33 100404 : void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
34 : std::shared_ptr<AsyncStackTrace>* asyncParent,
35 : std::shared_ptr<AsyncStackTrace>* asyncCreation,
36 : int* maxAsyncDepth) {
37 112512 : *asyncParent = debugger->currentAsyncParent();
38 112512 : *asyncCreation = debugger->currentAsyncCreation();
39 100404 : 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 120936 : if (contextGroupId && *asyncParent &&
48 : (*asyncParent)->contextGroupId() != contextGroupId) {
49 : asyncParent->reset();
50 6840 : asyncCreation->reset();
51 0 : if (maxAsyncDepth) *maxAsyncDepth = 0;
52 56256 : 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 74124 : if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() &&
59 : (*asyncParent)->isEmpty()) {
60 396 : *asyncParent = (*asyncParent)->parent().lock();
61 : }
62 : }
63 :
64 42537 : std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
65 84828 : const std::vector<std::shared_ptr<StackFrame>>& frames,
66 : const std::shared_ptr<AsyncStackTrace>& asyncParent,
67 : const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) {
68 : std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
69 : inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
70 169656 : for (size_t i = 0; i < frames.size(); i++) {
71 84582 : inspectorFrames->addItem(frames[i]->buildInspectorObject());
72 : }
73 : std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
74 : protocol::Runtime::StackTrace::create()
75 42537 : .setCallFrames(std::move(inspectorFrames))
76 : .build();
77 42537 : if (asyncParent && maxAsyncDepth > 0) {
78 : stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(),
79 3912 : maxAsyncDepth - 1));
80 : }
81 42537 : return stackTrace;
82 : }
83 :
84 : } // namespace
85 :
86 45131 : StackFrame::StackFrame(v8::Local<v8::StackFrame> v8Frame)
87 : : m_functionName(toProtocolString(v8Frame->GetFunctionName())),
88 : m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
89 : m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())),
90 45131 : m_lineNumber(v8Frame->GetLineNumber() - 1),
91 90262 : m_columnNumber(v8Frame->GetColumn() - 1) {
92 : DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo);
93 : DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo);
94 45131 : }
95 :
96 186 : void StackFrame::translate(WasmTranslation* wasmTranslation) {
97 : wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
98 186 : &m_scriptId, &m_lineNumber, &m_columnNumber);
99 186 : }
100 :
101 0 : const String16& StackFrame::functionName() const { return m_functionName; }
102 :
103 0 : const String16& StackFrame::scriptId() const { return m_scriptId; }
104 :
105 0 : const String16& StackFrame::sourceURL() const { return m_sourceURL; }
106 :
107 7334 : int StackFrame::lineNumber() const { return m_lineNumber; }
108 :
109 7322 : int StackFrame::columnNumber() const { return m_columnNumber; }
110 :
111 43923 : std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject()
112 : const {
113 : return protocol::Runtime::CallFrame::create()
114 43923 : .setFunctionName(m_functionName)
115 : .setScriptId(m_scriptId)
116 : .setUrl(m_sourceURL)
117 43923 : .setLineNumber(m_lineNumber)
118 43923 : .setColumnNumber(m_columnNumber)
119 43923 : .build();
120 : }
121 :
122 : // static
123 768 : void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
124 : v8::Isolate* isolate, bool capture) {
125 : isolate->SetCaptureStackTraceForUncaughtExceptions(
126 768 : capture, V8StackTraceImpl::maxCallStackSizeToCapture);
127 768 : }
128 :
129 : // static
130 44148 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
131 44148 : V8Debugger* debugger, int contextGroupId,
132 : v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
133 : DCHECK(debugger);
134 :
135 : v8::Isolate* isolate = debugger->isolate();
136 44148 : v8::HandleScope scope(isolate);
137 :
138 44148 : std::vector<std::shared_ptr<StackFrame>> frames;
139 44148 : if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
140 80290 : frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
141 : }
142 :
143 44148 : int maxAsyncDepth = 0;
144 44148 : std::shared_ptr<AsyncStackTrace> asyncParent;
145 44148 : std::shared_ptr<AsyncStackTrace> asyncCreation;
146 : calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
147 44148 : &maxAsyncDepth);
148 44148 : if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
149 : return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
150 165087 : std::move(frames), maxAsyncDepth, asyncParent, asyncCreation));
151 : }
152 :
153 : // static
154 44028 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
155 44028 : V8Debugger* debugger, int contextGroupId, int maxStackSize) {
156 : DCHECK(debugger);
157 : v8::Isolate* isolate = debugger->isolate();
158 44028 : v8::HandleScope handleScope(isolate);
159 : v8::Local<v8::StackTrace> v8StackTrace;
160 44028 : if (isolate->InContext()) {
161 : v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
162 43508 : stackTraceOptions);
163 : }
164 : return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
165 44028 : maxStackSize);
166 : }
167 :
168 40313 : V8StackTraceImpl::V8StackTraceImpl(
169 : std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth,
170 : std::shared_ptr<AsyncStackTrace> asyncParent,
171 : std::shared_ptr<AsyncStackTrace> asyncCreation)
172 : : m_frames(std::move(frames)),
173 : m_maxAsyncDepth(maxAsyncDepth),
174 : m_asyncParent(asyncParent),
175 120939 : m_asyncCreation(asyncCreation) {}
176 :
177 161252 : V8StackTraceImpl::~V8StackTraceImpl() {}
178 :
179 0 : std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
180 : return std::unique_ptr<V8StackTrace>(
181 : new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(),
182 0 : std::shared_ptr<AsyncStackTrace>()));
183 : }
184 :
185 80442 : bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
186 :
187 7254 : StringView V8StackTraceImpl::topSourceURL() const {
188 7254 : return toStringView(m_frames[0]->sourceURL());
189 : }
190 :
191 7304 : int V8StackTraceImpl::topLineNumber() const {
192 14608 : return m_frames[0]->lineNumber() + 1;
193 : }
194 :
195 7292 : int V8StackTraceImpl::topColumnNumber() const {
196 14584 : return m_frames[0]->columnNumber() + 1;
197 : }
198 :
199 50 : StringView V8StackTraceImpl::topScriptId() const {
200 50 : return toStringView(m_frames[0]->scriptId());
201 : }
202 :
203 0 : StringView V8StackTraceImpl::topFunctionName() const {
204 0 : return toStringView(m_frames[0]->functionName());
205 : }
206 :
207 : std::unique_ptr<protocol::Runtime::StackTrace>
208 40203 : V8StackTraceImpl::buildInspectorObjectImpl() const {
209 : return buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
210 120609 : m_asyncCreation.lock(), m_maxAsyncDepth);
211 : }
212 :
213 : std::unique_ptr<protocol::Runtime::API::StackTrace>
214 0 : V8StackTraceImpl::buildInspectorObject() const {
215 0 : return buildInspectorObjectImpl();
216 : }
217 :
218 12 : std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
219 12 : String16Builder stackTrace;
220 84 : for (size_t i = 0; i < m_frames.size(); ++i) {
221 72 : const StackFrame& frame = *m_frames[i];
222 102 : stackTrace.append("\n at " + (frame.functionName().length()
223 : ? frame.functionName()
224 30 : : "(anonymous function)"));
225 60 : stackTrace.append(" (");
226 30 : stackTrace.append(frame.sourceURL());
227 30 : stackTrace.append(':');
228 60 : stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
229 30 : stackTrace.append(':');
230 60 : stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
231 30 : stackTrace.append(')');
232 : }
233 12 : String16 string = stackTrace.toString();
234 36 : return StringBufferImpl::adopt(string);
235 : }
236 :
237 : // static
238 12108 : std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
239 12108 : V8Debugger* debugger, int contextGroupId, const String16& description,
240 : int maxStackSize) {
241 : DCHECK(debugger);
242 :
243 : v8::Isolate* isolate = debugger->isolate();
244 12108 : v8::HandleScope handleScope(isolate);
245 :
246 12108 : std::vector<std::shared_ptr<StackFrame>> frames;
247 12108 : if (isolate->InContext()) {
248 : v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
249 12108 : isolate, maxStackSize, stackTraceOptions);
250 24216 : frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
251 : }
252 :
253 12108 : std::shared_ptr<AsyncStackTrace> asyncParent;
254 12108 : std::shared_ptr<AsyncStackTrace> asyncCreation;
255 : calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
256 12108 : nullptr);
257 :
258 12108 : if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
259 :
260 : // When async call chain is empty but doesn't contain useful schedule stack
261 : // and parent async call chain contains creationg stack but doesn't
262 : // synchronous we can merge them together.
263 : // e.g. Promise ThenableJob.
264 32010 : if (asyncParent && frames.empty() &&
265 14286 : asyncParent->m_description == description && !asyncCreation) {
266 : return asyncParent;
267 : }
268 :
269 : DCHECK(contextGroupId || asyncParent);
270 8574 : if (!contextGroupId && asyncParent) {
271 0 : contextGroupId = asyncParent->m_contextGroupId;
272 : }
273 : return std::shared_ptr<AsyncStackTrace>(
274 : new AsyncStackTrace(contextGroupId, description, std::move(frames),
275 37830 : asyncParent, asyncCreation));
276 : }
277 :
278 8574 : AsyncStackTrace::AsyncStackTrace(
279 : int contextGroupId, const String16& description,
280 : std::vector<std::shared_ptr<StackFrame>> frames,
281 : std::shared_ptr<AsyncStackTrace> asyncParent,
282 : std::shared_ptr<AsyncStackTrace> asyncCreation)
283 : : m_contextGroupId(contextGroupId),
284 : m_description(description),
285 : m_frames(std::move(frames)),
286 : m_asyncParent(asyncParent),
287 8574 : m_asyncCreation(asyncCreation) {
288 : DCHECK(m_contextGroupId);
289 8574 : }
290 :
291 : std::unique_ptr<protocol::Runtime::StackTrace>
292 2334 : AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation,
293 : int maxAsyncDepth) const {
294 : std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
295 : buildInspectorObjectCommon(m_frames, m_asyncParent.lock(),
296 7002 : m_asyncCreation.lock(), maxAsyncDepth);
297 4668 : if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
298 3966 : if (asyncCreation && !asyncCreation->isEmpty()) {
299 : stackTrace->setPromiseCreationFrame(
300 3264 : asyncCreation->m_frames[0]->buildInspectorObject());
301 : }
302 2334 : return stackTrace;
303 : }
304 :
305 6840 : int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
306 :
307 0 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
308 0 : return m_asyncParent;
309 : }
310 :
311 0 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const {
312 0 : return m_asyncCreation;
313 : }
314 :
315 8220 : bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
316 :
317 : } // namespace v8_inspector
|