Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: e00002d2d87af12a8d1ab1fcc5c40802dd37c90f
https://github.com/WebKit/WebKit/commit/e00002d2d87af12a8d1ab1fcc5c40802dd37c90f
Author: Jonas Devlieghere <[email protected]>
Date: 2026-09-11 (Fri, 11 Sep 2026)
Changed paths:
M JSTests/wasm/debugger/tests/tests.py
M Source/JavaScriptCore/llint/InPlaceInterpreter.asm
M Source/JavaScriptCore/llint/InPlaceInterpreter64.asm
M Source/JavaScriptCore/wasm/WasmIPIntSlowPaths.cpp
M Source/JavaScriptCore/wasm/debugger/WasmBreakpointManager.cpp
M Source/JavaScriptCore/wasm/debugger/WasmBreakpointManager.h
M Source/JavaScriptCore/wasm/debugger/WasmDebugServerUtilities.cpp
M Source/JavaScriptCore/wasm/debugger/WasmDebugServerUtilities.h
M Source/JavaScriptCore/wasm/debugger/WasmExecutionHandler.cpp
M Source/JavaScriptCore/wasm/debugger/WasmExecutionHandler.h
M Source/JavaScriptCore/wasm/debugger/tests/ExecutionHandlerTest.cpp
Log Message:
-----------
[JSC] Wasm debugger should resume breakpoints by dispatching the displaced
opcode
https://bugs.webkit.org/show_bug.cgi?id=323845
rdar://187149541
Reviewed by Yijia Huang.
A breakpoint patches the first byte of an instruction with `unreachable`
so IPInt traps into the debugger. Resuming re-read the byte at PC and
dispatched whatever it found, which is only correct while the patch
happens to be gone. That holds when LLDB clears a site to step over it,
and nowhere else: any resume with the patch still in place dispatches
the patch itself and traps again at the same PC forever.
Capture the displaced opcode when the trap is handled and return it to
the interpreter, which dispatches that opcode instead of re-reading PC.
The saved byte is what the program was going to run, so the resume is
correct whether or not the patch is still there.
A site on a real `unreachable` has no displaced opcode to replay: the
instruction and the patch are the same byte. Report the breakpoint, then
fall through to the trap path, so the program's own trap is still
reported and propagates instead of being swallowed by the resume.
Stepping off such a site has no successor to break on either, so the
step resumes and lands on the trap.
Key breakpoints on the physical PC rather than the virtual address. The
patch is a property of a byte in module bytecode, which every instance
of a module shares, and the PC is what the trap path has in hand. A user
site and an internal step target can now name the same byte with the
displaced opcode saved once between them; the patch is reference counted
and restored when the last of the two goes away. Breakpoint holds a
RefPtr to the ModuleInformation owning the bytecode it patched.
Also stop crashing on a z0 for an address that holds no site. LLDB
clears a site to step over it and can send z0 for a byte the server no
longer breaks on, which used to hit a RELEASE_ASSERT. Reply OK, which is
what a client that already dropped its own site expects.
Test: BreakpointOnUnreachableTestCase and StepOffUnreachableTestCase
cover the two ways a site on a real `unreachable` resumes,
testPatchLifetime covers the reference counting and the byte-level
restore.
* JSTests/wasm/debugger/tests/tests.py:
(BreakpointOnUnreachableTestCase):
(BreakpointOnUnreachableTestCase.execute):
(StepOffUnreachableTestCase):
(StepOffUnreachableTestCase.execute):
* Source/JavaScriptCore/llint/InPlaceInterpreter.asm:
* Source/JavaScriptCore/llint/InPlaceInterpreter64.asm:
* Source/JavaScriptCore/wasm/WasmIPIntSlowPaths.cpp:
(JSC::IPInt::WASM_IPINT_EXTERN_CPP_DECL):
* Source/JavaScriptCore/wasm/debugger/WasmBreakpointManager.cpp:
(JSC::Wasm::BreakpointManager::hasOneTimeBreakpoints):
(JSC::Wasm::BreakpointManager::ensurePatched):
(JSC::Wasm::BreakpointManager::releasePatchIfUnused):
(JSC::Wasm::BreakpointManager::setStepBreakpoint):
(JSC::Wasm::BreakpointManager::setBreakpointAt):
(JSC::Wasm::BreakpointManager::removeBreakpointAt):
(JSC::Wasm::BreakpointManager::trapActionFor):
(JSC::Wasm::BreakpointManager::clearAllOneTimeBreakpoints):
(JSC::Wasm::BreakpointManager::clearAllBreakpoints):
(JSC::Wasm::BreakpointManager::hasBreakpoints): Deleted.
(JSC::Wasm::BreakpointManager::setBreakpoint): Deleted.
(JSC::Wasm::BreakpointManager::findBreakpoint): Deleted.
(JSC::Wasm::BreakpointManager::removeBreakpointImpl): Deleted.
(JSC::Wasm::BreakpointManager::removeBreakpoint): Deleted.
* Source/JavaScriptCore/wasm/debugger/WasmBreakpointManager.h:
* Source/JavaScriptCore/wasm/debugger/WasmDebugServerUtilities.cpp:
(JSC::Wasm::getWasmReturnPC):
* Source/JavaScriptCore/wasm/debugger/WasmDebugServerUtilities.h:
(JSC::Wasm::Breakpoint::create):
(JSC::Wasm::Breakpoint::Breakpoint):
(JSC::Wasm::Breakpoint::dump const):
(JSC::Wasm::WasmReturnSite::operator bool const):
* Source/JavaScriptCore/wasm/debugger/WasmExecutionHandler.cpp:
(JSC::Wasm::ExecutionHandler::handleDebuggerTrapIfNeeded):
(JSC::Wasm::ExecutionHandler::step):
(JSC::Wasm::ExecutionHandler::stepAtBytecode):
(JSC::Wasm::ExecutionHandler::setStepIntoBreakpointForCall):
(JSC::Wasm::ExecutionHandler::setStepIntoBreakpointForThrow):
(JSC::Wasm::ExecutionHandler::setStepBreakpointAtEntry):
(JSC::Wasm::ExecutionHandler::requireModuleAddress):
(JSC::Wasm::ExecutionHandler::setBreakpoint):
(JSC::Wasm::ExecutionHandler::removeBreakpoint):
(JSC::Wasm::ExecutionHandler::setBreakpointAtEntry): Deleted.
(JSC::Wasm::ExecutionHandler::setBreakpointAtPC): Deleted.
(JSC::Wasm::ExecutionHandler::hasBreakpoints const): Deleted.
* Source/JavaScriptCore/wasm/debugger/WasmExecutionHandler.h:
* Source/JavaScriptCore/wasm/debugger/tests/ExecutionHandlerTest.cpp:
(ExecutionHandlerTest::entryAddress):
(ExecutionHandlerTest::setBreakpointsAtAllFunctionEntries):
(ExecutionHandlerTest::testBreakpointContinueCycles):
(ExecutionHandlerTest::testBreakpointSingleStepping):
(ExecutionHandlerTest::testPatchLifetime):
(ExecutionHandlerTest::runTests):
Canonical link: https://commits.webkit.org/320918@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications