LCOV - code coverage report
Current view: top level - src/inspector - v8-stack-trace-impl.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 137 158 86.7 %
Date: 2017-10-20 Functions: 27 41 65.9 %

          Line data    Source code
       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 <algorithm>
       8             : 
       9             : #include "src/inspector/v8-debugger.h"
      10             : #include "src/inspector/wasm-translation.h"
      11             : 
      12             : namespace v8_inspector {
      13             : 
      14             : namespace {
      15             : 
      16             : static const v8::StackTrace::StackTraceOptions stackTraceOptions =
      17             :     static_cast<v8::StackTrace::StackTraceOptions>(
      18             :         v8::StackTrace::kDetailed |
      19             :         v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
      20             : 
      21       73889 : std::vector<std::shared_ptr<StackFrame>> toFramesVector(
      22             :     V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
      23             :     int maxStackSize) {
      24             :   DCHECK(debugger->isolate()->InContext());
      25      147778 :   int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
      26             :   std::vector<std::shared_ptr<StackFrame>> frames;
      27      143773 :   for (int i = 0; i < frameCount; ++i) {
      28      139768 :     frames.push_back(debugger->symbolize(v8StackTrace->GetFrame(i)));
      29             :   }
      30       73889 :   return frames;
      31             : }
      32             : 
      33      111048 : void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
      34             :                          std::shared_ptr<AsyncStackTrace>* asyncParent,
      35             :                          std::shared_ptr<AsyncStackTrace>* asyncCreation,
      36             :                          int* maxAsyncDepth) {
      37      156500 :   *asyncParent = debugger->currentAsyncParent();
      38      156500 :   *asyncCreation = debugger->currentAsyncCreation();
      39      111048 :   if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
      40             : 
      41             :   DCHECK(!*asyncParent || !*asyncCreation ||
      42             :          (*asyncParent)->contextGroupId() ==
      43             :              (*asyncCreation)->contextGroupId());
      44             :   // Do not accidentally append async call chain from another group. This should
      45             :   // not happen if we have proper instrumentation, but let's double-check to be
      46             :   // safe.
      47      170300 :   if (contextGroupId && *asyncParent &&
      48             :       (*asyncParent)->contextGroupId() != contextGroupId) {
      49             :     asyncParent->reset();
      50        6730 :     asyncCreation->reset();
      51           0 :     if (maxAsyncDepth) *maxAsyncDepth = 0;
      52       78250 :     return;
      53             :   }
      54             : 
      55             :   // Only the top stack in the chain may be empty and doesn't contain creation
      56             :   // stack, so ensure that second stack is non-empty (it's the top of appended
      57             :   // chain).
      58      111900 :   if (*asyncParent && !(*asyncCreation) && !(*asyncParent)->creation().lock() &&
      59             :       (*asyncParent)->isEmpty()) {
      60        1020 :     *asyncParent = (*asyncParent)->parent().lock();
      61             :   }
      62             : }
      63             : 
      64       29439 : std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
      65       59658 :     const std::vector<std::shared_ptr<StackFrame>>& frames,
      66             :     const String16& description,
      67             :     const std::shared_ptr<AsyncStackTrace>& asyncParent,
      68             :     const std::shared_ptr<AsyncStackTrace>& asyncCreation, int maxAsyncDepth) {
      69       89097 :   if (asyncParent && frames.empty() &&
      70       30179 :       description == asyncParent->description() && !asyncCreation) {
      71           0 :     return asyncParent->buildInspectorObject(nullptr, maxAsyncDepth);
      72             :   }
      73             : 
      74             :   std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
      75             :       inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
      76      119316 :   for (size_t i = 0; i < frames.size(); i++) {
      77       60438 :     inspectorFrames->addItem(frames[i]->buildInspectorObject());
      78             :   }
      79             :   std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
      80             :       protocol::Runtime::StackTrace::create()
      81       29439 :           .setCallFrames(std::move(inspectorFrames))
      82             :           .build();
      83       29439 :   if (!description.isEmpty()) stackTrace->setDescription(description);
      84       29439 :   if (asyncParent && maxAsyncDepth > 0) {
      85             :     stackTrace->setParent(asyncParent->buildInspectorObject(asyncCreation.get(),
      86        2220 :                                                             maxAsyncDepth - 1));
      87             :   }
      88             :   return stackTrace;
      89             : }
      90             : 
      91             : }  //  namespace
      92             : 
      93       32817 : StackFrame::StackFrame(v8::Local<v8::StackFrame> v8Frame)
      94             :     : m_functionName(toProtocolString(v8Frame->GetFunctionName())),
      95             :       m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
      96             :       m_sourceURL(toProtocolString(v8Frame->GetScriptNameOrSourceURL())),
      97       32817 :       m_lineNumber(v8Frame->GetLineNumber() - 1),
      98       65634 :       m_columnNumber(v8Frame->GetColumn() - 1) {
      99             :   DCHECK_NE(v8::Message::kNoLineNumberInfo, m_lineNumber + 1);
     100             :   DCHECK_NE(v8::Message::kNoColumnInfo, m_columnNumber + 1);
     101       32817 : }
     102             : 
     103         275 : void StackFrame::translate(WasmTranslation* wasmTranslation) {
     104             :   wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
     105         275 :       &m_scriptId, &m_lineNumber, &m_columnNumber);
     106         275 : }
     107             : 
     108           0 : const String16& StackFrame::functionName() const { return m_functionName; }
     109             : 
     110           0 : const String16& StackFrame::scriptId() const { return m_scriptId; }
     111             : 
     112           0 : const String16& StackFrame::sourceURL() const { return m_sourceURL; }
     113             : 
     114        6483 : int StackFrame::lineNumber() const { return m_lineNumber; }
     115             : 
     116        6468 : int StackFrame::columnNumber() const { return m_columnNumber; }
     117             : 
     118       30219 : std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject()
     119             :     const {
     120             :   return protocol::Runtime::CallFrame::create()
     121       60438 :       .setFunctionName(m_functionName)
     122       30219 :       .setScriptId(m_scriptId)
     123       30219 :       .setUrl(m_sourceURL)
     124       30219 :       .setLineNumber(m_lineNumber)
     125       30219 :       .setColumnNumber(m_columnNumber)
     126       30219 :       .build();
     127             : }
     128             : 
     129           0 : bool StackFrame::isEqual(StackFrame* frame) const {
     130             :   return m_scriptId == frame->m_scriptId &&
     131          50 :          m_lineNumber == frame->m_lineNumber &&
     132           0 :          m_columnNumber == frame->m_columnNumber;
     133             : }
     134             : 
     135             : // static
     136        1090 : void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
     137             :     v8::Isolate* isolate, bool capture) {
     138             :   isolate->SetCaptureStackTraceForUncaughtExceptions(
     139        1090 :       capture, V8StackTraceImpl::maxCallStackSizeToCapture);
     140        1090 : }
     141             : 
     142             : // static
     143       32798 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
     144       32798 :     V8Debugger* debugger, int contextGroupId,
     145             :     v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
     146             :   DCHECK(debugger);
     147             : 
     148             :   v8::Isolate* isolate = debugger->isolate();
     149       32798 :   v8::HandleScope scope(isolate);
     150             : 
     151       32798 :   std::vector<std::shared_ptr<StackFrame>> frames;
     152       32798 :   if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
     153       56874 :     frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
     154             :   }
     155             : 
     156       32798 :   int maxAsyncDepth = 0;
     157       32798 :   std::shared_ptr<AsyncStackTrace> asyncParent;
     158       32798 :   std::shared_ptr<AsyncStackTrace> asyncCreation;
     159             :   calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
     160       32798 :                       &maxAsyncDepth);
     161       32798 :   if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
     162             :   return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
     163      118679 :       std::move(frames), maxAsyncDepth, asyncParent, asyncCreation));
     164             : }
     165             : 
     166             : // static
     167       32523 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
     168       32523 :     V8Debugger* debugger, int contextGroupId, int maxStackSize) {
     169             :   DCHECK(debugger);
     170             :   v8::Isolate* isolate = debugger->isolate();
     171       32523 :   v8::HandleScope handleScope(isolate);
     172             :   v8::Local<v8::StackTrace> v8StackTrace;
     173       32523 :   if (isolate->InContext()) {
     174             :     v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
     175       31919 :                                                      stackTraceOptions);
     176             :   }
     177             :   return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
     178       32523 :                                   maxStackSize);
     179             : }
     180             : 
     181       28627 : V8StackTraceImpl::V8StackTraceImpl(
     182             :     std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth,
     183             :     std::shared_ptr<AsyncStackTrace> asyncParent,
     184             :     std::shared_ptr<AsyncStackTrace> asyncCreation)
     185             :     : m_frames(std::move(frames)),
     186             :       m_maxAsyncDepth(maxAsyncDepth),
     187             :       m_asyncParent(asyncParent),
     188       85881 :       m_asyncCreation(asyncCreation) {}
     189             : 
     190      114508 : V8StackTraceImpl::~V8StackTraceImpl() {}
     191             : 
     192           0 : std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
     193             :   return std::unique_ptr<V8StackTrace>(
     194             :       new V8StackTraceImpl(m_frames, 0, std::shared_ptr<AsyncStackTrace>(),
     195           0 :                            std::shared_ptr<AsyncStackTrace>()));
     196             : }
     197             : 
     198       56808 : bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
     199             : 
     200        6405 : StringView V8StackTraceImpl::topSourceURL() const {
     201        6405 :   return toStringView(m_frames[0]->sourceURL());
     202             : }
     203             : 
     204        6458 : int V8StackTraceImpl::topLineNumber() const {
     205       12916 :   return m_frames[0]->lineNumber() + 1;
     206             : }
     207             : 
     208        6443 : int V8StackTraceImpl::topColumnNumber() const {
     209       12886 :   return m_frames[0]->columnNumber() + 1;
     210             : }
     211             : 
     212          53 : StringView V8StackTraceImpl::topScriptId() const {
     213          53 :   return toStringView(m_frames[0]->scriptId());
     214             : }
     215             : 
     216           0 : StringView V8StackTraceImpl::topFunctionName() const {
     217           0 :   return toStringView(m_frames[0]->functionName());
     218             : }
     219             : 
     220             : std::unique_ptr<protocol::Runtime::StackTrace>
     221       28374 : V8StackTraceImpl::buildInspectorObjectImpl() const {
     222             :   return buildInspectorObjectCommon(m_frames, String16(), m_asyncParent.lock(),
     223      113496 :                                     m_asyncCreation.lock(), m_maxAsyncDepth);
     224             : }
     225             : 
     226             : std::unique_ptr<protocol::Runtime::API::StackTrace>
     227           0 : V8StackTraceImpl::buildInspectorObject() const {
     228           0 :   return buildInspectorObjectImpl();
     229             : }
     230             : 
     231          10 : std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
     232          10 :   String16Builder stackTrace;
     233          70 :   for (size_t i = 0; i < m_frames.size(); ++i) {
     234          60 :     const StackFrame& frame = *m_frames[i];
     235          90 :     stackTrace.append("\n    at " + (frame.functionName().length()
     236          15 :                                          ? frame.functionName()
     237          25 :                                          : "(anonymous function)"));
     238          50 :     stackTrace.append(" (");
     239          25 :     stackTrace.append(frame.sourceURL());
     240          25 :     stackTrace.append(':');
     241          50 :     stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
     242          25 :     stackTrace.append(':');
     243          50 :     stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
     244          25 :     stackTrace.append(')');
     245             :   }
     246          10 :   String16 string = stackTrace.toString();
     247          30 :   return StringBufferImpl::adopt(string);
     248             : }
     249             : 
     250          45 : bool V8StackTraceImpl::isEqualIgnoringTopFrame(
     251             :     V8StackTraceImpl* stackTrace) const {
     252          45 :   StackFrameIterator current(this);
     253          45 :   StackFrameIterator target(stackTrace);
     254             : 
     255          45 :   current.next();
     256          45 :   target.next();
     257         160 :   while (!current.done() && !target.done()) {
     258          50 :     if (!current.frame()->isEqual(target.frame())) {
     259             :       return false;
     260             :     }
     261          20 :     current.next();
     262          20 :     target.next();
     263             :   }
     264          15 :   return current.done() == target.done();
     265             : }
     266             : 
     267          90 : V8StackTraceImpl::StackFrameIterator::StackFrameIterator(
     268             :     const V8StackTraceImpl* stackTrace)
     269         180 :     : m_currentIt(stackTrace->m_frames.begin()),
     270             :       m_currentEnd(stackTrace->m_frames.end()),
     271         270 :       m_parent(stackTrace->m_asyncParent.lock().get()) {}
     272             : 
     273         130 : void V8StackTraceImpl::StackFrameIterator::next() {
     274         260 :   if (m_currentIt == m_currentEnd) return;
     275             :   ++m_currentIt;
     276         150 :   while (m_currentIt == m_currentEnd && m_parent) {
     277          40 :     const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
     278          20 :     m_currentIt = frames.begin();
     279          40 :     if (m_parent->description() == "async function") ++m_currentIt;
     280          20 :     m_currentEnd = frames.end();
     281          80 :     m_parent = m_parent->parent().lock().get();
     282             :   }
     283             : }
     284             : 
     285           0 : bool V8StackTraceImpl::StackFrameIterator::done() {
     286           0 :   return m_currentIt == m_currentEnd;
     287             : }
     288             : 
     289           0 : StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
     290          50 :   return m_currentIt->get();
     291             : }
     292             : 
     293             : // static
     294       45452 : std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
     295       45452 :     V8Debugger* debugger, int contextGroupId, const String16& description,
     296             :     int maxStackSize) {
     297             :   DCHECK(debugger);
     298             : 
     299             :   v8::Isolate* isolate = debugger->isolate();
     300       45452 :   v8::HandleScope handleScope(isolate);
     301             : 
     302       45452 :   std::vector<std::shared_ptr<StackFrame>> frames;
     303       45452 :   if (isolate->InContext()) {
     304             :     v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
     305       45452 :         isolate, maxStackSize, stackTraceOptions);
     306       90904 :     frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
     307             :   }
     308             : 
     309       45452 :   std::shared_ptr<AsyncStackTrace> asyncParent;
     310       45452 :   std::shared_ptr<AsyncStackTrace> asyncCreation;
     311             :   calculateAsyncChain(debugger, contextGroupId, &asyncParent, &asyncCreation,
     312       45452 :                       nullptr);
     313             : 
     314       45452 :   if (frames.empty() && !asyncCreation && !asyncParent) return nullptr;
     315             : 
     316             :   // When async call chain is empty but doesn't contain useful schedule stack
     317             :   // and parent async call chain contains creationg stack but doesn't
     318             :   // synchronous we can merge them together.
     319             :   // e.g. Promise ThenableJob.
     320       98459 :   if (asyncParent && frames.empty() &&
     321       47417 :       asyncParent->m_description == description && !asyncCreation) {
     322             :     return asyncParent;
     323             :   }
     324             : 
     325             :   DCHECK(contextGroupId || asyncParent);
     326       39947 :   if (!contextGroupId && asyncParent) {
     327           0 :     contextGroupId = asyncParent->m_contextGroupId;
     328             :   }
     329             :   return std::shared_ptr<AsyncStackTrace>(
     330             :       new AsyncStackTrace(contextGroupId, description, std::move(frames),
     331      165293 :                           asyncParent, asyncCreation));
     332             : }
     333             : 
     334       39947 : AsyncStackTrace::AsyncStackTrace(
     335             :     int contextGroupId, const String16& description,
     336             :     std::vector<std::shared_ptr<StackFrame>> frames,
     337             :     std::shared_ptr<AsyncStackTrace> asyncParent,
     338             :     std::shared_ptr<AsyncStackTrace> asyncCreation)
     339             :     : m_contextGroupId(contextGroupId),
     340             :       m_description(description),
     341             :       m_frames(std::move(frames)),
     342             :       m_asyncParent(asyncParent),
     343       39947 :       m_asyncCreation(asyncCreation) {
     344             :   DCHECK(m_contextGroupId);
     345       39947 : }
     346             : 
     347             : std::unique_ptr<protocol::Runtime::StackTrace>
     348        1065 : AsyncStackTrace::buildInspectorObject(AsyncStackTrace* asyncCreation,
     349             :                                       int maxAsyncDepth) const {
     350             :   return buildInspectorObjectCommon(m_frames, m_description,
     351             :                                     m_asyncParent.lock(),
     352        3195 :                                     m_asyncCreation.lock(), maxAsyncDepth);
     353             : }
     354             : 
     355        6730 : int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
     356             : 
     357        2010 : const String16& AsyncStackTrace::description() const { return m_description; }
     358             : 
     359           0 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
     360           0 :   return m_asyncParent;
     361             : }
     362             : 
     363           0 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::creation() const {
     364           0 :   return m_asyncCreation;
     365             : }
     366             : 
     367       41202 : bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
     368             : 
     369             : }  // namespace v8_inspector

Generated by: LCOV version 1.10