proc: catch fatal runtime errors (#1502)
Like we do with unrecovered panics, create a default breakpoint to catch runtime errors that will cause the program to terminate. Primarily intended to give users the opportunity to examine the state of a deadlocked process.
This commit is contained in:
parent
520d792422
commit
ac3b1c7a78
12
_fixtures/testdeadlock.go
Normal file
12
_fixtures/testdeadlock.go
Normal file
@ -0,0 +1,12 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
ch1 := make(chan string)
|
||||
ch2 := make(chan string)
|
||||
go func() {
|
||||
<-ch1
|
||||
ch2 <- "done"
|
||||
}()
|
||||
<-ch2
|
||||
ch1 <- "done"
|
||||
}
|
@ -19,8 +19,16 @@ var ErrNotExecutable = errors.New("not an executable file")
|
||||
// only possible on recorded (traced) programs.
|
||||
var ErrNotRecorded = errors.New("not a recording")
|
||||
|
||||
const (
|
||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
|
||||
const UnrecoveredPanic = "unrecovered-panic"
|
||||
UnrecoveredPanic = "unrecovered-panic"
|
||||
|
||||
// FatalThrow is the name given to the breakpoint triggered when the target process dies because of a fatal runtime error
|
||||
FatalThrow = "runtime-fatal-throw"
|
||||
|
||||
unrecoveredPanicID = -1
|
||||
fatalThrowID = -2
|
||||
)
|
||||
|
||||
// ErrProcessExited indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
@ -61,6 +69,7 @@ func PostInitializationSetup(p Process, path string, debugInfoDirs []string, wri
|
||||
p.SetSelectedGoroutine(g)
|
||||
|
||||
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
||||
createFatalThrowBreakpoint(p, writeBreakpoint)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -732,13 +741,22 @@ func createUnrecoveredPanicBreakpoint(p Process, writeBreakpoint WriteBreakpoint
|
||||
panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
|
||||
}
|
||||
if err == nil {
|
||||
bp, err := p.Breakpoints().SetWithID(-1, panicpc, writeBreakpoint)
|
||||
bp, err := p.Breakpoints().SetWithID(unrecoveredPanicID, panicpc, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.Name = UnrecoveredPanic
|
||||
bp.Variables = []string{"runtime.curg._panic.arg"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createFatalThrowBreakpoint(p Process, writeBreakpoint WriteBreakpointFn) {
|
||||
fatalpc, err := FindFunctionLocation(p, "runtime.fatalthrow", true, 0)
|
||||
if err == nil {
|
||||
bp, err := p.Breakpoints().SetWithID(fatalThrowID, fatalpc, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.Name = FatalThrow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FirstPCAfterPrologue returns the address of the first
|
||||
|
@ -4196,3 +4196,21 @@ func TestIssue1469(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeadlockBreakpoint(t *testing.T) {
|
||||
if buildMode == "pie" {
|
||||
t.Skip("See https://github.com/golang/go/issues/29322")
|
||||
}
|
||||
deadlockBp := proc.FatalThrow
|
||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
|
||||
deadlockBp = proc.UnrecoveredPanic
|
||||
}
|
||||
withTestProcess("testdeadlock", t, func(p proc.Process, fixture protest.Fixture) {
|
||||
assertNoError(proc.Continue(p), t, "Continue()")
|
||||
|
||||
bp := p.CurrentThread().Breakpoint()
|
||||
if bp.Breakpoint == nil || bp.Name != deadlockBp {
|
||||
t.Fatalf("did not stop at deadlock breakpoint %v", bp)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user