2015-06-12 19:49:23 +00:00
|
|
|
package proc
|
2015-01-14 02:37:10 +00:00
|
|
|
|
2016-01-12 08:01:42 +00:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"go/ast"
|
|
|
|
"go/constant"
|
|
|
|
"reflect"
|
|
|
|
)
|
2015-01-14 02:37:10 +00:00
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// Breakpoint represents a breakpoint. Stores information on the break
|
2015-01-14 02:37:10 +00:00
|
|
|
// 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
|
|
|
|
2016-07-26 13:34:31 +00:00
|
|
|
Addr uint64 // Address breakpoint is set for.
|
|
|
|
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
|
|
|
|
Name string // User defined name of the breakpoint
|
|
|
|
ID int // Monotonically increasing ID.
|
2016-09-30 06:35:29 +00:00
|
|
|
Kind BreakpointKind // Whether this is an internal breakpoint (for next'ing or stepping).
|
2015-07-01 03:16:52 +00:00
|
|
|
|
|
|
|
// Breakpoint information
|
2016-05-29 19:20:09 +00:00
|
|
|
Tracepoint bool // Tracepoint flag
|
|
|
|
Goroutine bool // Retrieve goroutine information
|
|
|
|
Stacktrace int // Number of stack frames to retrieve
|
|
|
|
Variables []string // Variables to evaluate
|
2016-04-24 17:15:39 +00:00
|
|
|
LoadArgs *LoadConfig
|
|
|
|
LoadLocals *LoadConfig
|
2015-09-26 00:04:39 +00:00
|
|
|
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
|
|
|
|
2016-07-26 13:34:31 +00:00
|
|
|
// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
|
|
|
|
// will also check if the caller is runtime.gopanic or if the return
|
|
|
|
// address is in the DeferReturns array.
|
|
|
|
// Next uses NextDeferBreakpoints for the breakpoint it sets on the
|
2016-07-14 07:43:39 +00:00
|
|
|
// 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
|
|
|
|
DeferReturns []uint64
|
2016-07-26 13:34:31 +00:00
|
|
|
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
|
|
|
Cond ast.Expr
|
2015-03-06 14:01:41 +00:00
|
|
|
}
|
|
|
|
|
2016-07-26 13:34:31 +00:00
|
|
|
// Breakpoint Kind determines the behavior of delve when the
|
|
|
|
// breakpoint is reached.
|
|
|
|
type BreakpointKind int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// UserBreakpoint is a user set breakpoint
|
|
|
|
UserBreakpoint BreakpointKind = iota
|
|
|
|
// NextBreakpoint is a breakpoint set by Next, Continue
|
|
|
|
// will stop on it and delete it
|
|
|
|
NextBreakpoint
|
|
|
|
// NextDeferBreakpoint is a breakpoint set by Next on the
|
|
|
|
// first deferred function. In addition to checking their condition
|
|
|
|
// breakpoints of this kind will also check that the function has been
|
|
|
|
// called by runtime.gopanic or through runtime.deferreturn.
|
|
|
|
NextDeferBreakpoint
|
|
|
|
// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
|
|
|
|
// Continue will set a new breakpoint (of NextBreakpoint kind) on the
|
|
|
|
// destination of CALL, delete this breakpoint and then continue again
|
|
|
|
StepBreakpoint
|
|
|
|
)
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// BreakpointExistsError is returned when trying to set a breakpoint at
|
2015-01-14 02:37:10 +00:00
|
|
|
// an address that already has a breakpoint set for it.
|
2015-06-12 19:32:32 +00:00
|
|
|
type BreakpointExistsError struct {
|
2017-04-21 06:55:53 +00:00
|
|
|
File string
|
|
|
|
Line int
|
|
|
|
Addr uint64
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func (bpe BreakpointExistsError) Error() string {
|
2017-04-21 06:55:53 +00:00
|
|
|
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.File, bpe.Line, bpe.Addr)
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// InvalidAddressError represents the result of
|
|
|
|
// attempting to set a breakpoint at an invalid address.
|
|
|
|
type InvalidAddressError struct {
|
2017-04-21 06:55:53 +00:00
|
|
|
Address uint64
|
2015-01-14 02:37:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (iae InvalidAddressError) Error() string {
|
2017-04-21 06:55:53 +00:00
|
|
|
return fmt.Sprintf("Invalid address %#v\n", iae.Address)
|
2015-06-26 13:59:11 +00:00
|
|
|
}
|
|
|
|
|
2017-04-21 06:55:53 +00:00
|
|
|
// CheckCondition evaluates bp's condition on thread.
|
2017-04-21 07:50:38 +00:00
|
|
|
func (bp *Breakpoint) CheckCondition(thread Thread) (bool, error) {
|
2016-01-12 08:01:42 +00:00
|
|
|
if bp.Cond == nil {
|
|
|
|
return true, nil
|
2015-11-18 09:07:08 +00:00
|
|
|
}
|
2016-07-26 13:34:31 +00:00
|
|
|
if bp.Kind == NextDeferBreakpoint {
|
2017-02-15 13:41:03 +00:00
|
|
|
frames, err := ThreadStacktrace(thread, 2)
|
2016-07-14 07:43:39 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-02-15 13:41:03 +00:00
|
|
|
scope, err := GoroutineScope(thread)
|
2015-11-18 09:07:08 +00:00
|
|
|
if err != nil {
|
2016-01-12 08:01:42 +00:00
|
|
|
return true, err
|
2015-11-18 09:07:08 +00:00
|
|
|
}
|
2016-01-12 08:01:42 +00:00
|
|
|
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
|
2015-11-18 09:07:08 +00:00
|
|
|
}
|
|
|
|
|
2016-07-26 13:34:31 +00:00
|
|
|
// Internal returns true for breakpoints not set directly by the user.
|
|
|
|
func (bp *Breakpoint) Internal() bool {
|
|
|
|
return bp.Kind != UserBreakpoint
|
|
|
|
}
|
|
|
|
|
2016-01-10 08:57:52 +00:00
|
|
|
// NoBreakpointError is returned when trying to
|
|
|
|
// clear a breakpoint that does not exist.
|
2015-06-12 19:32:32 +00:00
|
|
|
type NoBreakpointError struct {
|
2017-04-21 06:55:53 +00:00
|
|
|
Addr uint64
|
2015-04-20 18:03:22 +00:00
|
|
|
}
|
|
|
|
|
2015-06-12 19:32:32 +00:00
|
|
|
func (nbp NoBreakpointError) Error() string {
|
2017-04-21 06:55:53 +00:00
|
|
|
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
|
2015-04-20 18:03:22 +00:00
|
|
|
}
|