~skeeto/public-inbox

2 2

Re: Assertions should be more debugger-oriented

Mikhail Gusarov <dottedmag@dottedmag.net>
Details
Message ID
<C0D5E32E-4B42-4D30-AB17-C2A02363FAEE@dottedmag.net>
DKIM signature
pass
Download raw message
Hello Chris,

I had some fun with Go assembly, and got something similar to an assert.

breakpoint_arm64.s

TEXT	·breakpoint(SB),$0
	WORD $0xd4200000 // brk #0
	RET

Without debugger it aborts with SIGTRAP and no additional stack frames:

% go run .
SIGTRAP: trace trap
PC=0x1023749d0 m=0 sigcode=0

goroutine 1 [running]:
main.trap()
	/Users/dottedmag/tmp/a/trap_arm64.s:2 fp=0x1400006ef60 sp=0x1400006ef60 pc=0x1023749d0
main.main()
	/Users/dottedmag/tmp/a/a.go:6 +0x1c fp=0x1400006ef70 sp=0x1400006ef60 pc=0x1023749ac
<...>

Under delve it stops at assertion:

% dlv debug .
Type 'help' for list of commands.
(dlv) r
Process restarted with PID 64186
(dlv) c
> [hardcoded-breakpoint] main.trap() ./trap_arm64.s:3 (hits total:0) (PC: 0x100ce7c54)
     1:	TEXT	·trap(SB),$0
     2:		WORD $0xd4200000 // brk #0
=>   3:		RET
(dlv) bt
0  0x0000000100ce7c54 in main.trap
   at ./trap_arm64.s:3
1  0x0000000100ce7c2c in main.main
   at ./a.go:6
2  0x0000000100cc0a40 in runtime.main
   at /opt/homebrew/Cellar/go/1.19.5/libexec/src/runtime/proc.go:250
3  0x0000000100ce5cb4 in runtime.goexit
   at /opt/homebrew/Cellar/go/1.19.5/libexec/src/runtime/asm_arm64.s:1172
(dlv)

Best,
Mikhail.

Re: Assertions should be more debugger-oriented

Details
Message ID
<20230214040724.44bzhhlbjo6lvzy6@nullprogram.com>
In-Reply-To
<C0D5E32E-4B42-4D30-AB17-C2A02363FAEE@dottedmag.net> (view parent)
DKIM signature
missing
Download raw message
Fascinating, Mikhail! This works pretty nicely. Not only does this not 
unwind the stack, it's continuable. I wondered if the RET was necessary, 
so I tried removing it, and it seems the Go runtime gets lost. I suspect 
it's because the instruction pointer lands just beyond the break outside 
the function. Regardless, it's what makes continuation work anyway.

I also tried two versions for amd64, ud2 and int3:

TEXT	·breakpoint(SB),$0
	WORD $0x0b0f // ud2
	RET

TEXT	·breakpoint(SB),$0
	BYTE $0xcc   // int3
	RET

The former doesn't really work at all, but the latter behaves exactly like 
your arm64 breakpoint.

However, my one little complaint is that breaks in the assembly function, 
a stack frame off from the target. That's not so bad, and it's better than 
where delve breaks after a panic (deep inside the runtime), but I wondered 
if we could do better. Since gc Go can't inline assembly functions, I 
wondered if I could add a custom breakpoint intrinsic to the compiler so 
that the break instruction is inlined at the "call" site.

While trying to figure this out, I stumbled across the already existing 
solution: runtime.Breakpoint. This is just like your breakpoint, but delve 
stops at the right place, despite it compiling to a function call, and 
it's portable. This will be handy.

Thanks for the cool trick and leading me to runtime.Breakpoint!

Re: Assertions should be more debugger-oriented

Mikhail Gusarov <dottedmag@dottedmag.net>
Details
Message ID
<AE69E214-A870-4FEC-A95A-AB623918649D@dottedmag.net>
In-Reply-To
<20230214040724.44bzhhlbjo6lvzy6@nullprogram.com> (view parent)
DKIM signature
pass
Download raw message
Hey Kris,

On 14 Feb 2023, at 5:07, Christopher Wellons wrote:

> While trying to figure this out, I stumbled across the already existing solution: runtime.Breakpoint. This is just like your breakpoint, but delve stops at the right place, despite it compiling to a function call, and it's portable. This will be handy.

Very nice, TIL.

Best,
Mikhail.
Reply to thread Export thread (mbox)