v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
v8-stack-trace-impl.cc
Go to the documentation of this file.
1// Copyright 2016 the V8 project authors. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5
6#if defined(V8_OS_STARBOARD)
7#include "starboard/system.h"
8#define __builtin_abort SbSystemBreakIntoDebugger
9#endif
10
12
13#include <algorithm>
14#include <memory>
15#include <vector>
16
17#include "../../third_party/inspector_protocol/crdtp/json.h"
22
23using v8_crdtp::json::ConvertJSONToCBOR;
24
25namespace v8_inspector {
26namespace {
27
28static const char kId[] = "id";
29static const char kDebuggerId[] = "debuggerId";
30static const char kShouldPause[] = "shouldPause";
31
32static const v8::StackTrace::StackTraceOptions stackTraceOptions =
36
37std::vector<std::shared_ptr<StackFrame>> toFramesVector(
38 V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
39 int maxStackSize) {
40 DCHECK(debugger->isolate()->InContext());
41 int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
42
45 "v8.stack_trace"),
46 "toFramesVector", "frameCount", frameCount);
47
48 std::vector<std::shared_ptr<StackFrame>> frames(frameCount);
49 for (int i = 0; i < frameCount; ++i) {
50 frames[i] =
51 debugger->symbolize(v8StackTrace->GetFrame(debugger->isolate(), i));
52 }
53 return frames;
54}
55
56std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
57 V8Debugger* debugger,
58 const std::vector<std::shared_ptr<StackFrame>>& frames,
59 const String16& description,
60 const std::shared_ptr<AsyncStackTrace>& asyncParent,
61 const V8StackTraceId& externalParent, int maxAsyncDepth) {
62 if (asyncParent && frames.empty() &&
63 description == asyncParent->description()) {
64 return asyncParent->buildInspectorObject(debugger, maxAsyncDepth);
65 }
66
67 auto inspectorFrames =
68 std::make_unique<protocol::Array<protocol::Runtime::CallFrame>>();
69 for (const std::shared_ptr<StackFrame>& frame : frames) {
70 V8InspectorClient* client = nullptr;
71 if (debugger && debugger->inspector())
72 client = debugger->inspector()->client();
73 inspectorFrames->emplace_back(frame->buildInspectorObject(client));
74 }
75 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
76 protocol::Runtime::StackTrace::create()
77 .setCallFrames(std::move(inspectorFrames))
78 .build();
79 if (!description.isEmpty()) stackTrace->setDescription(description);
80 if (asyncParent) {
81 if (maxAsyncDepth > 0) {
82 stackTrace->setParent(
83 asyncParent->buildInspectorObject(debugger, maxAsyncDepth - 1));
84 } else if (debugger) {
85 stackTrace->setParentId(
86 protocol::Runtime::StackTraceId::create()
88 AsyncStackTrace::store(debugger, asyncParent)))
89 .build());
90 }
91 }
92 if (!externalParent.IsInvalid()) {
93 stackTrace->setParentId(
94 protocol::Runtime::StackTraceId::create()
95 .setId(stackTraceIdToString(externalParent.id))
96 .setDebuggerId(
97 internal::V8DebuggerId(externalParent.debugger_id).toString())
98 .build());
99 }
100 return stackTrace;
101}
102
103} // namespace
104
106 : id(0), debugger_id(internal::V8DebuggerId().pair()) {}
107
109 const std::pair<int64_t, int64_t> debugger_id)
110 : id(id), debugger_id(debugger_id) {}
111
113 const std::pair<int64_t, int64_t> debugger_id,
114 bool should_pause)
115 : id(id), debugger_id(debugger_id), should_pause(should_pause) {}
116
118 : id(0), debugger_id(internal::V8DebuggerId().pair()) {
119 if (json.length() == 0) return;
120 std::vector<uint8_t> cbor;
121 if (json.is8Bit()) {
122 ConvertJSONToCBOR(
123 v8_crdtp::span<uint8_t>(json.characters8(), json.length()), &cbor);
124 } else {
125 ConvertJSONToCBOR(
126 v8_crdtp::span<uint16_t>(json.characters16(), json.length()), &cbor);
127 }
128 auto dict = protocol::DictionaryValue::cast(
129 protocol::Value::parseBinary(cbor.data(), cbor.size()));
130 if (!dict) return;
131 String16 s;
132 if (!dict->getString(kId, &s)) return;
133 bool isOk = false;
134 int64_t parsedId = s.toInteger64(&isOk);
135 if (!isOk || !parsedId) return;
136 if (!dict->getString(kDebuggerId, &s)) return;
137 internal::V8DebuggerId debuggerId(s);
138 if (!debuggerId.isValid()) return;
139 if (!dict->getBoolean(kShouldPause, &should_pause)) return;
140 id = parsedId;
141 debugger_id = debuggerId.pair();
142}
143
144bool V8StackTraceId::IsInvalid() const { return !id; }
145
146std::unique_ptr<StringBuffer> V8StackTraceId::ToString() {
147 if (IsInvalid()) return nullptr;
148 auto dict = protocol::DictionaryValue::create();
149 dict->setString(kId, String16::fromInteger64(id));
150 dict->setString(kDebuggerId, internal::V8DebuggerId(debugger_id).toString());
151 dict->setBoolean(kShouldPause, should_pause);
152 std::vector<uint8_t> json;
153 v8_crdtp::json::ConvertCBORToJSON(v8_crdtp::SpanFrom(dict->Serialize()),
154 &json);
155 return StringBufferFrom(std::move(json));
156}
157
158StackFrame::StackFrame(String16&& functionName, int scriptId,
159 String16&& sourceURL, int lineNumber, int columnNumber,
160 bool hasSourceURLComment)
161 : m_functionName(std::move(functionName)),
162 m_scriptId(scriptId),
163 m_sourceURL(std::move(sourceURL)),
164 m_lineNumber(lineNumber),
165 m_columnNumber(columnNumber),
166 m_hasSourceURLComment(hasSourceURLComment) {
169}
170
172
173int StackFrame::scriptId() const { return m_scriptId; }
174
176
178
180
181std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject(
182 V8InspectorClient* client) const {
183 String16 frameUrl;
184 const char* dataURIPrefix = "data:";
185 if (m_sourceURL.substring(0, strlen(dataURIPrefix)) != dataURIPrefix) {
186 frameUrl = m_sourceURL;
187 }
188
189 if (client && !m_hasSourceURLComment && frameUrl.length() > 0) {
190 std::unique_ptr<StringBuffer> url =
192 if (url) {
193 frameUrl = toString16(url->string());
194 }
195 }
196 return protocol::Runtime::CallFrame::create()
197 .setFunctionName(m_functionName)
198 .setScriptId(String16::fromInteger(m_scriptId))
199 .setUrl(frameUrl)
200 .setLineNumber(m_lineNumber)
201 .setColumnNumber(m_columnNumber)
202 .build();
203}
204
205bool StackFrame::isEqual(StackFrame* frame) const {
206 return m_scriptId == frame->m_scriptId &&
207 m_lineNumber == frame->m_lineNumber &&
209}
210
211// static
212std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
213 V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
214 int maxStackSize) {
215 DCHECK(debugger);
216
217 v8::Isolate* isolate = debugger->isolate();
218 v8::HandleScope scope(isolate);
219
220 std::vector<std::shared_ptr<StackFrame>> frames;
221 if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
222 frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
223 }
224
225 int maxAsyncDepth = debugger->maxAsyncCallChainDepth();
226 std::shared_ptr<AsyncStackTrace> asyncParent;
227 V8StackTraceId externalParent;
228 if (!v8StackTrace.IsEmpty()) {
229 debugger->asyncParentFor(v8StackTrace->GetID(), &asyncParent,
230 &externalParent);
231 }
232 if (frames.empty() && !asyncParent && externalParent.IsInvalid()) return {};
233 return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
234 std::move(frames), maxAsyncDepth, asyncParent, externalParent));
235}
236
237// static
238std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
239 V8Debugger* debugger, int maxStackSize) {
240 DCHECK(debugger);
241
244 "v8.stack_trace"),
245 "V8StackTraceImpl::capture", "maxFrameCount", maxStackSize);
246
247 v8::Isolate* isolate = debugger->isolate();
248 v8::HandleScope handleScope(isolate);
249 v8::Local<v8::StackTrace> v8StackTrace;
250 if (isolate->InContext()) {
251 v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
252 stackTraceOptions);
253 }
254 return V8StackTraceImpl::create(debugger, v8StackTrace, maxStackSize);
255}
256
258 std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth,
259 std::shared_ptr<AsyncStackTrace> asyncParent,
260 const V8StackTraceId& externalParent)
261 : m_frames(std::move(frames)),
262 m_maxAsyncDepth(maxAsyncDepth),
263 m_asyncParent(std::move(asyncParent)),
264 m_externalParent(externalParent) {}
265
267
268std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
269 return std::unique_ptr<V8StackTrace>(new V8StackTraceImpl(
270 m_frames, 0, std::shared_ptr<AsyncStackTrace>(), V8StackTraceId()));
271}
272
275 while (!current.done()) {
276 if (current.frame()->sourceURL().length()) {
277 return toStringView(current.frame()->sourceURL());
278 }
279 current.next();
280 }
281 return StringView();
282}
283
284bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
285
287 return toStringView(m_frames[0]->sourceURL());
288}
289
291 return m_frames[0]->lineNumber() + 1;
292}
293
295 return m_frames[0]->columnNumber() + 1;
296}
297
298int V8StackTraceImpl::topScriptId() const { return m_frames[0]->scriptId(); }
299
301 return toStringView(m_frames[0]->functionName());
302}
303
304std::vector<V8StackFrame> V8StackTraceImpl::frames() const {
305 std::vector<V8StackFrame> ret;
306 ret.reserve(m_frames.size());
307
308 for (const auto& frame : m_frames) {
309 if (frame) {
310 ret.emplace_back(V8StackFrame{
311 toStringView(frame->sourceURL()), toStringView(frame->functionName()),
312 frame->lineNumber() + 1, frame->columnNumber() + 1,
313 frame->scriptId()});
314 }
315 }
316
317 return ret;
318}
319
320std::unique_ptr<protocol::Runtime::StackTrace>
324
325std::unique_ptr<protocol::Runtime::StackTrace>
327 int maxAsyncDepth) const {
328 return buildInspectorObjectCommon(debugger, m_frames, String16(),
330 maxAsyncDepth);
331}
332
333std::unique_ptr<protocol::Runtime::API::StackTrace>
335 return buildInspectorObjectImpl(nullptr,
336 std::min(maxAsyncDepth, m_maxAsyncDepth));
337}
338
339std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
340 String16Builder stackTrace;
341 for (size_t i = 0; i < m_frames.size(); ++i) {
342 const StackFrame& frame = *m_frames[i];
343 stackTrace.append("\n at " + (frame.functionName().length()
344 ? frame.functionName()
345 : "(anonymous function)"));
346 stackTrace.append(" (");
347 stackTrace.append(frame.sourceURL());
348 stackTrace.append(':');
349 stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
350 stackTrace.append(':');
351 stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
352 stackTrace.append(')');
353 }
354 return StringBufferFrom(stackTrace.toString());
355}
356
358 V8StackTraceImpl* stackTrace) const {
360 StackFrameIterator target(stackTrace);
361
362 current.next();
363 target.next();
364 while (!current.done() && !target.done()) {
365 if (!current.frame()->isEqual(target.frame())) {
366 return false;
367 }
368 current.next();
369 target.next();
370 }
371 return current.done() == target.done();
372}
373
375 const V8StackTraceImpl* stackTrace)
376 : m_currentIt(stackTrace->m_frames.begin()),
377 m_currentEnd(stackTrace->m_frames.end()),
378 m_parent(stackTrace->m_asyncParent.lock().get()) {}
379
381 if (m_currentIt == m_currentEnd) return;
382 ++m_currentIt;
383 while (m_currentIt == m_currentEnd && m_parent) {
384 const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
385 m_currentIt = frames.begin();
386 m_currentEnd = frames.end();
387 m_parent = m_parent->parent().lock().get();
388 }
389}
390
392 return m_currentIt == m_currentEnd;
393}
394
396 return m_currentIt->get();
397}
398
399// static
400std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
401 V8Debugger* debugger, const String16& description, bool skipTopFrame) {
402 DCHECK(debugger);
403
404 int maxStackSize = debugger->maxCallStackSizeToCapture();
407 "v8.stack_trace"),
408 "AsyncStackTrace::capture", "maxFrameCount", maxStackSize);
409
410 v8::Isolate* isolate = debugger->isolate();
411 v8::HandleScope handleScope(isolate);
412
413 std::vector<std::shared_ptr<StackFrame>> frames;
414 std::shared_ptr<AsyncStackTrace> asyncParent;
415 V8StackTraceId externalParent;
416 if (isolate->InContext()) {
418 isolate, maxStackSize, stackTraceOptions);
419 frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
420 if (skipTopFrame && !frames.empty()) {
421 frames.erase(frames.begin());
422 }
423
424 debugger->asyncParentFor(v8StackTrace->GetID(), &asyncParent,
425 &externalParent);
426 }
427
428 if (frames.empty() && !asyncParent && externalParent.IsInvalid())
429 return nullptr;
430
431 if (asyncParent && frames.empty() &&
432 (asyncParent->m_description == description || description.isEmpty())) {
433 return asyncParent;
434 }
435
436 return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace(
437 description, std::move(frames), asyncParent, externalParent));
438}
439
441 const String16& description,
442 std::vector<std::shared_ptr<StackFrame>> frames,
443 std::shared_ptr<AsyncStackTrace> asyncParent,
444 const V8StackTraceId& externalParent)
445 : m_id(0),
446 m_description(description),
447 m_frames(std::move(frames)),
448 m_asyncParent(std::move(asyncParent)),
449 m_externalParent(externalParent) {}
450
451std::unique_ptr<protocol::Runtime::StackTrace>
453 int maxAsyncDepth) const {
454 return buildInspectorObjectCommon(debugger, m_frames, m_description,
456 maxAsyncDepth);
457}
458
460 std::shared_ptr<AsyncStackTrace> stack) {
461 if (stack->m_id) return stack->m_id;
462 stack->m_id = debugger->storeStackTrace(stack);
463 return stack->m_id;
464}
465
467
468std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
469 return m_asyncParent;
470}
471
472bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
473
474} // namespace v8_inspector
static const int kNoLineNumberInfo
Definition v8-message.h:198
static const int kNoColumnInfo
Definition v8-message.h:199
static Local< StackTrace > CurrentStackTrace(Isolate *isolate, int frame_limit, StackTraceOptions options=kDetailed)
Definition api.cc:3064
@ kExposeFramesAcrossSecurityOrigins
Definition v8-debug.h:134
std::weak_ptr< AsyncStackTrace > parent() const
static std::shared_ptr< AsyncStackTrace > capture(V8Debugger *, const String16 &description, bool skipTopFrame=false)
const String16 & description() const
std::vector< std::shared_ptr< StackFrame > > m_frames
static uintptr_t store(V8Debugger *debugger, std::shared_ptr< AsyncStackTrace > stack)
std::unique_ptr< protocol::Runtime::StackTrace > buildInspectorObject(V8Debugger *debugger, int maxAsyncDepth) const
AsyncStackTrace(const AsyncStackTrace &)=delete
std::weak_ptr< AsyncStackTrace > m_asyncParent
StackFrame(String16 &&functionName, int scriptId, String16 &&sourceURL, int lineNumber, int columnNumber, bool hasSourceURLComment)
bool isEqual(StackFrame *frame) const
std::unique_ptr< protocol::Runtime::CallFrame > buildInspectorObject(V8InspectorClient *client) const
const String16 & functionName() const
const String16 & sourceURL() const
void append(const String16 &)
Definition string-16.cc:156
String16 substring(size_t pos, size_t len=UINT_MAX) const
Definition string-16.h:61
size_t length() const
Definition string-16.h:58
static String16 fromInteger(int)
Definition string-16.cc:71
static String16 fromInteger64(int64_t)
Definition string-16.cc:91
bool isEmpty() const
Definition string-16.h:59
int64_t toInteger64(bool *ok=nullptr) const
Definition string-16.cc:114
int maxCallStackSizeToCapture() const
uintptr_t storeStackTrace(std::shared_ptr< AsyncStackTrace > stack)
v8::Isolate * isolate() const
Definition v8-debugger.h:60
void asyncParentFor(int stackTraceId, std::shared_ptr< AsyncStackTrace > *asyncParent, V8StackTraceId *externalParent) const
virtual std::unique_ptr< StringBuffer > resourceNameToUrl(const StringView &resourceName)
StackFrameIterator(const V8StackTraceImpl *stackTrace)
static std::unique_ptr< V8StackTraceImpl > capture(V8Debugger *, int maxStackSize)
StringView firstNonEmptySourceURL() const override
std::unique_ptr< protocol::Runtime::StackTrace > buildInspectorObjectImpl(V8Debugger *debugger) const
std::unique_ptr< StringBuffer > toString() const override
static std::unique_ptr< V8StackTraceImpl > create(V8Debugger *, v8::Local< v8::StackTrace >, int maxStackSize)
StringView topSourceURL() const override
V8StackTraceImpl(const V8StackTraceImpl &)=delete
std::unique_ptr< protocol::Runtime::API::StackTrace > buildInspectorObject(int maxAsyncDepth) const override
bool isEqualIgnoringTopFrame(V8StackTraceImpl *stackTrace) const
std::unique_ptr< V8StackTrace > clone() override
std::vector< V8StackFrame > frames() const override
StringView topFunctionName() const override
std::weak_ptr< AsyncStackTrace > m_asyncParent
std::vector< std::shared_ptr< StackFrame > > m_frames
int end
static constexpr DebugProxyId kId
LineAndColumn current
TNode< Object > target
int s
Definition mul-fft.cc:297
STL namespace.
String16 stackTraceIdToString(uintptr_t id)
StringView toStringView(const String16 &string)
std::unique_ptr< StringBuffer > StringBufferFrom(String16 str)
String16 toString16(const StringView &string)
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
std::unique_ptr< StringBuffer > ToString()
std::pair< int64_t, int64_t > debugger_id
#define TRACE_DISABLED_BY_DEFAULT(name)
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
int m_lineNumber
int m_columnNumber
String16 m_description
int m_scriptId