Commit b88c5a8d authored by Kim-Anh Tran's avatar Kim-Anh Tran Committed by V8 LUCI CQ

[debug] Handle instrumentation breakpoints separate from regular breaks

This changes the way how we are handling instrumentation breakpoints.

Motivation:
with instrumentation breakpoints, we need a way to break
on (conditional) breakpoints that were just set by the client on
the instrumentation pause.

How:
We want to first find out if we have an instrumentation break, and
trigger a pause. For this to work, we need to distinguish between
regular and instrumentation breakpoints in the debugger back-end.

On resume, we want to check if we have hit any breakpoints (may
now contain new breakpoints due to the client setting new breakpoints
at the previous instrumentation pause) and trigger a separate pause
for them.

Fixed: chromium:1292930
Change-Id: Idaadd276c44c693f856c4b08c7a72ea67271f420
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3442676Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Kim-Anh Tran <kimanh@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79053}
parent 7c602011
......@@ -531,12 +531,12 @@ bool Script::SetBreakpoint(Local<String> condition, Location* location,
return true;
}
bool Script::SetBreakpointOnScriptEntry(BreakpointId* id) const {
bool Script::SetInstrumentationBreakpoint(BreakpointId* id) const {
i::Handle<i::Script> script = Utils::OpenHandle(this);
i::Isolate* isolate = script->GetIsolate();
#if V8_ENABLE_WEBASSEMBLY
if (script->type() == i::Script::TYPE_WASM) {
isolate->debug()->SetOnEntryBreakpointForWasmScript(script, id);
isolate->debug()->SetInstrumentationBreakpointForWasmScript(script, id);
return true;
}
#endif // V8_ENABLE_WEBASSEMBLY
......@@ -544,7 +544,8 @@ bool Script::SetBreakpointOnScriptEntry(BreakpointId* id) const {
for (i::SharedFunctionInfo sfi = it.Next(); !sfi.is_null(); sfi = it.Next()) {
if (sfi.is_toplevel()) {
return isolate->debug()->SetBreakpointForFunction(
handle(sfi, isolate), isolate->factory()->empty_string(), id);
handle(sfi, isolate), isolate->factory()->empty_string(), id,
internal::Debug::kInstrumentation);
}
}
return false;
......
......@@ -194,7 +194,7 @@ class V8_EXPORT_PRIVATE Script {
bool IsWasm() const;
void RemoveWasmBreakpoint(BreakpointId id);
#endif // V8_ENABLE_WEBASSEMBLY
bool SetBreakpointOnScriptEntry(BreakpointId* id) const;
bool SetInstrumentationBreakpoint(BreakpointId* id) const;
};
#if V8_ENABLE_WEBASSEMBLY
......@@ -239,6 +239,9 @@ class DebugDelegate {
v8::Local<v8::Context> paused_context,
const std::vector<debug::BreakpointId>& inspector_break_points_hit,
base::EnumSet<BreakReason> break_reasons = {}) {}
virtual void BreakOnInstrumentation(
v8::Local<v8::Context> paused_context,
const debug::BreakpointId instrumentationId) {}
virtual void ExceptionThrown(v8::Local<v8::Context> paused_context,
v8::Local<v8::Value> exception,
v8::Local<v8::Value> promise, bool is_uncaught,
......
This diff is collapsed.
......@@ -218,6 +218,7 @@ class V8_EXPORT_PRIVATE Debug {
// Debug event triggers.
void OnDebugBreak(Handle<FixedArray> break_points_hit, StepAction stepAction,
debug::BreakReasons break_reasons = {});
void OnInstrumentationBreak();
base::Optional<Object> OnThrow(Handle<Object> exception)
V8_WARN_UNUSED_RESULT;
......@@ -236,6 +237,7 @@ class V8_EXPORT_PRIVATE Debug {
Handle<FixedArray> GetLoadedScripts();
// Break point handling.
enum BreakPointKind { kRegular, kInstrumentation };
bool SetBreakpoint(Handle<SharedFunctionInfo> shared,
Handle<BreakPoint> break_point, int* source_position);
void ClearBreakPoint(Handle<BreakPoint> break_point);
......@@ -247,10 +249,12 @@ class V8_EXPORT_PRIVATE Debug {
bool SetBreakPointForScript(Handle<Script> script, Handle<String> condition,
int* source_position, int* id);
bool SetBreakpointForFunction(Handle<SharedFunctionInfo> shared,
Handle<String> condition, int* id);
Handle<String> condition, int* id,
BreakPointKind kind = kRegular);
void RemoveBreakpoint(int id);
#if V8_ENABLE_WEBASSEMBLY
void SetOnEntryBreakpointForWasmScript(Handle<Script> script, int* id);
void SetInstrumentationBreakpointForWasmScript(Handle<Script> script,
int* id);
void RemoveBreakpointForWasmScript(Handle<Script> script, int id);
void RecordWasmScriptWithBreakpoints(Handle<Script> script);
......@@ -258,9 +262,11 @@ class V8_EXPORT_PRIVATE Debug {
// Find breakpoints from the debug info and the break location and check
// whether they are hit. Return an empty handle if not, or a FixedArray with
// hit BreakPoint objects.
// hit BreakPoint objects. has_break_points is set to true if position has
// any non-instrumentation breakpoint.
MaybeHandle<FixedArray> GetHitBreakPoints(Handle<DebugInfo> debug_info,
int position);
int position,
bool* has_break_points);
// Stepping handling.
void PrepareStep(StepAction step_action);
......@@ -395,6 +401,9 @@ class V8_EXPORT_PRIVATE Debug {
// source position for break points.
static const int kBreakAtEntryPosition = 0;
// Use -1 to encode instrumentation breakpoints.
static const int kInstrumentationId = -1;
void RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info);
static char* Iterate(RootVisitor* v, char* thread_storage);
......@@ -448,9 +457,15 @@ class V8_EXPORT_PRIVATE Debug {
bool IsFrameBlackboxed(JavaScriptFrame* frame);
void ActivateStepOut(StackFrame* frame);
bool IsBreakOnInstrumentation(Handle<DebugInfo> debug_info,
const BreakLocation& location);
MaybeHandle<FixedArray> CheckBreakPoints(Handle<DebugInfo> debug_info,
BreakLocation* location,
bool* has_break_points = nullptr);
bool* has_break_points);
MaybeHandle<FixedArray> CheckBreakPointsForLocations(
Handle<DebugInfo> debug_info, std::vector<BreakLocation>& break_locations,
bool* has_break_points);
MaybeHandle<FixedArray> GetHitBreakpointsAtCurrentStatement(
JavaScriptFrame* frame, bool* hasBreakpoints);
......
......@@ -440,7 +440,6 @@ Response V8DebuggerAgentImpl::disable() {
for (const auto& it : m_debuggerBreakpointIdToBreakpointId) {
v8::debug::RemoveBreakpoint(m_isolate, it.first);
}
m_breakpointsOnScriptRun.clear();
m_breakpointIdToDebuggerBreakpointIds.clear();
m_debuggerBreakpointIdToBreakpointId.clear();
m_debugger->setAsyncCallStackDepth(this, 0);
......@@ -746,7 +745,6 @@ void V8DebuggerAgentImpl::removeBreakpointImpl(
#endif // V8_ENABLE_WEBASSEMBLY
v8::debug::RemoveBreakpoint(m_isolate, id);
m_debuggerBreakpointIdToBreakpointId.erase(id);
m_breakpointsOnScriptRun.erase(id);
}
m_breakpointIdToDebuggerBreakpointIds.erase(breakpointId);
}
......@@ -1735,19 +1733,53 @@ void V8DebuggerAgentImpl::setScriptInstrumentationBreakpointIfNeeded(
if (!breakpoints->get(breakpointId)) return;
}
v8::debug::BreakpointId debuggerBreakpointId;
if (!scriptRef->setBreakpointOnRun(&debuggerBreakpointId)) return;
if (!scriptRef->setInstrumentationBreakpoint(&debuggerBreakpointId)) return;
std::unique_ptr<protocol::DictionaryValue> data =
protocol::DictionaryValue::create();
data->setString("url", scriptRef->sourceURL());
data->setString("scriptId", scriptRef->scriptId());
if (!sourceMapURL.isEmpty()) data->setString("sourceMapURL", sourceMapURL);
m_breakpointsOnScriptRun[debuggerBreakpointId] = std::move(data);
m_debuggerBreakpointIdToBreakpointId[debuggerBreakpointId] = breakpointId;
m_breakpointIdToDebuggerBreakpointIds[breakpointId].push_back(
debuggerBreakpointId);
}
void V8DebuggerAgentImpl::didPauseOnInstrumentation(
v8::debug::BreakpointId instrumentationId) {
String16 breakReason = protocol::Debugger::Paused::ReasonEnum::Other;
std::unique_ptr<protocol::DictionaryValue> breakAuxData;
std::unique_ptr<Array<CallFrame>> protocolCallFrames;
Response response = currentCallFrames(&protocolCallFrames);
if (!response.IsSuccess())
protocolCallFrames = std::make_unique<Array<CallFrame>>();
if (m_debuggerBreakpointIdToBreakpointId.find(instrumentationId) !=
m_debuggerBreakpointIdToBreakpointId.end()) {
DCHECK_GT(protocolCallFrames->size(), 0);
if (protocolCallFrames->size() > 0) {
breakReason = protocol::Debugger::Paused::ReasonEnum::Instrumentation;
const String16 scriptId =
protocolCallFrames->at(0)->getLocation()->getScriptId();
DCHECK_NE(m_scripts.find(scriptId), m_scripts.end());
const auto& script = m_scripts[scriptId];
breakAuxData = protocol::DictionaryValue::create();
breakAuxData->setString("scriptId", script->scriptId());
breakAuxData->setString("url", script->sourceURL());
if (!script->sourceMappingURL().isEmpty()) {
breakAuxData->setString("sourceMapURL", (script->sourceMappingURL()));
}
}
}
m_frontend.paused(std::move(protocolCallFrames), breakReason,
std::move(breakAuxData),
std::make_unique<Array<String16>>(),
currentAsyncStackTrace(), currentExternalStackTrace());
}
void V8DebuggerAgentImpl::didPause(
int contextId, v8::Local<v8::Value> exception,
const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
......@@ -1788,25 +1820,8 @@ void V8DebuggerAgentImpl::didPause(
}
auto hitBreakpointIds = std::make_unique<Array<String16>>();
bool hitInstrumentationBreakpoint = false;
bool hitRegularBreakpoint = false;
for (const auto& id : hitBreakpoints) {
auto it = m_breakpointsOnScriptRun.find(id);
if (it != m_breakpointsOnScriptRun.end()) {
if (!hitInstrumentationBreakpoint) {
// We may hit several instrumentation breakpoints: 1. they are
// kept around, and 2. each session may set their own.
// Only report one.
// TODO(kimanh): This will not be needed anymore if we
// make sure that we can only hit an instrumentation
// breakpoint once. This workaround is currently for wasm.
hitInstrumentationBreakpoint = true;
hitReasons.push_back(std::make_pair(
protocol::Debugger::Paused::ReasonEnum::Instrumentation,
std::move(it->second)));
}
continue;
}
auto breakpointIterator = m_debuggerBreakpointIdToBreakpointId.find(id);
if (breakpointIterator == m_debuggerBreakpointIdToBreakpointId.end()) {
continue;
......@@ -1954,17 +1969,6 @@ void V8DebuggerAgentImpl::ScriptCollected(const V8DebuggerScript* script) {
}
}
std::vector<v8::debug::BreakpointId>
V8DebuggerAgentImpl::instrumentationBreakpointIdsMatching(
const std::vector<v8::debug::BreakpointId>& ids) {
std::vector<v8::debug::BreakpointId> instrumentationBreakpointIds;
for (const v8::debug::BreakpointId& id : ids) {
if (m_breakpointsOnScriptRun.count(id) > 0)
instrumentationBreakpointIds.push_back(id);
}
return instrumentationBreakpointIds;
}
Response V8DebuggerAgentImpl::processSkipList(
protocol::Array<protocol::Debugger::LocationRange>* skipList) {
std::unordered_map<String16, std::vector<std::pair<int, int>>> skipListInit;
......
......@@ -149,6 +149,8 @@ class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
void reset();
// Interface for V8InspectorImpl
void didPauseOnInstrumentation(v8::debug::BreakpointId instrumentationId);
void didPause(int contextId, v8::Local<v8::Value> exception,
const std::vector<v8::debug::BreakpointId>& hitBreakpoints,
v8::debug::ExceptionType exceptionType, bool isUncaught,
......
......@@ -255,9 +255,9 @@ class ActualScript : public V8DebuggerScript {
id);
}
bool setBreakpointOnRun(int* id) const override {
bool setInstrumentationBreakpoint(int* id) const override {
v8::HandleScope scope(m_isolate);
return script()->SetBreakpointOnScriptEntry(id);
return script()->SetInstrumentationBreakpoint(id);
}
const String16& hash() const override {
......
......@@ -97,7 +97,7 @@ class V8DebuggerScript {
virtual bool setBreakpoint(const String16& condition,
v8::debug::Location* location, int* id) const = 0;
virtual void MakeWeak() = 0;
virtual bool setBreakpointOnRun(int* id) const = 0;
virtual bool setInstrumentationBreakpoint(int* id) const = 0;
#if V8_ENABLE_WEBASSEMBLY
virtual v8::Maybe<v8::MemorySpan<const uint8_t>> wasmBytecode() const = 0;
......
......@@ -496,6 +496,42 @@ void V8Debugger::ScriptCompiled(v8::Local<v8::debug::Script> script,
});
}
void V8Debugger::BreakOnInstrumentation(
v8::Local<v8::Context> pausedContext,
v8::debug::BreakpointId instrumentationId) {
// Don't allow nested breaks.
if (isPaused()) return;
int contextGroupId = m_inspector->contextGroupId(pausedContext);
bool hasAgents = false;
m_inspector->forEachSession(
contextGroupId, [&hasAgents](V8InspectorSessionImpl* session) {
if (session->debuggerAgent()->acceptsPause(false /* isOOMBreak */))
hasAgents = true;
});
if (!hasAgents) return;
m_pausedContextGroupId = contextGroupId;
m_inspector->forEachSession(
contextGroupId, [instrumentationId](V8InspectorSessionImpl* session) {
if (session->debuggerAgent()->acceptsPause(false /* isOOMBreak */)) {
session->debuggerAgent()->didPauseOnInstrumentation(
instrumentationId);
}
});
{
v8::Context::Scope scope(pausedContext);
m_inspector->client()->runMessageLoopOnPause(contextGroupId);
m_pausedContextGroupId = 0;
}
m_inspector->forEachSession(contextGroupId,
[](V8InspectorSessionImpl* session) {
if (session->debuggerAgent()->enabled())
session->debuggerAgent()->didContinue();
});
}
void V8Debugger::BreakProgramRequested(
v8::Local<v8::Context> pausedContext,
const std::vector<v8::debug::BreakpointId>& break_points_hit,
......
......@@ -185,6 +185,8 @@ class V8Debugger : public v8::debug::DebugDelegate,
v8::Local<v8::Context> paused_context,
const std::vector<v8::debug::BreakpointId>& break_points_hit,
v8::debug::BreakReasons break_reasons) override;
void BreakOnInstrumentation(v8::Local<v8::Context> paused_context,
v8::debug::BreakpointId) override;
void ExceptionThrown(v8::Local<v8::Context> paused_context,
v8::Local<v8::Value> exception,
v8::Local<v8::Value> promise, bool is_uncaught,
......
......@@ -579,7 +579,7 @@ RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
// Enter the debugger.
DebugScope debug_scope(isolate->debug());
bool paused_on_instrumentation = false;
// Check for instrumentation breakpoint.
DCHECK_EQ(script->break_on_entry(), !!instance->break_on_entry());
if (script->break_on_entry()) {
......@@ -596,14 +596,9 @@ RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
.set_break_on_entry(false);
}
DCHECK(!instance->break_on_entry());
Handle<FixedArray> on_entry_breakpoints;
if (maybe_on_entry_breakpoints.ToHandle(&on_entry_breakpoints)) {
debug_info->ClearStepping(isolate);
StepAction step_action = isolate->debug()->last_step_action();
isolate->debug()->ClearStepping();
isolate->debug()->OnDebugBreak(on_entry_breakpoints, step_action);
// Don't process regular breakpoints.
return ReadOnlyRoots(isolate).undefined_value();
if (!maybe_on_entry_breakpoints.is_null()) {
isolate->debug()->OnInstrumentationBreak();
paused_on_instrumentation = true;
}
}
......@@ -631,6 +626,12 @@ RUNTIME_FUNCTION(Runtime_WasmDebugBreak) {
return ReadOnlyRoots(isolate).undefined_value();
}
// We only hit the instrumentation breakpoint, and there is no other reason to
// break.
if (paused_on_instrumentation) {
return ReadOnlyRoots(isolate).undefined_value();
}
// We did not hit a breakpoint. If we are in stepping code, but the user did
// not request stepping, clear this (to save further calls into this runtime
// function).
......
......@@ -921,8 +921,8 @@ bool WasmScript::SetBreakPoint(Handle<Script> script, int* position,
}
// static
void WasmScript::SetBreakPointOnEntry(Handle<Script> script,
Handle<BreakPoint> break_point) {
void WasmScript::SetInstrumentationBreakpoint(Handle<Script> script,
Handle<BreakPoint> break_point) {
// Special handling for on-entry breakpoints.
AddBreakpointToInfo(script, kOnEntryBreakpointPosition, break_point);
script->set_break_on_entry(true);
......
......@@ -811,7 +811,7 @@ class WasmScript : public AllStatic {
// Set an "on entry" breakpoint (a.k.a. instrumentation breakpoint) inside
// the given module. This will affect all live and future instances of the
// module.
V8_EXPORT_PRIVATE static void SetBreakPointOnEntry(
V8_EXPORT_PRIVATE static void SetInstrumentationBreakpoint(
Handle<Script>, Handle<BreakPoint> break_point);
// Set a breakpoint on first breakable position of the given function index
......
Test that all 'other' reasons are explicitly encoded on a pause event if they overlap with another reason
Running test: testBreakpointPauseReason
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"foo.js","scriptId":"3"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"3","url":"foo.js"}.
Paused with reason: other and data: {}.
Running test: testTriggeredPausePauseReason
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"foo.js","scriptId":"4"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"4","url":"foo.js"}.
Paused with reason: other and data: {}.
Running test: testSteppingPauseReason
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"foo.js","scriptId":"5"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"5","url":"foo.js"}.
Paused with reason: other and data: {}.
Paused with reason: other and data: {}.
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"bar.js","scriptId":"6"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"6","url":"bar.js"}.
Running test: testOnlyReportOtherWithEmptyDataOnce
Paused with reason: other and data: {}.
Running test: testDebuggerStatementReason
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"foo.js","scriptId":"8"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"8","url":"foo.js"}.
Paused with reason: other and data: {}.
Running test: testAsyncSteppingPauseReason
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"foo.js","scriptId":"9"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"9","url":"foo.js"}.
Paused with reason: other and data: {}.
Paused with reason: other and data: {}.
Paused with reason: ambiguous and data: {"reasons":[{"reason":"instrumentation","auxData":{"url":"bar.js","scriptId":"10"}},{"reason":"other"}]}.
Paused with reason: instrumentation and data: {"scriptId":"10","url":"bar.js"}.
Test if breakpoints are hit that are set on instrumentation pause in js
Running test: testSetBreakpointOnInstrumentationPause
set breakpoint and evaluate script..
Setting breakpoint at instrumentation break location
Paused at foo.js with reason "instrumentation".
Hit breakpoints: []
Paused at foo.js with reason "other".
Hit breakpoints: ["4:0:20:3"]
Done.
Running test: testSetConditionalBreakpointTrueConditionOnInstrumentationPause
set breakpoint and evaluate script..
Setting breakpoint at instrumentation break location
Paused at foo.js with reason "instrumentation".
Hit breakpoints: []
Paused at foo.js with reason "other".
Hit breakpoints: ["4:0:20:4"]
Done.
Running test: testSetConditionalBreakpointFalseConditionOnInstrumentationPause
set breakpoint and evaluate script..
Setting breakpoint at instrumentation break location
Paused at foo.js with reason "instrumentation".
Hit breakpoints: []
Done.
// 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(
'Test if breakpoints are hit that are set on instrumentation pause in js');
session.setupScriptMap();
function setBreakpoint(msg, condition) {
const reason = msg.params.reason;
if (reason === 'instrumentation') {
const top_frame = msg.params.callFrames[0];
const scriptId = top_frame.location.scriptId;
const columnNumber = top_frame.location.columnNumber;
InspectorTest.log('Setting breakpoint at instrumentation break location');
const breakpoint_info = {
'location': {scriptId, 'lineNumber': 0, columnNumber}
};
if (condition) {
breakpoint_info.condition = condition;
}
return Protocol.Debugger.setBreakpoint(breakpoint_info);
}
return Promise.resolve();
}
function handlePause(msg) {
const top_frame = msg.params.callFrames[0];
const reason = msg.params.reason;
const url = session.getCallFrameUrl(top_frame);
InspectorTest.log(`Paused at ${url} with reason "${reason}".`);
InspectorTest.log(
`Hit breakpoints: ${JSON.stringify(msg.params.hitBreakpoints)}`)
return Protocol.Debugger.resume();
};
// Helper function to check if we can successfully set and evaluate breakpoints
// on an instrumentation pause.
async function runSetBreakpointOnInstrumentationTest(condition) {
await Protocol.Runtime.enable();
await Protocol.Debugger.enable();
InspectorTest.log('set breakpoint and evaluate script..');
await Protocol.Debugger.setInstrumentationBreakpoint(
{instrumentation: 'beforeScriptExecution'});
const runPromise =
Protocol.Runtime.evaluate({expression: '//# sourceURL=foo.js'});
// First pause: instrumentation
const msg = await Protocol.Debugger.oncePaused();
await setBreakpoint(msg, condition);
await handlePause(msg);
// Second pause: if condition evaluates to true
if (!condition || eval(condition)) {
await handlePause(await Protocol.Debugger.oncePaused());
}
InspectorTest.log('Done.');
await runPromise;
await Protocol.Runtime.disable();
await Protocol.Debugger.disable();
}
InspectorTest.runAsyncTestSuite([
// Test if we can set a breakpoint on the first breakable location (which is
// the same location as where the instrumentation breakpoint hits) and
// successfully hit the breakpoint.
async function testSetBreakpointOnInstrumentationPause() {
await runSetBreakpointOnInstrumentationTest();
},
// Test if we can set a conditional breakpoint on the first breakable location
// and successfully hit the breakpoint.
async function
testSetConditionalBreakpointTrueConditionOnInstrumentationPause() {
await runSetBreakpointOnInstrumentationTest('4 > 3');
},
// Test if we can set a conditional breakpoint on the first breakable location
// which evaluates to false, and therefore does not trigger a pause.
async function
testSetConditionalBreakpointFalseConditionOnInstrumentationPause() {
await runSetBreakpointOnInstrumentationTest('3 > 4');
}
]);
......@@ -87,3 +87,28 @@ paused with reason: instrumentation
sourceMapURL : boo.js
url : foo.js
}
Running test: testRemoveAfterCompile
set breakpoint..
compile script..
Remove instrumentation breakpoint..
evaluate script..
no breakpoint was hit
Running test: testRemoveBeforeEvaluate
set breakpoint..
Remove instrumentation breakpoint..
evaluate script..
no breakpoint was hit
Running test: testRemoveAfterOnePause
set breakpoint..
evaluate script..
paused with reason: instrumentation
{
scriptId : <scriptId>
url : foo.js
}
Remove instrumentation breakpoint..
evaluate another script..
no breakpoint was hit
......@@ -125,6 +125,74 @@ InspectorTest.runAsyncTestSuite([
InspectorTest.log(`paused with reason: ${reason}`);
InspectorTest.logMessage(data);
}
await Protocol.Debugger.disable();
await Protocol.Runtime.disable();
},
async function testRemoveAfterCompile() {
await Protocol.Runtime.enable();
await Protocol.Debugger.enable();
InspectorTest.log('set breakpoint..');
const { result : {breakpointId} } = await Protocol.Debugger.setInstrumentationBreakpoint({
instrumentation: 'beforeScriptExecution'
});
InspectorTest.log('compile script..');
const { result: { scriptId } } = await Protocol.Runtime.compileScript({
expression: 'console.log(3)', sourceURL: 'foo.js', persistScript: true });
InspectorTest.log('Remove instrumentation breakpoint..');
await Protocol.Debugger.removeBreakpoint({breakpointId});
InspectorTest.log('evaluate script..');
await Protocol.Runtime.runScript({ scriptId });
InspectorTest.log('no breakpoint was hit');
await Protocol.Debugger.disable();
await Protocol.Runtime.disable();
},
async function testRemoveBeforeEvaluate() {
await Protocol.Runtime.enable();
await Protocol.Debugger.enable();
InspectorTest.log('set breakpoint..');
const { result : {breakpointId} } = await Protocol.Debugger.setInstrumentationBreakpoint({
instrumentation: 'beforeScriptExecution'
});
InspectorTest.log('Remove instrumentation breakpoint..');
await Protocol.Debugger.removeBreakpoint({breakpointId});
InspectorTest.log('evaluate script..');
await Protocol.Runtime.evaluate({expression: 'console.log(3) //# sourceURL=foo.js'});
InspectorTest.log('no breakpoint was hit');
await Protocol.Debugger.disable();
await Protocol.Runtime.disable();
},
async function testRemoveAfterOnePause() {
await Protocol.Runtime.enable();
await Protocol.Debugger.enable();
InspectorTest.log('set breakpoint..');
const { result : {breakpointId} } = await Protocol.Debugger.setInstrumentationBreakpoint({
instrumentation: 'beforeScriptExecution'
});
InspectorTest.log('evaluate script..');
Protocol.Runtime.evaluate({expression: 'console.log(3) //# sourceURL=foo.js'});
{
const { params: { reason, data } } = await Protocol.Debugger.oncePaused();
InspectorTest.log(`paused with reason: ${reason}`);
InspectorTest.logMessage(data);
}
InspectorTest.log('Remove instrumentation breakpoint..');
await Protocol.Debugger.removeBreakpoint({breakpointId});
InspectorTest.log('evaluate another script..');
await Protocol.Runtime.evaluate({expression: 'console.log(3) //# sourceURL=foo.js'});
InspectorTest.log('no breakpoint was hit');
await Protocol.Debugger.disable();
await Protocol.Runtime.disable();
}
......
Test if breakpoints are hit that are set on instrumentation pause in wasm.
Running test: testSetBreakpointOnInstrumentationPause
Setting instrumentation breakpoint
Compiling wasm module.
Paused at v8://test/compile_module with reason "instrumentation".
Hit breakpoints: []
Instantiating module.
Paused at v8://test/instantiate with reason "instrumentation".
Hit breakpoints: []
Setting breakpoint at instrumentation break location
Paused at wasm://wasm/20da547a with reason "instrumentation".
Script wasm://wasm/20da547a byte offset 26: Wasm opcode 0x01 (kExprNop)
Hit breakpoints: []
Paused at wasm://wasm/20da547a with reason "other".
Script wasm://wasm/20da547a byte offset 26: Wasm opcode 0x01 (kExprNop)
Hit breakpoints: ["4:0:26:4"]
Done.
Running test: testSetConditionalBreakpointTrueConditionOnInstrumentationPause
Setting instrumentation breakpoint
Compiling wasm module.
Paused at v8://test/compile_module with reason "instrumentation".
Hit breakpoints: []
Instantiating module.
Paused at v8://test/instantiate with reason "instrumentation".
Hit breakpoints: []
Setting breakpoint at instrumentation break location
Paused at wasm://wasm/20da547a with reason "instrumentation".
Script wasm://wasm/20da547a byte offset 26: Wasm opcode 0x01 (kExprNop)
Hit breakpoints: []
Paused at wasm://wasm/20da547a with reason "other".
Script wasm://wasm/20da547a byte offset 26: Wasm opcode 0x01 (kExprNop)
Hit breakpoints: ["4:0:26:4"]
Done.
Running test: testSetConditionalBreakpointFalseConditionOnInstrumentationPause
Setting instrumentation breakpoint
Compiling wasm module.
Paused at v8://test/compile_module with reason "instrumentation".
Hit breakpoints: []
Instantiating module.
Paused at v8://test/instantiate with reason "instrumentation".
Hit breakpoints: []
Setting breakpoint at instrumentation break location
Paused at wasm://wasm/20da547a with reason "instrumentation".
Script wasm://wasm/20da547a byte offset 26: Wasm opcode 0x01 (kExprNop)
Hit breakpoints: []
Done.
// 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.
utils.load('test/inspector/wasm-inspector-test.js');
const {session, contextGroup, Protocol} = InspectorTest.start(
'Test if breakpoints are hit that are set on instrumentation pause in wasm.');
session.setupScriptMap();
function setBreakpoint(msg, condition) {
const top_frame = msg.params.callFrames[0];
const reason = msg.params.reason;
const url = session.getCallFrameUrl(top_frame);
if (reason === 'instrumentation' && url.startsWith('wasm://')) {
const scriptId = top_frame.location.scriptId;
const columnNumber = top_frame.location.columnNumber;
InspectorTest.log('Setting breakpoint at instrumentation break location');
const breakpoint_info = {
'location': {scriptId, 'lineNumber': 0, columnNumber}
};
if (condition) {
breakpoint_info.condition = condition;
}
return Protocol.Debugger.setBreakpoint(breakpoint_info);
}
return Promise.resolve();
}
async function handlePause(msg) {
const top_frame = msg.params.callFrames[0];
const reason = msg.params.reason;
const url = session.getCallFrameUrl(top_frame);
InspectorTest.log(`Paused at ${url} with reason "${reason}".`);
if (!url.startsWith('v8://test/')) {
await session.logSourceLocation(top_frame.location);
}
InspectorTest.log(
`Hit breakpoints: ${JSON.stringify(msg.params.hitBreakpoints)}`)
return Protocol.Debugger.resume();
};
// Helper function to run tests to check if we can successfully set and evaluate
// breakpoints on an instrumentation pause.
async function runSetBreakpointOnInstrumentationTest(condition) {
const builder = new WasmModuleBuilder();
const start_fn = builder.addFunction('start', kSig_v_v).addBody([kExprNop]);
builder.addStart(start_fn.index);
await Protocol.Runtime.enable();
await Protocol.Debugger.enable();
InspectorTest.log('Setting instrumentation breakpoint');
await Protocol.Debugger.setInstrumentationBreakpoint(
{instrumentation: 'beforeScriptExecution'});
InspectorTest.log('Compiling wasm module.');
WasmInspectorTest.compile(builder.toArray());
// First pause: compile script.
await handlePause(await Protocol.Debugger.oncePaused());
InspectorTest.log('Instantiating module.');
const evalPromise = WasmInspectorTest.evalWithUrl(
'new WebAssembly.Instance(module)', 'instantiate');
// Second pause: instantiate script.
await handlePause(await Protocol.Debugger.oncePaused());
// Third pause: wasm script. This will set a breakpoint. Pass on a condition.
const msg = await Protocol.Debugger.oncePaused();
await setBreakpoint(msg, condition);
await handlePause(msg);
// Fourth pause: wasm script, if condition evaluates to true.
if (!condition || eval(condition)) {
await handlePause(await Protocol.Debugger.oncePaused());
}
InspectorTest.log('Done.');
await evalPromise;
await Protocol.Debugger.disable();
await Protocol.Runtime.disable();
}
InspectorTest.runAsyncTestSuite([
// Test if we can set a breakpoint on the first breakable location (which is
// the same location as where the instrumentation breakpoint hits) and
// successfully hit the breakpoint.
async function testSetBreakpointOnInstrumentationPause() {
await runSetBreakpointOnInstrumentationTest();
},
// Test if we can set a conditional breakpoint on the first breakable location
// and successfully hit the breakpoint.
async function
testSetConditionalBreakpointTrueConditionOnInstrumentationPause() {
await runSetBreakpointOnInstrumentationTest('3 < 5');
},
// Test if we can set a conditional breakpoint on the first breakable location
// which evaluates to false, and therefore does not trigger a pause.
async function
testSetConditionalBreakpointFalseConditionOnInstrumentationPause() {
await runSetBreakpointOnInstrumentationTest('3 > 5');
},
]);
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