breakpoints.go 5.0 KB
Newer Older
D
Derek Parker 已提交
1 2 3 4 5 6 7 8 9 10 11
package proctl

import (
	"fmt"
	"runtime"
)

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

	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).
	hardware     bool   // Breakpoint using CPU debug registers.
	reg          int    // If hardware breakpoint, what debug register it belongs to.
23 24 25 26
}

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

29 30
// Clear this breakpoint appropriately depending on whether it is a
// hardware or software breakpoint.
31 32 33 34 35 36 37 38 39 40 41 42 43
func (bp *BreakPoint) Clear(thread *ThreadContext) (*BreakPoint, error) {
	if bp.hardware {
		if err := clearHardwareBreakpoint(bp.reg, thread.Id); err != nil {
			return nil, err
		}
		return bp, nil
	}
	if _, err := writeMemory(thread, uintptr(bp.Addr), bp.OriginalData); err != nil {
		return nil, fmt.Errorf("could not clear breakpoint %s", err)
	}
	return bp, nil
}

D
Derek Parker 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
// 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)
}

// Returns whether or not a breakpoint has been set for the given address.
func (dbp *DebuggedProcess) BreakpointExists(addr uint64) bool {
	for _, bp := range dbp.HWBreakPoints {
		// TODO(darwin)
		if runtime.GOOS == "darwin" {
			break
		}
		if bp != nil && bp.Addr == addr {
			return true
		}
	}
D
Derek Parker 已提交
77 78
	_, ok := dbp.BreakPoints[addr]
	return ok
D
Derek Parker 已提交
79 80
}

81
func (dbp *DebuggedProcess) newBreakpoint(fn, f string, l int, addr uint64, data []byte, temp bool) *BreakPoint {
82 83 84 85 86 87 88 89
	var id int
	if temp {
		dbp.tempBreakpointIDCounter++
		id = dbp.tempBreakpointIDCounter
	} else {
		dbp.breakpointIDCounter++
		id = dbp.breakpointIDCounter
	}
D
Derek Parker 已提交
90 91 92 93 94 95
	return &BreakPoint{
		FunctionName: fn,
		File:         f,
		Line:         l,
		Addr:         addr,
		OriginalData: data,
96
		ID:           id,
97
		Temp:         temp,
D
Derek Parker 已提交
98 99 100
	}
}

101 102 103 104 105 106 107 108
func (dbp *DebuggedProcess) newHardwareBreakpoint(fn, f string, l int, addr uint64, data []byte, temp bool, reg int) *BreakPoint {
	bp := dbp.newBreakpoint(fn, f, l, addr, data, temp)
	bp.hardware = true
	bp.reg = reg
	return bp
}

func (dbp *DebuggedProcess) setBreakpoint(tid int, addr uint64, temp bool) (*BreakPoint, error) {
109
	var f, l, fn = dbp.goSymTable.PCToLine(uint64(addr))
D
Derek Parker 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122
	if fn == nil {
		return nil, InvalidAddressError{address: addr}
	}
	if dbp.BreakpointExists(addr) {
		return nil, BreakPointExistsError{f, l, addr}
	}
	// Try and set a hardware breakpoint.
	for i, v := range dbp.HWBreakPoints {
		// TODO(darwin)
		if runtime.GOOS == "darwin" {
			break
		}
		if v == nil {
123 124 125 126
			for t, _ := range dbp.Threads {
				if err := setHardwareBreakpoint(i, t, addr); err != nil {
					return nil, fmt.Errorf("could not set hardware breakpoint on thread %d: %s", t, err)
				}
D
Derek Parker 已提交
127
			}
128
			dbp.HWBreakPoints[i] = dbp.newHardwareBreakpoint(fn.Name, f, l, addr, nil, temp, i)
D
Derek Parker 已提交
129 130 131
			return dbp.HWBreakPoints[i], nil
		}
	}
D
Derek Parker 已提交
132
	// Fall back to software breakpoint. 0xCC is INT 3 trap interrupt.
D
Derek Parker 已提交
133
	thread := dbp.Threads[tid]
134
	originalData := make([]byte, dbp.arch.BreakpointSize())
D
Derek Parker 已提交
135 136 137
	if _, err := readMemory(thread, uintptr(addr), originalData); err != nil {
		return nil, err
	}
138
	if _, err := writeMemory(thread, uintptr(addr), dbp.arch.BreakpointInstruction()); err != nil {
D
Derek Parker 已提交
139 140
		return nil, err
	}
141
	dbp.BreakPoints[addr] = dbp.newBreakpoint(fn.Name, f, l, addr, originalData, temp)
D
Derek Parker 已提交
142 143
	return dbp.BreakPoints[addr], nil
}
144

D
Derek Parker 已提交
145
// Error thrown when trying to clear a breakpoint that does not exist.
146 147 148 149 150 151 152 153
type NoBreakPointError struct {
	addr uint64
}

func (nbp NoBreakPointError) Error() string {
	return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
}

154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
func (dbp *DebuggedProcess) clearBreakpoint(tid int, addr uint64) (*BreakPoint, error) {
	thread := dbp.Threads[tid]
	// Check for hardware breakpoint
	for i, bp := range dbp.HWBreakPoints {
		if bp == nil {
			continue
		}
		if bp.Addr == addr {
			_, err := bp.Clear(thread)
			if err != nil {
				return nil, err
			}
			dbp.HWBreakPoints[i] = nil
			return bp, nil
		}
	}
	// Check for software breakpoint
	if bp, ok := dbp.BreakPoints[addr]; ok {
		if _, err := bp.Clear(thread); err != nil {
			return nil, err
		}
		delete(dbp.BreakPoints, addr)
		return bp, nil
	}
178
	return nil, NoBreakPointError{addr: addr}
179
}