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 "src/inspector/string-util.h"
8 #include "src/inspector/v8-debugger.h"
9 #include "src/inspector/v8-inspector-impl.h"
10 #include "src/inspector/v8-profiler-agent-impl.h"
11
12 #include "include/v8-debug.h"
13 #include "include/v8-profiler.h"
14 #include "include/v8-version.h"
15
16 namespace v8_inspector {
17
18 namespace {
19
20 static const v8::StackTrace::StackTraceOptions stackTraceOptions =
21 static_cast<v8::StackTrace::StackTraceOptions>(
22 v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset |
23 v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL |
24 v8::StackTrace::kFunctionName);
25
toFrame(v8::Local<v8::StackFrame> frame)26 V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) {
27 String16 scriptId = String16::fromInteger(frame->GetScriptId());
28 String16 sourceName;
29 v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL());
30 if (!sourceNameValue.IsEmpty())
31 sourceName = toProtocolString(sourceNameValue);
32
33 String16 functionName;
34 v8::Local<v8::String> functionNameValue(frame->GetFunctionName());
35 if (!functionNameValue.IsEmpty())
36 functionName = toProtocolString(functionNameValue);
37
38 int sourceLineNumber = frame->GetLineNumber();
39 int sourceColumn = frame->GetColumn();
40 return V8StackTraceImpl::Frame(functionName, scriptId, sourceName,
41 sourceLineNumber, sourceColumn);
42 }
43
toFramesVector(v8::Local<v8::StackTrace> stackTrace,std::vector<V8StackTraceImpl::Frame> & frames,size_t maxStackSize,v8::Isolate * isolate)44 void toFramesVector(v8::Local<v8::StackTrace> stackTrace,
45 std::vector<V8StackTraceImpl::Frame>& frames,
46 size_t maxStackSize, v8::Isolate* isolate) {
47 DCHECK(isolate->InContext());
48 int frameCount = stackTrace->GetFrameCount();
49 if (frameCount > static_cast<int>(maxStackSize))
50 frameCount = static_cast<int>(maxStackSize);
51 for (int i = 0; i < frameCount; i++) {
52 v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i);
53 frames.push_back(toFrame(stackFrame));
54 }
55 }
56
57 } // namespace
58
Frame()59 V8StackTraceImpl::Frame::Frame()
60 : m_functionName("undefined"),
61 m_scriptId(""),
62 m_scriptName("undefined"),
63 m_lineNumber(0),
64 m_columnNumber(0) {}
65
Frame(const String16 & functionName,const String16 & scriptId,const String16 & scriptName,int lineNumber,int column)66 V8StackTraceImpl::Frame::Frame(const String16& functionName,
67 const String16& scriptId,
68 const String16& scriptName, int lineNumber,
69 int column)
70 : m_functionName(functionName),
71 m_scriptId(scriptId),
72 m_scriptName(scriptName),
73 m_lineNumber(lineNumber),
74 m_columnNumber(column) {
75 DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo);
76 DCHECK(m_columnNumber != v8::Message::kNoColumnInfo);
77 }
78
~Frame()79 V8StackTraceImpl::Frame::~Frame() {}
80
81 // buildInspectorObject() and SourceLocation's toTracedValue() should set the
82 // same fields.
83 // If either of them is modified, the other should be also modified.
84 std::unique_ptr<protocol::Runtime::CallFrame>
buildInspectorObject() const85 V8StackTraceImpl::Frame::buildInspectorObject() const {
86 return protocol::Runtime::CallFrame::create()
87 .setFunctionName(m_functionName)
88 .setScriptId(m_scriptId)
89 .setUrl(m_scriptName)
90 .setLineNumber(m_lineNumber - 1)
91 .setColumnNumber(m_columnNumber - 1)
92 .build();
93 }
94
clone() const95 V8StackTraceImpl::Frame V8StackTraceImpl::Frame::clone() const {
96 return Frame(m_functionName, m_scriptId, m_scriptName, m_lineNumber,
97 m_columnNumber);
98 }
99
100 // static
setCaptureStackTraceForUncaughtExceptions(v8::Isolate * isolate,bool capture)101 void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
102 v8::Isolate* isolate, bool capture) {
103 isolate->SetCaptureStackTraceForUncaughtExceptions(
104 capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions);
105 }
106
107 // static
create(V8Debugger * debugger,int contextGroupId,v8::Local<v8::StackTrace> stackTrace,size_t maxStackSize,const String16 & description)108 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
109 V8Debugger* debugger, int contextGroupId,
110 v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
111 const String16& description) {
112 v8::Isolate* isolate = v8::Isolate::GetCurrent();
113 v8::HandleScope scope(isolate);
114 std::vector<V8StackTraceImpl::Frame> frames;
115 if (!stackTrace.IsEmpty())
116 toFramesVector(stackTrace, frames, maxStackSize, isolate);
117
118 int maxAsyncCallChainDepth = 1;
119 V8StackTraceImpl* asyncCallChain = nullptr;
120 if (debugger && maxStackSize > 1) {
121 asyncCallChain = debugger->currentAsyncCallChain();
122 maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth();
123 }
124 // Do not accidentally append async call chain from another group. This should
125 // not
126 // happen if we have proper instrumentation, but let's double-check to be
127 // safe.
128 if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId &&
129 asyncCallChain->m_contextGroupId != contextGroupId) {
130 asyncCallChain = nullptr;
131 maxAsyncCallChainDepth = 1;
132 }
133
134 // Only the top stack in the chain may be empty, so ensure that second stack
135 // is non-empty (it's the top of appended chain).
136 if (asyncCallChain && asyncCallChain->isEmpty())
137 asyncCallChain = asyncCallChain->m_parent.get();
138
139 if (stackTrace.IsEmpty() && !asyncCallChain) return nullptr;
140
141 std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(
142 contextGroupId, description, frames,
143 asyncCallChain ? asyncCallChain->cloneImpl() : nullptr));
144
145 // Crop to not exceed maxAsyncCallChainDepth.
146 V8StackTraceImpl* deepest = result.get();
147 while (deepest && maxAsyncCallChainDepth) {
148 deepest = deepest->m_parent.get();
149 maxAsyncCallChainDepth--;
150 }
151 if (deepest) deepest->m_parent.reset();
152
153 return result;
154 }
155
156 // static
capture(V8Debugger * debugger,int contextGroupId,size_t maxStackSize,const String16 & description)157 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
158 V8Debugger* debugger, int contextGroupId, size_t maxStackSize,
159 const String16& description) {
160 v8::Isolate* isolate = v8::Isolate::GetCurrent();
161 v8::HandleScope handleScope(isolate);
162 v8::Local<v8::StackTrace> stackTrace;
163 if (isolate->InContext()) {
164 if (debugger) {
165 V8InspectorImpl* inspector = debugger->inspector();
166 V8ProfilerAgentImpl* profilerAgent =
167 inspector->enabledProfilerAgentForGroup(contextGroupId);
168 if (profilerAgent) profilerAgent->collectSample();
169 }
170 stackTrace = v8::StackTrace::CurrentStackTrace(
171 isolate, static_cast<int>(maxStackSize), stackTraceOptions);
172 }
173 return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace,
174 maxStackSize, description);
175 }
176
cloneImpl()177 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() {
178 std::vector<Frame> framesCopy(m_frames);
179 return wrapUnique(
180 new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy,
181 m_parent ? m_parent->cloneImpl() : nullptr));
182 }
183
clone()184 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
185 std::vector<Frame> frames;
186 for (size_t i = 0; i < m_frames.size(); i++)
187 frames.push_back(m_frames.at(i).clone());
188 return wrapUnique(
189 new V8StackTraceImpl(m_contextGroupId, m_description, frames, nullptr));
190 }
191
V8StackTraceImpl(int contextGroupId,const String16 & description,std::vector<Frame> & frames,std::unique_ptr<V8StackTraceImpl> parent)192 V8StackTraceImpl::V8StackTraceImpl(int contextGroupId,
193 const String16& description,
194 std::vector<Frame>& frames,
195 std::unique_ptr<V8StackTraceImpl> parent)
196 : m_contextGroupId(contextGroupId),
197 m_description(description),
198 m_parent(std::move(parent)) {
199 m_frames.swap(frames);
200 }
201
~V8StackTraceImpl()202 V8StackTraceImpl::~V8StackTraceImpl() {}
203
topSourceURL() const204 StringView V8StackTraceImpl::topSourceURL() const {
205 DCHECK(m_frames.size());
206 return toStringView(m_frames[0].m_scriptName);
207 }
208
topLineNumber() const209 int V8StackTraceImpl::topLineNumber() const {
210 DCHECK(m_frames.size());
211 return m_frames[0].m_lineNumber;
212 }
213
topColumnNumber() const214 int V8StackTraceImpl::topColumnNumber() const {
215 DCHECK(m_frames.size());
216 return m_frames[0].m_columnNumber;
217 }
218
topFunctionName() const219 StringView V8StackTraceImpl::topFunctionName() const {
220 DCHECK(m_frames.size());
221 return toStringView(m_frames[0].m_functionName);
222 }
223
topScriptId() const224 StringView V8StackTraceImpl::topScriptId() const {
225 DCHECK(m_frames.size());
226 return toStringView(m_frames[0].m_scriptId);
227 }
228
229 std::unique_ptr<protocol::Runtime::StackTrace>
buildInspectorObjectImpl() const230 V8StackTraceImpl::buildInspectorObjectImpl() const {
231 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames =
232 protocol::Array<protocol::Runtime::CallFrame>::create();
233 for (size_t i = 0; i < m_frames.size(); i++)
234 frames->addItem(m_frames.at(i).buildInspectorObject());
235
236 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
237 protocol::Runtime::StackTrace::create()
238 .setCallFrames(std::move(frames))
239 .build();
240 if (!m_description.isEmpty()) stackTrace->setDescription(m_description);
241 if (m_parent) stackTrace->setParent(m_parent->buildInspectorObjectImpl());
242 return stackTrace;
243 }
244
245 std::unique_ptr<protocol::Runtime::StackTrace>
buildInspectorObjectForTail(V8Debugger * debugger) const246 V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) const {
247 v8::HandleScope handleScope(v8::Isolate::GetCurrent());
248 // Next call collapses possible empty stack and ensures
249 // maxAsyncCallChainDepth.
250 std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(
251 debugger, m_contextGroupId, v8::Local<v8::StackTrace>(),
252 V8StackTraceImpl::maxCallStackSizeToCapture);
253 if (!fullChain || !fullChain->m_parent) return nullptr;
254 return fullChain->m_parent->buildInspectorObjectImpl();
255 }
256
257 std::unique_ptr<protocol::Runtime::API::StackTrace>
buildInspectorObject() const258 V8StackTraceImpl::buildInspectorObject() const {
259 return buildInspectorObjectImpl();
260 }
261
toString() const262 std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
263 String16Builder stackTrace;
264 for (size_t i = 0; i < m_frames.size(); ++i) {
265 const Frame& frame = m_frames[i];
266 stackTrace.append("\n at " + (frame.functionName().length()
267 ? frame.functionName()
268 : "(anonymous function)"));
269 stackTrace.append(" (");
270 stackTrace.append(frame.sourceURL());
271 stackTrace.append(':');
272 stackTrace.append(String16::fromInteger(frame.lineNumber()));
273 stackTrace.append(':');
274 stackTrace.append(String16::fromInteger(frame.columnNumber()));
275 stackTrace.append(')');
276 }
277 String16 string = stackTrace.toString();
278 return StringBufferImpl::adopt(string);
279 }
280
281 } // namespace v8_inspector
282