mirror of
https://github.com/beego/bee.git
synced 2025-07-07 16:40:19 +00:00
Update vendors
This commit is contained in:
437
vendor/github.com/derekparker/delve/pkg/proc/breakpoints.go
generated
vendored
Normal file
437
vendor/github.com/derekparker/delve/pkg/proc/breakpoints.go
generated
vendored
Normal file
@ -0,0 +1,437 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Breakpoint represents a breakpoint. Stores information on the break
|
||||
// point including the byte of data that originally was stored at that
|
||||
// address.
|
||||
type Breakpoint struct {
|
||||
// File & line information for printing.
|
||||
FunctionName string
|
||||
File string
|
||||
Line int
|
||||
|
||||
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.
|
||||
|
||||
// Kind describes whether this is an internal breakpoint (for next'ing or
|
||||
// stepping).
|
||||
// A single breakpoint can be both a UserBreakpoint and some kind of
|
||||
// internal breakpoint, but it can not be two different kinds of internal
|
||||
// breakpoint.
|
||||
Kind BreakpointKind
|
||||
|
||||
// Breakpoint information
|
||||
Tracepoint bool // Tracepoint flag
|
||||
Goroutine bool // Retrieve goroutine information
|
||||
Stacktrace int // Number of stack frames to retrieve
|
||||
Variables []string // Variables to evaluate
|
||||
LoadArgs *LoadConfig
|
||||
LoadLocals *LoadConfig
|
||||
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
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
||||
Cond ast.Expr
|
||||
// internalCond is the same as Cond but used for the condition of internal breakpoints
|
||||
internalCond ast.Expr
|
||||
|
||||
// ReturnInfo describes how to collect return variables when this
|
||||
// breakpoint is hit as a return breakpoint.
|
||||
returnInfo *returnBreakpointInfo
|
||||
}
|
||||
|
||||
// BreakpointKind determines the behavior of delve when the
|
||||
// breakpoint is reached.
|
||||
type BreakpointKind uint16
|
||||
|
||||
const (
|
||||
// UserBreakpoint is a user set breakpoint
|
||||
UserBreakpoint BreakpointKind = (1 << 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
|
||||
)
|
||||
|
||||
func (bp *Breakpoint) String() string {
|
||||
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
|
||||
}
|
||||
|
||||
// BreakpointExistsError is 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)
|
||||
}
|
||||
|
||||
type returnBreakpointInfo struct {
|
||||
retFrameCond ast.Expr
|
||||
fn *Function
|
||||
frameOffset int64
|
||||
spOffset int64
|
||||
}
|
||||
|
||||
// CheckCondition evaluates bp's condition on thread.
|
||||
func (bp *Breakpoint) CheckCondition(thread Thread) BreakpointState {
|
||||
bpstate := BreakpointState{Breakpoint: bp, Active: false, Internal: false, CondError: nil}
|
||||
if bp.Cond == nil && bp.internalCond == nil {
|
||||
bpstate.Active = true
|
||||
bpstate.Internal = bp.IsInternal()
|
||||
return bpstate
|
||||
}
|
||||
nextDeferOk := true
|
||||
if bp.Kind&NextDeferBreakpoint != 0 {
|
||||
frames, err := ThreadStacktrace(thread, 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
|
||||
}
|
||||
}
|
||||
}
|
||||
nextDeferOk = ispanic || isdeferreturn
|
||||
}
|
||||
}
|
||||
if bp.IsInternal() {
|
||||
// Check internalCondition if this is also an internal breakpoint
|
||||
bpstate.Active, bpstate.CondError = evalBreakpointCondition(thread, bp.internalCond)
|
||||
bpstate.Active = bpstate.Active && nextDeferOk
|
||||
if bpstate.Active || bpstate.CondError != nil {
|
||||
bpstate.Internal = true
|
||||
return bpstate
|
||||
}
|
||||
}
|
||||
if bp.IsUser() {
|
||||
// Check normal condition if this is also a user breakpoint
|
||||
bpstate.Active, bpstate.CondError = evalBreakpointCondition(thread, bp.Cond)
|
||||
}
|
||||
return bpstate
|
||||
}
|
||||
|
||||
// IsInternal returns true if bp is an internal breakpoint.
|
||||
// User-set breakpoints can overlap with internal breakpoints, in that case
|
||||
// both IsUser and IsInternal will be true.
|
||||
func (bp *Breakpoint) IsInternal() bool {
|
||||
return bp.Kind != UserBreakpoint
|
||||
}
|
||||
|
||||
// IsUser returns true if bp is a user-set breakpoint.
|
||||
// User-set breakpoints can overlap with internal breakpoints, in that case
|
||||
// both IsUser and IsInternal will be true.
|
||||
func (bp *Breakpoint) IsUser() bool {
|
||||
return bp.Kind&UserBreakpoint != 0
|
||||
}
|
||||
|
||||
func evalBreakpointCondition(thread Thread, cond ast.Expr) (bool, error) {
|
||||
if cond == nil {
|
||||
return true, nil
|
||||
}
|
||||
scope, err := GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
v, err := scope.evalAST(cond)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("error evaluating expression: %v", err)
|
||||
}
|
||||
if v.Kind != reflect.Bool {
|
||||
return true, errors.New("condition expression not boolean")
|
||||
}
|
||||
v.loadValue(loadFullValue)
|
||||
if v.Unreadable != nil {
|
||||
return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
|
||||
}
|
||||
return constant.BoolVal(v.Value), nil
|
||||
}
|
||||
|
||||
// NoBreakpointError is returned when trying to
|
||||
// clear a breakpoint that does not exist.
|
||||
type NoBreakpointError struct {
|
||||
Addr uint64
|
||||
}
|
||||
|
||||
func (nbp NoBreakpointError) Error() string {
|
||||
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
|
||||
}
|
||||
|
||||
// BreakpointMap represents an (address, breakpoint) map.
|
||||
type BreakpointMap struct {
|
||||
M map[uint64]*Breakpoint
|
||||
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
}
|
||||
|
||||
// NewBreakpointMap creates a new BreakpointMap.
|
||||
func NewBreakpointMap() BreakpointMap {
|
||||
return BreakpointMap{
|
||||
M: make(map[uint64]*Breakpoint),
|
||||
}
|
||||
}
|
||||
|
||||
// ResetBreakpointIDCounter resets the breakpoint ID counter of bpmap.
|
||||
func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
|
||||
bpmap.breakpointIDCounter = 0
|
||||
}
|
||||
|
||||
type writeBreakpointFn func(addr uint64) (file string, line int, fn *Function, originalData []byte, err error)
|
||||
type clearBreakpointFn func(*Breakpoint) error
|
||||
|
||||
// Set creates a breakpoint at addr calling writeBreakpoint. Do not call this
|
||||
// function, call proc.Process.SetBreakpoint instead, this function exists
|
||||
// to implement proc.Process.SetBreakpoint.
|
||||
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
|
||||
if bp, ok := bpmap.M[addr]; ok {
|
||||
// We can overlap one internal breakpoint with one user breakpoint, we
|
||||
// need to support this otherwise a conditional breakpoint can mask a
|
||||
// breakpoint set by next or step.
|
||||
if (kind != UserBreakpoint && bp.Kind != UserBreakpoint) || (kind == UserBreakpoint && bp.IsUser()) {
|
||||
return bp, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
bp.Kind |= kind
|
||||
if kind != UserBreakpoint {
|
||||
bp.internalCond = cond
|
||||
} else {
|
||||
bp.Cond = cond
|
||||
}
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
f, l, fn, originalData, err := writeBreakpoint(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newBreakpoint := &Breakpoint{
|
||||
FunctionName: fn.Name,
|
||||
File: f,
|
||||
Line: l,
|
||||
Addr: addr,
|
||||
Kind: kind,
|
||||
OriginalData: originalData,
|
||||
HitCount: map[int]uint64{},
|
||||
}
|
||||
|
||||
if kind != UserBreakpoint {
|
||||
bpmap.internalBreakpointIDCounter++
|
||||
newBreakpoint.ID = bpmap.internalBreakpointIDCounter
|
||||
newBreakpoint.internalCond = cond
|
||||
} else {
|
||||
bpmap.breakpointIDCounter++
|
||||
newBreakpoint.ID = bpmap.breakpointIDCounter
|
||||
newBreakpoint.Cond = cond
|
||||
}
|
||||
|
||||
bpmap.M[addr] = newBreakpoint
|
||||
|
||||
return newBreakpoint, nil
|
||||
}
|
||||
|
||||
// SetWithID creates a breakpoint at addr, with the specified ID.
|
||||
func (bpmap *BreakpointMap) SetWithID(id int, addr uint64, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
|
||||
bp, err := bpmap.Set(addr, UserBreakpoint, nil, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.ID = id
|
||||
bpmap.breakpointIDCounter--
|
||||
}
|
||||
return bp, err
|
||||
}
|
||||
|
||||
// Clear clears the breakpoint at addr.
|
||||
// Do not call this function call proc.Process.ClearBreakpoint instead.
|
||||
func (bpmap *BreakpointMap) Clear(addr uint64, clearBreakpoint clearBreakpointFn) (*Breakpoint, error) {
|
||||
bp, ok := bpmap.M[addr]
|
||||
if !ok {
|
||||
return nil, NoBreakpointError{Addr: addr}
|
||||
}
|
||||
|
||||
bp.Kind &= ^UserBreakpoint
|
||||
bp.Cond = nil
|
||||
if bp.Kind != 0 {
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
if err := clearBreakpoint(bp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(bpmap.M, addr)
|
||||
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// ClearInternalBreakpoints removes all internal breakpoints from the map,
|
||||
// calling clearBreakpoint on each one.
|
||||
// Do not call this function, call proc.Process.ClearInternalBreakpoints
|
||||
// instead, this function is used to implement that.
|
||||
func (bpmap *BreakpointMap) ClearInternalBreakpoints(clearBreakpoint clearBreakpointFn) error {
|
||||
for addr, bp := range bpmap.M {
|
||||
bp.Kind = bp.Kind & UserBreakpoint
|
||||
bp.internalCond = nil
|
||||
bp.returnInfo = nil
|
||||
if bp.Kind != 0 {
|
||||
continue
|
||||
}
|
||||
if err := clearBreakpoint(bp); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(bpmap.M, addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasInternalBreakpoints returns true if bpmap has at least one internal
|
||||
// breakpoint set.
|
||||
func (bpmap *BreakpointMap) HasInternalBreakpoints() bool {
|
||||
for _, bp := range bpmap.M {
|
||||
if bp.IsInternal() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// BreakpointState describes the state of a breakpoint in a thread.
|
||||
type BreakpointState struct {
|
||||
*Breakpoint
|
||||
// Active is true if the breakpoint condition was met.
|
||||
Active bool
|
||||
// Internal is true if the breakpoint was matched as an internal
|
||||
// breakpoint.
|
||||
Internal bool
|
||||
// CondError contains any error encountered while evaluating the
|
||||
// breakpoint's condition.
|
||||
CondError error
|
||||
}
|
||||
|
||||
// Clear zeros the struct.
|
||||
func (bpstate *BreakpointState) Clear() {
|
||||
bpstate.Breakpoint = nil
|
||||
bpstate.Active = false
|
||||
bpstate.Internal = false
|
||||
bpstate.CondError = nil
|
||||
}
|
||||
|
||||
func (bpstate *BreakpointState) String() string {
|
||||
s := bpstate.Breakpoint.String()
|
||||
if bpstate.Active {
|
||||
s += " active"
|
||||
}
|
||||
if bpstate.Internal {
|
||||
s += " internal"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func configureReturnBreakpoint(bi *BinaryInfo, bp *Breakpoint, topframe *Stackframe, retFrameCond ast.Expr) {
|
||||
if topframe.Current.Fn == nil {
|
||||
return
|
||||
}
|
||||
bp.returnInfo = &returnBreakpointInfo{
|
||||
retFrameCond: retFrameCond,
|
||||
fn: topframe.Current.Fn,
|
||||
frameOffset: topframe.FrameOffset(),
|
||||
spOffset: topframe.FrameOffset() - int64(bi.Arch.PtrSize()), // must be the value that SP had at the entry point of the function
|
||||
}
|
||||
}
|
||||
|
||||
func (rbpi *returnBreakpointInfo) Collect(thread Thread) []*Variable {
|
||||
if rbpi == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
g, err := GetG(thread)
|
||||
if err != nil {
|
||||
return returnInfoError("could not get g", err, thread)
|
||||
}
|
||||
scope, err := GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return returnInfoError("could not get scope", err, thread)
|
||||
}
|
||||
v, err := scope.evalAST(rbpi.retFrameCond)
|
||||
if err != nil || v.Unreadable != nil || v.Kind != reflect.Bool {
|
||||
// This condition was evaluated as part of the breakpoint condition
|
||||
// evaluation, if the errors happen they will be reported as part of the
|
||||
// condition errors.
|
||||
return nil
|
||||
}
|
||||
if !constant.BoolVal(v.Value) {
|
||||
// Breakpoint not hit as a return breakpoint.
|
||||
return nil
|
||||
}
|
||||
|
||||
oldFrameOffset := rbpi.frameOffset + int64(g.stackhi)
|
||||
oldSP := uint64(rbpi.spOffset + int64(g.stackhi))
|
||||
err = fakeFunctionEntryScope(scope, rbpi.fn, oldFrameOffset, oldSP)
|
||||
if err != nil {
|
||||
return returnInfoError("could not read function entry", err, thread)
|
||||
}
|
||||
|
||||
vars, err := scope.Locals()
|
||||
if err != nil {
|
||||
return returnInfoError("could not evaluate return variables", err, thread)
|
||||
}
|
||||
vars = filterVariables(vars, func(v *Variable) bool {
|
||||
return (v.Flags & VariableReturnArgument) != 0
|
||||
})
|
||||
|
||||
// Go saves the return variables in the opposite order that the user
|
||||
// specifies them so here we reverse the slice to make it easier to
|
||||
// understand.
|
||||
for i := 0; i < len(vars)/2; i++ {
|
||||
vars[i], vars[len(vars)-i-1] = vars[len(vars)-i-1], vars[i]
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
func returnInfoError(descr string, err error, mem MemoryReadWriter) []*Variable {
|
||||
v := newConstant(constant.MakeString(fmt.Sprintf("%s: %v", descr, err.Error())), mem)
|
||||
v.Name = "return value read error"
|
||||
return []*Variable{v}
|
||||
}
|
Reference in New Issue
Block a user