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