proc/native: refactor Halt/setCurrentBreakpoint call pair

This commit is contained in:
aarzilli 2018-02-13 17:10:44 +01:00 committed by Derek Parker
parent e47599d09a
commit f26bb0b875
8 changed files with 122 additions and 159 deletions

@ -246,10 +246,7 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := dbp.Halt(); err != nil { if err := dbp.stop(trapthread); err != nil {
return nil, dbp.exitGuard(err)
}
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
return nil, err return nil, err
} }
return trapthread, err return trapthread, err
@ -324,19 +321,6 @@ func (dbp *Process) SwitchGoroutine(gid int) error {
return nil return nil
} }
// Halt stops all threads.
func (dbp *Process) Halt() (err error) {
if dbp.exited {
return &proc.ProcessExitedError{Pid: dbp.Pid()}
}
for _, th := range dbp.threads {
if err := th.Halt(); err != nil {
return err
}
}
return nil
}
// FindBreakpoint finds the breakpoint for the given pc. // FindBreakpoint finds the breakpoint for the given pc.
func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) { func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit). // Check to see if address is past the breakpoint, (i.e. breakpoint was hit).

@ -111,12 +111,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := dbp.Halt(); err != nil { if err := dbp.stop(nil); err != nil {
return nil, dbp.exitGuard(err)
}
_, err = dbp.waitForStop()
if err != nil {
return nil, err return nil, err
} }
@ -384,23 +379,6 @@ func (dbp *Process) waitForStop() ([]int, error) {
} }
} }
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
ports, err := dbp.waitForStop()
if err != nil {
return err
}
trapthread.SetCurrentBreakpoint()
for _, port := range ports {
if th, ok := dbp.threads[port]; ok {
err := th.SetCurrentBreakpoint()
if err != nil {
return err
}
}
}
return nil
}
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) { func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
wg.Done() wg.Done()
} }
@ -446,6 +424,39 @@ func (dbp *Process) resume() error {
return nil return nil
} }
// stop stops all running threads threads and sets breakpoints
func (dbp *Process) stop(trapthread *Thread) (err error) {
if dbp.exited {
return &proc.ProcessExitedError{Pid: dbp.Pid()}
}
for _, th := range dbp.threads {
if !th.Stopped() {
if err := th.halt(); err != nil {
return dbp.exitGuard(err)
}
}
th.running = false
}
ports, err := dbp.waitForStop()
if err != nil {
return err
}
if !dbp.os.initialized {
return nil
}
trapthread.SetCurrentBreakpoint()
for _, port := range ports {
if th, ok := dbp.threads[port]; ok {
err := th.SetCurrentBreakpoint()
if err != nil {
return err
}
}
}
return nil
}
func (dbp *Process) detach(kill bool) error { func (dbp *Process) detach(kill bool) error {
return PtraceDetach(dbp.pid, 0) return PtraceDetach(dbp.pid, 0)
} }

