Improve handling of manual stops

This commit is contained in:
Derek Parker
2015-04-13 17:17:06 -05:00
parent 0d5e0dbd4a
commit 20c9e92cec
6 changed files with 62 additions and 21 deletions

View File

@ -185,12 +185,14 @@ func (dbp *DebuggedProcess) FindLocation(str string) (uint64, error) {
// Sends out a request that the debugged process halt
// execution. Sends SIGSTOP to all threads.
func (dbp *DebuggedProcess) RequestManualStop() {
func (dbp *DebuggedProcess) RequestManualStop() error {
dbp.halt = true
for _, th := range dbp.Threads {
th.Halt()
err := dbp.requestManualStop()
if err != nil {
return err
}
dbp.running = false
return nil
}
// Sets a breakpoint at addr, and stores it in the process wide
@ -331,10 +333,8 @@ func (dbp *DebuggedProcess) resume() error {
if err != nil {
return err
}
if dbp.CurrentBreakpoint != nil {
if !dbp.CurrentBreakpoint.Temp {
return dbp.Halt()
}
if dbp.CurrentBreakpoint != nil || dbp.halt {
return dbp.Halt()
}
// Check to see if we hit a runtime.breakpoint
fn := dbp.goSymTable.PCToFunc(pc)
@ -407,6 +407,15 @@ func (dbp *DebuggedProcess) GoroutinesInfo() ([]*G, error) {
return allg, nil
}
func (dbp *DebuggedProcess) Halt() (err error) {
for _, th := range dbp.Threads {
if err := th.Halt(); err != nil {
return err
}
}
return nil
}
// Obtains register values from what Delve considers to be the current
// thread of the traced process.
func (dbp *DebuggedProcess) Registers() (Registers, error) {

View File

@ -158,3 +158,8 @@ mach_port_wait(mach_port_t port_set) {
return thread;
}
kern_return_t
raise_exception(mach_port_t task, mach_port_t thread, mach_port_t exception_port, exception_type_t exception) {
return exception_raise(exception_port, thread, task, exception, 0, 0);
}

View File

@ -22,12 +22,15 @@ type OSProcessDetails struct {
notificationPort C.mach_port_t
}
func (dbp *DebuggedProcess) Halt() (err error) {
for _, th := range dbp.Threads {
err := th.Halt()
if err != nil {
return err
}
func (dbp *DebuggedProcess) requestManualStop() (err error) {
var (
task = C.mach_port_t(dbp.os.task)
thread = C.mach_port_t(dbp.CurrentThread.os.thread_act)
exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
)
kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
if kret != C.KERN_SUCCESS {
return fmt.Errorf("could not raise mach exception")
}
return nil
}

View File

@ -37,3 +37,7 @@ thread_count(task_t task);
mach_port_t
mach_port_wait(mach_port_t);
kern_return_t
raise_exception(mach_port_t, mach_port_t, mach_port_t, exception_type_t);

View File

@ -25,14 +25,8 @@ const (
// Not actually needed for Linux.
type OSProcessDetails interface{}
func (dbp *DebuggedProcess) Halt() (err error) {
for _, th := range dbp.Threads {
err := th.Halt()
if err != nil {
return err
}
}
return nil
func (dbp *DebuggedProcess) requestManualStop() (err error) {
return sys.Kill(dbp.Pid, sys.SIGSTOP)
}
// Attach to a newly created thread, and store that thread in our list of

View File

@ -8,6 +8,7 @@ import (
"path/filepath"
"runtime"
"testing"
"time"
)
func withTestProcess(name string, t *testing.T, fn func(p *DebuggedProcess)) {
@ -87,6 +88,31 @@ func TestExit(t *testing.T) {
})
}
func TestHalt(t *testing.T) {
withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
go func() {
time.Sleep(10 * time.Millisecond)
err := p.RequestManualStop()
if err != nil {
t.Fatal(err)
}
}()
err := p.Continue()
if err != nil {
t.Fatal(err)
}
// Loop through threads and make sure they are all
// actually stopped, err will not be nil if the process
// is still running.
for _, th := range p.Threads {
_, err := th.Registers()
if err != nil {
t.Error(err)
}
}
})
}
func TestStep(t *testing.T) {
withTestProcess("../_fixtures/testprog", t, func(p *DebuggedProcess) {
helloworldfunc := p.goSymTable.LookupFunc("main.helloworld")