Handle SIGINT

Handle SIGINT by stopping the traced program and then displaying a
prompt to the user for commands. If the traced process is not running,
this is a noop.

Closes #30
This commit is contained in:
Derek Parker 2015-01-09 16:24:33 -06:00
parent 6acb912a0c
commit bc39ddfbbc
3 changed files with 99 additions and 40 deletions

@ -5,6 +5,7 @@ import (
"io" "io"
"os" "os"
"os/exec" "os/exec"
"os/signal"
"strings" "strings"
"syscall" "syscall"
@ -47,6 +48,16 @@ func Run(run bool, pid int, args []string) {
} }
} }
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGINT)
go func() {
for _ = range ch {
if dbp.Running() {
dbp.RequestManualStop()
}
}
}()
cmds := command.DebugCommands() cmds := command.DebugCommands()
goreadline.LoadHistoryFromFile(historyFile) goreadline.LoadHistoryFromFile(historyFile)
fmt.Println("Type 'help' for list of commands.") fmt.Println("Type 'help' for list of commands.")

@ -28,6 +28,8 @@ type DebuggedProcess struct {
BreakPoints map[uint64]*BreakPoint BreakPoints map[uint64]*BreakPoint
Threads map[int]*ThreadContext Threads map[int]*ThreadContext
CurrentThread *ThreadContext CurrentThread *ThreadContext
running bool
halt bool
} }
// Represents a single breakpoint. Stores information on the break // Represents a single breakpoint. Stores information on the break
@ -49,6 +51,16 @@ type BreakPointExistsError struct {
addr uint64 addr uint64
} }
func (bpe BreakPointExistsError) Error() string {
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
}
type ManualStopError struct{}
func (mse ManualStopError) Error() string {
return "Manual stop requested"
}
// ProcessStatus is the result of parsing the data from // ProcessStatus is the result of parsing the data from
// the /proc/<pid>/stats psuedo file. // the /proc/<pid>/stats psuedo file.
type ProcessStatus struct { type ProcessStatus struct {
@ -68,10 +80,6 @@ var (
breakpointIDCounter = 0 breakpointIDCounter = 0
) )
func (bpe BreakPointExistsError) Error() string {
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
}
func Attach(pid int) (*DebuggedProcess, error) { func Attach(pid int) (*DebuggedProcess, error) {
dbp, err := newDebugProcess(pid, true) dbp, err := newDebugProcess(pid, true)
if err != nil { if err != nil {
@ -175,6 +183,10 @@ func (dbp *DebuggedProcess) AttachThread(tid int) (*ThreadContext, error) {
return dbp.addThread(tid) return dbp.addThread(tid)
} }
func (dbp *DebuggedProcess) Running() bool {
return dbp.running
}
// Find a location by string (file+line, function, breakpoint id, addr) // Find a location by string (file+line, function, breakpoint id, addr)
func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) { func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) {
// File + Line // File + Line
@ -222,6 +234,18 @@ func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) {
} }
} }
func (dbp *DebuggedProcess) RequestManualStop() {
dbp.halt = true
for _, th := range dbp.Threads {
ps, _ := parseProcessStatus(th.Id)
if ps.state == STATUS_TRACE_STOP {
continue
}
syscall.Tgkill(dbp.Pid, th.Id, syscall.SIGSTOP)
}
dbp.running = false
}
// Sets a breakpoint in the current thread. // Sets a breakpoint in the current thread.
func (dbp *DebuggedProcess) Break(addr uint64) (*BreakPoint, error) { func (dbp *DebuggedProcess) Break(addr uint64) (*BreakPoint, error) {
return dbp.CurrentThread.Break(addr) return dbp.CurrentThread.Break(addr)
@ -278,22 +302,25 @@ func (dbp *DebuggedProcess) Step() (err error) {
return err return err
} }
for _, m := range allm { fn := func() error {
th, ok = dbp.Threads[m.procid] for _, m := range allm {
if !ok { th, ok = dbp.Threads[m.procid]
th = dbp.Threads[dbp.Pid] if !ok {
} th = dbp.Threads[dbp.Pid]
if m.blocked == 0 {
err := th.Step()
if err != nil {
return err
} }
}
if m.blocked == 0 {
err := th.Step()
if err != nil {
return err
}
}
}
return nil
} }
return nil return dbp.run(fn)
} }
// Step over function calls. // Step over function calls.
@ -308,29 +335,32 @@ func (dbp *DebuggedProcess) Next() error {
return err return err
} }
for _, m := range allm { fn := func() error {
th, ok = dbp.Threads[m.procid] for _, m := range allm {
if !ok { th, ok = dbp.Threads[m.procid]
th = dbp.Threads[dbp.Pid] if !ok {
} th = dbp.Threads[dbp.Pid]
}
if m.blocked == 1 { if m.blocked == 1 {
// Continue any blocked M so that the // Continue any blocked M so that the
// scheduler can continue to do its' // scheduler can continue to do its'
// job correctly. // job correctly.
err := th.Continue() err := th.Continue()
if err != nil { if err != nil {
return err
}
continue
}
err := th.Next()
if err != nil && err != syscall.ESRCH {
return err return err
} }
continue
}
err := th.Next()
if err != nil && err != syscall.ESRCH {
return err
} }
return stopTheWorld(dbp)
} }
return stopTheWorld(dbp) return dbp.run(fn)
} }
// Resume process. // Resume process.
@ -342,11 +372,14 @@ func (dbp *DebuggedProcess) Continue() error {
} }
} }
wpid, _, err := trapWait(dbp, -1, 0) fn := func() error {
if err != nil { wpid, _, err := trapWait(dbp, -1)
return err if err != nil {
return err
}
return handleBreakPoint(dbp, wpid)
} }
return handleBreakPoint(dbp, wpid) return dbp.run(fn)
} }
// Obtains register values from what Delve considers to be the current // Obtains register values from what Delve considers to be the current
@ -377,6 +410,18 @@ func (dbp *DebuggedProcess) DwarfReader() *reader.Reader {
return reader.New(dbp.Dwarf) return reader.New(dbp.Dwarf)
} }
func (dbp *DebuggedProcess) run(fn func() error) error {
dbp.running = true
dbp.halt = false
defer func() { dbp.running = false }()
if err := fn(); err != nil {
if _, ok := err.(ManualStopError); !ok {
return err
}
}
return nil
}
type ProcessExitedError struct { type ProcessExitedError struct {
pid int pid int
} }
@ -385,7 +430,7 @@ func (pe ProcessExitedError) Error() string {
return fmt.Sprintf("process %d has exited", pe.pid) return fmt.Sprintf("process %d has exited", pe.pid)
} }
func trapWait(dbp *DebuggedProcess, pid int, options int) (int, *syscall.WaitStatus, error) { func trapWait(dbp *DebuggedProcess, pid int) (int, *syscall.WaitStatus, error) {
for { for {
wpid, status, err := wait(pid, 0) wpid, status, err := wait(pid, 0)
if err != nil { if err != nil {
@ -410,6 +455,9 @@ func trapWait(dbp *DebuggedProcess, pid int, options int) (int, *syscall.WaitSta
if status.StopSignal() == syscall.SIGTRAP { if status.StopSignal() == syscall.SIGTRAP {
return wpid, status, nil return wpid, status, nil
} }
if status.StopSignal() == syscall.SIGSTOP && dbp.halt {
return -1, nil, ManualStopError{}
}
} }
} }

@ -264,7 +264,7 @@ func (thread *ThreadContext) continueToReturnAddress(pc uint64, fde *frame.Frame
// change the goroutine context on us, we there is // change the goroutine context on us, we there is
// no guarantee that waiting on this tid will ever // no guarantee that waiting on this tid will ever
// return. // return.
wpid, _, err := trapWait(thread.Process, -1, 0) wpid, _, err := trapWait(thread.Process, -1)
if err != nil { if err != nil {
return err return err
} }