Commit 8f8d2fe4 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by V8 LUCI CQ

[inspector] Fix `Runtime.setMaxCallStackSizeToCapture`.

This change fixes the implementation of the previously introduced API
`Runtime.setMaxCallStackSizeToCapture` to work correctly and also apply
(consistently) to stack traces captured by V8 when exceptions are
thrown. It does so in a fully backwards compatible manner.

This change thus makes the previous fix for catapult (which landed in
http://crrev.com/c/3347789) effective, and therefore ensures that real
world performance benchmarks aren't affected by the use of the `Runtime`
domain in the catapult test framework.

Note this is basically a reland of crrev.com/c/3361839, but without
touching the stack traces for console messages (which led to the
regressions in crbug/1283516, crbug/1283523, etc.).

Fixed: chromium:1280831
Bug: chromium:1283162, chromium:1278650, chromium:1258599
Bug: chromium:1280803, chromium:1280832, chromium:1280818
Doc: https://bit.ly/v8-cheaper-inspector-stack-traces
Change-Id: I3dcec7b75d76ca267fac8bd6fcb2cda60d5e60dd
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3364086Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#78479}
parent cbdde6fb
...@@ -77,6 +77,8 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector) ...@@ -77,6 +77,8 @@ V8Debugger::V8Debugger(v8::Isolate* isolate, V8InspectorImpl* inspector)
m_continueToLocationBreakpointId(kNoBreakpointId), m_continueToLocationBreakpointId(kNoBreakpointId),
m_maxAsyncCallStacks(kMaxAsyncTaskStacks), m_maxAsyncCallStacks(kMaxAsyncTaskStacks),
m_maxAsyncCallStackDepth(0), m_maxAsyncCallStackDepth(0),
m_maxCallStackSizeToCapture(
V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture),
m_pauseOnExceptionsState(v8::debug::NoBreakOnException) {} m_pauseOnExceptionsState(v8::debug::NoBreakOnException) {}
V8Debugger::~V8Debugger() { V8Debugger::~V8Debugger() {
...@@ -361,7 +363,8 @@ Response V8Debugger::continueToLocation( ...@@ -361,7 +363,8 @@ Response V8Debugger::continueToLocation(
m_continueToLocationTargetCallFrames = targetCallFrames; m_continueToLocationTargetCallFrames = targetCallFrames;
if (m_continueToLocationTargetCallFrames != if (m_continueToLocationTargetCallFrames !=
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) { protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
m_continueToLocationStack = captureStackTrace(true); m_continueToLocationStack = V8StackTraceImpl::capture(
this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
DCHECK(m_continueToLocationStack); DCHECK(m_continueToLocationStack);
} }
continueProgram(targetContextGroupId); continueProgram(targetContextGroupId);
...@@ -377,7 +380,8 @@ bool V8Debugger::shouldContinueToCurrentLocation() { ...@@ -377,7 +380,8 @@ bool V8Debugger::shouldContinueToCurrentLocation() {
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) { protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Any) {
return true; return true;
} }
std::unique_ptr<V8StackTraceImpl> currentStack = captureStackTrace(true); std::unique_ptr<V8StackTraceImpl> currentStack = V8StackTraceImpl::capture(
this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
if (m_continueToLocationTargetCallFrames == if (m_continueToLocationTargetCallFrames ==
protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) { protocol::Debugger::ContinueToLocation::TargetCallFramesEnum::Current) {
return m_continueToLocationStack->isEqualIgnoringTopFrame( return m_continueToLocationStack->isEqualIgnoringTopFrame(
...@@ -817,8 +821,8 @@ v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context, ...@@ -817,8 +821,8 @@ v8::Local<v8::Array> V8Debugger::queryObjects(v8::Local<v8::Context> context,
std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace( std::unique_ptr<V8StackTraceImpl> V8Debugger::createStackTrace(
v8::Local<v8::StackTrace> v8StackTrace) { v8::Local<v8::StackTrace> v8StackTrace) {
return V8StackTraceImpl::create(this, v8StackTrace, return V8StackTraceImpl::create(
V8StackTraceImpl::maxCallStackSizeToCapture); this, v8StackTrace, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
} }
void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
...@@ -843,6 +847,44 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) { ...@@ -843,6 +847,44 @@ void V8Debugger::setAsyncCallStackDepth(V8DebuggerAgentImpl* agent, int depth) {
maxAsyncCallStackDepth ? this : nullptr); maxAsyncCallStackDepth ? this : nullptr);
} }
void V8Debugger::setMaxCallStackSizeToCapture(V8RuntimeAgentImpl* agent,
int size) {
if (size < 0) {
m_maxCallStackSizeToCaptureMap.erase(agent);
} else {
m_maxCallStackSizeToCaptureMap[agent] = size;
}
// The following logic is a bit complicated to decipher because we
// want to retain backwards compatible semantics:
//
// (a) When no `Runtime` domain is enabled, we stick to the default
// maximum call stack size, but don't let V8 collect stack traces
// for uncaught exceptions.
// (b) When `Runtime` is enabled for at least one front-end, we compute
// the maximum of the requested maximum call stack sizes of all the
// front-ends whose `Runtime` domains are enabled (which might be 0),
// and ask V8 to collect stack traces for uncaught exceptions.
//
// The latter allows performance test automation infrastructure to drive
// browser via `Runtime` domain while still minimizing the performance
// overhead of having the inspector attached - see the relevant design
// document https://bit.ly/v8-cheaper-inspector-stack-traces for more
if (m_maxCallStackSizeToCaptureMap.empty()) {
m_maxCallStackSizeToCapture =
V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture;
m_isolate->SetCaptureStackTraceForUncaughtExceptions(false);
} else {
m_maxCallStackSizeToCapture = 0;
for (auto const& pair : m_maxCallStackSizeToCaptureMap) {
if (m_maxCallStackSizeToCapture < pair.second)
m_maxCallStackSizeToCapture = pair.second;
}
m_isolate->SetCaptureStackTraceForUncaughtExceptions(
m_maxCallStackSizeToCapture > 0, m_maxCallStackSizeToCapture);
}
}
std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor( std::shared_ptr<AsyncStackTrace> V8Debugger::stackTraceFor(
int contextGroupId, const V8StackTraceId& id) { int contextGroupId, const V8StackTraceId& id) {
if (debuggerIdFor(contextGroupId).pair() != id.debugger_id) return nullptr; if (debuggerIdFor(contextGroupId).pair() != id.debugger_id) return nullptr;
...@@ -860,8 +902,7 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace( ...@@ -860,8 +902,7 @@ V8StackTraceId V8Debugger::storeCurrentStackTrace(
if (!contextGroupId) return V8StackTraceId(); if (!contextGroupId) return V8StackTraceId();
std::shared_ptr<AsyncStackTrace> asyncStack = std::shared_ptr<AsyncStackTrace> asyncStack =
AsyncStackTrace::capture(this, toString16(description), AsyncStackTrace::capture(this, toString16(description));
V8StackTraceImpl::maxCallStackSizeToCapture);
if (!asyncStack) return V8StackTraceId(); if (!asyncStack) return V8StackTraceId();
uintptr_t id = AsyncStackTrace::store(this, asyncStack); uintptr_t id = AsyncStackTrace::store(this, asyncStack);
...@@ -938,9 +979,8 @@ void V8Debugger::asyncTaskScheduledForStack(const StringView& taskName, ...@@ -938,9 +979,8 @@ void V8Debugger::asyncTaskScheduledForStack(const StringView& taskName,
bool skipTopFrame) { bool skipTopFrame) {
if (!m_maxAsyncCallStackDepth) return; if (!m_maxAsyncCallStackDepth) return;
v8::HandleScope scope(m_isolate); v8::HandleScope scope(m_isolate);
std::shared_ptr<AsyncStackTrace> asyncStack = AsyncStackTrace::capture( std::shared_ptr<AsyncStackTrace> asyncStack =
this, toString16(taskName), V8StackTraceImpl::maxCallStackSizeToCapture, AsyncStackTrace::capture(this, toString16(taskName), skipTopFrame);
skipTopFrame);
if (asyncStack) { if (asyncStack) {
m_asyncTaskStacks[task] = asyncStack; m_asyncTaskStacks[task] = asyncStack;
if (recurring) m_recurringTasks.insert(task); if (recurring) m_recurringTasks.insert(task);
...@@ -1044,20 +1084,17 @@ void V8Debugger::unmuteScriptParsedEvents() { ...@@ -1044,20 +1084,17 @@ void V8Debugger::unmuteScriptParsedEvents() {
std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace( std::unique_ptr<V8StackTraceImpl> V8Debugger::captureStackTrace(
bool fullStack) { bool fullStack) {
if (!m_isolate->InContext()) return nullptr;
v8::HandleScope handles(m_isolate);
int contextGroupId = currentContextGroupId(); int contextGroupId = currentContextGroupId();
if (!contextGroupId) return nullptr; if (!contextGroupId) return nullptr;
int stackSize = 1; int stackSize = 1;
if (fullStack) { if (fullStack) {
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; stackSize = V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture;
} else { } else {
m_inspector->forEachSession( m_inspector->forEachSession(
contextGroupId, [&stackSize](V8InspectorSessionImpl* session) { contextGroupId, [this, &stackSize](V8InspectorSessionImpl* session) {
if (session->runtimeAgent()->enabled()) if (session->runtimeAgent()->enabled())
stackSize = V8StackTraceImpl::maxCallStackSizeToCapture; stackSize = maxCallStackSizeToCapture();
}); });
} }
return V8StackTraceImpl::capture(this, stackSize); return V8StackTraceImpl::capture(this, stackSize);
......
...@@ -27,6 +27,7 @@ class StackFrame; ...@@ -27,6 +27,7 @@ class StackFrame;
class V8Debugger; class V8Debugger;
class V8DebuggerAgentImpl; class V8DebuggerAgentImpl;
class V8InspectorImpl; class V8InspectorImpl;
class V8RuntimeAgentImpl;
class V8StackTraceImpl; class V8StackTraceImpl;
struct V8StackTraceId; struct V8StackTraceId;
...@@ -85,6 +86,9 @@ class V8Debugger : public v8::debug::DebugDelegate, ...@@ -85,6 +86,9 @@ class V8Debugger : public v8::debug::DebugDelegate,
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; }
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int); void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int);
int maxCallStackSizeToCapture() const { return m_maxCallStackSizeToCapture; }
void setMaxCallStackSizeToCapture(V8RuntimeAgentImpl*, int);
std::shared_ptr<AsyncStackTrace> currentAsyncParent(); std::shared_ptr<AsyncStackTrace> currentAsyncParent();
V8StackTraceId currentExternalParent(); V8StackTraceId currentExternalParent();
...@@ -248,6 +252,7 @@ class V8Debugger : public v8::debug::DebugDelegate, ...@@ -248,6 +252,7 @@ class V8Debugger : public v8::debug::DebugDelegate,
size_t m_maxAsyncCallStacks; size_t m_maxAsyncCallStacks;
int m_maxAsyncCallStackDepth; int m_maxAsyncCallStackDepth;
int m_maxCallStackSizeToCapture;
std::vector<void*> m_currentTasks; std::vector<void*> m_currentTasks;
std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent; std::vector<std::shared_ptr<AsyncStackTrace>> m_currentAsyncParent;
...@@ -259,6 +264,7 @@ class V8Debugger : public v8::debug::DebugDelegate, ...@@ -259,6 +264,7 @@ class V8Debugger : public v8::debug::DebugDelegate,
std::list<std::shared_ptr<AsyncStackTrace>> m_allAsyncStacks; std::list<std::shared_ptr<AsyncStackTrace>> m_allAsyncStacks;
std::unordered_map<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap; std::unordered_map<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap;
std::unordered_map<V8RuntimeAgentImpl*, int> m_maxCallStackSizeToCaptureMap;
void* m_taskWithScheduledBreak = nullptr; void* m_taskWithScheduledBreak = nullptr;
// If any of the following three is true, we schedule pause on next JS // If any of the following three is true, we schedule pause on next JS
......
...@@ -62,7 +62,6 @@ V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate, ...@@ -62,7 +62,6 @@ V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate,
: m_isolate(isolate), : m_isolate(isolate),
m_client(client), m_client(client),
m_debugger(new V8Debugger(isolate, this)), m_debugger(new V8Debugger(isolate, this)),
m_capturingStackTracesCount(0),
m_lastExceptionId(0), m_lastExceptionId(0),
m_lastContextId(0), m_lastContextId(0),
m_isolateId(generateUniqueId()) { m_isolateId(generateUniqueId()) {
...@@ -112,19 +111,6 @@ v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript( ...@@ -112,19 +111,6 @@ v8::MaybeLocal<v8::Script> V8InspectorImpl::compileScript(
v8::ScriptCompiler::kNoCompileOptions); v8::ScriptCompiler::kNoCompileOptions);
} }
void V8InspectorImpl::enableStackCapturingIfNeeded() {
if (!m_capturingStackTracesCount)
V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
true);
++m_capturingStackTracesCount;
}
void V8InspectorImpl::disableStackCapturingIfNeeded() {
if (!(--m_capturingStackTracesCount))
V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate,
false);
}
void V8InspectorImpl::muteExceptions(int contextGroupId) { void V8InspectorImpl::muteExceptions(int contextGroupId) {
m_muteExceptionsMap[contextGroupId]++; m_muteExceptionsMap[contextGroupId]++;
} }
......
...@@ -115,8 +115,6 @@ class V8InspectorImpl : public V8Inspector { ...@@ -115,8 +115,6 @@ class V8InspectorImpl : public V8Inspector {
v8::Local<v8::Name> key, v8::Local<v8::Value> value) override; v8::Local<v8::Name> key, v8::Local<v8::Value> value) override;
unsigned nextExceptionId() { return ++m_lastExceptionId; } unsigned nextExceptionId() { return ++m_lastExceptionId; }
void enableStackCapturingIfNeeded();
void disableStackCapturingIfNeeded();
void muteExceptions(int contextGroupId); void muteExceptions(int contextGroupId);
void unmuteExceptions(int contextGroupId); void unmuteExceptions(int contextGroupId);
V8ConsoleMessageStorage* ensureConsoleMessageStorage(int contextGroupId); V8ConsoleMessageStorage* ensureConsoleMessageStorage(int contextGroupId);
...@@ -160,7 +158,6 @@ class V8InspectorImpl : public V8Inspector { ...@@ -160,7 +158,6 @@ class V8InspectorImpl : public V8Inspector {
v8::Global<v8::Context> m_regexContext; v8::Global<v8::Context> m_regexContext;
v8::Global<v8::Context> m_exceptionMetaDataContext; v8::Global<v8::Context> m_exceptionMetaDataContext;
v8::Global<v8::debug::EphemeronTable> m_exceptionMetaData; v8::Global<v8::debug::EphemeronTable> m_exceptionMetaData;
int m_capturingStackTracesCount;
unsigned m_lastExceptionId; unsigned m_lastExceptionId;
int m_lastContextId; int m_lastContextId;
int m_lastSessionId = 0; int m_lastSessionId = 0;
......
...@@ -145,15 +145,14 @@ std::unique_ptr<protocol::Profiler::Profile> createCPUProfile( ...@@ -145,15 +145,14 @@ std::unique_ptr<protocol::Profiler::Profile> createCPUProfile(
std::unique_ptr<protocol::Debugger::Location> currentDebugLocation( std::unique_ptr<protocol::Debugger::Location> currentDebugLocation(
V8InspectorImpl* inspector) { V8InspectorImpl* inspector) {
std::unique_ptr<V8StackTraceImpl> callStack = auto stackTrace = V8StackTraceImpl::capture(inspector->debugger(), 1);
inspector->debugger()->captureStackTrace(false /* fullStack */); CHECK(stackTrace);
auto location = CHECK(!stackTrace->isEmpty());
protocol::Debugger::Location::create() return protocol::Debugger::Location::create()
.setScriptId(String16::fromInteger(callStack->topScriptId())) .setScriptId(String16::fromInteger(stackTrace->topScriptId()))
.setLineNumber(callStack->topLineNumber()) .setLineNumber(stackTrace->topLineNumber())
.build(); .setColumnNumber(stackTrace->topColumnNumber())
location->setColumnNumber(callStack->topColumnNumber()); .build();
return location;
} }
volatile int s_lastProfileId = 0; volatile int s_lastProfileId = 0;
...@@ -213,10 +212,9 @@ void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) { ...@@ -213,10 +212,9 @@ void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) {
std::unique_ptr<protocol::Profiler::Profile> profile = std::unique_ptr<protocol::Profiler::Profile> profile =
stopProfiling(id, true); stopProfiling(id, true);
if (!profile) return; if (!profile) return;
std::unique_ptr<protocol::Debugger::Location> location = m_frontend.consoleProfileFinished(
currentDebugLocation(m_session->inspector()); id, currentDebugLocation(m_session->inspector()), std::move(profile),
m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile), resolvedTitle);
resolvedTitle);
} }
Response V8ProfilerAgentImpl::enable() { Response V8ProfilerAgentImpl::enable() {
......
...@@ -57,6 +57,7 @@ namespace v8_inspector { ...@@ -57,6 +57,7 @@ namespace v8_inspector {
namespace V8RuntimeAgentImplState { namespace V8RuntimeAgentImplState {
static const char customObjectFormatterEnabled[] = static const char customObjectFormatterEnabled[] =
"customObjectFormatterEnabled"; "customObjectFormatterEnabled";
static const char maxCallStackSizeToCapture[] = "maxCallStackSizeToCapture";
static const char runtimeEnabled[] = "runtimeEnabled"; static const char runtimeEnabled[] = "runtimeEnabled";
static const char bindings[] = "bindings"; static const char bindings[] = "bindings";
static const char globalBindingsKey[] = ""; static const char globalBindingsKey[] = "";
...@@ -503,7 +504,9 @@ Response V8RuntimeAgentImpl::setMaxCallStackSizeToCapture(int size) { ...@@ -503,7 +504,9 @@ Response V8RuntimeAgentImpl::setMaxCallStackSizeToCapture(int size) {
TRACE_DISABLED_BY_DEFAULT("v8.inspector"), TRACE_DISABLED_BY_DEFAULT("v8.inspector"),
"V8RuntimeAgentImpl::setMaxCallStackSizeToCapture", this, "V8RuntimeAgentImpl::setMaxCallStackSizeToCapture", this,
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "size", size); TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "size", size);
V8StackTraceImpl::maxCallStackSizeToCapture = size; if (!m_enabled) return Response::ServerError("Runtime agent is not enabled");
m_state->setInteger(V8RuntimeAgentImplState::maxCallStackSizeToCapture, size);
m_inspector->debugger()->setMaxCallStackSizeToCapture(this, size);
return Response::Success(); return Response::Success();
} }
...@@ -843,6 +846,11 @@ void V8RuntimeAgentImpl::restore() { ...@@ -843,6 +846,11 @@ void V8RuntimeAgentImpl::restore() {
V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) V8RuntimeAgentImplState::customObjectFormatterEnabled, false))
m_session->setCustomObjectFormatterEnabled(true); m_session->setCustomObjectFormatterEnabled(true);
int size;
if (m_state->getInteger(V8RuntimeAgentImplState::maxCallStackSizeToCapture,
&size))
m_inspector->debugger()->setMaxCallStackSizeToCapture(this, size);
m_inspector->forEachContext( m_inspector->forEachContext(
m_session->contextGroupId(), m_session->contextGroupId(),
[this](InspectedContext* context) { addBindings(context); }); [this](InspectedContext* context) { addBindings(context); });
...@@ -857,7 +865,8 @@ Response V8RuntimeAgentImpl::enable() { ...@@ -857,7 +865,8 @@ Response V8RuntimeAgentImpl::enable() {
m_session->contextGroupId()); m_session->contextGroupId());
m_enabled = true; m_enabled = true;
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true);
m_inspector->enableStackCapturingIfNeeded(); m_inspector->debugger()->setMaxCallStackSizeToCapture(
this, V8StackTraceImpl::kDefaultMaxCallStackSizeToCapture);
m_session->reportAllContexts(this); m_session->reportAllContexts(this);
V8ConsoleMessageStorage* storage = V8ConsoleMessageStorage* storage =
m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId());
...@@ -875,7 +884,7 @@ Response V8RuntimeAgentImpl::disable() { ...@@ -875,7 +884,7 @@ Response V8RuntimeAgentImpl::disable() {
m_enabled = false; m_enabled = false;
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false);
m_state->remove(V8RuntimeAgentImplState::bindings); m_state->remove(V8RuntimeAgentImplState::bindings);
m_inspector->disableStackCapturingIfNeeded(); m_inspector->debugger()->setMaxCallStackSizeToCapture(this, -1);
m_session->setCustomObjectFormatterEnabled(false); m_session->setCustomObjectFormatterEnabled(false);
reset(); reset();
m_inspector->client()->endEnsureAllContextsInGroup( m_inspector->client()->endEnsureAllContextsInGroup(
......
...@@ -22,9 +22,6 @@ using v8_crdtp::json::ConvertCBORToJSON; ...@@ -22,9 +22,6 @@ using v8_crdtp::json::ConvertCBORToJSON;
using v8_crdtp::json::ConvertJSONToCBOR; using v8_crdtp::json::ConvertJSONToCBOR;
namespace v8_inspector { namespace v8_inspector {
int V8StackTraceImpl::maxCallStackSizeToCapture = 200;
namespace { namespace {
static const char kId[] = "id"; static const char kId[] = "id";
...@@ -223,13 +220,6 @@ bool StackFrame::isEqual(StackFrame* frame) const { ...@@ -223,13 +220,6 @@ bool StackFrame::isEqual(StackFrame* frame) const {
m_columnNumber == frame->m_columnNumber; m_columnNumber == frame->m_columnNumber;
} }
// static
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(
v8::Isolate* isolate, bool capture) {
isolate->SetCaptureStackTraceForUncaughtExceptions(
capture, V8StackTraceImpl::maxCallStackSizeToCapture);
}
// static // static
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create(
V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace, V8Debugger* debugger, v8::Local<v8::StackTrace> v8StackTrace,
...@@ -402,10 +392,10 @@ StackFrame* V8StackTraceImpl::StackFrameIterator::frame() { ...@@ -402,10 +392,10 @@ StackFrame* V8StackTraceImpl::StackFrameIterator::frame() {
// static // static
std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture( std::shared_ptr<AsyncStackTrace> AsyncStackTrace::capture(
V8Debugger* debugger, const String16& description, int maxStackSize, V8Debugger* debugger, const String16& description, bool skipTopFrame) {
bool skipTopFrame) {
DCHECK(debugger); DCHECK(debugger);
int maxStackSize = debugger->maxCallStackSizeToCapture();
TRACE_EVENT1( TRACE_EVENT1(
TRACE_DISABLED_BY_DEFAULT("v8.inspector") "," TRACE_DISABLED_BY_DEFAULT( TRACE_DISABLED_BY_DEFAULT("v8.inspector") "," TRACE_DISABLED_BY_DEFAULT(
"v8.stack_trace"), "v8.stack_trace"),
......
...@@ -51,9 +51,8 @@ class StackFrame { ...@@ -51,9 +51,8 @@ class StackFrame {
class V8StackTraceImpl : public V8StackTrace { class V8StackTraceImpl : public V8StackTrace {
public: public:
static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*, static constexpr int kDefaultMaxCallStackSizeToCapture = 200;
bool capture);
static int maxCallStackSizeToCapture;
static std::unique_ptr<V8StackTraceImpl> create(V8Debugger*, static std::unique_ptr<V8StackTraceImpl> create(V8Debugger*,
v8::Local<v8::StackTrace>, v8::Local<v8::StackTrace>,
int maxStackSize); int maxStackSize);
...@@ -117,7 +116,6 @@ class AsyncStackTrace { ...@@ -117,7 +116,6 @@ class AsyncStackTrace {
AsyncStackTrace& operator=(const AsyncStackTrace&) = delete; AsyncStackTrace& operator=(const AsyncStackTrace&) = delete;
static std::shared_ptr<AsyncStackTrace> capture(V8Debugger*, static std::shared_ptr<AsyncStackTrace> capture(V8Debugger*,
const String16& description, const String16& description,
int maxStackSize,
bool skipTopFrame = false); bool skipTopFrame = false);
static uintptr_t store(V8Debugger* debugger, static uintptr_t store(V8Debugger* debugger,
std::shared_ptr<AsyncStackTrace> stack); std::shared_ptr<AsyncStackTrace> stack);
......
Checks that console messages before Runtime.enable include a single stack frame
Running test: testEnable
{
args : [
[0] : {
type : string
value : Error on toplevel
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 8
functionName :
lineNumber : 10
scriptId : <scriptId>
url : test.js
}
]
}
timestamp : <timestamp>
type : error
}
{
args : [
[0] : {
type : string
value : Hello from foo!
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : foo
lineNumber : 2
scriptId : <scriptId>
url : test.js
}
]
}
timestamp : <timestamp>
type : log
}
{
args : [
[0] : {
type : string
value : Hello from bar!
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : bar
lineNumber : 6
scriptId : <scriptId>
url : test.js
}
]
}
timestamp : <timestamp>
type : trace
}
{
args : [
[0] : {
type : string
value : Hello from foo!
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : foo
lineNumber : 2
scriptId : <scriptId>
url : test.js
}
]
}
timestamp : <timestamp>
type : log
}
Running test: testEnableAfterDiscard
// Copyright 2022 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.
const {session, contextGroup, Protocol} = InspectorTest.start(
'Checks that console messages before Runtime.enable include a single stack frame');
contextGroup.addScript(`
function foo() {
console.log("Hello from foo!");
}
function bar() {
console.trace("Hello from bar!");
foo();
}
console.error('Error on toplevel');
foo();
bar();
//# sourceURL=test.js`);
Protocol.Runtime.onConsoleAPICalled(
({params}) => InspectorTest.logMessage(params));
InspectorTest.runAsyncTestSuite([
async function testEnable() {
await Protocol.Runtime.enable();
await Protocol.Runtime.disable();
},
async function testEnableAfterDiscard() {
await Protocol.Runtime.discardConsoleEntries();
await Protocol.Runtime.enable();
await Protocol.Runtime.disable();
}
]);
Checks Runtime.setMaxCallStackSizeToCapture. Checks Runtime.setMaxCallStackSizeToCapture.
Running test: testBeforeEnable
{
code : -32000
message : Runtime agent is not enabled
}
Running test: testNegativeSize
{
code : -32000
message : maxCallStackSizeToCapture should be non-negative
}
Running test: testConsoleLogBeforeEnable
{
args : [
[0] : {
type : string
value : Log message.
}
]
executionContextId : <executionContextId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : testConsoleLog
lineNumber : 2
scriptId : <scriptId>
url : test.js
}
]
}
timestamp : <timestamp>
type : log
}
Running test: testConsoleTrace
Test with max size 0. Test with max size 0.
{ {
args : [ args : [
...@@ -23,9 +61,9 @@ Test with max size 1. ...@@ -23,9 +61,9 @@ Test with max size 1.
stackTrace : { stackTrace : {
callFrames : [ callFrames : [
[0] : { [0] : {
columnNumber : 10 columnNumber : 12
functionName : bar functionName : bar
lineNumber : 2 lineNumber : 7
scriptId : <scriptId> scriptId : <scriptId>
url : test.js url : test.js
} }
...@@ -33,9 +71,9 @@ Test with max size 1. ...@@ -33,9 +71,9 @@ Test with max size 1.
parent : { parent : {
callFrames : [ callFrames : [
[0] : { [0] : {
columnNumber : 2 columnNumber : 4
functionName : test functionName : executor
lineNumber : 10 lineNumber : 16
scriptId : <scriptId> scriptId : <scriptId>
url : test.js url : test.js
} }
...@@ -58,16 +96,16 @@ Test with max size 2. ...@@ -58,16 +96,16 @@ Test with max size 2.
stackTrace : { stackTrace : {
callFrames : [ callFrames : [
[0] : { [0] : {
columnNumber : 10 columnNumber : 12
functionName : bar functionName : bar
lineNumber : 2 lineNumber : 7
scriptId : <scriptId> scriptId : <scriptId>
url : test.js url : test.js
} }
[1] : { [1] : {
columnNumber : 2 columnNumber : 4
functionName : foo functionName : foo
lineNumber : 6 lineNumber : 12
scriptId : <scriptId> scriptId : <scriptId>
url : test.js url : test.js
} }
...@@ -75,18 +113,18 @@ Test with max size 2. ...@@ -75,18 +113,18 @@ Test with max size 2.
parent : { parent : {
callFrames : [ callFrames : [
[0] : { [0] : {
columnNumber : 2 columnNumber : 4
functionName : test functionName : executor
lineNumber : 10 lineNumber : 16
scriptId : <scriptId> scriptId : <scriptId>
url : test.js url : test.js
} }
[1] : { [1] : {
columnNumber : 0 columnNumber : 9
functionName : functionName : testConsoleTrace
lineNumber : 0 lineNumber : 15
scriptId : <scriptId> scriptId : <scriptId>
url : expr.js url : test.js
} }
] ]
description : setTimeout description : setTimeout
...@@ -95,3 +133,79 @@ Test with max size 2. ...@@ -95,3 +133,79 @@ Test with max size 2.
timestamp : <timestamp> timestamp : <timestamp>
type : trace type : trace
} }
Running test: testException
Test with max size 0.
{
columnNumber : 4
exception : {
className : Error
description : Error at bar (test.js:23:11) at foo (test.js:27:5) at testThrow (test.js:30:3) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
}
exceptionId : <exceptionId>
lineNumber : 22
scriptId : <scriptId>
text : Uncaught
}
Test with max size 1.
{
columnNumber : 4
exception : {
className : Error
description : Error at bar (test.js:23:11) at foo (test.js:27:5) at testThrow (test.js:30:3) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
}
exceptionId : <exceptionId>
lineNumber : 22
scriptId : <scriptId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : bar
lineNumber : 22
scriptId : <scriptId>
url : test.js
}
]
}
text : Uncaught
}
Test with max size 2.
{
columnNumber : 4
exception : {
className : Error
description : Error at bar (test.js:23:11) at foo (test.js:27:5) at testThrow (test.js:30:3) at <anonymous>:1:1
objectId : <objectId>
subtype : error
type : object
}
exceptionId : <exceptionId>
lineNumber : 22
scriptId : <scriptId>
stackTrace : {
callFrames : [
[0] : {
columnNumber : 10
functionName : bar
lineNumber : 22
scriptId : <scriptId>
url : test.js
}
[1] : {
columnNumber : 4
functionName : foo
lineNumber : 26
scriptId : <scriptId>
url : test.js
}
]
}
text : Uncaught
}
...@@ -4,34 +4,98 @@ ...@@ -4,34 +4,98 @@
let {session, contextGroup, Protocol} = InspectorTest.start('Checks Runtime.setMaxCallStackSizeToCapture.'); let {session, contextGroup, Protocol} = InspectorTest.start('Checks Runtime.setMaxCallStackSizeToCapture.');
Protocol.Runtime.enable();
Protocol.Runtime.onConsoleAPICalled( Protocol.Runtime.onConsoleAPICalled(
message => InspectorTest.logMessage(message.params)); ({params}) => InspectorTest.logMessage(params));
contextGroup.addScript(` contextGroup.addScript(`
function bar() { function testConsoleLog() {
console.trace("Nested call."); console.log("Log message.");
} }
function foo() { function testConsoleTrace() {
bar(); function bar(callback) {
console.trace("Nested call.");
callback();
}
function foo(callback) {
bar(callback);
}
return new Promise(function executor(resolve) {
setTimeout(foo.bind(undefined, resolve), 0);
});
} }
async function test() { function testThrow() {
setTimeout(foo, 0); function bar() {
throw new Error();
}
function foo() {
bar();
}
foo();
} }
//# sourceURL=test.js`); //# sourceURL=test.js`);
Protocol.Runtime.setAsyncCallStackDepth({maxDepth: 10}); InspectorTest.runAsyncTestSuite([
(async function test() { async function testBeforeEnable() {
await Protocol.Runtime.setMaxCallStackSizeToCapture({size: 0}); const {error} =
InspectorTest.log('Test with max size 0.'); await Protocol.Runtime.setMaxCallStackSizeToCapture({size: 0});
await Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr.js'}); InspectorTest.logMessage(error);
await Protocol.Runtime.setMaxCallStackSizeToCapture({size: 1}); },
InspectorTest.log('Test with max size 1.');
await Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr.js'}); async function testNegativeSize() {
await Protocol.Runtime.setMaxCallStackSizeToCapture({size: 2}); await Protocol.Runtime.enable();
InspectorTest.log('Test with max size 2.'); const {error} =
await Protocol.Runtime.evaluate({ expression: 'test()//# sourceURL=expr.js'}); await Protocol.Runtime.setMaxCallStackSizeToCapture({size: -42});
InspectorTest.completeTest(); InspectorTest.logMessage(error);
})(); await Protocol.Runtime.disable();
},
async function testConsoleLogBeforeEnable() {
await Protocol.Runtime.evaluate({expression: 'testConsoleLog()'});
await Protocol.Runtime.enable();
await Promise.all([
Protocol.Runtime.discardConsoleEntries(),
Protocol.Runtime.disable(),
]);
},
async function testConsoleTrace() {
await Promise.all([
Protocol.Runtime.enable(),
Protocol.Runtime.setAsyncCallStackDepth({maxDepth: 10}),
]);
for (let size = 0; size <= 2; ++size) {
await Protocol.Runtime.setMaxCallStackSizeToCapture({size});
InspectorTest.log(`Test with max size ${size}.`);
await Protocol.Runtime.evaluate(
{expression: 'testConsoleTrace()', awaitPromise: true});
}
await Promise.all([
Protocol.Runtime.discardConsoleEntries(),
Protocol.Runtime.disable(),
]);
},
async function testException() {
await Promise.all([
Protocol.Runtime.enable(),
Protocol.Runtime.setAsyncCallStackDepth({maxDepth: 0}),
]);
for (let size = 0; size <= 2; ++size) {
await Protocol.Runtime.setMaxCallStackSizeToCapture({size});
InspectorTest.log(`Test with max size ${size}.`);
const {result: {exceptionDetails}} =
await Protocol.Runtime.evaluate({expression: 'testThrow()'});
InspectorTest.logMessage(exceptionDetails);
}
await Promise.all([
Protocol.Runtime.discardConsoleEntries(),
Protocol.Runtime.disable(),
]);
}
])
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment