2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2015-01-14 02:37:10 +00:00
|
|
|
|
2015-09-26 20:56:24 +00:00
|
|
|
import "fmt"
|
2015-01-14 02:37:10 +00:00
|
|
|
|
|
|
|
// Represents a single breakpoint. Stores information on the break
|
|
|
|
// point including the byte of data that originally was stored at that
|
|
|
|
// address.
|
2015-06-12 19:32:32 +00:00
|
|
|
type Breakpoint struct {
|
2015-04-22 13:18:25 +00:00
|
|
|
// File & line information for printing.
|
2015-01-14 02:37:10 +00:00
|
|
|
FunctionName string
|
|
|
|
File string
|
|
|
|
Line int
|
2015-04-22 13:18:25 +00:00
|
|
|
|
|
|
|
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).
|
2015-07-01 03:16:52 +00:00
|
|
|
|
|
|
|
// Breakpoint information
|
2015-09-26 00:04:39 +00:00
|
|
|
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
|
2015-11-18 09:07:08 +00:00
|
|
|
|
|
|
|
Cond int // When Cond is greater than zero this breakpoint will trigger only when the current goroutine id is equal to it
|
2015-03-06 14:01:41 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func (bp *Breakpoint) String() string {
|
2015-09-26 00:04:39 +00:00
|
|
|
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
|
|
|
|
2015-04-22 13:18:25 +00:00
|
|
|
// Clear this breakpoint appropriately depending on whether it is a
|
|
|
|
// hardware or software breakpoint.
|
2015-06-12 19:51:23 +00:00
|
|
|
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
|
2015-08-02 02:43:03 +00:00
|
|
|
if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
2015-04-19 22:11:33 +00:00
|
|
|
return nil, fmt.Errorf("could not clear breakpoint %s", err)
|
|
|
|
}
|
|
|
|
return bp, nil
|
|
|
|
}
|
|
|
|
|
2015-01-14 02:37:10 +00:00
|
|
|
// Returned when trying to set a breakpoint at
|
|
|
|
// an address that already has a breakpoint set for it.
|
2015-06-12 19:32:32 +00:00
|
|
|
type BreakpointExistsError struct {
|
2015-01-14 02:37:10 +00:00
|
|
|
file string
|
|
|
|
line int
|
|
|
|
addr uint64
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func (bpe BreakpointExistsError) Error() string {
|
2015-01-14 02:37:10 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2015-06-20 22:54:52 +00:00
|
|
|
func (dbp *Process) setBreakpoint(tid int, addr uint64, temp bool) (*Breakpoint, error) {
|
2015-06-17 18:09:55 +00:00
|
|
|
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}
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
|
|
|
|
2015-08-02 02:16:58 +00:00
|
|
|
newBreakpoint := &Breakpoint{
|
|
|
|
FunctionName: fn.Name,
|
|
|
|
File: f,
|
|
|
|
Line: l,
|
|
|
|
Addr: addr,
|
|
|
|
Temp: temp,
|
2015-11-18 09:07:08 +00:00
|
|
|
Cond: -1,
|
2015-09-26 00:04:39 +00:00
|
|
|
HitCount: map[int]uint64{},
|
2015-08-02 02:16:58 +00:00
|
|
|
}
|
|
|
|
|
2015-04-29 13:17:35 +00:00
|
|
|
if temp {
|
|
|
|
dbp.tempBreakpointIDCounter++
|
2015-08-02 02:16:58 +00:00
|
|
|
newBreakpoint.ID = dbp.tempBreakpointIDCounter
|
2015-04-29 13:17:35 +00:00
|
|
|
} else {
|
|
|
|
dbp.breakpointIDCounter++
|
2015-08-02 02:16:58 +00:00
|
|
|
newBreakpoint.ID = dbp.breakpointIDCounter
|
2015-04-29 13:17:35 +00:00
|
|
|
}
|
2015-01-14 02:37:10 +00:00
|
|
|
|
|
|
|
thread := dbp.Threads[tid]
|
2015-08-02 02:43:03 +00:00
|
|
|
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
|
|
|
|
if err != nil {
|
2015-01-14 02:37:10 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-06-26 13:59:11 +00:00
|
|
|
if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
|
2015-01-14 02:37:10 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
2015-08-02 02:16:58 +00:00
|
|
|
newBreakpoint.OriginalData = originalData
|
|
|
|
dbp.Breakpoints[addr] = newBreakpoint
|
2015-06-17 18:09:55 +00:00
|
|
|
|
2015-08-02 02:16:58 +00:00
|
|
|
return newBreakpoint, nil
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
2015-04-19 22:11:33 +00:00
|
|
|
|
2015-06-26 13:59:11 +00:00
|
|
|
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
2015-08-02 02:43:03 +00:00
|
|
|
_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
|
2015-06-26 13:59:11 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-11-18 09:07:08 +00:00
|
|
|
func (bp *Breakpoint) checkCondition(thread *Thread) bool {
|
|
|
|
if bp.Cond < 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
g, err := thread.GetG()
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return g.Id == bp.Cond
|
|
|
|
}
|
|
|
|
|
2015-04-23 14:17:14 +00:00
|
|
|
// Error thrown when trying to clear a breakpoint that does not exist.
|
2015-06-12 19:32:32 +00:00
|
|
|
type NoBreakpointError struct {
|
2015-04-20 18:03:22 +00:00
|
|
|
addr uint64
|
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func (nbp NoBreakpointError) Error() string {
|
2015-04-20 18:03:22 +00:00
|
|
|
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
|
|
|
|
}
|