提交 3a8730de 编写于 作者: A aarzilli

proc: Do not panic when a command is executed on an exited process

Fixes #355
上级 fb721093
......@@ -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()")
})
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册