mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +08:00 
			
		
		
		
	proc: Do not panic when a command is executed on an exited process
Fixes #355
This commit is contained in:
		
							
								
								
									
										30
									
								
								proc/proc.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								proc/proc.go
									
									
									
									
									
								
							| @ -229,6 +229,9 @@ func (dbp *Process) CurrentLocation() (*Location, error) { | ||||
| // RequestManualStop sets the `halt` flag and | ||||
| // sends SIGSTOP to all threads. | ||||
| func (dbp *Process) RequestManualStop() error { | ||||
| 	if dbp.exited { | ||||
| 		return &ProcessExitedError{} | ||||
| 	} | ||||
| 	dbp.halt = true | ||||
| 	return dbp.requestManualStop() | ||||
| } | ||||
| @ -237,6 +240,9 @@ func (dbp *Process) RequestManualStop() error { | ||||
| // break point table. Setting a break point must be thread specific due to | ||||
| // ptrace actions needing the thread to be in a signal-delivery-stop. | ||||
| func (dbp *Process) SetBreakpoint(addr uint64) (*Breakpoint, error) { | ||||
| 	if dbp.exited { | ||||
| 		return nil, &ProcessExitedError{} | ||||
| 	} | ||||
| 	return dbp.setBreakpoint(dbp.CurrentThread.ID, addr, false) | ||||
| } | ||||
|  | ||||
| @ -247,6 +253,9 @@ func (dbp *Process) SetTempBreakpoint(addr uint64) (*Breakpoint, error) { | ||||
|  | ||||
| // ClearBreakpoint clears the breakpoint at addr. | ||||
| func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) { | ||||
| 	if dbp.exited { | ||||
| 		return nil, &ProcessExitedError{} | ||||
| 	} | ||||
| 	bp, ok := dbp.FindBreakpoint(addr) | ||||
| 	if !ok { | ||||
| 		return nil, NoBreakpointError{addr: addr} | ||||
| @ -268,6 +277,9 @@ func (dbp *Process) Status() *WaitStatus { | ||||
|  | ||||
| // Next continues execution until the next source line. | ||||
| func (dbp *Process) Next() (err error) { | ||||
| 	if dbp.exited { | ||||
| 		return &ProcessExitedError{} | ||||
| 	} | ||||
| 	for i := range dbp.Breakpoints { | ||||
| 		if dbp.Breakpoints[i].Temp { | ||||
| 			return fmt.Errorf("next while nexting") | ||||
| @ -357,6 +369,9 @@ func (dbp *Process) setChanRecvBreakpoints() (int, error) { | ||||
| // process. It will continue until it hits a breakpoint | ||||
| // or is otherwise stopped. | ||||
| func (dbp *Process) Continue() error { | ||||
| 	if dbp.exited { | ||||
| 		return &ProcessExitedError{} | ||||
| 	} | ||||
| 	for { | ||||
| 		if err := dbp.resume(); err != nil { | ||||
| 			return err | ||||
| @ -498,6 +513,9 @@ func (dbp *Process) StepInstruction() (err error) { | ||||
|  | ||||
| // SwitchThread changes from current thread to the thread specified by `tid`. | ||||
| func (dbp *Process) SwitchThread(tid int) error { | ||||
| 	if dbp.exited { | ||||
| 		return &ProcessExitedError{} | ||||
| 	} | ||||
| 	if th, ok := dbp.Threads[tid]; ok { | ||||
| 		dbp.CurrentThread = th | ||||
| 		dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG() | ||||
| @ -509,6 +527,9 @@ func (dbp *Process) SwitchThread(tid int) error { | ||||
| // SwitchGoroutine changes from current thread to the thread | ||||
| // running the specified goroutine. | ||||
| func (dbp *Process) SwitchGoroutine(gid int) error { | ||||
| 	if dbp.exited { | ||||
| 		return &ProcessExitedError{} | ||||
| 	} | ||||
| 	g, err := dbp.FindGoroutine(gid) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @ -527,6 +548,9 @@ func (dbp *Process) SwitchGoroutine(gid int) error { | ||||
| // GoroutinesInfo returns an array of G structures representing the information | ||||
| // Delve cares about from the internal runtime G structure. | ||||
| func (dbp *Process) GoroutinesInfo() ([]*G, error) { | ||||
| 	if dbp.exited { | ||||
| 		return nil, &ProcessExitedError{} | ||||
| 	} | ||||
| 	if dbp.allGCache != nil { | ||||
| 		return dbp.allGCache, nil | ||||
| 	} | ||||
| @ -597,6 +621,9 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) { | ||||
|  | ||||
| // Halt stops all threads. | ||||
| func (dbp *Process) Halt() (err error) { | ||||
| 	if dbp.exited { | ||||
| 		return &ProcessExitedError{} | ||||
| 	} | ||||
| 	for _, th := range dbp.Threads { | ||||
| 		if err := th.Halt(); err != nil { | ||||
| 			return err | ||||
| @ -817,6 +844,9 @@ func (dbp *Process) FindGoroutine(gid int) (*G, error) { | ||||
| // ConvertEvalScope returns a new EvalScope in the context of the | ||||
| // specified goroutine ID and stack frame. | ||||
| func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) { | ||||
| 	if dbp.exited { | ||||
| 		return nil, &ProcessExitedError{} | ||||
| 	} | ||||
| 	g, err := dbp.FindGoroutine(gid) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|  | ||||
| @ -213,22 +213,29 @@ func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint { | ||||
| } | ||||
|  | ||||
| // Threads returns the threads of the target process. | ||||
| func (d *Debugger) Threads() []*api.Thread { | ||||
| func (d *Debugger) Threads() ([]*api.Thread, error) { | ||||
| 	if d.process.Exited() { | ||||
| 		return nil, &proc.ProcessExitedError{} | ||||
| 	} | ||||
| 	threads := []*api.Thread{} | ||||
| 	for _, th := range d.process.Threads { | ||||
| 		threads = append(threads, api.ConvertThread(th)) | ||||
| 	} | ||||
| 	return threads | ||||
| 	return threads, nil | ||||
| } | ||||
|  | ||||
| // FindThread returns the thread for the given 'id'. | ||||
| func (d *Debugger) FindThread(id int) *api.Thread { | ||||
| 	for _, thread := range d.Threads() { | ||||
| func (d *Debugger) FindThread(id int) (*api.Thread, error) { | ||||
| 	threads, err := d.Threads() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, thread := range threads { | ||||
| 		if thread.ID == id { | ||||
| 			return thread | ||||
| 			return thread, nil | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| // Command handles commands which control the debugger lifecycle | ||||
|  | ||||
| @ -162,13 +162,16 @@ func (s *RPCServer) ClearBreakpoint(id int, breakpoint *api.Breakpoint) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) error { | ||||
| 	*threads = s.debugger.Threads() | ||||
| 	return nil | ||||
| func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) { | ||||
| 	*threads, err = s.debugger.Threads() | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func (s *RPCServer) GetThread(id int, thread *api.Thread) error { | ||||
| 	t := s.debugger.FindThread(id) | ||||
| 	t, err := s.debugger.FindThread(id) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if t == nil { | ||||
| 		return fmt.Errorf("no thread with id %d", id) | ||||
| 	} | ||||
| @ -201,7 +204,11 @@ type ThreadListArgs struct { | ||||
| } | ||||
|  | ||||
| func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api.Variable) error { | ||||
| 	if thread := s.debugger.FindThread(args.Id); thread == nil { | ||||
| 	thread, err := s.debugger.FindThread(args.Id) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if thread == nil { | ||||
| 		return fmt.Errorf("no thread with id %d", args.Id) | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @ -28,6 +28,14 @@ func assertNoError(err error, t *testing.T, s string) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func assertError(err error, t *testing.T, s string) { | ||||
| 	if err == nil { | ||||
| 		_, file, line, _ := runtime.Caller(1) | ||||
| 		fname := filepath.Base(file) | ||||
| 		t.Fatalf("failed assertion at %s:%d: %s (no error)\n", fname, line, s) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestMain(m *testing.M) { | ||||
| 	os.Exit(protest.RunTestsWithFixtures(m)) | ||||
| } | ||||
| @ -734,3 +742,59 @@ func TestClientServer_FullStacktrace(t *testing.T) { | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestIssue355(t *testing.T) { | ||||
| 	// After the target process has terminated should return an error but not crash | ||||
| 	withTestClient("continuetestprog", t, func(c service.Client) { | ||||
| 		bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: -1}) | ||||
| 		assertNoError(err, t, "CreateBreakpoint()") | ||||
| 		ch := c.Continue() | ||||
| 		state := <-ch | ||||
| 		tid := state.CurrentThread.ID | ||||
| 		gid := state.SelectedGoroutine.ID | ||||
| 		assertNoError(state.Err, t, "First Continue()") | ||||
| 		ch = c.Continue() | ||||
| 		state = <-ch | ||||
| 		if !state.Exited { | ||||
| 			t.Fatalf("Target did not terminate after second continue") | ||||
| 		} | ||||
|  | ||||
| 		ch = c.Continue() | ||||
| 		state = <-ch | ||||
| 		assertError(state.Err, t, "Continue()") | ||||
|  | ||||
| 		_, err = c.Next() | ||||
| 		assertError(err, t, "Next()") | ||||
| 		_, err = c.Step() | ||||
| 		assertError(err, t, "Step()") | ||||
| 		_, err = c.StepInstruction() | ||||
| 		assertError(err, t, "StepInstruction()") | ||||
| 		_, err = c.SwitchThread(tid) | ||||
| 		assertError(err, t, "SwitchThread()") | ||||
| 		_, err = c.SwitchGoroutine(gid) | ||||
| 		assertError(err, t, "SwitchGoroutine()") | ||||
| 		_, err = c.Halt() | ||||
| 		assertError(err, t, "Halt()") | ||||
| 		_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: -1}) | ||||
| 		assertError(err, t, "CreateBreakpoint()") | ||||
| 		_, err = c.ClearBreakpoint(bp.ID) | ||||
| 		assertError(err, t, "ClearBreakpoint()") | ||||
| 		_, err = c.ListThreads() | ||||
| 		assertError(err, t, "ListThreads()") | ||||
| 		_, err = c.GetThread(tid) | ||||
| 		assertError(err, t, "GetThread()") | ||||
| 		assertError(c.SetVariable(api.EvalScope{gid, 0}, "a", "10"), t, "SetVariable()") | ||||
| 		_, err = c.ListLocalVariables(api.EvalScope{gid, 0}) | ||||
| 		assertError(err, t, "ListLocalVariables()") | ||||
| 		_, err = c.ListFunctionArgs(api.EvalScope{gid, 0}) | ||||
| 		assertError(err, t, "ListFunctionArgs()") | ||||
| 		_, err = c.ListRegisters() | ||||
| 		assertError(err, t, "ListRegisters()") | ||||
| 		_, err = c.ListGoroutines() | ||||
| 		assertError(err, t, "ListGoroutines()") | ||||
| 		_, err = c.Stacktrace(gid, 10, false) | ||||
| 		assertError(err, t, "Stacktrace()") | ||||
| 		_, err = c.FindLocation(api.EvalScope{gid, 0}, "+1") | ||||
| 		assertError(err, t, "FindLocation()") | ||||
| 	}) | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 aarzilli
					aarzilli