@ -377,35 +377,6 @@ func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
} }
} }
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
// wait for all threads to stop
for {
allstopped := true
for _, th := range dbp.threads {
if th.running {
allstopped = false
break
}
}
if allstopped {
break
}
dbp.halt = true
_, err := dbp.trapWait(-1)
if err != nil {
return err
}
}
for _, th := range dbp.threads {
if th.CurrentBreakpoint.Breakpoint == nil {
if err := th.SetCurrentBreakpoint(); err != nil {
return err
}
}
}
return nil
}
func (dbp *Process) exitGuard(err error) error { func (dbp *Process) exitGuard(err error) error {
if err != sys.ESRCH { if err != sys.ESRCH {
return err return err
@ -437,6 +408,49 @@ func (dbp *Process) resume() error {
return nil return nil
} }
// stop stops all running threads threads and sets breakpoints
func (dbp *Process) stop(trapthread *Thread) (err error) {
if dbp.exited {
return &proc.ProcessExitedError{Pid: dbp.Pid()}
}
for _, th := range dbp.threads {
if !th.Stopped() {
if err := th.halt(); err != nil {
return dbp.exitGuard(err)
}
}
}
// wait for all threads to stop
for {
allstopped := true
for _, th := range dbp.threads {
if th.running {
allstopped = false
break
}
}
if allstopped {
break
}
dbp.halt = true
_, err := dbp.trapWait(-1)
if err != nil {
return err
}
}
// set breakpoints on all threads
for _, th := range dbp.threads {
if th.CurrentBreakpoint.Breakpoint == nil {
if err := th.SetCurrentBreakpoint(); err != nil {
return err
}
}
}
return nil
}
func (dbp *Process) detach(kill bool) error { func (dbp *Process) detach(kill bool) error {
for threadID := range dbp.threads { for threadID := range dbp.threads {
err := PtraceDetach(threadID, 0) err := PtraceDetach(threadID, 0)

@ -2,7 +2,6 @@ package native
import ( import (
"debug/pe" "debug/pe"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -175,9 +174,6 @@ func (dbp *Process) kill() error {
if dbp.exited { if dbp.exited {
return nil return nil
} }
if !dbp.threads[dbp.pid].Stopped() {
return errors.New("process must be stopped in order to kill it")
}
p, err := os.FindProcess(dbp.pid) p, err := os.FindProcess(dbp.pid)
if err != nil { if err != nil {
@ -393,7 +389,40 @@ func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
return 0, nil, fmt.Errorf("not implemented: wait") return 0, nil, fmt.Errorf("not implemented: wait")
} }
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error { func (dbp *Process) exitGuard(err error) error {
return err
}
func (dbp *Process) resume() error {
for _, thread := range dbp.threads {
if thread.CurrentBreakpoint.Breakpoint != nil {
if err := thread.StepInstruction(); err != nil {
return err
}
thread.CurrentBreakpoint.Clear()
}
}
for _, thread := range dbp.threads {
thread.running = true
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
return err
}
}
return nil
}
// stop stops all running threads threads and sets breakpoints
func (dbp *Process) stop(trapthread *Thread) (err error) {
if dbp.exited {
return &proc.ProcessExitedError{Pid: dbp.Pid()}
}
for _, th := range dbp.threads {
th.running = false
}
// While the debug event that stopped the target was being propagated // While the debug event that stopped the target was being propagated
// other target threads could generate other debug events. // other target threads could generate other debug events.
// After this function we need to know about all the threads // After this function we need to know about all the threads
@ -404,7 +433,7 @@ func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
// call to _ContinueDebugEvent will resume execution of some of the // call to _ContinueDebugEvent will resume execution of some of the
// target threads. // target threads.
err := trapthread.SetCurrentBreakpoint() err = trapthread.SetCurrentBreakpoint()
if err != nil { if err != nil {
return err return err
} }
@ -441,31 +470,6 @@ func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
return nil return nil
} }
func (dbp *Process) exitGuard(err error) error {
return err
}
func (dbp *Process) resume() error {
for _, thread := range dbp.threads {
if thread.CurrentBreakpoint.Breakpoint != nil {
if err := thread.StepInstruction(); err != nil {
return err
}
thread.CurrentBreakpoint.Clear()
}
}
for _, thread := range dbp.threads {
thread.running = true
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
return err
}
}
return nil
}
func (dbp *Process) detach(kill bool) error { func (dbp *Process) detach(kill bool) error {
if !kill { if !kill {
for _, thread := range dbp.threads { for _, thread := range dbp.threads {

@ -113,13 +113,6 @@ func (thread *Thread) SetPC(pc uint64) error {
return regs.SetPC(thread, pc) return regs.SetPC(thread, pc)
} }
// Stopped returns whether the thread is stopped at
// the operating system level. Actual implementation
// is OS dependant, look in OS thread file.
func (thread *Thread) Stopped() bool {
return thread.stopped()
}
// SetCurrentBreakpoint sets the current breakpoint that this // SetCurrentBreakpoint sets the current breakpoint that this
// thread is stopped at as CurrentBreakpoint on the thread struct. // thread is stopped at as CurrentBreakpoint on the thread struct.
func (thread *Thread) SetCurrentBreakpoint() error { func (thread *Thread) SetCurrentBreakpoint() error {

@ -27,20 +27,6 @@ type OSSpecificDetails struct {
// be continued. // be continued.
var ErrContinueThread = fmt.Errorf("could not continue thread") var ErrContinueThread = fmt.Errorf("could not continue thread")
// Halt stops this thread from executing.
func (thread *Thread) Halt() (err error) {
defer func() {
if err == nil {
thread.running = false
}
}()
if thread.Stopped() {
return
}
err = thread.halt()
return
}
func (t *Thread) halt() (err error) { func (t *Thread) halt() (err error) {
kret := C.thread_suspend(t.os.threadAct) kret := C.thread_suspend(t.os.threadAct)
if kret != C.KERN_SUCCESS { if kret != C.KERN_SUCCESS {
@ -116,7 +102,9 @@ func (t *Thread) Blocked() bool {
} }
} }
func (t *Thread) stopped() bool { // Stopped returns whether the thread is stopped at
// the operating system level.
func (t *Thread) Stopped() bool {
return C.thread_blocked(t.os.threadAct) > C.int(0) return C.thread_blocked(t.os.threadAct) > C.int(0)
} }

@ -16,15 +16,6 @@ type OSSpecificDetails struct {
registers sys.PtraceRegs registers sys.PtraceRegs
} }
// Halt stops this thread from executing.
func (thread *Thread) Halt() (err error) {
if thread.Stopped() {
return
}
err = thread.halt()
return
}
func (t *Thread) halt() (err error) { func (t *Thread) halt() (err error) {
err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP) err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
if err != nil { if err != nil {
@ -34,7 +25,9 @@ func (t *Thread) halt() (err error) {
return return
} }
func (t *Thread) stopped() bool { // Stopped returns whether the thread is stopped at
// the operating system level.
func (t *Thread) Stopped() bool {
state := status(t.ID, t.dbp.os.comm) state := status(t.ID, t.dbp.os.comm)
return state == StatusTraceStop || state == StatusTraceStopT return state == StatusTraceStop || state == StatusTraceStopT
} }

@ -18,30 +18,6 @@ type OSSpecificDetails struct {
hThread syscall.Handle hThread syscall.Handle
} }
// Halt stops this thread from executing.
func (thread *Thread) Halt() (err error) {
defer func() {
if err == nil {
thread.running = false
}
}()
if thread.Stopped() {
return
}
err = thread.halt()
return
}
func (t *Thread) halt() (err error) {
// Ignore the request to halt. On Windows, all threads are halted
// on return from WaitForDebugEvent.
return nil
// TODO - This may not be correct in all usages of dbp.Halt. There
// are some callers who use dbp.Halt() to stop the process when it is not
// already broken on a debug event.
}
func (t *Thread) singleStep() error { func (t *Thread) singleStep() error {
context := newCONTEXT() context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL context.ContextFlags = _CONTEXT_ALL
@ -140,9 +116,9 @@ func (t *Thread) Blocked() bool {
} }
} }
func (t *Thread) stopped() bool { // Stopped returns whether the thread is stopped at the operating system
// TODO: We are assuming that threads are always stopped // level. On windows this always returns true.
// during command execution. func (t *Thread) Stopped() bool {
return true return true
} }