mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-01 03:42:59 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			149 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			4.3 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.
 | |
| 	Name         string // User defined name of the breakpoint
 | |
| 	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)
 | |
| }
 | 
