Stream: git-wasmtime

Topic: wasmtime / issue #11964 Debug: plan for simple libcall/in...


view this post on Zulip Wasmtime GitHub notifications bot (Nov 01 2025 at 19:11):

cfallin opened issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 and followups, so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 01 2025 at 19:14):

cfallin assigned cfallin to issue #11964.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 01 2025 at 19:14):

cfallin added the wasmtime:debugging label to Issue #11964.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 03 2025 at 17:03):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 04 2025 at 23:40):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 05 2025 at 01:38):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 20 2025 at 00:37):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 20 2025 at 01:43):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 22 2025 at 02:16):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 22 2025 at 04:33):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 23 2025 at 18:50):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Nov 23 2025 at 18:50):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Dec 02 2025 at 04:00):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Dec 02 2025 at 04:00):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.

view this post on Zulip Wasmtime GitHub notifications bot (Dec 03 2025 at 01:45):

cfallin edited issue #11964:

After offline discussion with @alexcrichton and @fitzgen, we've discussed some of the design choices that were brought up in discussion in #11826, #11921, #11930, and elsewhere, and settled on a reasonable path for "simplest possible debug instrumentation that can work". I wanted to document that here as a meta-issue with a checklist.

Background and Main Choice: Hardware vs. Software Debugger Entries

To start: a debugger of bytecode-compiled-to-native-code needs to be able to

We have the state inspection part covered in #11769 (built on #11768 and #11783, and with #11873 and #11899 as followups), and we also have a callback/hook framework to register a debugger and listen for events (#11895), so the remaining focus is how to build the "control-flow interjection" aspect.

There is a fundamental choice in the design space: we could either make maximal use of hardware traps, and redirect them to the debugger; including creating new scenarios where a hardware trap occurs, for debugger purposes (e.g. patching in "break" instructions for breakpoints that raise SIGILL, and resuming by jumping past them). Or we could perform all checks for trapping conditions, breakpoint conditions, etc., in software, and do a "normal" libcall into the runtime.

Some aspects of the tradeoff are:

Despite the increased cost, the complexities of call injection on traps are significant and this has pushed us to limit scope and make the software-based approach work. That, in turn, has led to some brainstorming around bringing that cost down. To that end, the plan...

Plan: Software-based with Patchable Code

Our plan for a simple yet reasonably performant debugger that can handle traps and insert breakpoints is:

To highlight the new thinking/insights here: if we don't do call injection on traps, then the two lost capabilities are using hardware to catch normal Wasm traps, and using hardware to do very efficient "patching in of breakpoints". But patchable calls are nearly as good for the latter (exactly as good on aarch64, 2 bytes vs 5 bytes on x86-64; the ABI is key to ensuring only the call instruction itself is needed); so the "only" loss is that we need explicit bounds-checking, and we can probably live with that.

I have WIP branches for the patchable ABI, for private code, and for Pulley to use libcall-based traps universally; I'll keep working through the checklist above as time allows.


Last updated: Dec 06 2025 at 06:05 UTC