mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-28 20:53:42 +08:00 
			
		
		
		
	 d2bca7a307
			
		
	
	d2bca7a307
	
	
	
		
			
			A RequestManualStop received while the target program is stopped can induce a crash when the target is restarted. This is caused by the phantom breakpoint detection that was introduced in PR #2179 / commit e69d536. Instead of always interpreting an unexplained SIGTRAP as a phantom breakpoint memorize all possible unreported breakpoint hits and only act on it when the thread hasn't moved from one. Also clarifies the behavior of the halt command when it is received while the target is stopped or in the process of stopping.
		
			
				
	
	
		
			105 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package native
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	sys "golang.org/x/sys/unix"
 | |
| 
 | |
| 	"github.com/go-delve/delve/pkg/proc"
 | |
| )
 | |
| 
 | |
| type waitStatus sys.WaitStatus
 | |
| 
 | |
| // osSpecificDetails hold Linux specific
 | |
| // process details.
 | |
| type osSpecificDetails struct {
 | |
| 	delayedSignal       int
 | |
| 	running             bool
 | |
| 	setbp               bool
 | |
| 	phantomBreakpointPC uint64
 | |
| }
 | |
| 
 | |
| func (t *nativeThread) stop() (err error) {
 | |
| 	err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
 | |
| 	if err != nil {
 | |
| 		err = fmt.Errorf("stop err %s on thread %d", err, t.ID)
 | |
| 		return
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Stopped returns whether the thread is stopped at
 | |
| // the operating system level.
 | |
| func (t *nativeThread) Stopped() bool {
 | |
| 	state := status(t.ID, t.dbp.os.comm)
 | |
| 	return state == statusTraceStop || state == statusTraceStopT
 | |
| }
 | |
| 
 | |
| func (t *nativeThread) resume() error {
 | |
| 	sig := t.os.delayedSignal
 | |
| 	t.os.delayedSignal = 0
 | |
| 	return t.resumeWithSig(sig)
 | |
| }
 | |
| 
 | |
| func (t *nativeThread) resumeWithSig(sig int) (err error) {
 | |
| 	t.os.running = true
 | |
| 	t.dbp.execPtraceFunc(func() { err = ptraceCont(t.ID, sig) })
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (t *nativeThread) singleStep() (err error) {
 | |
| 	for {
 | |
| 		t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		wpid, status, err := t.dbp.waitFast(t.ID)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if (status == nil || status.Exited()) && wpid == t.dbp.pid {
 | |
| 			t.dbp.postExit()
 | |
| 			rs := 0
 | |
| 			if status != nil {
 | |
| 				rs = status.ExitStatus()
 | |
| 			}
 | |
| 			return proc.ErrProcessExited{Pid: t.dbp.pid, Status: rs}
 | |
| 		}
 | |
| 		if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
 | |
| 			return nil
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (t *nativeThread) WriteMemory(addr uint64, data []byte) (written int, err error) {
 | |
| 	if t.dbp.exited {
 | |
| 		return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
 | |
| 	}
 | |
| 	if len(data) == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	// ProcessVmWrite can't poke read-only memory like ptrace, so don't
 | |
| 	// even bother for small writes -- likely breakpoints and such.
 | |
| 	if len(data) > sys.SizeofPtr {
 | |
| 		written, _ = processVmWrite(t.ID, uintptr(addr), data)
 | |
| 	}
 | |
| 	if written == 0 {
 | |
| 		t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, uintptr(addr), data) })
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (t *nativeThread) ReadMemory(data []byte, addr uint64) (n int, err error) {
 | |
| 	if t.dbp.exited {
 | |
| 		return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
 | |
| 	}
 | |
| 	if len(data) == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	n, _ = processVmRead(t.ID, uintptr(addr), data)
 | |
| 	if n == 0 {
 | |
| 		t.dbp.execPtraceFunc(func() { n, err = sys.PtracePeekData(t.ID, uintptr(addr), data) })
 | |
| 	}
 | |
| 	return
 | |
| }
 |