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