LCOV - code coverage report
Current view: top level - src/inspector - v8-stack-trace-impl.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 162 186 87.1 %
Date: 2019-04-17 Functions: 31 46 67.4 %

          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/v8-inspector-impl.h"
      11             : #include "src/inspector/wasm-translation.h"
      12             : 
      13             : namespace v8_inspector {
      14             : 
      15             : int V8StackTraceImpl::maxCallStackSizeToCapture = 200;
      16             : 
      17             : namespace {
      18             : 
      19             : static const v8::StackTrace::StackTraceOptions stackTraceOptions =
      20             :     static_cast<v8::StackTrace::StackTraceOptions>(
      21             :         v8::StackTrace::kDetailed |
      22             :         v8::StackTrace::kExposeFramesAcrossSecurityOrigins);
      23             : 
      24       59215 : std::vector<std::shared_ptr<StackFrame>> toFramesVector(
      25             :     V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
      26             :     int maxStackSize) {
      27             :   DCHECK(debugger->isolate()->InContext());
      28      118430 :   int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize);
      29       59215 :   std::vector<std::shared_ptr<StackFrame>> frames(frameCount);
      30      182215 :   for (int i = 0; i < frameCount; ++i) {
      31       61500 :     frames[i] =
      32      123000 :         debugger->symbolize(v8StackTrace->GetFrame(debugger->isolate(), i));
      33             :   }
      34       59215 :   return frames;
      35             : }
      36             : 
      37       66959 : void calculateAsyncChain(V8Debugger* debugger, int contextGroupId,
      38             :                          std::shared_ptr<AsyncStackTrace>* asyncParent,
      39             :                          V8StackTraceId* externalParent, int* maxAsyncDepth) {
      40      133918 :   *asyncParent = debugger->currentAsyncParent();
      41      133918 :   *externalParent = debugger->currentExternalParent();
      42             :   DCHECK(externalParent->IsInvalid() || !*asyncParent);
      43       66959 :   if (maxAsyncDepth) *maxAsyncDepth = debugger->maxAsyncCallChainDepth();
      44             : 
      45             :   // Do not accidentally append async call chain from another group. This should
      46             :   // not happen if we have proper instrumentation, but let's double-check to be
      47             :   // safe.
      48      135588 :   if (contextGroupId && *asyncParent &&
      49       68734 :       (*asyncParent)->externalParent().IsInvalid() &&
      50             :       (*asyncParent)->contextGroupId() != contextGroupId) {
      51             :     asyncParent->reset();
      52             :     *externalParent = V8StackTraceId();
      53           0 :     if (maxAsyncDepth) *maxAsyncDepth = 0;
      54             :     return;
      55             :   }
      56             : 
      57             :   // Only the top stack in the chain may be empty, so ensure that second stack
      58             :   // is non-empty (it's the top of appended chain).
      59       66959 :   if (*asyncParent && (*asyncParent)->isEmpty()) {
      60           0 :     *asyncParent = (*asyncParent)->parent().lock();
      61             :   }
      62             : }
      63             : 
      64       57455 : std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon(
      65             :     V8Debugger* debugger,
      66             :     const std::vector<std::shared_ptr<StackFrame>>& frames,
      67             :     const String16& description,
      68             :     const std::shared_ptr<AsyncStackTrace>& asyncParent,
      69             :     const V8StackTraceId& externalParent, int maxAsyncDepth) {
      70       57470 :   if (asyncParent && frames.empty() &&
      71             :       description == asyncParent->description()) {
      72           0 :     return asyncParent->buildInspectorObject(debugger, maxAsyncDepth);
      73             :   }
      74             : 
      75             :   std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>>
      76       57455 :       inspectorFrames = protocol::Array<protocol::Runtime::CallFrame>::create();
      77      176255 :   for (size_t i = 0; i < frames.size(); i++) {
      78             :     V8InspectorClient* client = nullptr;
      79       59400 :     if (debugger && debugger->inspector())
      80             :       client = debugger->inspector()->client();
      81      118800 :     inspectorFrames->addItem(frames[i]->buildInspectorObject(client));
      82             :   }
      83             :   std::unique_ptr<protocol::Runtime::StackTrace> stackTrace =
      84       57455 :       protocol::Runtime::StackTrace::create()
      85      114910 :           .setCallFrames(std::move(inspectorFrames))
      86       57455 :           .build();
      87       57455 :   if (!description.isEmpty()) stackTrace->setDescription(description);
      88       57455 :   if (asyncParent) {
      89        1315 :     if (maxAsyncDepth > 0) {
      90             :       stackTrace->setParent(
      91        2420 :           asyncParent->buildInspectorObject(debugger, maxAsyncDepth - 1));
      92         105 :     } else if (debugger) {
      93             :       stackTrace->setParentId(
      94         105 :           protocol::Runtime::StackTraceId::create()
      95         315 :               .setId(stackTraceIdToString(
      96             :                   AsyncStackTrace::store(debugger, asyncParent)))
      97             :               .build());
      98             :     }
      99             :   }
     100       57455 :   if (!externalParent.IsInvalid()) {
     101             :     stackTrace->setParentId(
     102          25 :         protocol::Runtime::StackTraceId::create()
     103          50 :             .setId(stackTraceIdToString(externalParent.id))
     104          50 :             .setDebuggerId(debuggerIdToString(externalParent.debugger_id))
     105             :             .build());
     106             :   }
     107             :   return stackTrace;
     108             : }
     109             : 
     110             : }  //  namespace
     111             : 
     112      321203 : V8StackTraceId::V8StackTraceId() : id(0), debugger_id(std::make_pair(0, 0)) {}
     113             : 
     114         190 : V8StackTraceId::V8StackTraceId(uintptr_t id,
     115             :                                const std::pair<int64_t, int64_t> debugger_id)
     116         190 :     : id(id), debugger_id(debugger_id) {}
     117             : 
     118      177860 : bool V8StackTraceId::IsInvalid() const { return !id; }
     119             : 
     120       60191 : StackFrame::StackFrame(v8::Isolate* isolate, v8::Local<v8::StackFrame> v8Frame)
     121             :     : m_functionName(toProtocolString(isolate, v8Frame->GetFunctionName())),
     122             :       m_scriptId(String16::fromInteger(v8Frame->GetScriptId())),
     123             :       m_sourceURL(
     124             :           toProtocolString(isolate, v8Frame->GetScriptNameOrSourceURL())),
     125       60191 :       m_lineNumber(v8Frame->GetLineNumber() - 1),
     126       60191 :       m_columnNumber(v8Frame->GetColumn() - 1),
     127      120382 :       m_hasSourceURLComment(v8Frame->GetScriptName() !=
     128      300955 :                             v8Frame->GetScriptNameOrSourceURL()) {
     129             :   DCHECK_NE(v8::Message::kNoLineNumberInfo, m_lineNumber + 1);
     130             :   DCHECK_NE(v8::Message::kNoColumnInfo, m_columnNumber + 1);
     131       60191 : }
     132             : 
     133         340 : void StackFrame::translate(WasmTranslation* wasmTranslation) {
     134         340 :   wasmTranslation->TranslateWasmScriptLocationToProtocolLocation(
     135         340 :       &m_scriptId, &m_lineNumber, &m_columnNumber);
     136         340 : }
     137             : 
     138           0 : const String16& StackFrame::functionName() const { return m_functionName; }
     139             : 
     140         295 : const String16& StackFrame::scriptId() const { return m_scriptId; }
     141             : 
     142        6625 : const String16& StackFrame::sourceURL() const { return m_sourceURL; }
     143             : 
     144        6920 : int StackFrame::lineNumber() const { return m_lineNumber; }
     145             : 
     146        6920 : int StackFrame::columnNumber() const { return m_columnNumber; }
     147             : 
     148       59400 : std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject(
     149             :     V8InspectorClient* client) const {
     150             :   String16 frameUrl = m_sourceURL;
     151       59400 :   if (client && !m_hasSourceURLComment && frameUrl.length() > 0) {
     152             :     std::unique_ptr<StringBuffer> url =
     153       47708 :         client->resourceNameToUrl(toStringView(m_sourceURL));
     154       47708 :     if (url) {
     155          40 :       frameUrl = toString16(url->string());
     156             :     }
     157             :   }
     158       59400 :   return protocol::Runtime::CallFrame::create()
     159             :       .setFunctionName(m_functionName)
     160             :       .setScriptId(m_scriptId)
     161             :       .setUrl(frameUrl)
     162       59400 :       .setLineNumber(m_lineNumber)
     163       59400 :       .setColumnNumber(m_columnNumber)
     164       59400 :       .build();
     165             : }
     166             : 
     167           0 : bool StackFrame::isEqual(StackFrame* frame) const {
     168             :   return m_scriptId == frame->m_scriptId &&
     169          50 :          m_lineNumber == frame->m_lineNumber &&
     170           0 :          m_columnNumber == frame->m_columnNumber;
     171             : }
     172             : 
     173             : // static
     174        1214 : void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
     175             :     v8::Isolate* isolate, bool capture) {
     176        1214 :   isolate->SetCaptureStackTraceForUncaughtExceptions(
     177        2428 :       capture, V8StackTraceImpl::maxCallStackSizeToCapture);
     178        1214 : }
     179             : 
     180             : // static
     181       63509 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
     182             :     V8Debugger* debugger, int contextGroupId,
     183             :     v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) {
     184             :   DCHECK(debugger);
     185             : 
     186             :   v8::Isolate* isolate = debugger->isolate();
     187      127018 :   v8::HandleScope scope(isolate);
     188             : 
     189       63509 :   std::vector<std::shared_ptr<StackFrame>> frames;
     190       63509 :   if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) {
     191      111730 :     frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
     192             :   }
     193             : 
     194       63509 :   int maxAsyncDepth = 0;
     195       63509 :   std::shared_ptr<AsyncStackTrace> asyncParent;
     196             :   V8StackTraceId externalParent;
     197             :   calculateAsyncChain(debugger, contextGroupId, &asyncParent, &externalParent,
     198       63509 :                       &maxAsyncDepth);
     199       71008 :   if (frames.empty() && !asyncParent && externalParent.IsInvalid())
     200             :     return nullptr;
     201             :   return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl(
     202      112020 :       std::move(frames), maxAsyncDepth, asyncParent, externalParent));
     203             : }
     204             : 
     205             : // static
     206       63089 : std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture(
     207             :     V8Debugger* debugger, int contextGroupId, int maxStackSize) {
     208             :   DCHECK(debugger);
     209             :   v8::Isolate* isolate = debugger->isolate();
     210      126178 :   v8::HandleScope handleScope(isolate);
     211             :   v8::Local<v8::StackTrace> v8StackTrace;
     212       63089 :   if (isolate->InContext()) {
     213             :     v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize,
     214       61120 :                                                      stackTraceOptions);
     215             :   }
     216             :   return V8StackTraceImpl::create(debugger, contextGroupId, v8StackTrace,
     217      126178 :                                   maxStackSize);
     218             : }
     219             : 
     220       56010 : V8StackTraceImpl::V8StackTraceImpl(
     221             :     std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth,
     222             :     std::shared_ptr<AsyncStackTrace> asyncParent,
     223             :     const V8StackTraceId& externalParent)
     224             :     : m_frames(std::move(frames)),
     225             :       m_maxAsyncDepth(maxAsyncDepth),
     226             :       m_asyncParent(std::move(asyncParent)),
     227      168030 :       m_externalParent(externalParent) {}
     228             : 
     229             : V8StackTraceImpl::~V8StackTraceImpl() = default;
     230             : 
     231           0 : std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() {
     232             :   return std::unique_ptr<V8StackTrace>(new V8StackTraceImpl(
     233           0 :       m_frames, 0, std::shared_ptr<AsyncStackTrace>(), V8StackTraceId()));
     234             : }
     235             : 
     236           0 : StringView V8StackTraceImpl::firstNonEmptySourceURL() const {
     237           0 :   StackFrameIterator current(this);
     238           0 :   while (!current.done()) {
     239           0 :     if (current.frame()->sourceURL().length()) {
     240           0 :       return toStringView(current.frame()->sourceURL());
     241             :     }
     242           0 :     current.next();
     243             :   }
     244             :   return StringView();
     245             : }
     246             : 
     247      111120 : bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); }
     248             : 
     249        6600 : StringView V8StackTraceImpl::topSourceURL() const {
     250        6600 :   return toStringView(m_frames[0]->sourceURL());
     251             : }
     252             : 
     253        6895 : int V8StackTraceImpl::topLineNumber() const {
     254        6895 :   return m_frames[0]->lineNumber() + 1;
     255             : }
     256             : 
     257        6895 : int V8StackTraceImpl::topColumnNumber() const {
     258        6895 :   return m_frames[0]->columnNumber() + 1;
     259             : }
     260             : 
     261         295 : StringView V8StackTraceImpl::topScriptId() const {
     262         295 :   return toStringView(m_frames[0]->scriptId());
     263             : }
     264             : 
     265           0 : StringView V8StackTraceImpl::topFunctionName() const {
     266           0 :   return toStringView(m_frames[0]->functionName());
     267             : }
     268             : 
     269             : std::unique_ptr<protocol::Runtime::StackTrace>
     270         645 : V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger) const {
     271         645 :   return buildInspectorObjectImpl(debugger, m_maxAsyncDepth);
     272             : }
     273             : 
     274             : std::unique_ptr<protocol::Runtime::StackTrace>
     275       55585 : V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger,
     276             :                                            int maxAsyncDepth) const {
     277             :   return buildInspectorObjectCommon(debugger, m_frames, String16(),
     278       55585 :                                     m_asyncParent.lock(), m_externalParent,
     279      277925 :                                     maxAsyncDepth);
     280             : }
     281             : 
     282             : std::unique_ptr<protocol::Runtime::API::StackTrace>
     283           0 : V8StackTraceImpl::buildInspectorObject() const {
     284           0 :   return buildInspectorObjectImpl(nullptr);
     285             : }
     286             : 
     287          10 : std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const {
     288          10 :   String16Builder stackTrace;
     289          60 :   for (size_t i = 0; i < m_frames.size(); ++i) {
     290             :     const StackFrame& frame = *m_frames[i];
     291          85 :     stackTrace.append("\n    at " + (frame.functionName().length()
     292             :                                          ? frame.functionName()
     293          25 :                                          : "(anonymous function)"));
     294          50 :     stackTrace.append(" (");
     295          25 :     stackTrace.append(frame.sourceURL());
     296          25 :     stackTrace.append(':');
     297          50 :     stackTrace.append(String16::fromInteger(frame.lineNumber() + 1));
     298          25 :     stackTrace.append(':');
     299          50 :     stackTrace.append(String16::fromInteger(frame.columnNumber() + 1));
     300          25 :     stackTrace.append(')');
     301             :   }
     302          10 :   String16 string = stackTrace.toString();
     303          30 :   return StringBufferImpl::adopt(string);
     304             : }
     305             : 
     306          45 : bool V8StackTraceImpl::isEqualIgnoringTopFrame(
     307             :     V8StackTraceImpl* stackTrace) const {
     308          45 :   StackFrameIterator current(this);
     309          45 :   StackFrameIterator target(stackTrace);
     310             : 
     311          45 :   current.next();
     312          45 :   target.next();
     313         135 :   while (!current.done() && !target.done()) {
     314          50 :     if (!current.frame()->isEqual(target.frame())) {
     315             :       return false;
     316             :     }
     317          20 :     current.next();
     318          20 :     target.next();
     319             :   }
     320          15 :   return current.done() == target.done();
     321             : }
     322             : 
     323          90 : V8StackTraceImpl::StackFrameIterator::StackFrameIterator(
     324             :     const V8StackTraceImpl* stackTrace)
     325             :     : m_currentIt(stackTrace->m_frames.begin()),
     326             :       m_currentEnd(stackTrace->m_frames.end()),
     327         270 :       m_parent(stackTrace->m_asyncParent.lock().get()) {}
     328             : 
     329         130 : void V8StackTraceImpl::StackFrameIterator::next() {
     330         130 :   if (m_currentIt == m_currentEnd) return;
     331             :   ++m_currentIt;
     332         170 :   while (m_currentIt == m_currentEnd && m_parent) {
     333          20 :     const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames();
     334          20 :     m_currentIt = frames.begin();
     335          40 :     if (m_parent->description() == "async function") ++m_currentIt;
     336          20 :     m_currentEnd = frames.end();
     337          80 :     m_parent = m_parent->parent().lock().get();
     338             :   }
     339             : }
     340             : 
     341           0 : bool V8StackTraceImpl::StackFrameIterator::done() {
     342           0 :   return m_currentIt == m_currentEnd;
     343             : }
     344             : 
     345           0 : StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
     346           0 :   return m_currentIt->get();
     347             : }
     348             : 
     349             : // static
     350        3450 : std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
     351             :     V8Debugger* debugger, int contextGroupId, const String16& description,
     352             :     int maxStackSize) {
     353             :   DCHECK(debugger);
     354             : 
     355             :   v8::Isolate* isolate = debugger->isolate();
     356        6900 :   v8::HandleScope handleScope(isolate);
     357             : 
     358        3450 :   std::vector<std::shared_ptr<StackFrame>> frames;
     359        3450 :   if (isolate->InContext()) {
     360             :     v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace(
     361        3350 :         isolate, maxStackSize, stackTraceOptions);
     362        6700 :     frames = toFramesVector(debugger, v8StackTrace, maxStackSize);
     363             :   }
     364             : 
     365        3450 :   std::shared_ptr<AsyncStackTrace> asyncParent;
     366             :   V8StackTraceId externalParent;
     367             :   calculateAsyncChain(debugger, contextGroupId, &asyncParent, &externalParent,
     368        3450 :                       nullptr);
     369             : 
     370        3535 :   if (frames.empty() && !asyncParent && externalParent.IsInvalid())
     371             :     return nullptr;
     372             : 
     373             :   // When async call chain is empty but doesn't contain useful schedule stack
     374             :   // but doesn't synchronous we can merge them together. e.g. Promise
     375             :   // ThenableJob.
     376        6780 :   if (asyncParent && frames.empty() &&
     377          15 :       (asyncParent->m_description == description || description.isEmpty())) {
     378             :     return asyncParent;
     379             :   }
     380             : 
     381             :   DCHECK(contextGroupId || asyncParent || !externalParent.IsInvalid());
     382        3355 :   if (!contextGroupId && asyncParent) {
     383          10 :     contextGroupId = asyncParent->m_contextGroupId;
     384             :   }
     385             : 
     386             :   return std::shared_ptr<AsyncStackTrace>(
     387             :       new AsyncStackTrace(contextGroupId, description, std::move(frames),
     388        6710 :                           asyncParent, externalParent));
     389             : }
     390             : 
     391        3355 : AsyncStackTrace::AsyncStackTrace(
     392             :     int contextGroupId, const String16& description,
     393             :     std::vector<std::shared_ptr<StackFrame>> frames,
     394             :     std::shared_ptr<AsyncStackTrace> asyncParent,
     395             :     const V8StackTraceId& externalParent)
     396             :     : m_contextGroupId(contextGroupId),
     397             :       m_id(0),
     398             :       m_suspendedTaskId(nullptr),
     399             :       m_description(description),
     400             :       m_frames(std::move(frames)),
     401             :       m_asyncParent(std::move(asyncParent)),
     402        6710 :       m_externalParent(externalParent) {
     403             :   DCHECK(m_contextGroupId || (!externalParent.IsInvalid() && m_frames.empty()));
     404        3355 : }
     405             : 
     406             : std::unique_ptr<protocol::Runtime::StackTrace>
     407        1870 : AsyncStackTrace::buildInspectorObject(V8Debugger* debugger,
     408             :                                       int maxAsyncDepth) const {
     409             :   return buildInspectorObjectCommon(debugger, m_frames, m_description,
     410        1870 :                                     m_asyncParent.lock(), m_externalParent,
     411        5610 :                                     maxAsyncDepth);
     412             : }
     413             : 
     414        1775 : int AsyncStackTrace::contextGroupId() const { return m_contextGroupId; }
     415             : 
     416        3620 : void AsyncStackTrace::setSuspendedTaskId(void* task) {
     417        3620 :   m_suspendedTaskId = task;
     418        3620 : }
     419             : 
     420          50 : void* AsyncStackTrace::suspendedTaskId() const { return m_suspendedTaskId; }
     421             : 
     422         165 : uintptr_t AsyncStackTrace::store(V8Debugger* debugger,
     423             :                                  std::shared_ptr<AsyncStackTrace> stack) {
     424         165 :   if (stack->m_id) return stack->m_id;
     425         310 :   stack->m_id = debugger->storeStackTrace(stack);
     426         155 :   return stack->m_id;
     427             : }
     428             : 
     429           0 : const String16& AsyncStackTrace::description() const { return m_description; }
     430             : 
     431          80 : std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const {
     432          80 :   return m_asyncParent;
     433             : }
     434             : 
     435           0 : bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); }
     436             : 
     437             : }  // namespace v8_inspector

Generated by: LCOV version 1.10