breakpoints.go 4.2 KB
Newer Older
D
Derek Parker 已提交
1
package proc
D
Derek Parker 已提交
2

3 4 5 6 7 8 9
import (
	"errors"
	"fmt"
	"go/ast"
	"go/constant"
	"reflect"
)
D
Derek Parker 已提交
10

D
Derek Parker 已提交
11
// Breakpoint represents a breakpoint. Stores information on the break
D
Derek Parker 已提交
12 13
// point including the byte of data that originally was stored at that
// address.
D
Derek Parker 已提交
14
type Breakpoint struct {
15
	// File & line information for printing.
D
Derek Parker 已提交
16 17 18
	FunctionName string
	File         string
	Line         int
19 20 21 22 23

	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).
D
Derek Parker 已提交
24 25

	// Breakpoint information
26 27 28 29 30 31
	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
32

33
	Cond ast.Expr // When Cond is not nil the breakpoint will be triggered only if evaluating Cond returns true
34 35
}

D
Derek Parker 已提交
36
func (bp *Breakpoint) String() string {
37
	return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
D
Derek Parker 已提交
38 39
}

40 41
// Clear this breakpoint appropriately depending on whether it is a
// hardware or software breakpoint.
D
Derek Parker 已提交
42
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
D
Derek Parker 已提交
43
	if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
44 45 46 47 48
		return nil, fmt.Errorf("could not clear breakpoint %s", err)
	}
	return bp, nil
}

D
Derek Parker 已提交
49
// BreakpointExistsError is returned when trying to set a breakpoint at
D
Derek Parker 已提交
50
// an address that already has a breakpoint set for it.
D
Derek Parker 已提交
51
type BreakpointExistsError struct {
D
Derek Parker 已提交
52 53 54 55 56
	file string
	line int
	addr uint64
}

D
Derek Parker 已提交
57
func (bpe BreakpointExistsError) Error() string {
D
Derek Parker 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70
	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)
}

D
Derek Parker 已提交
71
func (dbp *Process) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, error) {
72 73 74 75 76 77 78
	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}
D
Derek Parker 已提交
79 80
	}

D
Derek Parker 已提交
81 82 83 84 85 86
	newBreakpoint := &Breakpoint{
		FunctionName: fn.Name,
		File:         f,
		Line:         l,
		Addr:         addr,
		Temp:         temp,
87
		Cond:         nil,
88
		HitCount:     map[int]uint64{},
D
Derek Parker 已提交
89 90
	}

91 92
	if temp {
		dbp.tempBreakpointIDCounter++
D
Derek Parker 已提交
93
		newBreakpoint.ID = dbp.tempBreakpointIDCounter
94 95
	} else {
		dbp.breakpointIDCounter++
D
Derek Parker 已提交
96
		newBreakpoint.ID = dbp.breakpointIDCounter
97
	}
D
Derek Parker 已提交
98 99

	thread := dbp.Threads[tid]
D
Derek Parker 已提交
100 101
	originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
	if err != nil {
D
Derek Parker 已提交
102 103
		return nil, err
	}
104
	if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
D
Derek Parker 已提交
105 106
		return nil, err
	}
D
Derek Parker 已提交
107 108
	newBreakpoint.OriginalData = originalData
	dbp.Breakpoints[addr] = newBreakpoint
109

D
Derek Parker 已提交
110
	return newBreakpoint, nil
D
Derek Parker 已提交
111
}
112

113
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
D
Derek Parker 已提交
114
	_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
115 116 117
	return err
}

118 119 120
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
	if bp.Cond == nil {
		return true, nil
121
	}
122
	scope, err := thread.Scope()
123
	if err != nil {
124
		return true, err
125
	}
126 127 128 129 130 131 132 133 134 135 136
	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
137 138
}

D
Derek Parker 已提交
139 140
// NoBreakpointError is returned when trying to
// clear a breakpoint that does not exist.
D
Derek Parker 已提交
141
type NoBreakpointError struct {
142 143 144
	addr uint64
}

D
Derek Parker 已提交
145
func (nbp NoBreakpointError) Error() string {
146 147
	return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
}