diff --git a/proctl/proctl.go b/proctl/proctl.go index 057cb73386aae2375aa874d7fabe338780e9fe91..31e7744f75d72c47dec1aec94e365c905d9f0ed5 100644 --- a/proctl/proctl.go +++ b/proctl/proctl.go @@ -52,6 +52,7 @@ func Attach(pid int) (*DebuggedProcess, error) { return nil, err } // Attach to all currently active threads. + // TODO(dp) doing this in newDebugProcess already for mach if err := dbp.updateThreadList(); err != nil { return nil, err } @@ -289,6 +290,44 @@ func (dbp *DebuggedProcess) DwarfReader() *reader.Reader { return reader.New(dbp.Dwarf) } +// Returns a new DebuggedProcess struct. +func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) { + dbp := DebuggedProcess{ + Pid: pid, + Threads: make(map[int]*ThreadContext), + BreakPoints: make(map[uint64]*BreakPoint), + os: new(OSProcessDetails), + } + + if attach { + err := sys.PtraceAttach(pid) + if err != nil { + return nil, err + } + _, _, err = wait(pid, 0) + if err != nil { + return nil, err + } + } + + proc, err := os.FindProcess(pid) + if err != nil { + return nil, err + } + + dbp.Process = proc + err = dbp.LoadInformation() + if err != nil { + return nil, err + } + + if err := dbp.updateThreadList(); err != nil { + return nil, err + } + + return &dbp, nil +} + func (dbp *DebuggedProcess) run(fn func() error) error { dbp.running = true dbp.halt = false diff --git a/proctl/proctl_darwin.go b/proctl/proctl_darwin.go index 485bcb7827c1a2895a7e21b0ae94fb3a02b03506..a7e0da38b4d4617ecf6cb81b4c542b6530c46279 100644 --- a/proctl/proctl_darwin.go +++ b/proctl/proctl_darwin.go @@ -62,33 +62,6 @@ func (dbp *DebuggedProcess) LoadInformation() error { return nil } -// Returns a new DebuggedProcess struct. -func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) { - dbp := DebuggedProcess{ - Pid: pid, - Threads: make(map[int]*ThreadContext), - BreakPoints: make(map[uint64]*BreakPoint), - os: new(OSProcessDetails), - } - - proc, err := os.FindProcess(pid) - if err != nil { - return nil, err - } - - dbp.Process = proc - err = dbp.LoadInformation() - if err != nil { - return nil, err - } - - if err := dbp.updateThreadList(); err != nil { - return nil, err - } - - return &dbp, nil -} - func (dbp *DebuggedProcess) updateThreadList() error { var ( err error diff --git a/proctl/proctl_linux.go b/proctl/proctl_linux.go index 267587b778c36136f46cbef49d2caccbbece17a3..c7681f6bbeba2d92359ead1ce91f01f8c7132f94 100644 --- a/proctl/proctl_linux.go +++ b/proctl/proctl_linux.go @@ -33,44 +33,6 @@ func (dbp *DebuggedProcess) Halt() (err error) { return nil } -// Steps through process. -func (dbp *DebuggedProcess) Step() (err error) { - var ( - th *ThreadContext - ok bool - ) - - allm, err := dbp.CurrentThread.AllM() - if err != nil { - return err - } - - fn := func() error { - for _, m := range allm { - th, ok = dbp.Threads[m.procid] - fmt.Println(ok, m.procid) - if !ok { - if m.procid == 0 { - // TODO(dp) might not work for linux - th = dbp.Threads[dbp.CurrentThread.Id] - } - return fmt.Errorf("m->procid is invalid port") - } - - if m.blocked == 0 { - err := th.Step() - if err != nil { - return err - } - } - - } - return nil - } - - return dbp.run(fn) -} - // Finds the executable from /proc//exe and then // uses that to parse the following information: // * Dwarf .debug_frame section @@ -144,19 +106,21 @@ func (dbp *DebuggedProcess) addThread(tid int, attach bool) (*ThreadContext, err return dbp.Threads[tid], nil } -func (dbp *DebuggedProcess) refreshThreadList() error { +func (dbp *DebuggedProcess) updateThreadList() error { allm, err := dbp.CurrentThread.AllM() if err != nil { return err } + // TODO(dp) user /proc//task to remove reliance on allm for _, m := range allm { - if tid == 0 { + if m.procid == 0 { continue } if _, err := dbp.addThread(m.procid, false); err != nil { return err } } + return nil } func (dbp *DebuggedProcess) findExecutable() (*elf.File, error) { @@ -232,46 +196,11 @@ func (dbp *DebuggedProcess) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) { dbp.GoSymTable = tab } -func newDebugProcess(pid int, attach bool) (*DebuggedProcess, error) { - dbp := DebuggedProcess{ - Pid: pid, - Threads: make(map[int]*ThreadContext), - BreakPoints: make(map[uint64]*BreakPoint), - os: new(OSProcessDetails), - } - - if attach { - thread, err := dbp.AttachThread(pid) - if err != nil { - return nil, err - } - dbp.CurrentThread = thread - } else { - thread, err := dbp.addThread(pid) - if err != nil { - return nil, err - } - dbp.CurrentThread = thread - } - - proc, err := os.FindProcess(pid) - if err != nil { - return nil, err - } - - dbp.Process = proc - err = dbp.LoadInformation() - if err != nil { - return nil, err - } - - return &dbp, nil -} - +// TODO(dp) seems like it could be unneccessary func addNewThread(dbp *DebuggedProcess, cloner, cloned int) error { fmt.Println("new thread spawned", cloned) - th, err := dbp.addThread(cloned) + th, err := dbp.addThread(cloned, false) if err != nil { return err } diff --git a/proctl/registers_linux_amd64.go b/proctl/registers_linux_amd64.go index ddb5f0da3a4d5bc1ffe51095a8c67014ab75b8ba..2a2793b6db9cf4148b9e1391fcb1dfd690c7bd3c 100644 --- a/proctl/registers_linux_amd64.go +++ b/proctl/registers_linux_amd64.go @@ -14,14 +14,14 @@ func (r *Regs) SP() uint64 { return r.regs.Rsp } -func (r *Regs) SetPC(tid int, pc uint64) error { +func (r *Regs) SetPC(thread *ThreadContext, pc uint64) error { r.regs.SetPC(pc) - return sys.PtraceSetRegs(tid, r.regs) + return sys.PtraceSetRegs(thread.Id, r.regs) } -func registers(tid int) (Registers, error) { +func registers(thread *ThreadContext) (Registers, error) { var regs sys.PtraceRegs - err := sys.PtraceGetRegs(tid, ®s) + err := sys.PtraceGetRegs(thread.Id, ®s) if err != nil { return nil, err } diff --git a/proctl/threads_linux.go b/proctl/threads_linux.go index 5f356d00a443a1258dee2928b53fdb98c749a6f3..bfaf676d11131f1414193ab3a04814aad8f00b3c 100644 --- a/proctl/threads_linux.go +++ b/proctl/threads_linux.go @@ -16,32 +16,43 @@ func (t *ThreadContext) Halt() error { } err := sys.Tgkill(t.Process.Pid, t.Id, sys.SIGSTOP) if err != nil { - return fmt.Errorf("Halt err %s %d", err, pid) + return fmt.Errorf("Halt err %s %d", err, t.Id) } - pid, _, err := wait(th.Id, sys.WNOHANG) + _, _, err = wait(t.Id, sys.WNOHANG) if err != nil { - return fmt.Errorf("wait err %s %d", err, pid) + return fmt.Errorf("wait err %s %d", err, t.Id) } return nil } func (t *ThreadContext) cont() error { - return PtraceCont(thread.Id, 0) + return PtraceCont(t.Id, 0) } func (t *ThreadContext) singleStep() error { err := sys.PtraceSingleStep(t.Id) - if err != nill { + if err != nil { return err } - _, _, err = wait(thread.Id, 0) + _, _, err = wait(t.Id, 0) return err } -func writeMemory(tid int, addr uintptr, data []byte) (int, error) { - return sys.PtracePokeData(tid, addr, data) +func (t *ThreadContext) blocked() bool { + // TODO(dp) cache the func pc to remove this lookup + // TODO(dp) check err + pc, _ := t.CurrentPC() + fn := t.Process.GoSymTable.PCToFunc(pc) + if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep")) { + return true + } + return false +} + +func writeMemory(thread *ThreadContext, addr uintptr, data []byte) (int, error) { + return sys.PtracePokeData(thread.Id, addr, data) } -func readMemory(tid int, addr uintptr, data []byte) (int, error) { - return sys.PtracePeekData(tid, addr, data) +func readMemory(thread *ThreadContext, addr uintptr, data []byte) (int, error) { + return sys.PtracePeekData(thread.Id, addr, data) }