mirror of
https://github.com/go-delve/delve.git
synced 2025-11-02 04:36:29 +08:00
Improve handling of manual stops
This commit is contained in:
@ -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) {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
Reference in New Issue
Block a user