mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-01 03:42:59 +08:00 
			
		
		
		
	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:
		
							
								
								
									
										5
									
								
								_fixtures/panic.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								_fixtures/panic.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | package main | ||||||
|  |  | ||||||
|  | func main() { | ||||||
|  | 	panic("BOOM!") | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								proc/proc.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								proc/proc.go
									
									
									
									
									
								
							| @ -741,6 +741,16 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e | |||||||
| 	// the offset of g struct inside TLS | 	// the offset of g struct inside TLS | ||||||
| 	dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG() | 	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 | 	return dbp, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -269,7 +269,7 @@ func TestClearBreakpointBreakpoint(t *testing.T) { | |||||||
| 			t.Fatalf("Breakpoint was not cleared data: %#v, int3: %#v", data, int3) | 			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") | 			t.Fatal("Breakpoint not removed internally") | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| @ -279,6 +279,16 @@ type nextTest struct { | |||||||
| 	begin, end int | 	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) { | func testnext(program string, testcases []nextTest, initialLocation string, t *testing.T) { | ||||||
| 	withTestProcess(program, t, func(p *Process, fixture protest.Fixture) { | 	withTestProcess(program, t, func(p *Process, fixture protest.Fixture) { | ||||||
| 		bp, err := setFunctionBreakpoint(p, initialLocation) | 		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)) | 			t.Fatal("Not all breakpoints were cleaned up", len(p.Breakpoints)) | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
| @ -1635,3 +1645,13 @@ func TestIssue149(t *testing.T) { | |||||||
| 		assertNoError(err, t, "FindFileLocation()") | 		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) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | |||||||
| @ -114,6 +114,9 @@ func (d *Debugger) Restart() error { | |||||||
| 		return fmt.Errorf("could not launch process: %s", err) | 		return fmt.Errorf("could not launch process: %s", err) | ||||||
| 	} | 	} | ||||||
| 	for _, oldBp := range d.breakpoints() { | 	for _, oldBp := range d.breakpoints() { | ||||||
|  | 		if oldBp.ID < 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
| 		newBp, err := p.SetBreakpoint(oldBp.Addr) | 		newBp, err := p.SetBreakpoint(oldBp.Addr) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
|  | |||||||
| @ -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) { | func TestClientServer_clearBreakpoint(t *testing.T) { | ||||||
| 	withTestClient("testprog", t, func(c service.Client) { | 	withTestClient("testprog", t, func(c service.Client) { | ||||||
| 		bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sleepytime", Line: 1}) | 		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) | 			t.Fatalf("Unexpected error: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		bps, err := c.ListBreakpoints() | 		if e, a := 1, countBreakpoints(t, c); e != a { | ||||||
| 		if e, a := 1, len(bps); e != a { |  | ||||||
| 			t.Fatalf("Expected breakpoint count %d, got %d", 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) | 			t.Fatalf("Expected deleted breakpoint ID %v, got %v", bp.ID, deleted.ID) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		bps, err = c.ListBreakpoints() | 		if e, a := 0, countBreakpoints(t, c); e != a { | ||||||
| 		if e, a := 0, len(bps); e != a { |  | ||||||
| 			t.Fatalf("Expected breakpoint count %d, got %d", e, a) | 			t.Fatalf("Expected breakpoint count %d, got %d", e, a) | ||||||
| 		} | 		} | ||||||
| 	}) | 	}) | ||||||
|  | |||||||
| @ -515,6 +515,10 @@ func clearAll(t *Term, ctx callContext, args string) error { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		if bp.ID < 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		_, err := t.client.ClearBreakpoint(bp.ID) | 		_, err := t.client.ClearBreakpoint(bp.ID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), formatBreakpointLocation(bp), err) | 			fmt.Printf("Couldn't delete %s at %s: %s\n", formatBreakpointName(bp, false), formatBreakpointLocation(bp), err) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 aarzilli
					aarzilli