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/V8InspectorImpl.h" | |
10 #include "src/inspector/V8ProfilerAgentImpl.h" | |
11 #include "src/inspector/protocol/Protocol.h" | |
12 | |
13 #include "include/v8-debug.h" | |
14 #include "include/v8-profiler.h" | |
15 #include "include/v8-version.h" | |
16 | |
17 namespace v8_inspector { | |
18 | |
19 namespace { | |
20 | |
21 static const v8::StackTrace::StackTraceOptions stackTraceOptions = | |
22 static_cast<v8::StackTrace::StackTraceOptions>( | |
23 v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset | | |
24 v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL | | |
25 v8::StackTrace::kFunctionName); | |
26 | |
27 V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) { | |
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, | |
42 sourceLineNumber, sourceColumn); | |
43 } | |
44 | |
45 void toFramesVector(v8::Local<v8::StackTrace> stackTrace, | |
46 std::vector<V8StackTraceImpl::Frame>& frames, | |
47 size_t maxStackSize, v8::Isolate* isolate) { | |
48 DCHECK(isolate->InContext()); | |
49 int frameCount = stackTrace->GetFrameCount(); | |
50 if (frameCount > static_cast<int>(maxStackSize)) frameCount = 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 | |
59 V8StackTraceImpl::Frame::Frame() | |
60 : m_functionName("undefined"), | |
61 m_scriptId(""), | |
62 m_scriptName("undefined"), | |
63 m_lineNumber(0), | |
64 m_columnNumber(0) {} | |
65 | |
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 | |
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> | |
85 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 | |
95 V8StackTraceImpl::Frame V8StackTraceImpl::Frame::clone() const { | |
96 return Frame(m_functionName, m_scriptId, m_scriptName, m_lineNumber, | |
97 m_columnNumber); | |
98 } | |
99 | |
100 // static | |
101 void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions( | |
102 v8::Isolate* isolate, bool capture) { | |
103 isolate->SetCaptureStackTraceForUncaughtExceptions( | |
104 capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions); | |
105 } | |
106 | |
107 // static | |
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 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( | |
157 V8Debugger* debugger, int contextGroupId, size_t maxStackSize, | |
158 const String16& description) { | |
159 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | |
160 v8::HandleScope handleScope(isolate); | |
161 v8::Local<v8::StackTrace> stackTrace; | |
162 if (isolate->InContext()) { | |
163 if (debugger) { | |
164 V8InspectorImpl* inspector = debugger->inspector(); | |
165 V8ProfilerAgentImpl* profilerAgent = | |
166 inspector->enabledProfilerAgentForGroup(contextGroupId); | |
167 if (profilerAgent) profilerAgent->collectSample(); | |
168 } | |
169 stackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, | |
170 stackTraceOptions); | |
171 } | |
172 return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace, | |
173 maxStackSize, description); | |
174 } | |
175 | |
176 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() { | |
177 std::vector<Frame> framesCopy(m_frames); | |
178 return wrapUnique( | |
179 new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy, | |
180 m_parent ? m_parent->cloneImpl() : nullptr)); | |
181 } | |
182 | |
183 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { | |
184 std::vector<Frame> frames; | |
185 for (size_t i = 0; i < m_frames.size(); i++) | |
186 frames.push_back(m_frames.at(i).clone()); | |
187 return wrapUnique( | |
188 new V8StackTraceImpl(m_contextGroupId, m_description, frames, nullptr)); | |
189 } | |
190 | |
191 V8StackTraceImpl::V8StackTraceImpl(int contextGroupId, | |
192 const String16& description, | |
193 std::vector<Frame>& frames, | |
194 std::unique_ptr<V8StackTraceImpl> parent) | |
195 : m_contextGroupId(contextGroupId), | |
196 m_description(description), | |
197 m_parent(std::move(parent)) { | |
198 m_frames.swap(frames); | |
199 } | |
200 | |
201 V8StackTraceImpl::~V8StackTraceImpl() {} | |
202 | |
203 StringView V8StackTraceImpl::topSourceURL() const { | |
204 DCHECK(m_frames.size()); | |
205 return toStringView(m_frames[0].m_scriptName); | |
206 } | |
207 | |
208 int V8StackTraceImpl::topLineNumber() const { | |
209 DCHECK(m_frames.size()); | |
210 return m_frames[0].m_lineNumber; | |
211 } | |
212 | |
213 int V8StackTraceImpl::topColumnNumber() const { | |
214 DCHECK(m_frames.size()); | |
215 return m_frames[0].m_columnNumber; | |
216 } | |
217 | |
218 StringView V8StackTraceImpl::topFunctionName() const { | |
219 DCHECK(m_frames.size()); | |
220 return toStringView(m_frames[0].m_functionName); | |
221 } | |
222 | |
223 StringView V8StackTraceImpl::topScriptId() const { | |
224 DCHECK(m_frames.size()); | |
225 return toStringView(m_frames[0].m_scriptId); | |
226 } | |
227 | |
228 std::unique_ptr<protocol::Runtime::StackTrace> | |
229 V8StackTraceImpl::buildInspectorObjectImpl() const { | |
230 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = | |
231 protocol::Array<protocol::Runtime::CallFrame>::create(); | |
232 for (size_t i = 0; i < m_frames.size(); i++) | |
233 frames->addItem(m_frames.at(i).buildInspectorObject()); | |
234 | |
235 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = | |
236 protocol::Runtime::StackTrace::create() | |
237 .setCallFrames(std::move(frames)) | |
238 .build(); | |
239 if (!m_description.isEmpty()) stackTrace->setDescription(m_description); | |
240 if (m_parent) stackTrace->setParent(m_parent->buildInspectorObjectImpl()); | |
241 return stackTrace; | |
242 } | |
243 | |
244 std::unique_ptr<protocol::Runtime::StackTrace> | |
245 V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) const { | |
246 v8::HandleScope handleScope(v8::Isolate::GetCurrent()); | |
247 // Next call collapses possible empty stack and ensures | |
248 // maxAsyncCallChainDepth. | |
249 std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create( | |
250 debugger, m_contextGroupId, v8::Local<v8::StackTrace>(), | |
251 V8StackTraceImpl::maxCallStackSizeToCapture); | |
252 if (!fullChain || !fullChain->m_parent) return nullptr; | |
253 return fullChain->m_parent->buildInspectorObjectImpl(); | |
254 } | |
255 | |
256 std::unique_ptr<protocol::Runtime::API::StackTrace> | |
257 V8StackTraceImpl::buildInspectorObject() const { | |
258 return buildInspectorObjectImpl(); | |
259 } | |
260 | |
261 std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { | |
262 String16Builder stackTrace; | |
263 for (size_t i = 0; i < m_frames.size(); ++i) { | |
264 const Frame& frame = m_frames[i]; | |
265 stackTrace.append("\n at " + (frame.functionName().length() | |
266 ? frame.functionName() | |
267 : "(anonymous function)")); | |
268 stackTrace.append(" ("); | |
269 stackTrace.append(frame.sourceURL()); | |
270 stackTrace.append(':'); | |
271 stackTrace.append(String16::fromInteger(frame.lineNumber())); | |
272 stackTrace.append(':'); | |
273 stackTrace.append(String16::fromInteger(frame.columnNumber())); | |
274 stackTrace.append(')'); | |
275 } | |
276 String16 string = stackTrace.toString(); | |
277 return StringBufferImpl::adopt(string); | |
278 } | |
279 | |
280 } // namespace v8_inspector | |
OLD | NEW |