mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 18:57:18 +08:00 
			
		
		
		
	 00df758d57
			
		
	
	00df758d57
	
	
	
		
			
			- use PT_SUSPEND/PT_RESUME to control running threads in resume/stop/singleStep - change manual stop signal from SIGTRAP to SIGSTOP to make manual stop handling simpler - change (*nativeProcess).trapWaitInternal to suspend newly created threads when we are stepping a thread - change (*nativeProcess).trapWaitInternal to handle some unhandled stop events - remove misleading (*nativeProcess).waitFast which does not do anything different from the normal wait variant - rewrite (*nativeProcess).stop to only set breakpoints for threads of which we have received SIGTRAP - rewrite (*nativeThread).singleStep to actually execute a single instruction and to properly route signals
		
			
				
	
	
		
			183 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package native
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/go-delve/delve/pkg/proc"
 | |
| )
 | |
| 
 | |
| // Thread represents a single thread in the traced process
 | |
| // ID represents the thread id or port, Process holds a reference to the
 | |
| // Process struct that contains info on the process as
 | |
| // a whole, and Status represents the last result of a `wait` call
 | |
| // on this thread.
 | |
| type nativeThread struct {
 | |
| 	ID                int                  // Thread ID or mach port
 | |
| 	Status            *waitStatus          // Status returned from last wait call
 | |
| 	CurrentBreakpoint proc.BreakpointState // Breakpoint thread is currently stopped at
 | |
| 
 | |
| 	dbp            *nativeProcess
 | |
| 	singleStepping bool
 | |
| 	os             *osSpecificDetails
 | |
| 	common         proc.CommonThread
 | |
| }
 | |
| 
 | |
| // StepInstruction steps a single instruction.
 | |
| //
 | |
| // Executes exactly one instruction and then returns.
 | |
| // If the thread is at a breakpoint, we first clear it,
 | |
| // execute the instruction, and then replace the breakpoint.
 | |
| // Otherwise we simply execute the next instruction.
 | |
| func (t *nativeThread) StepInstruction() (err error) {
 | |
| 	t.singleStepping = true
 | |
| 	defer func() {
 | |
| 		t.singleStepping = false
 | |
| 	}()
 | |
| 
 | |
| 	if bp := t.CurrentBreakpoint.Breakpoint; bp != nil && bp.WatchType != 0 && t.dbp.Breakpoints().M[bp.Addr] == bp {
 | |
| 		err = t.clearHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		defer func() {
 | |
| 			err = t.writeHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex)
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	pc, err := t.PC()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	bp, ok := t.dbp.FindBreakpoint(pc, false)
 | |
| 	if ok {
 | |
| 		// Clear the breakpoint so that we can continue execution.
 | |
| 		err = t.clearSoftwareBreakpoint(bp)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		// Restore breakpoint now that we have passed it.
 | |
| 		defer func() {
 | |
| 			err = t.dbp.writeSoftwareBreakpoint(t, bp.Addr)
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	err = t.singleStep()
 | |
| 	if err != nil {
 | |
| 		if _, exited := err.(proc.ErrProcessExited); exited {
 | |
| 			return err
 | |
| 		}
 | |
| 		return fmt.Errorf("step failed: %s", err.Error())
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Location returns the threads location, including the file:line
 | |
| // of the corresponding source code, the function we're in
 | |
| // and the current instruction address.
 | |
| func (t *nativeThread) Location() (*proc.Location, error) {
 | |
| 	pc, err := t.PC()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	f, l, fn := t.dbp.bi.PCToLine(pc)
 | |
| 	return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
 | |
| }
 | |
| 
 | |
| // BinInfo returns information on the binary.
 | |
| func (t *nativeThread) BinInfo() *proc.BinaryInfo {
 | |
| 	return t.dbp.bi
 | |
| }
 | |
| 
 | |
| // Common returns information common across Process
 | |
| // implementations.
 | |
| func (t *nativeThread) Common() *proc.CommonThread {
 | |
| 	return &t.common
 | |
| }
 | |
| 
 | |
| // SetCurrentBreakpoint sets the current breakpoint that this
 | |
| // thread is stopped at as CurrentBreakpoint on the thread struct.
 | |
| func (t *nativeThread) SetCurrentBreakpoint(adjustPC bool) error {
 | |
| 	t.CurrentBreakpoint.Clear()
 | |
| 
 | |
| 	var bp *proc.Breakpoint
 | |
| 
 | |
| 	if t.dbp.Breakpoints().HasHWBreakpoints() {
 | |
| 		var err error
 | |
| 		bp, err = t.findHardwareBreakpoint()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	if bp == nil {
 | |
| 		pc, err := t.PC()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		// If the breakpoint instruction does not change the value
 | |
| 		// of PC after being executed we should look for breakpoints
 | |
| 		// with bp.Addr == PC and there is no need to call SetPC
 | |
| 		// after finding one.
 | |
| 		adjustPC = adjustPC && t.BinInfo().Arch.BreakInstrMovesPC()
 | |
| 
 | |
| 		var ok bool
 | |
| 		bp, ok = t.dbp.FindBreakpoint(pc, adjustPC)
 | |
| 		if ok {
 | |
| 			if adjustPC {
 | |
| 				if err = t.setPC(bp.Addr); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	t.CurrentBreakpoint.Breakpoint = bp
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Breakpoint returns the current breakpoint that is active
 | |
| // on this thread.
 | |
| func (t *nativeThread) Breakpoint() *proc.BreakpointState {
 | |
| 	return &t.CurrentBreakpoint
 | |
| }
 | |
| 
 | |
| // ThreadID returns the ID of this thread.
 | |
| func (t *nativeThread) ThreadID() int {
 | |
| 	return t.ID
 | |
| }
 | |
| 
 | |
| // clearSoftwareBreakpoint clears the specified breakpoint.
 | |
| func (t *nativeThread) clearSoftwareBreakpoint(bp *proc.Breakpoint) error {
 | |
| 	if _, err := t.WriteMemory(bp.Addr, bp.OriginalData); err != nil {
 | |
| 		return fmt.Errorf("could not clear breakpoint %s", err)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Registers obtains register values from the debugged process.
 | |
| func (t *nativeThread) Registers() (proc.Registers, error) {
 | |
| 	return registers(t)
 | |
| }
 | |
| 
 | |
| // RestoreRegisters will set the value of the CPU registers to those
 | |
| // passed in via 'savedRegs'.
 | |
| func (t *nativeThread) RestoreRegisters(savedRegs proc.Registers) error {
 | |
| 	return t.restoreRegisters(savedRegs)
 | |
| }
 | |
| 
 | |
| // PC returns the current program counter value for this thread.
 | |
| func (t *nativeThread) PC() (uint64, error) {
 | |
| 	regs, err := t.Registers()
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return regs.PC(), nil
 | |
| }
 | |
| 
 | |
| // ProcessMemory returns this thread's process memory.
 | |
| func (t *nativeThread) ProcessMemory() proc.MemoryReadWriter {
 | |
| 	return t.dbp.Memory()
 | |
| }
 |