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