| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium 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 "platform/v8_inspector/V8StackTraceImpl.h" | |
| 6 | |
| 7 #include "platform/v8_inspector/StringUtil.h" | |
| 8 #include "platform/v8_inspector/V8Debugger.h" | |
| 9 #include "platform/v8_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 = static_cast<v
8::StackTrace::StackTraceOptions>( | |
| 20 v8::StackTrace::kLineNumber | | |
| 21 v8::StackTrace::kColumnOffset | | |
| 22 v8::StackTrace::kScriptId | | |
| 23 v8::StackTrace::kScriptNameOrSourceURL | | |
| 24 v8::StackTrace::kFunctionName); | |
| 25 | |
| 26 V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) | |
| 27 { | |
| 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, sourceLin
eNumber, sourceColumn); | |
| 42 } | |
| 43 | |
| 44 void toFramesVector(v8::Local<v8::StackTrace> stackTrace, std::vector<V8StackTra
ceImpl::Frame>& frames, size_t maxStackSize, v8::Isolate* isolate) | |
| 45 { | |
| 46 DCHECK(isolate->InContext()); | |
| 47 int frameCount = stackTrace->GetFrameCount(); | |
| 48 if (frameCount > static_cast<int>(maxStackSize)) | |
| 49 frameCount = maxStackSize; | |
| 50 for (int i = 0; i < frameCount; i++) { | |
| 51 v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i); | |
| 52 frames.push_back(toFrame(stackFrame)); | |
| 53 } | |
| 54 } | |
| 55 | |
| 56 } // namespace | |
| 57 | |
| 58 V8StackTraceImpl::Frame::Frame() | |
| 59 : m_functionName("undefined") | |
| 60 , m_scriptId("") | |
| 61 , m_scriptName("undefined") | |
| 62 , m_lineNumber(0) | |
| 63 , m_columnNumber(0) | |
| 64 { | |
| 65 } | |
| 66 | |
| 67 V8StackTraceImpl::Frame::Frame(const String16& functionName, const String16& scr
iptId, const String16& scriptName, int lineNumber, int column) | |
| 68 : m_functionName(functionName) | |
| 69 , m_scriptId(scriptId) | |
| 70 , m_scriptName(scriptName) | |
| 71 , m_lineNumber(lineNumber) | |
| 72 , m_columnNumber(column) | |
| 73 { | |
| 74 DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo); | |
| 75 DCHECK(m_columnNumber != v8::Message::kNoColumnInfo); | |
| 76 } | |
| 77 | |
| 78 V8StackTraceImpl::Frame::~Frame() | |
| 79 { | |
| 80 } | |
| 81 | |
| 82 // buildInspectorObject() and SourceLocation's toTracedValue() should set the sa
me fields. | |
| 83 // If either of them is modified, the other should be also modified. | |
| 84 std::unique_ptr<protocol::Runtime::CallFrame> V8StackTraceImpl::Frame::buildInsp
ectorObject() const | |
| 85 { | |
| 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 { | |
| 97 return Frame(m_functionName, m_scriptId, m_scriptName, m_lineNumber, m_colum
nNumber); | |
| 98 } | |
| 99 | |
| 100 // static | |
| 101 void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(v8::Isolate* is
olate, bool capture) | |
| 102 { | |
| 103 isolate->SetCaptureStackTraceForUncaughtExceptions(capture, V8StackTraceImpl
::maxCallStackSizeToCapture, stackTraceOptions); | |
| 104 } | |
| 105 | |
| 106 // static | |
| 107 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(V8Debugger* debugger,
int contextGroupId, v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize,
const String16& description) | |
| 108 { | |
| 109 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | |
| 110 v8::HandleScope scope(isolate); | |
| 111 std::vector<V8StackTraceImpl::Frame> frames; | |
| 112 if (!stackTrace.IsEmpty()) | |
| 113 toFramesVector(stackTrace, frames, maxStackSize, isolate); | |
| 114 | |
| 115 int maxAsyncCallChainDepth = 1; | |
| 116 V8StackTraceImpl* asyncCallChain = nullptr; | |
| 117 if (debugger && maxStackSize > 1) { | |
| 118 asyncCallChain = debugger->currentAsyncCallChain(); | |
| 119 maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth(); | |
| 120 } | |
| 121 // Do not accidentally append async call chain from another group. This shou
ld not | |
| 122 // happen if we have proper instrumentation, but let's double-check to be sa
fe. | |
| 123 if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId &&
asyncCallChain->m_contextGroupId != contextGroupId) { | |
| 124 asyncCallChain = nullptr; | |
| 125 maxAsyncCallChainDepth = 1; | |
| 126 } | |
| 127 | |
| 128 // Only the top stack in the chain may be empty, so ensure that second stack
is non-empty (it's the top of appended chain). | |
| 129 if (asyncCallChain && asyncCallChain->isEmpty()) | |
| 130 asyncCallChain = asyncCallChain->m_parent.get(); | |
| 131 | |
| 132 if (stackTrace.IsEmpty() && !asyncCallChain) | |
| 133 return nullptr; | |
| 134 | |
| 135 std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl(contextGroupId
, description, frames, asyncCallChain ? asyncCallChain->cloneImpl() : nullptr)); | |
| 136 | |
| 137 // Crop to not exceed maxAsyncCallChainDepth. | |
| 138 V8StackTraceImpl* deepest = result.get(); | |
| 139 while (deepest && maxAsyncCallChainDepth) { | |
| 140 deepest = deepest->m_parent.get(); | |
| 141 maxAsyncCallChainDepth--; | |
| 142 } | |
| 143 if (deepest) | |
| 144 deepest->m_parent.reset(); | |
| 145 | |
| 146 return result; | |
| 147 } | |
| 148 | |
| 149 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(V8Debugger* debugger
, int contextGroupId, size_t maxStackSize, const String16& description) | |
| 150 { | |
| 151 v8::Isolate* isolate = v8::Isolate::GetCurrent(); | |
| 152 v8::HandleScope handleScope(isolate); | |
| 153 v8::Local<v8::StackTrace> stackTrace; | |
| 154 if (isolate->InContext()) { | |
| 155 #if V8_MAJOR_VERSION >= 5 | |
| 156 isolate->GetCpuProfiler()->CollectSample(); | |
| 157 #endif | |
| 158 stackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, st
ackTraceOptions); | |
| 159 } | |
| 160 return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace, maxSta
ckSize, description); | |
| 161 } | |
| 162 | |
| 163 std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() | |
| 164 { | |
| 165 std::vector<Frame> framesCopy(m_frames); | |
| 166 return wrapUnique(new V8StackTraceImpl(m_contextGroupId, m_description, fram
esCopy, m_parent ? m_parent->cloneImpl() : nullptr)); | |
| 167 } | |
| 168 | |
| 169 std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() | |
| 170 { | |
| 171 std::vector<Frame> frames; | |
| 172 for (size_t i = 0; i < m_frames.size(); i++) | |
| 173 frames.push_back(m_frames.at(i).clone()); | |
| 174 return wrapUnique(new V8StackTraceImpl(m_contextGroupId, m_description, fram
es, nullptr)); | |
| 175 } | |
| 176 | |
| 177 V8StackTraceImpl::V8StackTraceImpl(int contextGroupId, const String16& descripti
on, std::vector<Frame>& frames, std::unique_ptr<V8StackTraceImpl> parent) | |
| 178 : m_contextGroupId(contextGroupId) | |
| 179 , m_description(description) | |
| 180 , m_parent(std::move(parent)) | |
| 181 { | |
| 182 m_frames.swap(frames); | |
| 183 } | |
| 184 | |
| 185 V8StackTraceImpl::~V8StackTraceImpl() | |
| 186 { | |
| 187 } | |
| 188 | |
| 189 StringView V8StackTraceImpl::topSourceURL() const | |
| 190 { | |
| 191 DCHECK(m_frames.size()); | |
| 192 return toStringView(m_frames[0].m_scriptName); | |
| 193 } | |
| 194 | |
| 195 int V8StackTraceImpl::topLineNumber() const | |
| 196 { | |
| 197 DCHECK(m_frames.size()); | |
| 198 return m_frames[0].m_lineNumber; | |
| 199 } | |
| 200 | |
| 201 int V8StackTraceImpl::topColumnNumber() const | |
| 202 { | |
| 203 DCHECK(m_frames.size()); | |
| 204 return m_frames[0].m_columnNumber; | |
| 205 } | |
| 206 | |
| 207 StringView V8StackTraceImpl::topFunctionName() const | |
| 208 { | |
| 209 DCHECK(m_frames.size()); | |
| 210 return toStringView(m_frames[0].m_functionName); | |
| 211 } | |
| 212 | |
| 213 StringView V8StackTraceImpl::topScriptId() const | |
| 214 { | |
| 215 DCHECK(m_frames.size()); | |
| 216 return toStringView(m_frames[0].m_scriptId); | |
| 217 } | |
| 218 | |
| 219 std::unique_ptr<protocol::Runtime::StackTrace> V8StackTraceImpl::buildInspectorO
bjectImpl() const | |
| 220 { | |
| 221 std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = prot
ocol::Array<protocol::Runtime::CallFrame>::create(); | |
| 222 for (size_t i = 0; i < m_frames.size(); i++) | |
| 223 frames->addItem(m_frames.at(i).buildInspectorObject()); | |
| 224 | |
| 225 std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = protocol::Runtim
e::StackTrace::create() | |
| 226 .setCallFrames(std::move(frames)).build(); | |
| 227 if (!m_description.isEmpty()) | |
| 228 stackTrace->setDescription(m_description); | |
| 229 if (m_parent) | |
| 230 stackTrace->setParent(m_parent->buildInspectorObjectImpl()); | |
| 231 return stackTrace; | |
| 232 } | |
| 233 | |
| 234 std::unique_ptr<protocol::Runtime::StackTrace> V8StackTraceImpl::buildInspectorO
bjectForTail(V8Debugger* debugger) const | |
| 235 { | |
| 236 v8::HandleScope handleScope(v8::Isolate::GetCurrent()); | |
| 237 // Next call collapses possible empty stack and ensures maxAsyncCallChainDep
th. | |
| 238 std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create(debug
ger, m_contextGroupId, v8::Local<v8::StackTrace>(), V8StackTraceImpl::maxCallSta
ckSizeToCapture); | |
| 239 if (!fullChain || !fullChain->m_parent) | |
| 240 return nullptr; | |
| 241 return fullChain->m_parent->buildInspectorObjectImpl(); | |
| 242 } | |
| 243 | |
| 244 std::unique_ptr<protocol::Runtime::API::StackTrace> V8StackTraceImpl::buildInspe
ctorObject() const | |
| 245 { | |
| 246 return buildInspectorObjectImpl(); | |
| 247 } | |
| 248 | |
| 249 std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const | |
| 250 { | |
| 251 String16Builder stackTrace; | |
| 252 for (size_t i = 0; i < m_frames.size(); ++i) { | |
| 253 const Frame& frame = m_frames[i]; | |
| 254 stackTrace.append("\n at " + (frame.functionName().length() ? frame.f
unctionName() : "(anonymous function)")); | |
| 255 stackTrace.append(" ("); | |
| 256 stackTrace.append(frame.sourceURL()); | |
| 257 stackTrace.append(':'); | |
| 258 stackTrace.append(String16::fromInteger(frame.lineNumber())); | |
| 259 stackTrace.append(':'); | |
| 260 stackTrace.append(String16::fromInteger(frame.columnNumber())); | |
| 261 stackTrace.append(')'); | |
| 262 } | |
| 263 String16 string = stackTrace.toString(); | |
| 264 return StringBufferImpl::adopt(string); | |
| 265 } | |
| 266 | |
| 267 } // namespace v8_inspector | |
| OLD | NEW |