/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
deps/v8/src/inspector/v8-stack-trace-impl.cc
474 строки
16 KB
Michaël Zasso
deps: update V8 to 13.6.233.8
02 май 2025, 16:06
Не верифицирован
02 май 2025, 16:06
918fe04
Код
Авторство
О чём код?
// Copyright 2016 the V8 project authors. All rights reserved. // // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if defined(V8_OS_STARBOARD) #include "starboard/system.h" #define __builtin_abort SbSystemBreakIntoDebugger #endif #include "src/inspector/v8-stack-trace-impl.h" #include <algorithm> #include <memory> #include <vector> #include "../../third_party/inspector_protocol/crdtp/json.h" #include "src/debug/debug-interface.h" #include "src/inspector/v8-debugger.h" #include "src/inspector/v8-inspector-impl.h" #include "src/tracing/trace-event.h" using v8_crdtp::json::ConvertJSONToCBOR; namespace v8_inspector { namespace { static const char kId[] = "id"; static const char kDebuggerId[] = "debuggerId"; static const char kShouldPause[] = "shouldPause"; static const v8::StackTrace::StackTraceOptions stackTraceOptions = static_cast<v8::StackTrace::StackTraceOptions>( v8::StackTrace::kDetailed | v8::StackTrace::kExposeFramesAcrossSecurityOrigins); std::vector<std::shared_ptr<StackFrame>> toFramesVector( V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) { DCHECK(debugger->isolate()->InContext()); int frameCount = std::min(v8StackTrace->GetFrameCount(), maxStackSize); TRACE_EVENT1( TRACE_DISABLED_BY_DEFAULT("v8.inspector") "," TRACE_DISABLED_BY_DEFAULT( "v8.stack_trace"), "toFramesVector", "frameCount", frameCount); std::vector<std::shared_ptr<StackFrame>> frames(frameCount); for (int i = 0; i < frameCount; ++i) { frames[i] = debugger->symbolize(v8StackTrace->GetFrame(debugger->isolate(), i)); } return frames; } std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectCommon( V8Debugger* debugger, const std::vector<std::shared_ptr<StackFrame>>& frames, const String16& description, const std::shared_ptr<AsyncStackTrace>& asyncParent, const V8StackTraceId& externalParent, int maxAsyncDepth) { if (asyncParent && frames.empty() && description == asyncParent->description()) { return asyncParent->buildInspectorObject(debugger, maxAsyncDepth); } auto inspectorFrames = std::make_unique<protocol::Array<protocol::Runtime::CallFrame>>(); for (const std::shared_ptr<StackFrame>& frame : frames) { V8InspectorClient* client = nullptr; if (debugger && debugger->inspector()) client = debugger->inspector()->client(); inspectorFrames->emplace_back(frame->buildInspectorObject(client)); } std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = protocol::Runtime::StackTrace::create() .setCallFrames(std::move(inspectorFrames)) .build(); if (!description.isEmpty()) stackTrace->setDescription(description); if (asyncParent) { if (maxAsyncDepth > 0) { stackTrace->setParent( asyncParent->buildInspectorObject(debugger, maxAsyncDepth - 1)); } else if (debugger) { stackTrace->setParentId( protocol::Runtime::StackTraceId::create() .setId(stackTraceIdToString( AsyncStackTrace::store(debugger, asyncParent))) .build()); } } if (!externalParent.IsInvalid()) { stackTrace->setParentId( protocol::Runtime::StackTraceId::create() .setId(stackTraceIdToString(externalParent.id)) .setDebuggerId( internal::V8DebuggerId(externalParent.debugger_id).toString()) .build()); } return stackTrace; } } // namespace V8StackTraceId::V8StackTraceId() : id(0), debugger_id(internal::V8DebuggerId().pair()) {} V8StackTraceId::V8StackTraceId(uintptr_t id, const std::pair<int64_t, int64_t> debugger_id) : id(id), debugger_id(debugger_id) {} V8StackTraceId::V8StackTraceId(uintptr_t id, const std::pair<int64_t, int64_t> debugger_id, bool should_pause) : id(id), debugger_id(debugger_id), should_pause(should_pause) {} V8StackTraceId::V8StackTraceId(StringView json) : id(0), debugger_id(internal::V8DebuggerId().pair()) { if (json.length() == 0) return; std::vector<uint8_t> cbor; if (json.is8Bit()) { ConvertJSONToCBOR( v8_crdtp::span<uint8_t>(json.characters8(), json.length()), &cbor); } else { ConvertJSONToCBOR( v8_crdtp::span<uint16_t>(json.characters16(), json.length()), &cbor); } auto dict = protocol::DictionaryValue::cast( protocol::Value::parseBinary(cbor.data(), cbor.size())); if (!dict) return; String16 s; if (!dict->getString(kId, &s)) return; bool isOk = false; int64_t parsedId = s.toInteger64(&isOk); if (!isOk || !parsedId) return; if (!dict->getString(kDebuggerId, &s)) return; internal::V8DebuggerId debuggerId(s); if (!debuggerId.isValid()) return; if (!dict->getBoolean(kShouldPause, &should_pause)) return; id = parsedId; debugger_id = debuggerId.pair(); } bool V8StackTraceId::IsInvalid() const { return !id; } std::unique_ptr<StringBuffer> V8StackTraceId::ToString() { if (IsInvalid()) return nullptr; auto dict = protocol::DictionaryValue::create(); dict->setString(kId, String16::fromInteger64(id)); dict->setString(kDebuggerId, internal::V8DebuggerId(debugger_id).toString()); dict->setBoolean(kShouldPause, should_pause); std::vector<uint8_t> json; v8_crdtp::json::ConvertCBORToJSON(v8_crdtp::SpanFrom(dict->Serialize()), &json); return StringBufferFrom(std::move(json)); } StackFrame::StackFrame(String16&& functionName, int scriptId, String16&& sourceURL, int lineNumber, int columnNumber, bool hasSourceURLComment) : m_functionName(std::move(functionName)), m_scriptId(scriptId), m_sourceURL(std::move(sourceURL)), m_lineNumber(lineNumber), m_columnNumber(columnNumber), m_hasSourceURLComment(hasSourceURLComment) { DCHECK_NE(v8::Message::kNoLineNumberInfo, m_lineNumber + 1); DCHECK_NE(v8::Message::kNoColumnInfo, m_columnNumber + 1); } const String16& StackFrame::functionName() const { return m_functionName; } int StackFrame::scriptId() const { return m_scriptId; } const String16& StackFrame::sourceURL() const { return m_sourceURL; } int StackFrame::lineNumber() const { return m_lineNumber; } int StackFrame::columnNumber() const { return m_columnNumber; } std::unique_ptr<protocol::Runtime::CallFrame> StackFrame::buildInspectorObject( V8InspectorClient* client) const { String16 frameUrl; const char* dataURIPrefix = "data:"; if (m_sourceURL.substring(0, strlen(dataURIPrefix)) != dataURIPrefix) { frameUrl = m_sourceURL; } if (client && !m_hasSourceURLComment && frameUrl.length() > 0) { std::unique_ptr<StringBuffer> url = client->resourceNameToUrl(toStringView(m_sourceURL)); if (url) { frameUrl = toString16(url->string()); } } return protocol::Runtime::CallFrame::create() .setFunctionName(m_functionName) .setScriptId(String16::fromInteger(m_scriptId)) .setUrl(frameUrl) .setLineNumber(m_lineNumber) .setColumnNumber(m_columnNumber) .build(); } bool StackFrame::isEqual(StackFrame* frame) const { return m_scriptId == frame->m_scriptId && m_lineNumber == frame->m_lineNumber && m_columnNumber == frame->m_columnNumber; } // static std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace, int maxStackSize) { DCHECK(debugger); v8::Isolate* isolate = debugger->isolate(); v8::HandleScope scope(isolate); std::vector<std::shared_ptr<StackFrame>> frames; if (!v8StackTrace.IsEmpty() && v8StackTrace->GetFrameCount()) { frames = toFramesVector(debugger, v8StackTrace, maxStackSize); } int maxAsyncDepth = debugger->maxAsyncCallChainDepth(); std::shared_ptr<AsyncStackTrace> asyncParent; V8StackTraceId externalParent; if (!v8StackTrace.IsEmpty()) { debugger->asyncParentFor(v8StackTrace->GetID(), &asyncParent, &externalParent); } if (frames.empty() && !asyncParent && externalParent.IsInvalid()) return {}; return std::unique_ptr<V8StackTraceImpl>(new V8StackTraceImpl( std::move(frames), maxAsyncDepth, asyncParent, externalParent)); } // static std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( V8Debugger* debugger, int maxStackSize) { DCHECK(debugger); TRACE_EVENT1( TRACE_DISABLED_BY_DEFAULT("v8.inspector") "," TRACE_DISABLED_BY_DEFAULT( "v8.stack_trace"), "V8StackTraceImpl::capture", "maxFrameCount", maxStackSize); v8::Isolate* isolate = debugger->isolate(); v8::HandleScope handleScope(isolate); v8::Local<v8::StackTrace> v8StackTrace; if (isolate->InContext()) { v8StackTrace = v8::StackTrace::CurrentStackTrace(isolate, maxStackSize, stackTraceOptions); } return V8StackTraceImpl::create(debugger, v8StackTrace, maxStackSize); } V8StackTraceImpl::V8StackTraceImpl( std::vector<std::shared_ptr<StackFrame>> frames, int maxAsyncDepth, std::shared_ptr<AsyncStackTrace> asyncParent, const V8StackTraceId& externalParent) : m_frames(std::move(frames)), m_maxAsyncDepth(maxAsyncDepth), m_asyncParent(std::move(asyncParent)), m_externalParent(externalParent) {} V8StackTraceImpl::~V8StackTraceImpl() = default; std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { return std::unique_ptr<V8StackTrace>(new V8StackTraceImpl( m_frames, 0, std::shared_ptr<AsyncStackTrace>(), V8StackTraceId())); } StringView V8StackTraceImpl::firstNonEmptySourceURL() const { StackFrameIterator current(this); while (!current.done()) { if (current.frame()->sourceURL().length()) { return toStringView(current.frame()->sourceURL()); } current.next(); } return StringView(); } bool V8StackTraceImpl::isEmpty() const { return m_frames.empty(); } StringView V8StackTraceImpl::topSourceURL() const { return toStringView(m_frames[0]->sourceURL()); } int V8StackTraceImpl::topLineNumber() const { return m_frames[0]->lineNumber() + 1; } int V8StackTraceImpl::topColumnNumber() const { return m_frames[0]->columnNumber() + 1; } int V8StackTraceImpl::topScriptId() const { return m_frames[0]->scriptId(); } StringView V8StackTraceImpl::topFunctionName() const { return toStringView(m_frames[0]->functionName()); } std::vector<V8StackFrame> V8StackTraceImpl::frames() const { std::vector<V8StackFrame> ret; ret.reserve(m_frames.size()); for (const auto& frame : m_frames) { if (frame) { ret.emplace_back(V8StackFrame{ toStringView(frame->sourceURL()), toStringView(frame->functionName()), frame->lineNumber() + 1, frame->columnNumber() + 1, frame->scriptId()}); } } return ret; } std::unique_ptr<protocol::Runtime::StackTrace> V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger) const { return buildInspectorObjectImpl(debugger, m_maxAsyncDepth); } std::unique_ptr<protocol::Runtime::StackTrace> V8StackTraceImpl::buildInspectorObjectImpl(V8Debugger* debugger, int maxAsyncDepth) const { return buildInspectorObjectCommon(debugger, m_frames, String16(), m_asyncParent.lock(), m_externalParent, maxAsyncDepth); } std::unique_ptr<protocol::Runtime::API::StackTrace> V8StackTraceImpl::buildInspectorObject(int maxAsyncDepth) const { return buildInspectorObjectImpl(nullptr, std::min(maxAsyncDepth, m_maxAsyncDepth)); } std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { String16Builder stackTrace; for (size_t i = 0; i < m_frames.size(); ++i) { const StackFrame& frame = *m_frames[i]; stackTrace.append("\n at " + (frame.functionName().length() ? frame.functionName() : "(anonymous function)")); stackTrace.append(" ("); stackTrace.append(frame.sourceURL()); stackTrace.append(':'); stackTrace.append(String16::fromInteger(frame.lineNumber() + 1)); stackTrace.append(':'); stackTrace.append(String16::fromInteger(frame.columnNumber() + 1)); stackTrace.append(')'); } return StringBufferFrom(stackTrace.toString()); } bool V8StackTraceImpl::isEqualIgnoringTopFrame( V8StackTraceImpl* stackTrace) const { StackFrameIterator current(this); StackFrameIterator target(stackTrace); current.next(); target.next(); while (!current.done() && !target.done()) { if (!current.frame()->isEqual(target.frame())) { return false; } current.next(); target.next(); } return current.done() == target.done(); } V8StackTraceImpl::StackFrameIterator::StackFrameIterator( const V8StackTraceImpl* stackTrace) : m_currentIt(stackTrace->m_frames.begin()), m_currentEnd(stackTrace->m_frames.end()), m_parent(stackTrace->m_asyncParent.lock().get()) {} void V8StackTraceImpl::StackFrameIterator::next() { if (m_currentIt == m_currentEnd) return; ++m_currentIt; while (m_currentIt == m_currentEnd && m_parent) { const std::vector<std::shared_ptr<StackFrame>>& frames = m_parent->frames(); m_currentIt = frames.begin(); m_currentEnd = frames.end(); m_parent = m_parent->parent().lock().get(); } } bool V8StackTraceImpl::StackFrameIterator::done() { return m_currentIt == m_currentEnd; } StackFrame* V8StackTraceImpl::StackFrameIterator::frame() { return m_currentIt->get(); } // static std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture( V8Debugger* debugger, const String16& description, bool skipTopFrame) { DCHECK(debugger); int maxStackSize = debugger->maxCallStackSizeToCapture(); TRACE_EVENT1( TRACE_DISABLED_BY_DEFAULT("v8.inspector") "," TRACE_DISABLED_BY_DEFAULT( "v8.stack_trace"), "AsyncStackTrace::capture", "maxFrameCount", maxStackSize); v8::Isolate* isolate = debugger->isolate(); v8::HandleScope handleScope(isolate); std::vector<std::shared_ptr<StackFrame>> frames; std::shared_ptr<AsyncStackTrace> asyncParent; V8StackTraceId externalParent; if (isolate->InContext()) { v8::Local<v8::StackTrace> v8StackTrace = v8::StackTrace::CurrentStackTrace( isolate, maxStackSize, stackTraceOptions); frames = toFramesVector(debugger, v8StackTrace, maxStackSize); if (skipTopFrame && !frames.empty()) { frames.erase(frames.begin()); } debugger->asyncParentFor(v8StackTrace->GetID(), &asyncParent, &externalParent); } if (frames.empty() && !asyncParent && externalParent.IsInvalid()) return nullptr; if (asyncParent && frames.empty() && (asyncParent->m_description == description || description.isEmpty())) { return asyncParent; } return std::shared_ptr<AsyncStackTrace>(new AsyncStackTrace( description, std::move(frames), asyncParent, externalParent)); } AsyncStackTrace::AsyncStackTrace( const String16& description, std::vector<std::shared_ptr<StackFrame>> frames, std::shared_ptr<AsyncStackTrace> asyncParent, const V8StackTraceId& externalParent) : m_id(0), m_description(description), m_frames(std::move(frames)), m_asyncParent(std::move(asyncParent)), m_externalParent(externalParent) {} std::unique_ptr<protocol::Runtime::StackTrace> AsyncStackTrace::buildInspectorObject(V8Debugger* debugger, int maxAsyncDepth) const { return buildInspectorObjectCommon(debugger, m_frames, m_description, m_asyncParent.lock(), m_externalParent, maxAsyncDepth); } uintptr_t AsyncStackTrace::store(V8Debugger* debugger, std::shared_ptr<AsyncStackTrace> stack) { if (stack->m_id) return stack->m_id; stack->m_id = debugger->storeStackTrace(stack); return stack->m_id; } const String16& AsyncStackTrace::description() const { return m_description; } std::weak_ptr<AsyncStackTrace> AsyncStackTrace::parent() const { return m_asyncParent; } bool AsyncStackTrace::isEmpty() const { return m_frames.empty(); } } // namespace v8_inspector