Home | History | Annotate | Download | only in inspector
      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