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.
|
// only possible on recorded (traced) programs.
|
||||||
var ErrNotRecorded = errors.New("not a recording")
|
var ErrNotRecorded = errors.New("not a recording")
|
||||||
|
|
||||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
|
const (
|
||||||
const UnrecoveredPanic = "unrecovered-panic"
|
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
|
||||||
|
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
|
// ErrProcessExited indicates that the process has exited and contains both
|
||||||
// process id and exit status.
|
// process id and exit status.
|
||||||
@ -61,6 +69,7 @@ func PostInitializationSetup(p Process, path string, debugInfoDirs []string, wri
|
|||||||
p.SetSelectedGoroutine(g)
|
p.SetSelectedGoroutine(g)
|
||||||
|
|
||||||
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
||||||
|
createFatalThrowBreakpoint(p, writeBreakpoint)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -732,13 +741,22 @@ func createUnrecoveredPanicBreakpoint(p Process, writeBreakpoint WriteBreakpoint
|
|||||||
panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
|
panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
bp, err := p.Breakpoints().SetWithID(-1, panicpc, writeBreakpoint)
|
bp, err := p.Breakpoints().SetWithID(unrecoveredPanicID, panicpc, writeBreakpoint)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
bp.Name = UnrecoveredPanic
|
bp.Name = UnrecoveredPanic
|
||||||
bp.Variables = []string{"runtime.curg._panic.arg"}
|
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
|
// 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