breakpoints.go 5.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
A
Alessandro Arzilli 已提交
27 28 29 30
	Tracepoint    bool     // Tracepoint flag
	Goroutine     bool     // Retrieve goroutine information
	Stacktrace    int      // Number of stack frames to retrieve
	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 37 38 39 40 41 42 43 44 45 46 47
	// When DeferCond is set the breakpoint will only trigger
	// if the caller is runtime.gopanic or if the return address
	// is in the DeferReturns array.
	// Next sets DeferCond for the breakpoint it sets on the
	// deferred function, DeferReturns is populated with the
	// addresses of calls to runtime.deferreturn in the current
	// function. This insures that the breakpoint on the deferred
	// function only triggers on panic or on the defer call to
	// the function, not when the function is called directly
	DeferCond    bool
	DeferReturns []uint64
	Cond         ast.Expr // When Cond is not nil the breakpoint will be triggered only if evaluating Cond returns true
48 49
}

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

54 55
// Clear this breakpoint appropriately depending on whether it is a
// hardware or software breakpoint.
D
Derek Parker 已提交
56
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
D
Derek Parker 已提交
57
	if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
58 59 60 61 62
		return nil, fmt.Errorf("could not clear breakpoint %s", err)
	}
	return bp, nil
}

D
Derek Parker 已提交
63
// BreakpointExistsError is returned when trying to set a breakpoint at
D
Derek Parker 已提交
64
// an address that already has a breakpoint set for it.
D
Derek Parker 已提交
65
type BreakpointExistsError struct {
D
Derek Parker 已提交
66 67 68 69 70
	file string
	line int
	addr uint64
}

D
Derek Parker 已提交
71
func (bpe BreakpointExistsError) Error() string {
D
Derek Parker 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84
	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 已提交
85
func (dbp *Process) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, error) {
86 87 88 89 90 91 92
	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 已提交
93 94
	}

D
Derek Parker 已提交
95 96 97 98 99 100
	newBreakpoint := &Breakpoint{
		FunctionName: fn.Name,
		File:         f,
		Line:         l,
		Addr:         addr,
		Temp:         temp,
101
		Cond:         nil,
102
		HitCount:     map[int]uint64{},
D
Derek Parker 已提交
103 104
	}

105 106
	if temp {
		dbp.tempBreakpointIDCounter++
D
Derek Parker 已提交
107
		newBreakpoint.ID = dbp.tempBreakpointIDCounter
108 109
	} else {
		dbp.breakpointIDCounter++
D
Derek Parker 已提交
110
		newBreakpoint.ID = dbp.breakpointIDCounter
111
	}
D
Derek Parker 已提交
112 113

	thread := dbp.Threads[tid]
D
Derek Parker 已提交
114 115
	originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
	if err != nil {
D
Derek Parker 已提交
116 117
		return nil, err
	}
118
	if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
D
Derek Parker 已提交
119 120
		return nil, err
	}
D
Derek Parker 已提交
121 122
	newBreakpoint.OriginalData = originalData
	dbp.Breakpoints[addr] = newBreakpoint
123

D
Derek Parker 已提交
124
	return newBreakpoint, nil
D
Derek Parker 已提交
125
}
126

127
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
D
Derek Parker 已提交
128
	_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
129 130 131
	return err
}

132 133 134
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
	if bp.Cond == nil {
		return true, nil
135
	}
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	if bp.DeferCond {
		frames, err := thread.Stacktrace(2)
		if err == nil {
			ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
			isdeferreturn := false
			if len(frames) >= 1 {
				for _, pc := range bp.DeferReturns {
					if frames[0].Ret == pc {
						isdeferreturn = true
						break
					}
				}
			}
			if !ispanic && !isdeferreturn {
				return false, nil
			}
		}
	}
154
	scope, err := thread.Scope()
155
	if err != nil {
156
		return true, err
157
	}
158 159 160 161 162 163 164 165 166 167 168
	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
169 170
}

D
Derek Parker 已提交
171 172
// NoBreakpointError is returned when trying to
// clear a breakpoint that does not exist.
D
Derek Parker 已提交
173
type NoBreakpointError struct {
174 175 176
	addr uint64
}

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