[wasm][debug] Clear breakpoints when debugger is disabled
Currently WebAssembly breakpoint information survive disabling and re-enabling the debugger. This is different from JavaScript, where they are all removed. The frontend is expected to re-set the breakpoint then. Thus this CL remembers all wasm scripts where breakpoints have been set in the Debug object, and clears them all when the debugger gets disabled. R=bmeurer@chromium.org Bug: v8:10403 Change-Id: I5f8a8f3123727c954921920897ee7bf3b73f0ae8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2184969 Commit-Queue: Clemens Backes <clemensb@chromium.org> Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Cr-Commit-Position: refs/heads/master@{#67639}
This commit is contained in:
parent
a40e093856
commit
3c51da6cec
@ -622,6 +622,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script,
|
|||||||
Handle<BreakPoint> break_point =
|
Handle<BreakPoint> break_point =
|
||||||
isolate_->factory()->NewBreakPoint(*id, condition);
|
isolate_->factory()->NewBreakPoint(*id, condition);
|
||||||
if (script->type() == Script::TYPE_WASM) {
|
if (script->type() == Script::TYPE_WASM) {
|
||||||
|
RecordWasmScriptWithBreakpoints(script);
|
||||||
return WasmScript::SetBreakPoint(script, source_position, break_point);
|
return WasmScript::SetBreakPoint(script, source_position, break_point);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -777,12 +778,53 @@ void Debug::RemoveBreakpointForWasmScript(Handle<Script> script, int id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Debug::RecordWasmScriptWithBreakpoints(Handle<Script> script) {
|
||||||
|
if (wasm_scripts_with_breakpoints_.is_null()) {
|
||||||
|
Handle<WeakArrayList> new_list = isolate_->factory()->NewWeakArrayList(4);
|
||||||
|
wasm_scripts_with_breakpoints_ =
|
||||||
|
isolate_->global_handles()->Create(*new_list);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
DisallowHeapAllocation no_gc;
|
||||||
|
for (int idx = wasm_scripts_with_breakpoints_->length() - 1; idx >= 0;
|
||||||
|
--idx) {
|
||||||
|
HeapObject wasm_script;
|
||||||
|
if (wasm_scripts_with_breakpoints_->Get(idx).GetHeapObject(
|
||||||
|
&wasm_script) &&
|
||||||
|
wasm_script == *script) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handle<WeakArrayList> new_list = WeakArrayList::Append(
|
||||||
|
isolate_, wasm_scripts_with_breakpoints_, MaybeObjectHandle{script});
|
||||||
|
if (*new_list != *wasm_scripts_with_breakpoints_) {
|
||||||
|
isolate_->global_handles()->Destroy(
|
||||||
|
wasm_scripts_with_breakpoints_.location());
|
||||||
|
wasm_scripts_with_breakpoints_ =
|
||||||
|
isolate_->global_handles()->Create(*new_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Clear out all the debug break code.
|
// Clear out all the debug break code.
|
||||||
void Debug::ClearAllBreakPoints() {
|
void Debug::ClearAllBreakPoints() {
|
||||||
ClearAllDebugInfos([=](Handle<DebugInfo> info) {
|
ClearAllDebugInfos([=](Handle<DebugInfo> info) {
|
||||||
ClearBreakPoints(info);
|
ClearBreakPoints(info);
|
||||||
info->ClearBreakInfo(isolate_);
|
info->ClearBreakInfo(isolate_);
|
||||||
});
|
});
|
||||||
|
// Clear all wasm breakpoints.
|
||||||
|
if (!wasm_scripts_with_breakpoints_.is_null()) {
|
||||||
|
DisallowHeapAllocation no_gc;
|
||||||
|
for (int idx = wasm_scripts_with_breakpoints_->length() - 1; idx >= 0;
|
||||||
|
--idx) {
|
||||||
|
HeapObject raw_wasm_script;
|
||||||
|
if (wasm_scripts_with_breakpoints_->Get(idx).GetHeapObject(
|
||||||
|
&raw_wasm_script)) {
|
||||||
|
WasmScript::ClearAllBreakpoints(Script::cast(raw_wasm_script));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wasm_scripts_with_breakpoints_ = Handle<WeakArrayList>{};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
|
void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared,
|
||||||
|
@ -248,6 +248,8 @@ class V8_EXPORT_PRIVATE Debug {
|
|||||||
void RemoveBreakpoint(int id);
|
void RemoveBreakpoint(int id);
|
||||||
void RemoveBreakpointForWasmScript(Handle<Script> script, int id);
|
void RemoveBreakpointForWasmScript(Handle<Script> script, int id);
|
||||||
|
|
||||||
|
void RecordWasmScriptWithBreakpoints(Handle<Script> script);
|
||||||
|
|
||||||
// Find breakpoints from the debug info and the break location and check
|
// 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
|
// whether they are hit. Return an empty handle if not, or a FixedArray with
|
||||||
// hit BreakPoint objects.
|
// hit BreakPoint objects.
|
||||||
@ -546,6 +548,9 @@ class V8_EXPORT_PRIVATE Debug {
|
|||||||
// Storage location for registers when handling debug break calls
|
// Storage location for registers when handling debug break calls
|
||||||
ThreadLocal thread_local_;
|
ThreadLocal thread_local_;
|
||||||
|
|
||||||
|
// This is a global handle, lazily initialized.
|
||||||
|
Handle<WeakArrayList> wasm_scripts_with_breakpoints_;
|
||||||
|
|
||||||
Isolate* isolate_;
|
Isolate* isolate_;
|
||||||
|
|
||||||
friend class Isolate;
|
friend class Isolate;
|
||||||
|
@ -1117,6 +1117,12 @@ bool WasmScript::ClearBreakPointById(Handle<Script> script, int breakpoint_id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void WasmScript::ClearAllBreakpoints(Script script) {
|
||||||
|
script.set_wasm_breakpoint_infos(
|
||||||
|
ReadOnlyRoots(script.GetIsolate()).empty_fixed_array());
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position,
|
void WasmScript::AddBreakpointToInfo(Handle<Script> script, int position,
|
||||||
Handle<BreakPoint> break_point) {
|
Handle<BreakPoint> break_point) {
|
||||||
|
@ -882,6 +882,9 @@ class WasmScript : public AllStatic {
|
|||||||
V8_EXPORT_PRIVATE static bool ClearBreakPointById(Handle<Script>,
|
V8_EXPORT_PRIVATE static bool ClearBreakPointById(Handle<Script>,
|
||||||
int breakpoint_id);
|
int breakpoint_id);
|
||||||
|
|
||||||
|
// Remove all set breakpoints.
|
||||||
|
static void ClearAllBreakpoints(Script);
|
||||||
|
|
||||||
// Get a list of all possible breakpoints within a given range of this module.
|
// Get a list of all possible breakpoints within a given range of this module.
|
||||||
V8_EXPORT_PRIVATE static bool GetPossibleBreakpoints(
|
V8_EXPORT_PRIVATE static bool GetPossibleBreakpoints(
|
||||||
wasm::NativeModule* native_module, const debug::Location& start,
|
wasm::NativeModule* native_module, const debug::Location& start,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
Test that breakpoints do not survive a restart of the debugger.
|
||||||
|
Instantiating.
|
||||||
|
Waiting for wasm script (ignoring first non-wasm script).
|
||||||
|
Setting breakpoint.
|
||||||
|
Calling func.
|
||||||
|
Script wasm://wasm/8c388106 byte offset 33: Wasm opcode 0x01
|
||||||
|
func returned.
|
||||||
|
Restarting debugger.
|
||||||
|
Calling func.
|
||||||
|
func returned.
|
||||||
|
Finished.
|
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2020 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 that breakpoints do not survive a restart of the debugger.');
|
||||||
|
session.setupScriptMap();
|
||||||
|
|
||||||
|
utils.load('test/mjsunit/wasm/wasm-module-builder.js');
|
||||||
|
|
||||||
|
const builder = new WasmModuleBuilder();
|
||||||
|
|
||||||
|
const func =
|
||||||
|
builder.addFunction('func', kSig_v_v).addBody([kExprNop]).exportFunc();
|
||||||
|
|
||||||
|
const module_bytes = JSON.stringify(builder.toArray());
|
||||||
|
|
||||||
|
function instantiate(bytes) {
|
||||||
|
let buffer = new ArrayBuffer(bytes.length);
|
||||||
|
let view = new Uint8Array(buffer);
|
||||||
|
for (let i = 0; i < bytes.length; ++i) {
|
||||||
|
view[i] = bytes[i] | 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let module = new WebAssembly.Module(buffer);
|
||||||
|
return new WebAssembly.Instance(module);
|
||||||
|
}
|
||||||
|
contextGroup.addScript(instantiate.toString());
|
||||||
|
|
||||||
|
Protocol.Debugger.onPaused(async msg => {
|
||||||
|
await session.logSourceLocation(msg.params.callFrames[0].location);
|
||||||
|
Protocol.Debugger.resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
(async function test() {
|
||||||
|
await Protocol.Debugger.enable();
|
||||||
|
InspectorTest.log('Instantiating.');
|
||||||
|
// Spawn asynchronously:
|
||||||
|
Protocol.Runtime.evaluate(
|
||||||
|
{'expression': `const instance = instantiate(${module_bytes});`});
|
||||||
|
InspectorTest.log(
|
||||||
|
'Waiting for wasm script (ignoring first non-wasm script).');
|
||||||
|
const [, {params: wasm_script}] = await Protocol.Debugger.onceScriptParsed(2);
|
||||||
|
InspectorTest.log('Setting breakpoint.');
|
||||||
|
await Protocol.Debugger.setBreakpoint({
|
||||||
|
'location': {
|
||||||
|
'scriptId': wasm_script.scriptId,
|
||||||
|
'lineNumber': 0,
|
||||||
|
'columnNumber': func.body_offset
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (let run of [0, 1]) {
|
||||||
|
InspectorTest.log('Calling func.');
|
||||||
|
await Protocol.Runtime.evaluate({'expression': 'instance.exports.func()'});
|
||||||
|
InspectorTest.log('func returned.');
|
||||||
|
if (run == 1) continue;
|
||||||
|
InspectorTest.log('Restarting debugger.');
|
||||||
|
await Protocol.Debugger.disable();
|
||||||
|
await Protocol.Debugger.enable();
|
||||||
|
}
|
||||||
|
InspectorTest.log('Finished.');
|
||||||
|
InspectorTest.completeTest();
|
||||||
|
})();
|
Loading…
Reference in New Issue
Block a user