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