mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 02:36:18 +08:00 
			
		
		
		
	 60c58acb8e
			
		
	
	60c58acb8e
	
	
	
		
			
			Displays the return values of the current function when we step out of it after executing a step, next or stepout command. Implementation of this feature is tricky: when the function has returned the return variables are not in scope anymore. Implementing this feature requires evaluating variables that are out of scope, using a stack frame that doesn't exist anymore. We can't calculate the address of these variables when the next/step/stepout command is initiated either, because between that point and the time where the stepout breakpoint is actually hit the goroutine stack could grow and be moved to a different memory address.
		
			
				
	
	
		
			169 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package native
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/derekparker/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 Thread 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            *Process
 | |
| 	singleStepping bool
 | |
| 	os             *OSSpecificDetails
 | |
| 	common         proc.CommonThread
 | |
| }
 | |
| 
 | |
| // Continue the execution of this thread.
 | |
| //
 | |
| // If we are currently at a breakpoint, we'll clear it
 | |
| // first and then resume execution. Thread will continue until
 | |
| // it hits a breakpoint or is signaled.
 | |
| func (thread *Thread) Continue() error {
 | |
| 	pc, err := thread.PC()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	// Check whether we are stopped at a breakpoint, and
 | |
| 	// if so, single step over it before continuing.
 | |
| 	if _, ok := thread.dbp.FindBreakpoint(pc); ok {
 | |
| 		if err := thread.StepInstruction(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return thread.resume()
 | |
| }
 | |
| 
 | |
| // 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 (thread *Thread) StepInstruction() (err error) {
 | |
| 	thread.singleStepping = true
 | |
| 	defer func() {
 | |
| 		thread.singleStepping = false
 | |
| 	}()
 | |
| 	pc, err := thread.PC()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	bp, ok := thread.dbp.FindBreakpoint(pc)
 | |
| 	if ok {
 | |
| 		// Clear the breakpoint so that we can continue execution.
 | |
| 		err = thread.ClearBreakpoint(bp)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 
 | |
| 		// Restore breakpoint now that we have passed it.
 | |
| 		defer func() {
 | |
| 			err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
 | |
| 		}()
 | |
| 	}
 | |
| 
 | |
| 	err = thread.singleStep()
 | |
| 	if err != nil {
 | |
| 		if _, exited := err.(proc.ProcessExitedError); 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 (thread *Thread) Location() (*proc.Location, error) {
 | |
| 	pc, err := thread.PC()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	f, l, fn := thread.dbp.bi.PCToLine(pc)
 | |
| 	return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
 | |
| }
 | |
| 
 | |
| func (thread *Thread) Arch() proc.Arch {
 | |
| 	return thread.dbp.bi.Arch
 | |
| }
 | |
| 
 | |
| func (thread *Thread) BinInfo() *proc.BinaryInfo {
 | |
| 	return &thread.dbp.bi
 | |
| }
 | |
| 
 | |
| func (thread *Thread) Common() *proc.CommonThread {
 | |
| 	return &thread.common
 | |
| }
 | |
| 
 | |
| // SetPC sets the PC for this thread.
 | |
| func (thread *Thread) SetPC(pc uint64) error {
 | |
| 	regs, err := thread.Registers(false)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return regs.SetPC(thread, pc)
 | |
| }
 | |
| 
 | |
| // SetCurrentBreakpoint sets the current breakpoint that this
 | |
| // thread is stopped at as CurrentBreakpoint on the thread struct.
 | |
| func (thread *Thread) SetCurrentBreakpoint() error {
 | |
| 	thread.CurrentBreakpoint.Clear()
 | |
| 	pc, err := thread.PC()
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
 | |
| 		if err = thread.SetPC(bp.Addr); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		thread.CurrentBreakpoint = bp.CheckCondition(thread)
 | |
| 		if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.Active {
 | |
| 			if g, err := proc.GetG(thread); err == nil {
 | |
| 				thread.CurrentBreakpoint.HitCount[g.ID]++
 | |
| 			}
 | |
| 			thread.CurrentBreakpoint.TotalHitCount++
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (th *Thread) Breakpoint() proc.BreakpointState {
 | |
| 	return th.CurrentBreakpoint
 | |
| }
 | |
| 
 | |
| func (th *Thread) ThreadID() int {
 | |
| 	return th.ID
 | |
| }
 | |
| 
 | |
| // ClearBreakpoint clears the specified breakpoint.
 | |
| func (thread *Thread) ClearBreakpoint(bp *proc.Breakpoint) error {
 | |
| 	if _, err := thread.WriteMemory(uintptr(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 *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | |
| 	return registers(t, floatingPoint)
 | |
| }
 | |
| 
 | |
| func (t *Thread) PC() (uint64, error) {
 | |
| 	regs, err := t.Registers(false)
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return regs.PC(), nil
 | |
| }
 |