Open mmcloughlin opened 4 years ago
One useful feature would be the ability to define assertions in a "debug build" that would panic on failure. One way this could work is raising a panic
from the assembly code. How does this actually work?
package main
//go:noinline
func raise() {
panic("fail")
}
func main() {
raise()
}
This compiles to:
TEXT main.raise(SB) /Users/michaelmcloughlin/Development/misc/panicasm/panic.go
panic.go:4 0x104e580 65488b0c2530000000 MOVQ GS:0x30, CX
panic.go:4 0x104e589 483b6110 CMPQ 0x10(CX), SP
panic.go:4 0x104e58d 762c JBE 0x104e5bb
panic.go:4 0x104e58f 4883ec18 SUBQ $0x18, SP
panic.go:4 0x104e593 48896c2410 MOVQ BP, 0x10(SP)
panic.go:4 0x104e598 488d6c2410 LEAQ 0x10(SP), BP
panic.go:5 0x104e59d 488d059cac0000 LEAQ runtime.types+44000(SB), AX
panic.go:5 0x104e5a4 48890424 MOVQ AX, 0(SP)
panic.go:5 0x104e5a8 488d05d1c40200 LEAQ main.statictmp_0(SB), AX
panic.go:5 0x104e5af 4889442408 MOVQ AX, 0x8(SP)
panic.go:5 0x104e5b4 e8a740fdff CALL runtime.gopanic(SB)
panic.go:5 0x104e5b9 0f0b UD2
panic.go:4 0x104e5bb e80085ffff CALL runtime.morestack_noctxt(SB)
panic.go:4 0x104e5c0 ebbe JMP main.raise(SB)
:-1 0x104e5c2 cc INT $0x3
...
gopanic
takes an interface{}
:
Discussed on Slack: https://gophers.slack.com/archives/C0VP8EF3R/p1562989032212400. Some great contributions from @acln0 @dgryski @agnivade. Ideas:
Check for any function that calls a `throw`. That panics.
// called from assembly
func badmcall(fn func(*g)) {
throw("runtime: mcall called on m->g0 stack")
}
func badmcall2(fn func(*g)) {
throw("runtime: mcall function returned")
}
These are called from assembly. And they are called on panic conditions
panicdivide
called from here:
runtime
has many examples of calling badmcall
. throw
is defined here
https://github.com/golang/go/blob/726b1bf9871f4905d85a53051301f636e8273328/src/runtime/panic.go#L764
@zeebo also makes a good suggestion regarding breakpoints. This hypothetical avo/debug
package could also have a Breakpoint()
function. See:
https://golang.org/pkg/runtime/#Breakpoint
This is simply implemented as the INT3
instruction
Not clear to me right now why this is encoded with BYTE 0xCC
. Does the Go assembler not support INT3
? It seems the avo
instruction database doesn't have INT3
or INT
right now.
@klauspost shared some adhoc code he was using to help debug his S2 implementation https://gophers.slack.com/archives/C0VP8EF3R/p1579706207182000
var assertCounter int
// insert extra checks here and there.
const debug = false
// assert will insert code if debug is enabled.
// The code should jump to 'ok' is assertion is success.
func assert(fn func(ok LabelRef)) {
if debug {
caller := [100]uintptr{0}
runtime.Callers(2, caller[:])
frame, _ := runtime.CallersFrames(caller[:]).Next()
ok := fmt.Sprintf("assert_check_%d_ok_srcline_%d", assertCounter, frame.Line)
fn(LabelRef(ok))
// Emit several since delve is imprecise.
INT(Imm(3))
INT(Imm(3))
Label(ok)
assertCounter++
}
}
Example:
assert(func(ok LabelRef) {
// Check if somebody changed src
tmp := GP64()
MOVQ(srcBaseQ, tmp)
CMPQ(tmp, src)
JEQ(ok)
})
It's currently very hard to debug complex
avo
programs. We could provide some simple functions to make this easier. This could even form adebug
package in the hypothetical standard library #117. For example:assert
helper that would panic or otherwise crash