proc: Breakpoint to catch unrecovered panics

Automatically sets a breakpoint on runtime.startpanic, the function
that gets called by runtime.dopanic when a panic is not recovered.

Implements #317
This commit is contained in:
aarzilli
2016-03-06 18:54:43 +01:00
parent c66c6408a5
commit c4797ea445
6 changed files with 58 additions and 6 deletions

5
_fixtures/panic.go Normal file
View File

@ -0,0 +1,5 @@
package main
func main() {
panic("BOOM!")
}

View File

@ -741,6 +741,16 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
// the offset of g struct inside TLS
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
if err == nil {
bp, err := dbp.SetBreakpoint(panicpc)
if err == nil {
bp.Name = "unrecovered-panic"
bp.ID = -1
dbp.breakpointIDCounter--
}
}
return dbp, nil
}

View File

@ -269,7 +269,7 @@ func TestClearBreakpointBreakpoint(t *testing.T) {
t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3)
}
if len(p.Breakpoints) != 0 {
if countBreakpoints(p) != 0 {
t.Fatal("Breakpoint not removed internally")
}
})
@ -279,6 +279,16 @@ type nextTest struct {
begin, end int
}
func countBreakpoints(p *Process) int {
bpcount := 0
for _, bp := range p.Breakpoints {
if bp.ID >= 0 {
bpcount++
}
}
return bpcount
}
func testnext(program string, testcases []nextTest, initialLocation string, t *testing.T) {
withTestProcess(program, t, func(p *Process, fixture protest.Fixture) {
bp, err := setFunctionBreakpoint(p, initialLocation)
@ -301,7 +311,7 @@ func testnext(program string, testcases []nextTest, initialLocation string, t *t
}
}
if len(p.Breakpoints) != 0 {
if countBreakpoints(p) != 0 {
t.Fatal("Not all breakpoints were cleaned up", len(p.Breakpoints))
}
})
@ -1635,3 +1645,13 @@ func TestIssue149(t *testing.T) {
assertNoError(err, t, "FindFileLocation()")
})
}
func TestPanicBreakpoint(t *testing.T) {
withTestProcess("panic", t, func(p *Process, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue()")
bp := p.CurrentBreakpoint()
if bp == nil || bp.Name != "unrecovered-panic" {
t.Fatalf("not on unrecovered-panic breakpoint: %v", p.CurrentBreakpoint)
}
})
}

View File

@ -114,6 +114,9 @@ func (d *Debugger) Restart() error {
return fmt.Errorf("could not launch process: %s", err)
}
for _, oldBp := range d.breakpoints() {
if oldBp.ID < 0 {
continue
}
newBp, err := p.SetBreakpoint(oldBp.Addr)
if err != nil {
return err

View File

@ -358,6 +358,18 @@ func TestClientServer_breakAtNonexistentPoint(t *testing.T) {
})
}
func countBreakpoints(t *testing.T, c service.Client) int {
bps, err := c.ListBreakpoints()
assertNoError(err, t, "ListBreakpoints()")
bpcount := 0
for _, bp := range bps {
if bp.ID >= 0 {
bpcount++
}
}
return bpcount
}
func TestClientServer_clearBreakpoint(t *testing.T) {
withTestClient("testprog", t, func(c service.Client) {
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sleepytime", Line: 1})
@ -365,8 +377,7 @@ func TestClientServer_clearBreakpoint(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}
bps, err := c.ListBreakpoints()
if e, a := 1, len(bps); e != a {
if e, a := 1, countBreakpoints(t, c); e != a {
t.Fatalf("Expected breakpoint count %d, got %d", e, a)
}
@ -379,8 +390,7 @@ func TestClientServer_clearBreakpoint(t *testing.T) {
t.Fatalf("Expected deleted breakpoint ID %v, got %v", bp.ID, deleted.ID)
}
bps, err = c.ListBreakpoints()
if e, a := 0, len(bps); e != a {
if e, a := 0, countBreakpoints(t, c); e != a {
t.Fatalf("Expected breakpoint count %d, got %d", e, a)
}
})

View File

@ -515,6 +515,10 @@ func clearAll(t *Term, ctx callContext, args string) error {
}
}
if bp.ID < 0 {
continue
}
_, err := t.client.ClearBreakpoint(bp.ID)
if err != nil {
fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), formatBreakpointLocation(bp), err)