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