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