breakpoints.go 4.3 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

	Addr         uint64 // Address breakpoint is set for.
	OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
22
	Name         string // User defined name of the breakpoint
23 24
	ID           int    // Monotonically increasing ID.
	Temp         bool   // Whether this is a temp breakpoint (for next'ing).
D
Derek Parker 已提交
25 26

	// Breakpoint information
27 28
	Tracepoint    bool           // Tracepoint flag
	Goroutine     bool           // Retrieve goroutine information
H
Hubert Krauze 已提交
29
	Stacktrace    int            // Number of stack frames to retrieve
30
	Variables     []string       // Variables to evaluate
31 32
	LoadArgs      *LoadConfig
	LoadLocals    *LoadConfig
33 34
	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
35

36
	Cond ast.Expr // When Cond is not nil the breakpoint will be triggered only if evaluating Cond returns true
37 38
}

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

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

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

D
Derek Parker 已提交
60
func (bpe BreakpointExistsError) Error() string {
D
Derek Parker 已提交
61 62 63 64 65 66 67 68 69 70 71 72 73
	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 已提交
74
func (dbp *Process) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, error) {
75 76 77 78 79 80 81
	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 已提交
82 83
	}

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

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

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

D
Derek Parker 已提交
113
	return newBreakpoint, nil
D
Derek Parker 已提交
114
}
115

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

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

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

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