OLD | NEW |
---|---|
1 // Copyright 2016 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/inspector/v8-stack-trace-impl.h" | 5 #include "src/inspector/v8-stack-trace-impl.h" |
6 | 6 |
7 #include "src/inspector/string-util.h" | 7 #include <algorithm> |
8 #include "src/inspector/v8-debugger-agent-impl.h" | 8 |
9 #include "src/inspector/v8-debugger.h" | 9 #include "src/inspector/v8-debugger.h" |
10 #include "src/inspector/v8-inspector-impl.h" | 10 #include "src/inspector/wasm-translation.h" |
11 | |
12 #include "include/v8-version.h" | |
13 | 11 |
14 namespace v8_inspector { | 12 namespace v8_inspector { |
15 | 13 |
16 namespace { | 14 namespace { |
17 | 15 |
18 static const v8::StackTrace::StackTraceOptions stackTraceOptions = | 16 static const v8::StackTrace::StackTraceOptions stackTraceOptions = |
19 static_cast<v8::StackTrace::StackTraceOptions>( | 17 static_cast<v8::StackTrace::StackTraceOptions>( |
20 v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset | | 18 v8::StackTrace::kDetailed | |
21 v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL | | |
22 v8::StackTrace::kFunctionName | | |
23 v8::StackTrace::kExposeFramesAcrossSecurityOrigins); | 19 v8::StackTrace::kExposeFramesAcrossSecurityOrigins); |
24 | 20 |
25 V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame, | 21 std::vector<V8StackTraceImpl::Frame> toFramesVector( |
26 WasmTranslation* wasmTranslation) { | 22 V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace, |
27 String16 scriptId = String16::fromInteger(frame->GetScriptId()); | 23 int maxStackSize) { |
28 String16 sourceName; | 24 DCHECK(debugger->isolate()->InContext()); |
29 v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL()); | 25 int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize); |
30 if (!sourceNameValue.IsEmpty()) | 26 std::vector<V8StackTraceImpl::Frame> frames; |
dgozman
2017/04/17 17:49:08
Where did all IsEmpty checks go?
kozy
2017/04/17 21:34:17
Our toProtocolString function checks it internally
| |
31 sourceName = toProtocolString(sourceNameValue); | 27 for (int i = 0; i < frameCount; ++i) { |
32 | 28 v8::Local<v8::StackFrame> v8Frame = v8StackTrace->GetFrame(i); |
33 String16 functionName; | 29 frames.emplace_back(v8Frame); |
34 v8::Local<v8::String> functionNameValue(frame->GetFunctionName()); | 30 // TODO(clemensh): Figure out a way to do this translation only right before |
35 if (!functionNameValue.IsEmpty()) | 31 // sending the stack trace over wire. |
36 functionName = toProtocolString(functionNameValue); | 32 if (v8Frame->IsWasm()) frames.back().translate(debugger->wasmTranslation()); |
37 | |
38 int sourceLineNumber = frame->GetLineNumber() - 1; | |
39 int sourceColumn = frame->GetColumn() - 1; | |
40 // TODO(clemensh): Figure out a way to do this translation only right before | |
41 // sending the stack trace over wire. | |
42 if (frame->IsWasm()) { | |
43 wasmTranslation->TranslateWasmScriptLocationToProtocolLocation( | |
44 &scriptId, &sourceLineNumber, &sourceColumn); | |
45 } | 33 } |
46 return V8StackTraceImpl::Frame(functionName, scriptId, sourceName, | 34 return frames; |
47 sourceLineNumber + 1, sourceColumn + 1); | |
48 } | |
49 | |
50 void toFramesVector(v8::Local<v8::StackTrace> stackTrace, | |
51 std::vector<V8StackTraceImpl::Frame>& frames, | |
52 size_t maxStackSize, v8::Isolate* isolate, | |
53 V8Debugger* debugger) { | |
54 DCHECK(isolate->InContext()); | |
55 int frameCount = stackTrace->GetFrameCount(); | |
56 if (frameCount > static_cast<int>(maxStackSize)) | |
57 frameCount = static_cast<int>(maxStackSize); | |
58 WasmTranslation* wasmTranslation = debugger->wasmTranslation(); | |
59 for (int i = 0; i < frameCount; i++) { | |
60 v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i); | |
61 frames.push_back(toFrame(stackFrame, wasmTranslation)); | |
62 } | |
63 } | 35 } |
64 | 36 |
65 } // namespace | 37 } // namespace |
66 | 38 |
67 V8StackTraceImpl::Frame::Frame(const String16& functionName, | 39 V8StackTraceImpl::Frame::Frame(v8::Local<v8::StackFrame> v8Frame) |
68 const String16& scriptId, | 40 : m_functionName(toProtocolString(v8Frame->GetFunctionName())), |
69 const String16& scriptName, int lineNumber, | 41 m_scriptId(String16::fromInteger(v8Frame->GetScriptId())), |
70 int column) | 42 m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())), |
71 : m_functionName(functionName), | 43 m_lineNumber(v8Frame->GetLineNumber() - 1), |
72 m_scriptId(scriptId), | 44 m_columnNumber(v8Frame->GetColumn() - 1) { |
73 m_scriptName(scriptName), | 45 DCHECK(m_lineNumber + 1 != v8::Message::kNoLineNumberInfo); |
74 m_lineNumber(lineNumber), | 46 DCHECK(m_columnNumber + 1 != v8::Message::kNoColumnInfo); |
75 m_columnNumber(column) { | |
76 DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo); | |
77 DCHECK(m_columnNumber != v8::Message::kNoColumnInfo); | |
78 } | 47 } |
79 | 48 |
80 // buildInspectorObject() and SourceLocation's toTracedValue() should set the | 49 void V8StackTraceImpl::Frame::translate(WasmTranslation* wasmTranslation) { |
81 // same fields. | 50 wasmTranslation->TranslateWasmScriptLocationToProtocolLocation( |
82 // If either of them is modified, the other should be also modified. | 51 &m_scriptId, &m_lineNumber, &m_columnNumber); |
52 } | |
53 | |
54 const String16& V8StackTraceImpl::Frame::functionName() const { | |
55 return m_functionName; | |
56 } | |
57 | |
58 const String16& V8StackTraceImpl::Frame::scriptId() const { return m_scriptId; } | |
59 | |
60 const String16& V8StackTraceImpl::Frame::sourceURL() const { | |
61 return m_sourceURL; | |
62 } | |
63 | |
64 int V8StackTraceImpl::Frame::lineNumber() const { return m_lineNumber; } | |
65 | |
66 int V8StackTraceImpl::Frame::columnNumber() const { return m_columnNumber; } | |
67 | |
83 std::unique_ptr<protocol::Runtime::CallFrame> | 68 std::unique_ptr<protocol::Runtime::CallFrame> |
84 V8StackTraceImpl::Frame::buildInspectorObject() const { | 69 V8StackTraceImpl::Frame::buildInspectorObject() const { |
85 return protocol::Runtime::CallFrame::create() | 70 return protocol::Runtime::CallFrame::create() |
86 .setFunctionName(m_functionName) | 71 .setFunctionName(m_functionName) |
87 .setScriptId(m_scriptId) | 72 .setScriptId(m_scriptId) |
88 .setUrl(m_scriptName) | 73 .setUrl(m_sourceURL) |
89 .setLineNumber(m_lineNumber - 1) | 74 .setLineNumber(m_lineNumber) |
90 .setColumnNumber(m_columnNumber - 1) | 75 .setColumnNumber(m_columnNumber) |
91 .build(); | 76 .build(); |
92 } | 77 } |
93 | 78 |
94 // static | 79 // static |
95 void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions( | 80 void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions( |
96 v8::Isolate* isolate, bool capture) { | 81 v8::Isolate* isolate, bool capture) { |
97 isolate->SetCaptureStackTraceForUncaughtExceptions( | 82 isolate->SetCaptureStackTraceForUncaughtExceptions( |
98 capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions); | 83 capture, V8StackTraceImpl::maxCallStackSizeToCapture); |
99 } | 84 } |
100 | 85 |
101 // static | 86 // static |
102 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( | 87 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( |
103 V8Debugger* debugger, int contextGroupId, | 88 V8Debugger* debugger, int contextGroupId, |
104 v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize, | 89 v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) { |
105 const String16& description) { | |
106 DCHECK(debugger); | 90 DCHECK(debugger); |
107 v8::Isolate* isolate = debugger->inspector()->isolate(); | 91 |
92 v8::Isolate* isolate = debugger->isolate(); | |
108 v8::HandleScope scope(isolate); | 93 v8::HandleScope scope(isolate); |
109 std::vector<V8StackTraceImpl::Frame> frames; | 94 std::vector<V8StackTraceImpl::Frame> frames; |
110 if (!stackTrace.IsEmpty()) { | 95 if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) { |
111 toFramesVector(stackTrace, frames, maxStackSize, isolate, debugger); | 96 frames = toFramesVector(debugger, v8StackTrace, maxStackSize); |
112 } | 97 } |
113 | 98 |
114 int maxAsyncCallChainDepth = 1; | 99 int maxAsyncDepth; |
dgozman
2017/04/17 17:49:08
= 0;
kozy
2017/04/17 21:34:17
Done.
| |
115 V8StackTraceImpl* asyncCallChain = nullptr; | 100 std::shared_ptr<AsyncStackTrace> asyncParent; |
116 V8StackTraceImpl* creationStack = nullptr; | 101 std::shared_ptr<AsyncStackTrace> asyncCreation; |
117 if (maxStackSize > 1) { | 102 if (maxStackSize > 1) { |
118 asyncCallChain = debugger->currentAsyncCallChain(); | 103 asyncParent = debugger->currentAsyncParent(); |
119 creationStack = debugger->currentAsyncTaskCreationStack(); | 104 asyncCreation = debugger->currentAsyncCreation(); |
120 maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth(); | 105 maxAsyncDepth = debugger->maxAsyncCallChainDepth(); |
121 } | 106 } |
122 | 107 |
108 DCHECK(!asyncParent || !asyncCreation || | |
109 asyncParent->contextGroupId() == asyncCreation->contextGroupId()); | |
123 // Do not accidentally append async call chain from another group. This should | 110 // Do not accidentally append async call chain from another group. This should |
124 // not happen if we have proper instrumentation, but let's double-check to be | 111 // not happen if we have proper instrumentation, but let's double-check to be |
125 // safe. | 112 // safe. |
126 if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId && | 113 if (contextGroupId && asyncParent && |
127 asyncCallChain->m_contextGroupId != contextGroupId) { | 114 asyncParent->contextGroupId() != contextGroupId) { |
128 asyncCallChain = nullptr; | 115 asyncParent.reset(); |
129 creationStack = nullptr; | 116 asyncCreation.reset(); |
130 maxAsyncCallChainDepth = 1; | 117 maxAsyncDepth = 0; |
131 } | 118 } |
132 | 119 |
133 // Only the top stack in the chain may be empty and doesn't contain creation | 120 // Only the top stack in the chain may be empty and doesn't contain creation |
134 // stack , so ensure that second stack is non-empty (it's the top of appended | 121 // stack , so ensure that second stack is non-empty (it's the top of appended |
135 // chain). | 122 // chain). |
136 if (asyncCallChain && !creationStack && !asyncCallChain->m_creation && | 123 if (!asyncCreation && asyncParent && !asyncParent->creation().lock().get() && |
137 asyncCallChain->isEmpty()) { | 124 asyncParent->isEmpty()) { |
138 asyncCallChain = asyncCallChain->m_parent.get(); | 125 asyncParent = asyncParent->parent().lock(); |
139 } | 126 } |
140 | 127 |
141 if (frames.empty() && !creationStack && !asyncCallChain) return nullptr; | 128 if (frames.empty() && !asyncCreation && !asyncParent) return nullptr; |
142 | 129 return std::unique_ptr<V8StackTraceImpl>( |
143 // When async call chain is empty but doesn't contain useful schedule stack | 130 new V8StackTraceImpl(frames, maxAsyncDepth, asyncParent, asyncCreation)); |
144 // and parent async call chain contains creationg stack but doesn't | |
145 // synchronous we can merge them together. | |
146 // e.g. Promise ThenableJob. | |
147 if (asyncCallChain && frames.empty() && | |
148 asyncCallChain->m_description == description && !creationStack) { | |
149 return asyncCallChain->cloneImpl(); | |
150 } | |
151 | |
152 std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl( | |
153 contextGroupId, description, frames, | |
154 asyncCallChain ? asyncCallChain->cloneImpl() : nullptr, | |
155 creationStack ? creationStack->cloneImpl() : nullptr)); | |
156 | |
157 // Crop to not exceed maxAsyncCallChainDepth. | |
158 V8StackTraceImpl* deepest = result.get(); | |
159 while (deepest && maxAsyncCallChainDepth) { | |
160 deepest = deepest->m_parent.get(); | |
161 maxAsyncCallChainDepth--; | |
162 } | |
163 if (deepest) deepest->m_parent.reset(); | |
164 | |
165 return result; | |
166 } | 131 } |
167 | 132 |
168 // static | 133 // static |
169 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( | 134 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( |
170 V8Debugger* debugger, int contextGroupId, size_t maxStackSize, | 135 V8Debugger* debugger, int contextGroupId, int maxStackSize) { |
171 const String16& description) { | |
172 DCHECK(debugger); | 136 DCHECK(debugger); |
173 v8::Isolate* isolate = debugger->inspector()->isolate(); | 137 v8::Isolate* isolate = debugger->isolate(); |
174 v8::HandleScope handleScope(isolate); | 138 v8::HandleScope handleScope(isolate); |
175 v8::Local<v8::StackTrace> stackTrace; | 139 v8::Local<v8::StackTrace> v8StackTrace; |
176 if (isolate->InContext()) { | 140 if (isolate->InContext()) { |
177 stackTrace = v8::StackTrace::CurrentStackTrace( | 141 v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, |
178 isolate, static_cast<int>(maxStackSize), stackTraceOptions); | 142 stackTraceOptions); |
179 } | 143 } |
180 return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace, | 144 return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace, |
181 maxStackSize, description); | 145 maxStackSize); |
182 } | 146 } |
183 | 147 |
184 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() { | 148 V8StackTraceImpl::V8StackTraceImpl( |
185 std::vector<Frame> framesCopy(m_frames); | 149 const std::vector<Frame> frames, int maxAsyncDepth, |
186 std::unique_ptr<V8StackTraceImpl> copy( | 150 std::shared_ptr<AsyncStackTrace> asyncParent, |
187 new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy, | 151 std::shared_ptr<AsyncStackTrace> asyncCreation) |
188 m_parent ? m_parent->cloneImpl() : nullptr, | 152 : m_frames(frames), |
189 m_creation ? m_creation->cloneImpl() : nullptr)); | 153 m_maxAsyncDepth(maxAsyncDepth), |
190 return copy; | 154 m_asyncParent(asyncParent), |
191 } | 155 m_asyncCreation(asyncCreation) {} |
192 | |
193 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { | |
194 std::vector<Frame> frames; | |
195 for (size_t i = 0; i < m_frames.size(); i++) { | |
196 frames.push_back(m_frames.at(i)); | |
197 } | |
198 return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl( | |
199 m_contextGroupId, m_description, frames, nullptr, nullptr)); | |
200 } | |
201 | |
202 V8StackTraceImpl::V8StackTraceImpl(int contextGroupId, | |
203 const String16& description, | |
204 std::vector<Frame>& frames, | |
205 std::unique_ptr<V8StackTraceImpl> parent, | |
206 std::unique_ptr<V8StackTraceImpl> creation) | |
207 : m_contextGroupId(contextGroupId), | |
208 m_description(description), | |
209 m_parent(std::move(parent)), | |
210 m_creation(std::move(creation)) { | |
211 m_frames.swap(frames); | |
212 } | |
213 | 156 |
214 V8StackTraceImpl::~V8StackTraceImpl() {} | 157 V8StackTraceImpl::~V8StackTraceImpl() {} |
215 | 158 |
159 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { | |
160 return std::unique_ptr<V8StackTrace>( | |
161 new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(), | |
162 std::shared_ptr<AsyncStackTrace>())); | |
163 } | |
164 | |
165 bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); } | |
166 | |
216 StringView V8StackTraceImpl::topSourceURL() const { | 167 StringView V8StackTraceImpl::topSourceURL() const { |
217 DCHECK(m_frames.size()); | 168 return toStringView(m_frames[0].sourceURL()); |
218 return toStringView(m_frames[0].m_scriptName); | |
219 } | 169 } |
220 | 170 |
221 int V8StackTraceImpl::topLineNumber() const { | 171 int V8StackTraceImpl::topLineNumber() const { |
222 DCHECK(m_frames.size()); | 172 return m_frames[0].lineNumber() + 1; |
223 return m_frames[0].m_lineNumber; | |
224 } | 173 } |
225 | 174 |
226 int V8StackTraceImpl::topColumnNumber() const { | 175 int V8StackTraceImpl::topColumnNumber() const { |
227 DCHECK(m_frames.size()); | 176 return m_frames[0].columnNumber() + 1; |
228 return m_frames[0].m_columnNumber; | 177 } |
178 | |
179 StringView V8StackTraceImpl::topScriptId() const { | |
180 return toStringView(m_frames[0].scriptId()); | |
229 } | 181 } |
230 | 182 |
231 StringView V8StackTraceImpl::topFunctionName() const { | 183 StringView V8StackTraceImpl::topFunctionName() const { |
232 DCHECK(m_frames.size()); | 184 return toStringView(m_frames[0].functionName()); |
233 return toStringView(m_frames[0].m_functionName); | |
234 } | |
235 | |
236 StringView V8StackTraceImpl::topScriptId() const { | |
237 DCHECK(m_frames.size()); | |
238 return toStringView(m_frames[0].m_scriptId); | |
239 } | 185 } |
240 | 186 |
241 std::unique_ptr<protocol::Runtime::StackTrace> | 187 std::unique_ptr<protocol::Runtime::StackTrace> |
242 V8StackTraceImpl::buildInspectorObjectImpl() const { | 188 V8StackTraceImpl::buildInspectorObjectImpl() const { |
243 return buildInspectorObjectImpl(nullptr); | |
244 } | |
245 | |
246 std::unique_ptr<protocol::Runtime::StackTrace> | |
247 V8StackTraceImpl::buildInspectorObjectImpl(V8StackTraceImpl* creation) const { | |
248 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = | 189 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = |
249 protocol::Array<protocol::Runtime::CallFrame>::create(); | 190 protocol::Array<protocol::Runtime::CallFrame>::create(); |
250 for (size_t i = 0; i < m_frames.size(); i++) | 191 for (size_t i = 0; i < m_frames.size(); i++) { |
251 frames->addItem(m_frames.at(i).buildInspectorObject()); | 192 frames->addItem(m_frames[i].buildInspectorObject()); |
193 } | |
252 | 194 |
253 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = | 195 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = |
254 protocol::Runtime::StackTrace::create() | 196 protocol::Runtime::StackTrace::create() |
255 .setCallFrames(std::move(frames)) | 197 .setCallFrames(std::move(frames)) |
256 .build(); | 198 .build(); |
257 if (!m_description.isEmpty()) stackTrace->setDescription(m_description); | 199 std::shared_ptr<AsyncStackTrace> asyncParent = m_asyncParent.lock(); |
258 if (m_parent) { | 200 if (asyncParent && m_maxAsyncDepth > 0) { |
259 stackTrace->setParent(m_parent->buildInspectorObjectImpl(m_creation.get())); | 201 stackTrace->setParent(asyncParent->buildInspectorObject( |
260 } | 202 m_asyncCreation.lock().get(), m_maxAsyncDepth - 1)); |
261 if (creation && creation->m_frames.size()) { | |
262 stackTrace->setPromiseCreationFrame( | |
263 creation->m_frames[0].buildInspectorObject()); | |
264 } | 203 } |
265 return stackTrace; | 204 return stackTrace; |
266 } | 205 } |
267 | 206 |
268 // static | |
269 std::unique_ptr<protocol::Runtime::StackTrace> | |
270 V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) { | |
271 DCHECK(debugger); | |
272 v8::HandleScope handleScope(debugger->inspector()->isolate()); | |
273 // Next call collapses possible empty stack and ensures | |
274 // maxAsyncCallChainDepth. | |
275 V8StackTraceImpl* asyncChain = debugger->currentAsyncCallChain(); | |
276 if (!asyncChain) return nullptr; | |
277 std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create( | |
278 debugger, asyncChain->m_contextGroupId, v8::Local<v8::StackTrace>(), | |
279 V8StackTraceImpl::maxCallStackSizeToCapture); | |
280 if (!fullChain || !fullChain->m_parent) return nullptr; | |
281 return fullChain->m_parent->buildInspectorObjectImpl( | |
282 fullChain->m_creation.get()); | |
283 } | |
284 | |
285 std::unique_ptr<protocol::Runtime::API::StackTrace> | 207 std::unique_ptr<protocol::Runtime::API::StackTrace> |
286 V8StackTraceImpl::buildInspectorObject() const { | 208 V8StackTraceImpl::buildInspectorObject() const { |
287 return buildInspectorObjectImpl(nullptr); | 209 return buildInspectorObjectImpl(); |
288 } | 210 } |
289 | 211 |
290 std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { | 212 std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { |
291 String16Builder stackTrace; | 213 String16Builder stackTrace; |
292 for (size_t i = 0; i < m_frames.size(); ++i) { | 214 for (size_t i = 0; i < m_frames.size(); ++i) { |
293 const Frame& frame = m_frames[i]; | 215 const Frame& frame = m_frames[i]; |
294 stackTrace.append("\n at " + (frame.functionName().length() | 216 stackTrace.append("\n at " + (frame.functionName().length() |
295 ? frame.functionName() | 217 ? frame.functionName() |
296 : "(anonymous function)")); | 218 : "(anonymous function)")); |
297 stackTrace.append(" ("); | 219 stackTrace.append(" ("); |
298 stackTrace.append(frame.sourceURL()); | 220 stackTrace.append(frame.sourceURL()); |
299 stackTrace.append(':'); | 221 stackTrace.append(':'); |
300 stackTrace.append(String16::fromInteger(frame.lineNumber())); | 222 stackTrace.append(String16::fromInteger(frame.lineNumber())); |
301 stackTrace.append(':'); | 223 stackTrace.append(':'); |
302 stackTrace.append(String16::fromInteger(frame.columnNumber())); | 224 stackTrace.append(String16::fromInteger(frame.columnNumber())); |
303 stackTrace.append(')'); | 225 stackTrace.append(')'); |
304 } | 226 } |
305 String16 string = stackTrace.toString(); | 227 String16 string = stackTrace.toString(); |
306 return StringBufferImpl::adopt(string); | 228 return StringBufferImpl::adopt(string); |
307 } | 229 } |
308 | 230 |
231 // static | |
232 std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture( | |
dgozman
2017/04/17 17:49:08
Can we extract common logic from this and V8StackT
kozy
2017/04/17 21:34:17
Done.
| |
233 V8Debugger* debugger, int contextGroupId, const String16& description, | |
234 int maxStackSize) { | |
235 DCHECK(debugger); | |
236 DCHECK(contextGroupId); | |
237 | |
238 v8::Isolate* isolate = debugger->isolate(); | |
239 v8::HandleScope handleScope(isolate); | |
240 | |
241 std::vector<V8StackTraceImpl::Frame> frames; | |
242 if (isolate->InContext()) { | |
243 v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace( | |
244 isolate, maxStackSize, stackTraceOptions); | |
245 frames = toFramesVector(debugger, v8StackTrace, maxStackSize); | |
246 } | |
247 | |
248 std::shared_ptr<AsyncStackTrace> asyncParent = debugger->currentAsyncParent(); | |
249 std::shared_ptr<AsyncStackTrace> asyncCreation = | |
250 debugger->currentAsyncCreation(); | |
251 DCHECK(!asyncParent || !asyncCreation || | |
252 asyncParent->contextGroupId() == asyncCreation->contextGroupId()); | |
253 // Do not accidentally append async call chain from another group. This should | |
254 // not happen if we have proper instrumentation, but let's double-check to be | |
255 // safe. | |
256 if (asyncParent && asyncParent->contextGroupId() != contextGroupId) { | |
257 asyncParent.reset(); | |
258 asyncCreation.reset(); | |
259 } | |
260 | |
261 // Only the top stack in the chain may be empty and doesn't contain creation | |
262 // stack, so ensure that second stack is non-empty (it's the top of appended | |
263 // chain). | |
264 if (!asyncCreation && asyncParent && !asyncParent->creation().lock().get() && | |
265 asyncParent->isEmpty()) { | |
266 asyncParent = asyncParent->parent().lock(); | |
267 } | |
268 | |
269 if (frames.empty() && !asyncCreation && !asyncParent) return nullptr; | |
270 | |
271 // When async call chain is empty but doesn't contain useful schedule stack | |
272 // and parent async call chain contains creationg stack but doesn't | |
273 // synchronous we can merge them together. | |
274 // e.g. Promise ThenableJob. | |
275 if (asyncParent && frames.empty() && | |
276 asyncParent->m_description == description && !asyncCreation) { | |
277 return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace( | |
278 contextGroupId, asyncParent->m_description, asyncParent->m_frames, | |
279 asyncParent->m_asyncParent.lock(), | |
280 asyncParent->m_asyncCreation.lock())); | |
281 } | |
282 | |
283 return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace( | |
284 contextGroupId, description, frames, asyncParent, asyncCreation)); | |
285 } | |
286 | |
287 AsyncStackTrace::AsyncStackTrace( | |
288 int contextGroupId, const String16& description, | |
289 const std::vector<V8StackTraceImpl::Frame>& frames, | |
290 std::shared_ptr<AsyncStackTrace> asyncParent, | |
291 std::shared_ptr<AsyncStackTrace> asyncCreation) | |
292 : m_contextGroupId(contextGroupId), | |
293 m_description(description), | |
294 m_frames(frames), | |
295 m_asyncParent(asyncParent), | |
296 m_asyncCreation(asyncCreation) {} | |
297 | |
298 std::unique_ptr<protocol::Runtime::StackTrace> | |
299 AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation, | |
dgozman
2017/04/17 17:49:08
Same for buildInspectorObject.
kozy
2017/04/17 21:34:17
Done.
| |
300 int maxAsyncDepth) const { | |
301 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = | |
302 protocol::Array<protocol::Runtime::CallFrame>::create(); | |
303 for (size_t i = 0; i < m_frames.size(); i++) { | |
304 frames->addItem(m_frames[i].buildInspectorObject()); | |
305 } | |
306 | |
307 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = | |
308 protocol::Runtime::StackTrace::create() | |
309 .setCallFrames(std::move(frames)) | |
310 .build(); | |
311 if (!m_description.isEmpty()) stackTrace->setDescription(m_description); | |
312 std::shared_ptr<AsyncStackTrace> asyncParent = m_asyncParent.lock(); | |
313 if (asyncParent && maxAsyncDepth > 0) { | |
314 stackTrace->setParent(asyncParent->buildInspectorObject( | |
315 m_asyncCreation.lock().get(), maxAsyncDepth - 1)); | |
316 } | |
317 if (asyncCreation && !asyncCreation->isEmpty()) { | |
318 stackTrace->setPromiseCreationFrame( | |
319 asyncCreation->m_frames[0].buildInspectorObject()); | |
320 } | |
321 return stackTrace; | |
322 } | |
323 | |
324 int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; } | |
325 | |
326 std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const { | |
327 return m_asyncParent; | |
328 } | |
329 | |
330 std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const { | |
331 return m_asyncCreation; | |
332 } | |
333 | |
334 bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); } | |
335 | |
309 } // namespace v8_inspector | 336 } // namespace v8_inspector |
OLD | NEW |