mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-04 14:36:47 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			148 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package proc
 | 
						|
 | 
						|
import (
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"go/ast"
 | 
						|
	"go/constant"
 | 
						|
	"reflect"
 | 
						|
)
 | 
						|
 | 
						|
// Breakpoint represents a breakpoint. Stores information on the break
 | 
						|
// point including the byte of data that originally was stored at that
 | 
						|
// address.
 | 
						|
type Breakpoint struct {
 | 
						|
	// File & line information for printing.
 | 
						|
	FunctionName string
 | 
						|
	File         string
 | 
						|
	Line         int
 | 
						|
 | 
						|
	Addr         uint64 // Address breakpoint is set for.
 | 
						|
	OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
 | 
						|
	ID           int    // Monotonically increasing ID.
 | 
						|
	Temp         bool   // Whether this is a temp breakpoint (for next'ing).
 | 
						|
 | 
						|
	// Breakpoint information
 | 
						|
	Tracepoint    bool           // Tracepoint flag
 | 
						|
	Stacktrace    int            // Number of stack frames to retrieve
 | 
						|
	Goroutine     bool           // Retrieve goroutine information
 | 
						|
	Variables     []string       // Variables to evaluate
 | 
						|
	HitCount      map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
 | 
						|
	TotalHitCount uint64         // Number of times a breakpoint has been reached
 | 
						|
 | 
						|
	Cond ast.Expr // When Cond is not nil the breakpoint will be triggered only if evaluating Cond returns true
 | 
						|
}
 | 
						|
 | 
						|
func (bp *Breakpoint) String() string {
 | 
						|
	return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
 | 
						|
}
 | 
						|
 | 
						|
// Clear this breakpoint appropriately depending on whether it is a
 | 
						|
// hardware or software breakpoint.
 | 
						|
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
 | 
						|
	if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
 | 
						|
		return nil, fmt.Errorf("could not clear breakpoint %s", err)
 | 
						|
	}
 | 
						|
	return bp, nil
 | 
						|
}
 | 
						|
 | 
						|
// BreakpointExistsError is returned when trying to set a breakpoint at
 | 
						|
// an address that already has a breakpoint set for it.
 | 
						|
type BreakpointExistsError struct {
 | 
						|
	file string
 | 
						|
	line int
 | 
						|
	addr uint64
 | 
						|
}
 | 
						|
 | 
						|
func (bpe BreakpointExistsError) Error() string {
 | 
						|
	return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
 | 
						|
}
 | 
						|
 | 
						|
// InvalidAddressError represents the result of
 | 
						|
// attempting to set a breakpoint at an invalid address.
 | 
						|
type InvalidAddressError struct {
 | 
						|
	address uint64
 | 
						|
}
 | 
						|
 | 
						|
func (iae InvalidAddressError) Error() string {
 | 
						|
	return fmt.Sprintf("Invalid address %#v\n", iae.address)
 | 
						|
}
 | 
						|
 | 
						|
func (dbp *Process) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, error) {
 | 
						|
	if bp, ok := dbp.FindBreakpoint(addr); ok {
 | 
						|
		return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
 | 
						|
	}
 | 
						|
 | 
						|
	f, l, fn := dbp.goSymTable.PCToLine(uint64(addr))
 | 
						|
	if fn == nil {
 | 
						|
		return nil, InvalidAddressError{address: addr}
 | 
						|
	}
 | 
						|
 | 
						|
	newBreakpoint := &Breakpoint{
 | 
						|
		FunctionName: fn.Name,
 | 
						|
		File:         f,
 | 
						|
		Line:         l,
 | 
						|
		Addr:         addr,
 | 
						|
		Temp:         temp,
 | 
						|
		Cond:         nil,
 | 
						|
		HitCount:     map[int]uint64{},
 | 
						|
	}
 | 
						|
 | 
						|
	if temp {
 | 
						|
		dbp.tempBreakpointIDCounter++
 | 
						|
		newBreakpoint.ID = dbp.tempBreakpointIDCounter
 | 
						|
	} else {
 | 
						|
		dbp.breakpointIDCounter++
 | 
						|
		newBreakpoint.ID = dbp.breakpointIDCounter
 | 
						|
	}
 | 
						|
 | 
						|
	thread := dbp.Threads[tid]
 | 
						|
	originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	newBreakpoint.OriginalData = originalData
 | 
						|
	dbp.Breakpoints[addr] = newBreakpoint
 | 
						|
 | 
						|
	return newBreakpoint, nil
 | 
						|
}
 | 
						|
 | 
						|
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
 | 
						|
	_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
 | 
						|
	return err
 | 
						|
}
 | 
						|
 | 
						|
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
 | 
						|
	if bp.Cond == nil {
 | 
						|
		return true, nil
 | 
						|
	}
 | 
						|
	scope, err := thread.Scope()
 | 
						|
	if err != nil {
 | 
						|
		return true, err
 | 
						|
	}
 | 
						|
	v, err := scope.evalAST(bp.Cond)
 | 
						|
	if err != nil {
 | 
						|
		return true, fmt.Errorf("error evaluating expression: %v", err)
 | 
						|
	}
 | 
						|
	if v.Unreadable != nil {
 | 
						|
		return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
 | 
						|
	}
 | 
						|
	if v.Kind != reflect.Bool {
 | 
						|
		return true, errors.New("condition expression not boolean")
 | 
						|
	}
 | 
						|
	return constant.BoolVal(v.Value), nil
 | 
						|
}
 | 
						|
 | 
						|
// NoBreakpointError is returned when trying to
 | 
						|
// clear a breakpoint that does not exist.
 | 
						|
type NoBreakpointError struct {
 | 
						|
	addr uint64
 | 
						|
}
 | 
						|
 | 
						|
func (nbp NoBreakpointError) Error() string {
 | 
						|
	return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
 | 
						|
}
 |