5 #include "src/inspector/v8-stack-trace-impl.h" 9 #include "src/inspector/v8-debugger.h" 10 #include "src/inspector/v8-inspector-impl.h" 11 #include "src/inspector/wasm-translation.h" 15 int V8StackTraceImpl::maxCallStackSizeToCapture = 200;
21 v8::StackTrace::kDetailed |
22 v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
24 std::vector<std::shared_ptr<StackFrame>> toFramesVector(
27 DCHECK(debugger->isolate()->InContext());
28 int frameCount = std::min(v8StackTrace->
GetFrameCount(), maxStackSize);
29 std::vector<std::shared_ptr<StackFrame>> frames(frameCount);
30 for (
int i = 0;
i < frameCount; ++
i) {
32 debugger->symbolize(v8StackTrace->
GetFrame(debugger->isolate(),
i));
37 void calculateAsyncChain(V8Debugger* debugger,
int contextGroupId,
38 std::shared_ptr<AsyncStackTrace>* asyncParent,
39 V8StackTraceId* externalParent,
int* maxAsyncDepth) {
40 *asyncParent = debugger->currentAsyncParent();
41 *externalParent = debugger->currentExternalParent();
42 DCHECK(externalParent->IsInvalid() || !*asyncParent);
43 if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
48 if (contextGroupId && *asyncParent &&
49 (*asyncParent)->externalParent().IsInvalid() &&
50 (*asyncParent)->contextGroupId() != contextGroupId) {
52 *externalParent = V8StackTraceId();
53 if (maxAsyncDepth) *maxAsyncDepth = 0;
59 if (*asyncParent && (*asyncParent)->isEmpty()) {
60 *asyncParent = (*asyncParent)->parent().lock();
64 std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
66 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 if (asyncParent && frames.empty() &&
71 description == asyncParent->description()) {
72 return asyncParent->buildInspectorObject(debugger, maxAsyncDepth);
75 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
76 inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
77 for (
size_t i = 0;
i < frames.size();
i++) {
78 V8InspectorClient* client =
nullptr;
79 if (debugger && debugger->inspector())
80 client = debugger->inspector()->client();
81 inspectorFrames->addItem(frames[
i]->buildInspectorObject(client));
83 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
84 protocol::Runtime::StackTrace::create()
85 .setCallFrames(std::move(inspectorFrames))
87 if (!description.isEmpty()) stackTrace->setDescription(description);
89 if (maxAsyncDepth > 0) {
90 stackTrace->setParent(
91 asyncParent->buildInspectorObject(debugger, maxAsyncDepth - 1));
92 }
else if (debugger) {
93 stackTrace->setParentId(
94 protocol::Runtime::StackTraceId::create()
95 .setId(stackTraceIdToString(
96 AsyncStackTrace::store(debugger, asyncParent)))
100 if (!externalParent.IsInvalid()) {
101 stackTrace->setParentId(
102 protocol::Runtime::StackTraceId::create()
103 .setId(stackTraceIdToString(externalParent.id))
104 .setDebuggerId(debuggerIdToString(externalParent.debugger_id))
112 V8StackTraceId::V8StackTraceId() : id(0), debugger_id(
std::make_pair(0, 0)) {}
114 V8StackTraceId::V8StackTraceId(
uintptr_t id,
115 const std::pair<int64_t, int64_t> debugger_id)
116 : id(id), debugger_id(debugger_id) {}
118 bool V8StackTraceId::IsInvalid()
const {
return !id; }
121 : m_functionName(toProtocolString(isolate, v8Frame->GetFunctionName())),
122 m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
124 toProtocolString(isolate, v8Frame->GetScriptNameOrSourceURL())),
125 m_lineNumber(v8Frame->GetLineNumber() - 1),
126 m_columnNumber(v8Frame->GetColumn() - 1),
127 m_hasSourceURLComment(v8Frame->GetScriptName() !=
128 v8Frame->GetScriptNameOrSourceURL()) {
129 DCHECK_NE(v8::Message::kNoLineNumberInfo, m_lineNumber + 1);
130 DCHECK_NE(v8::Message::kNoColumnInfo, m_columnNumber + 1);
133 void StackFrame::translate(WasmTranslation* wasmTranslation) {
134 wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
135 &m_scriptId, &m_lineNumber, &m_columnNumber);
138 const String16& StackFrame::functionName()
const {
return m_functionName; }
140 const String16& StackFrame::scriptId()
const {
return m_scriptId; }
142 const String16& StackFrame::sourceURL()
const {
return m_sourceURL; }
144 int StackFrame::lineNumber()
const {
return m_lineNumber; }
146 int StackFrame::columnNumber()
const {
return m_columnNumber; }
148 std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject(
149 V8InspectorClient* client)
const {
150 String16 frameUrl = m_sourceURL;
151 if (client && !m_hasSourceURLComment && frameUrl.length() > 0) {
152 std::unique_ptr<StringBuffer> url =
153 client->resourceNameToUrl(toStringView(m_sourceURL));
155 frameUrl = toString16(url->string());
158 return protocol::Runtime::CallFrame::create()
159 .setFunctionName(m_functionName)
160 .setScriptId(m_scriptId)
162 .setLineNumber(m_lineNumber)
163 .setColumnNumber(m_columnNumber)
167 bool StackFrame::isEqual(StackFrame* frame)
const {
168 return m_scriptId == frame->m_scriptId &&
169 m_lineNumber == frame->m_lineNumber &&
170 m_columnNumber == frame->m_columnNumber;
174 void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
175 v8::Isolate* isolate,
bool capture) {
176 isolate->SetCaptureStackTraceForUncaughtExceptions(
177 capture, V8StackTraceImpl::maxCallStackSizeToCapture);
181 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
182 V8Debugger* debugger,
int contextGroupId,
186 v8::Isolate* isolate = debugger->isolate();
189 std::vector<std::shared_ptr<StackFrame>> frames;
191 frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
194 int maxAsyncDepth = 0;
195 std::shared_ptr<AsyncStackTrace> asyncParent;
196 V8StackTraceId externalParent;
197 calculateAsyncChain(debugger, contextGroupId, &asyncParent, &externalParent,
199 if (frames.empty() && !asyncParent && externalParent.IsInvalid())
201 return std::unique_ptr<V8StackTraceImpl>(
new V8StackTraceImpl(
202 std::move(frames), maxAsyncDepth, asyncParent, externalParent));
206 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
207 V8Debugger* debugger,
int contextGroupId,
int maxStackSize) {
209 v8::Isolate* isolate = debugger->isolate();
212 if (isolate->InContext()) {
216 return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
220 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 m_externalParent(externalParent) {}
229 V8StackTraceImpl::~V8StackTraceImpl() =
default;
231 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
232 return std::unique_ptr<V8StackTrace>(
new V8StackTraceImpl(
233 m_frames, 0, std::shared_ptr<AsyncStackTrace>(), V8StackTraceId()));
236 StringView V8StackTraceImpl::firstNonEmptySourceURL()
const {
237 StackFrameIterator current(
this);
238 while (!current.done()) {
239 if (current.frame()->sourceURL().length()) {
240 return toStringView(current.frame()->sourceURL());
247 bool V8StackTraceImpl::isEmpty()
const {
return m_frames.empty(); }
249 StringView V8StackTraceImpl::topSourceURL()
const {
250 return toStringView(m_frames[0]->sourceURL());
253 int V8StackTraceImpl::topLineNumber()
const {
254 return m_frames[0]->lineNumber() + 1;
257 int V8StackTraceImpl::topColumnNumber()
const {
258 return m_frames[0]->columnNumber() + 1;
261 StringView V8StackTraceImpl::topScriptId()
const {
262 return toStringView(m_frames[0]->scriptId());
265 StringView V8StackTraceImpl::topFunctionName()
const {
266 return toStringView(m_frames[0]->functionName());
269 std::unique_ptr<protocol::Runtime::StackTrace>
270 V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger)
const {
271 return buildInspectorObjectImpl(debugger, m_maxAsyncDepth);
274 std::unique_ptr<protocol::Runtime::StackTrace>
275 V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger,
276 int maxAsyncDepth)
const {
277 return buildInspectorObjectCommon(debugger, m_frames, String16(),
278 m_asyncParent.lock(), m_externalParent,
282 std::unique_ptr<protocol::Runtime::API::StackTrace>
283 V8StackTraceImpl::buildInspectorObject()
const {
284 return buildInspectorObjectImpl(
nullptr);
287 std::unique_ptr<StringBuffer> V8StackTraceImpl::toString()
const {
288 String16Builder stackTrace;
289 for (
size_t i = 0;
i < m_frames.size(); ++
i) {
290 const StackFrame& frame = *m_frames[
i];
291 stackTrace.append(
"\n at " + (frame.functionName().length()
292 ? frame.functionName()
293 :
"(anonymous function)"));
294 stackTrace.append(
" (");
295 stackTrace.append(frame.sourceURL());
296 stackTrace.append(
':');
297 stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
298 stackTrace.append(
':');
299 stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
300 stackTrace.append(
')');
302 String16
string = stackTrace.toString();
303 return StringBufferImpl::adopt(
string);
306 bool V8StackTraceImpl::isEqualIgnoringTopFrame(
307 V8StackTraceImpl* stackTrace)
const {
308 StackFrameIterator current(
this);
309 StackFrameIterator target(stackTrace);
313 while (!current.done() && !target.done()) {
314 if (!current.frame()->isEqual(target.frame())) {
320 return current.done() == target.done();
323 V8StackTraceImpl::StackFrameIterator::StackFrameIterator(
324 const V8StackTraceImpl* stackTrace)
325 : m_currentIt(stackTrace->m_frames.begin()),
326 m_currentEnd(stackTrace->m_frames.end()),
327 m_parent(stackTrace->m_asyncParent.lock().get()) {}
329 void V8StackTraceImpl::StackFrameIterator::next() {
330 if (m_currentIt == m_currentEnd)
return;
332 while (m_currentIt == m_currentEnd && m_parent) {
333 const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
334 m_currentIt = frames.begin();
335 if (m_parent->description() ==
"async function") ++m_currentIt;
336 m_currentEnd = frames.end();
337 m_parent = m_parent->parent().lock().get();
341 bool V8StackTraceImpl::StackFrameIterator::done() {
342 return m_currentIt == m_currentEnd;
345 StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
346 return m_currentIt->get();
350 std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
351 V8Debugger* debugger,
int contextGroupId,
const String16& description,
355 v8::Isolate* isolate = debugger->isolate();
358 std::vector<std::shared_ptr<StackFrame>> frames;
359 if (isolate->InContext()) {
361 isolate, maxStackSize, stackTraceOptions);
362 frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
365 std::shared_ptr<AsyncStackTrace> asyncParent;
366 V8StackTraceId externalParent;
367 calculateAsyncChain(debugger, contextGroupId, &asyncParent, &externalParent,
370 if (frames.empty() && !asyncParent && externalParent.IsInvalid())
376 if (asyncParent && frames.empty() &&
377 (asyncParent->m_description == description || description.isEmpty())) {
381 DCHECK(contextGroupId || asyncParent || !externalParent.IsInvalid());
382 if (!contextGroupId && asyncParent) {
383 contextGroupId = asyncParent->m_contextGroupId;
386 return std::shared_ptr<AsyncStackTrace>(
387 new AsyncStackTrace(contextGroupId, description, std::move(frames),
388 asyncParent, externalParent));
391 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),
398 m_suspendedTaskId(nullptr),
399 m_description(description),
400 m_frames(
std::move(frames)),
401 m_asyncParent(
std::move(asyncParent)),
402 m_externalParent(externalParent) {
403 DCHECK(m_contextGroupId || (!externalParent.IsInvalid() && m_frames.empty()));
406 std::unique_ptr<protocol::Runtime::StackTrace>
407 AsyncStackTrace::buildInspectorObject(V8Debugger* debugger,
408 int maxAsyncDepth)
const {
409 return buildInspectorObjectCommon(debugger, m_frames, m_description,
410 m_asyncParent.lock(), m_externalParent,
414 int AsyncStackTrace::contextGroupId()
const {
return m_contextGroupId; }
416 void AsyncStackTrace::setSuspendedTaskId(
void* task) {
417 m_suspendedTaskId = task;
420 void* AsyncStackTrace::suspendedTaskId()
const {
return m_suspendedTaskId; }
422 uintptr_t AsyncStackTrace::store(V8Debugger* debugger,
423 std::shared_ptr<AsyncStackTrace> stack) {
424 if (stack->m_id)
return stack->m_id;
425 stack->m_id = debugger->storeStackTrace(stack);
429 const String16& AsyncStackTrace::description()
const {
return m_description; }
431 std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent()
const {
432 return m_asyncParent;
435 bool AsyncStackTrace::isEmpty()
const {
return m_frames.empty(); }
Local< StackFrame > GetFrame(Isolate *isolate, uint32_t index) const
V8_INLINE bool IsEmpty() const
int GetFrameCount() const
static Local< StackTrace > CurrentStackTrace(Isolate *isolate, int frame_limit, StackTraceOptions options=kDetailed)