proc: next should not skip lines with conditional bps
Conditional breakpoints with unmet conditions would cause next and step to skip the line. This breakpoint changes the Kind field of proc.Breakpoint from a single value to a bit field, each breakpoint object can represent simultaneously a user breakpoint and one internal breakpoint (of which we have several different kinds). The breakpoint condition for internal breakpoints is stored in the new internalCond field of proc.Breakpoint so that it will not conflict with user specified conditions. The breakpoint setting code is changed to allow overlapping one internal breakpoint on a user breakpoint, or a user breakpoint on an existing internal breakpoint. All other combinations are rejected. The breakpoint clearing code is changed to clear the UserBreakpoint bit and only remove the phisical breakpoint if no other bits are set in the Kind field. ClearInternalBreakpoints does the same thing but clearing all bits that aren't the UserBreakpoint bit. Fixes #844
This commit is contained in:
parent
178589a4e7
commit
1ced7c3a60
12
_fixtures/nextcond.go
Normal file
12
_fixtures/nextcond.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
var n = 10
|
||||||
|
|
||||||
|
func f1(x int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
f1(n)
|
||||||
|
f1(n)
|
||||||
|
f1(n)
|
||||||
|
}
|
@ -17,11 +17,17 @@ type Breakpoint struct {
|
|||||||
File string
|
File string
|
||||||
Line int
|
Line int
|
||||||
|
|
||||||
Addr uint64 // Address breakpoint is set for.
|
Addr uint64 // Address breakpoint is set for.
|
||||||
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
|
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
|
||||||
Name string // User defined name of the breakpoint
|
Name string // User defined name of the breakpoint
|
||||||
ID int // Monotonically increasing ID.
|
ID int // Monotonically increasing ID.
|
||||||
Kind BreakpointKind // Whether this is an internal breakpoint (for next'ing or stepping).
|
|
||||||
|
// 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
|
// Breakpoint information
|
||||||
Tracepoint bool // Tracepoint flag
|
Tracepoint bool // Tracepoint flag
|
||||||
@ -45,15 +51,17 @@ type Breakpoint struct {
|
|||||||
DeferReturns []uint64
|
DeferReturns []uint64
|
||||||
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
||||||
Cond ast.Expr
|
Cond ast.Expr
|
||||||
|
// internalCond is the same as Cond but used for the condition of internal breakpoints
|
||||||
|
internalCond ast.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Breakpoint Kind determines the behavior of delve when the
|
// Breakpoint Kind determines the behavior of delve when the
|
||||||
// breakpoint is reached.
|
// breakpoint is reached.
|
||||||
type BreakpointKind int
|
type BreakpointKind uint16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// UserBreakpoint is a user set breakpoint
|
// UserBreakpoint is a user set breakpoint
|
||||||
UserBreakpoint BreakpointKind = iota
|
UserBreakpoint BreakpointKind = (1 << iota)
|
||||||
// NextBreakpoint is a breakpoint set by Next, Continue
|
// NextBreakpoint is a breakpoint set by Next, Continue
|
||||||
// will stop on it and delete it
|
// will stop on it and delete it
|
||||||
NextBreakpoint
|
NextBreakpoint
|
||||||
@ -95,11 +103,15 @@ func (iae InvalidAddressError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckCondition evaluates bp's condition on thread.
|
// CheckCondition evaluates bp's condition on thread.
|
||||||
func (bp *Breakpoint) CheckCondition(thread Thread) (bool, error) {
|
func (bp *Breakpoint) CheckCondition(thread Thread) BreakpointState {
|
||||||
if bp.Cond == nil {
|
bpstate := BreakpointState{Breakpoint: bp, Active: false, Internal: false, CondError: nil}
|
||||||
return true, nil
|
if bp.Cond == nil && bp.internalCond == nil {
|
||||||
|
bpstate.Active = true
|
||||||
|
bpstate.Internal = bp.Kind != UserBreakpoint
|
||||||
|
return bpstate
|
||||||
}
|
}
|
||||||
if bp.Kind == NextDeferBreakpoint {
|
nextDeferOk := true
|
||||||
|
if bp.Kind&NextDeferBreakpoint != 0 {
|
||||||
frames, err := ThreadStacktrace(thread, 2)
|
frames, err := ThreadStacktrace(thread, 2)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
|
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
|
||||||
@ -112,15 +124,43 @@ func (bp *Breakpoint) CheckCondition(thread Thread) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ispanic && !isdeferreturn {
|
nextDeferOk = ispanic || isdeferreturn
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return evalBreakpointCondition(thread, bp.Cond)
|
if bp.Kind != UserBreakpoint {
|
||||||
|
// 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.Kind&UserBreakpoint != 0 {
|
||||||
|
// 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) {
|
func evalBreakpointCondition(thread Thread, cond ast.Expr) (bool, error) {
|
||||||
|
if cond == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
scope, err := GoroutineScope(thread)
|
scope, err := GoroutineScope(thread)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return true, err
|
return true, err
|
||||||
@ -138,11 +178,6 @@ func evalBreakpointCondition(thread Thread, cond ast.Expr) (bool, error) {
|
|||||||
return constant.BoolVal(v.Value), nil
|
return constant.BoolVal(v.Value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal returns true for breakpoints not set directly by the user.
|
|
||||||
func (bp *Breakpoint) Internal() bool {
|
|
||||||
return bp.Kind != UserBreakpoint
|
|
||||||
}
|
|
||||||
|
|
||||||
// NoBreakpointError is returned when trying to
|
// NoBreakpointError is returned when trying to
|
||||||
// clear a breakpoint that does not exist.
|
// clear a breakpoint that does not exist.
|
||||||
type NoBreakpointError struct {
|
type NoBreakpointError struct {
|
||||||
@ -153,6 +188,7 @@ func (nbp NoBreakpointError) Error() string {
|
|||||||
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
|
return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BreakpointMap represents an (address, breakpoint) map.
|
||||||
type BreakpointMap struct {
|
type BreakpointMap struct {
|
||||||
M map[uint64]*Breakpoint
|
M map[uint64]*Breakpoint
|
||||||
|
|
||||||
@ -160,12 +196,14 @@ type BreakpointMap struct {
|
|||||||
internalBreakpointIDCounter int
|
internalBreakpointIDCounter int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewBreakpointMap creates a new BreakpointMap.
|
||||||
func NewBreakpointMap() BreakpointMap {
|
func NewBreakpointMap() BreakpointMap {
|
||||||
return BreakpointMap{
|
return BreakpointMap{
|
||||||
M: make(map[uint64]*Breakpoint),
|
M: make(map[uint64]*Breakpoint),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetBreakpointIDCounter resets the breakpoint ID counter of bpmap.
|
||||||
func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
|
func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
|
||||||
bpmap.breakpointIDCounter = 0
|
bpmap.breakpointIDCounter = 0
|
||||||
}
|
}
|
||||||
@ -178,7 +216,19 @@ type clearBreakpointFn func(*Breakpoint) error
|
|||||||
// to implement proc.Process.SetBreakpoint.
|
// to implement proc.Process.SetBreakpoint.
|
||||||
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
|
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
|
||||||
if bp, ok := bpmap.M[addr]; ok {
|
if bp, ok := bpmap.M[addr]; ok {
|
||||||
return bp, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
// 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.Kind&UserBreakpoint != 0) {
|
||||||
|
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)
|
f, l, fn, originalData, err := writeBreakpoint(addr)
|
||||||
@ -192,7 +242,6 @@ func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr,
|
|||||||
Line: l,
|
Line: l,
|
||||||
Addr: addr,
|
Addr: addr,
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
Cond: cond,
|
|
||||||
OriginalData: originalData,
|
OriginalData: originalData,
|
||||||
HitCount: map[int]uint64{},
|
HitCount: map[int]uint64{},
|
||||||
}
|
}
|
||||||
@ -200,9 +249,11 @@ func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr,
|
|||||||
if kind != UserBreakpoint {
|
if kind != UserBreakpoint {
|
||||||
bpmap.internalBreakpointIDCounter++
|
bpmap.internalBreakpointIDCounter++
|
||||||
newBreakpoint.ID = bpmap.internalBreakpointIDCounter
|
newBreakpoint.ID = bpmap.internalBreakpointIDCounter
|
||||||
|
newBreakpoint.internalCond = cond
|
||||||
} else {
|
} else {
|
||||||
bpmap.breakpointIDCounter++
|
bpmap.breakpointIDCounter++
|
||||||
newBreakpoint.ID = bpmap.breakpointIDCounter
|
newBreakpoint.ID = bpmap.breakpointIDCounter
|
||||||
|
newBreakpoint.Cond = cond
|
||||||
}
|
}
|
||||||
|
|
||||||
bpmap.M[addr] = newBreakpoint
|
bpmap.M[addr] = newBreakpoint
|
||||||
@ -228,6 +279,12 @@ func (bpmap *BreakpointMap) Clear(addr uint64, clearBreakpoint clearBreakpointFn
|
|||||||
return nil, NoBreakpointError{Addr: addr}
|
return nil, NoBreakpointError{Addr: addr}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bp.Kind &= ^UserBreakpoint
|
||||||
|
bp.Cond = nil
|
||||||
|
if bp.Kind != 0 {
|
||||||
|
return bp, nil
|
||||||
|
}
|
||||||
|
|
||||||
if err := clearBreakpoint(bp); err != nil {
|
if err := clearBreakpoint(bp); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -243,7 +300,9 @@ func (bpmap *BreakpointMap) Clear(addr uint64, clearBreakpoint clearBreakpointFn
|
|||||||
// instead, this function is used to implement that.
|
// instead, this function is used to implement that.
|
||||||
func (bpmap *BreakpointMap) ClearInternalBreakpoints(clearBreakpoint clearBreakpointFn) error {
|
func (bpmap *BreakpointMap) ClearInternalBreakpoints(clearBreakpoint clearBreakpointFn) error {
|
||||||
for addr, bp := range bpmap.M {
|
for addr, bp := range bpmap.M {
|
||||||
if !bp.Internal() {
|
bp.Kind = bp.Kind & UserBreakpoint
|
||||||
|
bp.internalCond = nil
|
||||||
|
if bp.Kind != 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := clearBreakpoint(bp); err != nil {
|
if err := clearBreakpoint(bp); err != nil {
|
||||||
@ -253,3 +312,45 @@ func (bpmap *BreakpointMap) ClearInternalBreakpoints(clearBreakpoint clearBreakp
|
|||||||
}
|
}
|
||||||
return nil
|
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.Kind != UserBreakpoint {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
@ -222,8 +222,8 @@ func (t *Thread) Location() (*proc.Location, error) {
|
|||||||
return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) Breakpoint() (*proc.Breakpoint, bool, error) {
|
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||||
return nil, false, nil
|
return proc.BreakpointState{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) ThreadID() int {
|
func (t *Thread) ThreadID() int {
|
||||||
|
@ -127,14 +127,12 @@ type Process struct {
|
|||||||
|
|
||||||
// Thread is a thread.
|
// Thread is a thread.
|
||||||
type Thread struct {
|
type Thread struct {
|
||||||
ID int
|
ID int
|
||||||
strID string
|
strID string
|
||||||
regs gdbRegisters
|
regs gdbRegisters
|
||||||
CurrentBreakpoint *proc.Breakpoint
|
CurrentBreakpoint proc.BreakpointState
|
||||||
BreakpointConditionMet bool
|
p *Process
|
||||||
BreakpointConditionError error
|
setbp bool // thread was stopped because of a breakpoint
|
||||||
p *Process
|
|
||||||
setbp bool // thread was stopped because of a breakpoint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// gdbRegisters represents the current value of the registers of a thread.
|
// gdbRegisters represents the current value of the registers of a thread.
|
||||||
@ -557,7 +555,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
|
|||||||
if p.conn.direction == proc.Forward {
|
if p.conn.direction == proc.Forward {
|
||||||
// step threads stopped at any breakpoint over their breakpoint
|
// step threads stopped at any breakpoint over their breakpoint
|
||||||
for _, thread := range p.threads {
|
for _, thread := range p.threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||||
if err := thread.stepInstruction(&threadUpdater{p: p}); err != nil {
|
if err := thread.stepInstruction(&threadUpdater{p: p}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -914,10 +912,8 @@ func (p *Process) Direction(dir proc.Direction) error {
|
|||||||
if p.conn.direction == dir {
|
if p.conn.direction == dir {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
for _, bp := range p.Breakpoints().M {
|
if p.Breakpoints().HasInternalBreakpoints() {
|
||||||
if bp.Internal() {
|
return ErrDirChange
|
||||||
return ErrDirChange
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
p.conn.direction = dir
|
p.conn.direction = dir
|
||||||
return nil
|
return nil
|
||||||
@ -971,8 +967,8 @@ func (p *Process) ClearInternalBreakpoints() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, thread := range p.threads {
|
for _, thread := range p.threads {
|
||||||
if thread.CurrentBreakpoint == bp {
|
if thread.CurrentBreakpoint.Breakpoint == bp {
|
||||||
thread.CurrentBreakpoint = nil
|
thread.clearBreakpointState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -1098,7 +1094,7 @@ func (p *Process) setCurrentBreakpoints() error {
|
|||||||
}
|
}
|
||||||
if !p.threadStopInfo {
|
if !p.threadStopInfo {
|
||||||
for _, th := range p.threads {
|
for _, th := range p.threads {
|
||||||
if th.CurrentBreakpoint == nil {
|
if th.CurrentBreakpoint.Breakpoint == nil {
|
||||||
err := th.SetCurrentBreakpoint()
|
err := th.SetCurrentBreakpoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1131,8 +1127,8 @@ func (t *Thread) Location() (*proc.Location, error) {
|
|||||||
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) Breakpoint() (breakpoint *proc.Breakpoint, active bool, condErr error) {
|
func (t *Thread) Breakpoint() proc.BreakpointState {
|
||||||
return t.CurrentBreakpoint, (t.CurrentBreakpoint != nil && t.BreakpointConditionMet), t.BreakpointConditionError
|
return t.CurrentBreakpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) ThreadID() int {
|
func (t *Thread) ThreadID() int {
|
||||||
@ -1431,13 +1427,11 @@ func (t *Thread) reloadGAlloc() error {
|
|||||||
|
|
||||||
func (t *Thread) clearBreakpointState() {
|
func (t *Thread) clearBreakpointState() {
|
||||||
t.setbp = false
|
t.setbp = false
|
||||||
t.CurrentBreakpoint = nil
|
t.CurrentBreakpoint.Clear()
|
||||||
t.BreakpointConditionMet = false
|
|
||||||
t.BreakpointConditionError = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||||
thread.CurrentBreakpoint = nil
|
thread.clearBreakpointState()
|
||||||
regs, err := thread.Registers(false)
|
regs, err := thread.Registers(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1449,9 +1443,8 @@ func (thread *Thread) SetCurrentBreakpoint() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
thread.CurrentBreakpoint = bp
|
thread.CurrentBreakpoint = bp.CheckCondition(thread)
|
||||||
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread)
|
if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.Active {
|
||||||
if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet {
|
|
||||||
if g, err := proc.GetG(thread); err == nil {
|
if g, err := proc.GetG(thread); err == nil {
|
||||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||||
}
|
}
|
||||||
|
@ -210,8 +210,7 @@ func (dbp *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, [
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
||||||
// break point table. Setting a break point must be thread specific due to
|
// break point table.
|
||||||
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
|
||||||
func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
|
||||||
return dbp.breakpoints.Set(addr, kind, cond, dbp.writeBreakpoint)
|
return dbp.breakpoints.Set(addr, kind, cond, dbp.writeBreakpoint)
|
||||||
}
|
}
|
||||||
@ -235,7 +234,7 @@ func (dbp *Process) ContinueOnce() (proc.Thread, error) {
|
|||||||
|
|
||||||
dbp.allGCache = nil
|
dbp.allGCache = nil
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
th.clearBreakpointState()
|
th.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
if dbp.resumeChan != nil {
|
if dbp.resumeChan != nil {
|
||||||
@ -276,7 +275,7 @@ func (dbp *Process) StepInstruction() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
return &proc.ProcessExitedError{Pid: dbp.Pid()}
|
||||||
}
|
}
|
||||||
thread.clearBreakpointState()
|
thread.CurrentBreakpoint.Clear()
|
||||||
err = thread.StepInstruction()
|
err = thread.StepInstruction()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -386,8 +385,8 @@ func (dbp *Process) ClearInternalBreakpoints() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.threads {
|
||||||
if thread.CurrentBreakpoint == bp {
|
if thread.CurrentBreakpoint.Breakpoint == bp {
|
||||||
thread.CurrentBreakpoint = nil
|
thread.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -99,7 +99,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
|||||||
|
|
||||||
dbp.allGCache = nil
|
dbp.allGCache = nil
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
th.clearBreakpointState()
|
th.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
trapthread, err := dbp.trapWait(-1)
|
trapthread, err := dbp.trapWait(-1)
|
||||||
@ -425,11 +425,11 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
func (dbp *Process) resume() error {
|
func (dbp *Process) resume() error {
|
||||||
// all threads stopped over a breakpoint are made to step over it
|
// all threads stopped over a breakpoint are made to step over it
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||||
if err := thread.StepInstruction(); err != nil {
|
if err := thread.StepInstruction(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
thread.CurrentBreakpoint = nil
|
thread.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// everything is resumed
|
// everything is resumed
|
||||||
|
@ -369,7 +369,7 @@ func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
|||||||
|
|
||||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
if th.CurrentBreakpoint == nil {
|
if th.CurrentBreakpoint.Breakpoint == nil {
|
||||||
if err := th.SetCurrentBreakpoint(); err != nil {
|
if err := th.SetCurrentBreakpoint(); err != nil {
|
||||||
if err == sys.ESRCH {
|
if err == sys.ESRCH {
|
||||||
// This thread quit between the point where we received the breakpoint and
|
// This thread quit between the point where we received the breakpoint and
|
||||||
@ -399,11 +399,11 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
func (dbp *Process) resume() error {
|
func (dbp *Process) resume() error {
|
||||||
// all threads stopped over a breakpoint are made to step over it
|
// all threads stopped over a breakpoint are made to step over it
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||||
if err := thread.StepInstruction(); err != nil {
|
if err := thread.StepInstruction(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
thread.CurrentBreakpoint = nil
|
thread.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// everything is resumed
|
// everything is resumed
|
||||||
|
@ -447,11 +447,11 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
|
|
||||||
func (dbp *Process) resume() error {
|
func (dbp *Process) resume() error {
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint.Breakpoint != nil {
|
||||||
if err := thread.StepInstruction(); err != nil {
|
if err := thread.StepInstruction(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
thread.CurrentBreakpoint = nil
|
thread.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,11 +12,9 @@ import (
|
|||||||
// a whole, and Status represents the last result of a `wait` call
|
// a whole, and Status represents the last result of a `wait` call
|
||||||
// on this thread.
|
// on this thread.
|
||||||
type Thread struct {
|
type Thread struct {
|
||||||
ID int // Thread ID or mach port
|
ID int // Thread ID or mach port
|
||||||
Status *WaitStatus // Status returned from last wait call
|
Status *WaitStatus // Status returned from last wait call
|
||||||
CurrentBreakpoint *proc.Breakpoint // Breakpoint thread is currently stopped at
|
CurrentBreakpoint proc.BreakpointState // Breakpoint thread is currently stopped at
|
||||||
BreakpointConditionMet bool // Output of evaluating the breakpoint's condition
|
|
||||||
BreakpointConditionError error // Error evaluating the breakpoint's condition
|
|
||||||
|
|
||||||
dbp *Process
|
dbp *Process
|
||||||
singleStepping bool
|
singleStepping bool
|
||||||
@ -141,18 +139,17 @@ func (thread *Thread) Halt() (err error) {
|
|||||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
||||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||||
thread.CurrentBreakpoint = nil
|
thread.CurrentBreakpoint.Clear()
|
||||||
pc, err := thread.PC()
|
pc, err := thread.PC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
|
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||||
thread.CurrentBreakpoint = bp
|
|
||||||
if err = thread.SetPC(bp.Addr); err != nil {
|
if err = thread.SetPC(bp.Addr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.CheckCondition(thread)
|
thread.CurrentBreakpoint = bp.CheckCondition(thread)
|
||||||
if thread.CurrentBreakpoint != nil && thread.BreakpointConditionMet {
|
if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.Active {
|
||||||
if g, err := proc.GetG(thread); err == nil {
|
if g, err := proc.GetG(thread); err == nil {
|
||||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||||
}
|
}
|
||||||
@ -162,14 +159,8 @@ func (thread *Thread) SetCurrentBreakpoint() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (thread *Thread) clearBreakpointState() {
|
func (th *Thread) Breakpoint() proc.BreakpointState {
|
||||||
thread.CurrentBreakpoint = nil
|
return th.CurrentBreakpoint
|
||||||
thread.BreakpointConditionMet = false
|
|
||||||
thread.BreakpointConditionError = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *Thread) Breakpoint() (*proc.Breakpoint, bool, error) {
|
|
||||||
return th.CurrentBreakpoint, th.CurrentBreakpoint != nil && th.BreakpointConditionMet, th.BreakpointConditionError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (th *Thread) ThreadID() int {
|
func (th *Thread) ThreadID() int {
|
||||||
|
@ -68,10 +68,8 @@ func Next(dbp Process) (err error) {
|
|||||||
if dbp.Exited() {
|
if dbp.Exited() {
|
||||||
return &ProcessExitedError{Pid: dbp.Pid()}
|
return &ProcessExitedError{Pid: dbp.Pid()}
|
||||||
}
|
}
|
||||||
for _, bp := range dbp.Breakpoints().M {
|
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||||
if bp.Internal() {
|
return fmt.Errorf("next while nexting")
|
||||||
return fmt.Errorf("next while nexting")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = next(dbp, false); err != nil {
|
if err = next(dbp, false); err != nil {
|
||||||
@ -106,10 +104,10 @@ func Continue(dbp Process) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
curthread := dbp.CurrentThread()
|
curthread := dbp.CurrentThread()
|
||||||
curbp, curbpActive, _ := curthread.Breakpoint()
|
curbp := curthread.Breakpoint()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case curbp == nil:
|
case curbp.Breakpoint == nil:
|
||||||
// runtime.Breakpoint or manual stop
|
// runtime.Breakpoint or manual stop
|
||||||
if recorded, _ := dbp.Recorded(); onRuntimeBreakpoint(curthread) && !recorded {
|
if recorded, _ := dbp.Recorded(); onRuntimeBreakpoint(curthread) && !recorded {
|
||||||
// Single-step current thread until we exit runtime.breakpoint and
|
// Single-step current thread until we exit runtime.breakpoint and
|
||||||
@ -127,7 +125,7 @@ func Continue(dbp Process) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return conditionErrors(threads)
|
return conditionErrors(threads)
|
||||||
case curbpActive && curbp.Internal():
|
case curbp.Active && curbp.Internal:
|
||||||
if curbp.Kind == StepBreakpoint {
|
if curbp.Kind == StepBreakpoint {
|
||||||
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
||||||
if err := conditionErrors(threads); err != nil {
|
if err := conditionErrors(threads); err != nil {
|
||||||
@ -154,7 +152,7 @@ func Continue(dbp Process) error {
|
|||||||
}
|
}
|
||||||
return conditionErrors(threads)
|
return conditionErrors(threads)
|
||||||
}
|
}
|
||||||
case curbpActive:
|
case curbp.Active:
|
||||||
onNextGoroutine, err := onNextGoroutine(curthread, dbp.Breakpoints())
|
onNextGoroutine, err := onNextGoroutine(curthread, dbp.Breakpoints())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -178,9 +176,9 @@ func Continue(dbp Process) error {
|
|||||||
func conditionErrors(threads []Thread) error {
|
func conditionErrors(threads []Thread) error {
|
||||||
var condErr error
|
var condErr error
|
||||||
for _, th := range threads {
|
for _, th := range threads {
|
||||||
if bp, _, bperr := th.Breakpoint(); bp != nil && bperr != nil {
|
if bp := th.Breakpoint(); bp.Breakpoint != nil && bp.CondError != nil {
|
||||||
if condErr == nil {
|
if condErr == nil {
|
||||||
condErr = bperr
|
condErr = bp.CondError
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("multiple errors evaluating conditions")
|
return fmt.Errorf("multiple errors evaluating conditions")
|
||||||
}
|
}
|
||||||
@ -195,15 +193,15 @@ func conditionErrors(threads []Thread) error {
|
|||||||
// - trapthread
|
// - trapthread
|
||||||
func pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
|
func pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
|
||||||
for _, th := range threads {
|
for _, th := range threads {
|
||||||
if bp, active, _ := th.Breakpoint(); active && bp.Internal() {
|
if bp := th.Breakpoint(); bp.Active && bp.Internal {
|
||||||
return dbp.SwitchThread(th.ThreadID())
|
return dbp.SwitchThread(th.ThreadID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if _, active, _ := trapthread.Breakpoint(); active {
|
if bp := trapthread.Breakpoint(); bp.Active {
|
||||||
return dbp.SwitchThread(trapthread.ThreadID())
|
return dbp.SwitchThread(trapthread.ThreadID())
|
||||||
}
|
}
|
||||||
for _, th := range threads {
|
for _, th := range threads {
|
||||||
if _, active, _ := th.Breakpoint(); active {
|
if bp := th.Breakpoint(); bp.Active {
|
||||||
return dbp.SwitchThread(th.ThreadID())
|
return dbp.SwitchThread(th.ThreadID())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,10 +214,8 @@ func Step(dbp Process) (err error) {
|
|||||||
if dbp.Exited() {
|
if dbp.Exited() {
|
||||||
return &ProcessExitedError{Pid: dbp.Pid()}
|
return &ProcessExitedError{Pid: dbp.Pid()}
|
||||||
}
|
}
|
||||||
for _, bp := range dbp.Breakpoints().M {
|
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||||
if bp.Internal() {
|
return fmt.Errorf("next while nexting")
|
||||||
return fmt.Errorf("next while nexting")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = next(dbp, true); err != nil {
|
if err = next(dbp, true); err != nil {
|
||||||
@ -336,7 +332,7 @@ func StepOut(dbp Process) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if bp, _, _ := curthread.Breakpoint(); bp == nil {
|
if bp := curthread.Breakpoint(); bp.Breakpoint == nil {
|
||||||
curthread.SetCurrentBreakpoint()
|
curthread.SetCurrentBreakpoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,7 +550,7 @@ func TestNextConcurrentVariant2(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assertNoError(err, t, "EvalVariable")
|
assertNoError(err, t, "EvalVariable")
|
||||||
vval, _ = constant.Int64Val(v.Value)
|
vval, _ = constant.Int64Val(v.Value)
|
||||||
if bp, _, _ := p.CurrentThread().Breakpoint(); bp == nil {
|
if bpstate := p.CurrentThread().Breakpoint(); bpstate.Breakpoint == nil {
|
||||||
if vval != initVval {
|
if vval != initVval {
|
||||||
t.Fatal("Did not end up on same goroutine")
|
t.Fatal("Did not end up on same goroutine")
|
||||||
}
|
}
|
||||||
@ -1038,11 +1038,11 @@ func TestContinueMulti(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assertNoError(err, t, "Continue()")
|
assertNoError(err, t, "Continue()")
|
||||||
|
|
||||||
if bp, _, _ := p.CurrentThread().Breakpoint(); bp.ID == bp1.ID {
|
if bp := p.CurrentThread().Breakpoint(); bp.ID == bp1.ID {
|
||||||
mainCount++
|
mainCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
if bp, _, _ := p.CurrentThread().Breakpoint(); bp.ID == bp2.ID {
|
if bp := p.CurrentThread().Breakpoint(); bp.ID == bp2.ID {
|
||||||
sayhiCount++
|
sayhiCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1447,7 +1447,7 @@ func TestBreakpointCountsWithDetection(t *testing.T) {
|
|||||||
assertNoError(err, t, "Continue()")
|
assertNoError(err, t, "Continue()")
|
||||||
}
|
}
|
||||||
for _, th := range p.ThreadList() {
|
for _, th := range p.ThreadList() {
|
||||||
if bp, _, _ := th.Breakpoint(); bp == nil {
|
if bp := th.Breakpoint(); bp.Breakpoint == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
scope, err := proc.GoroutineScope(th)
|
scope, err := proc.GoroutineScope(th)
|
||||||
@ -1864,8 +1864,8 @@ func TestPanicBreakpoint(t *testing.T) {
|
|||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("panic", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("panic", t, func(p proc.Process, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "Continue()")
|
assertNoError(proc.Continue(p), t, "Continue()")
|
||||||
bp, _, _ := p.CurrentThread().Breakpoint()
|
bp := p.CurrentThread().Breakpoint()
|
||||||
if bp == nil || bp.Name != proc.UnrecoveredPanic {
|
if bp.Breakpoint == nil || bp.Name != proc.UnrecoveredPanic {
|
||||||
t.Fatalf("not on unrecovered-panic breakpoint: %v", bp)
|
t.Fatalf("not on unrecovered-panic breakpoint: %v", bp)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1874,8 +1874,8 @@ func TestPanicBreakpoint(t *testing.T) {
|
|||||||
func TestCmdLineArgs(t *testing.T) {
|
func TestCmdLineArgs(t *testing.T) {
|
||||||
expectSuccess := func(p proc.Process, fixture protest.Fixture) {
|
expectSuccess := func(p proc.Process, fixture protest.Fixture) {
|
||||||
err := proc.Continue(p)
|
err := proc.Continue(p)
|
||||||
bp, _, _ := p.CurrentThread().Breakpoint()
|
bp := p.CurrentThread().Breakpoint()
|
||||||
if bp != nil && bp.Name == proc.UnrecoveredPanic {
|
if bp.Breakpoint != nil && bp.Name == proc.UnrecoveredPanic {
|
||||||
t.Fatalf("testing args failed on unrecovered-panic breakpoint: %v", bp)
|
t.Fatalf("testing args failed on unrecovered-panic breakpoint: %v", bp)
|
||||||
}
|
}
|
||||||
exit, exited := err.(proc.ProcessExitedError)
|
exit, exited := err.(proc.ProcessExitedError)
|
||||||
@ -1890,8 +1890,8 @@ func TestCmdLineArgs(t *testing.T) {
|
|||||||
|
|
||||||
expectPanic := func(p proc.Process, fixture protest.Fixture) {
|
expectPanic := func(p proc.Process, fixture protest.Fixture) {
|
||||||
proc.Continue(p)
|
proc.Continue(p)
|
||||||
bp, _, _ := p.CurrentThread().Breakpoint()
|
bp := p.CurrentThread().Breakpoint()
|
||||||
if bp == nil || bp.Name != proc.UnrecoveredPanic {
|
if bp.Breakpoint == nil || bp.Name != proc.UnrecoveredPanic {
|
||||||
t.Fatalf("not on unrecovered-panic breakpoint: %v", bp)
|
t.Fatalf("not on unrecovered-panic breakpoint: %v", bp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2326,15 +2326,6 @@ func TestStepConcurrentDirect(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextInProgress(p proc.Process) bool {
|
|
||||||
for _, bp := range p.Breakpoints().M {
|
|
||||||
if bp.Internal() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStepConcurrentPtr(t *testing.T) {
|
func TestStepConcurrentPtr(t *testing.T) {
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
|
||||||
@ -2364,10 +2355,9 @@ func TestStepConcurrentPtr(t *testing.T) {
|
|||||||
f, ln := currentLineNumber(p, t)
|
f, ln := currentLineNumber(p, t)
|
||||||
if ln != 24 {
|
if ln != 24 {
|
||||||
for _, th := range p.ThreadList() {
|
for _, th := range p.ThreadList() {
|
||||||
bp, bpactive, bperr := th.Breakpoint()
|
t.Logf("thread %d stopped on breakpoint %v", th.ThreadID(), th.Breakpoint())
|
||||||
t.Logf("thread %d stopped on breakpoint %v %v %v", th.ThreadID(), bp, bpactive, bperr)
|
|
||||||
}
|
}
|
||||||
curbp, _, _ := p.CurrentThread().Breakpoint()
|
curbp := p.CurrentThread().Breakpoint()
|
||||||
t.Fatalf("Program did not continue at expected location (24): %s:%d %#x [%v] (gid %d count %d)", f, ln, currentPC(p, t), curbp, p.SelectedGoroutine().ID, count)
|
t.Fatalf("Program did not continue at expected location (24): %s:%d %#x [%v] (gid %d count %d)", f, ln, currentPC(p, t), curbp, p.SelectedGoroutine().ID, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2385,7 +2375,7 @@ func TestStepConcurrentPtr(t *testing.T) {
|
|||||||
kvals[gid] = k
|
kvals[gid] = k
|
||||||
|
|
||||||
assertNoError(proc.Step(p), t, "Step()")
|
assertNoError(proc.Step(p), t, "Step()")
|
||||||
for nextInProgress(p) {
|
for p.Breakpoints().HasInternalBreakpoints() {
|
||||||
if p.SelectedGoroutine().ID == gid {
|
if p.SelectedGoroutine().ID == gid {
|
||||||
t.Fatalf("step did not step into function call (but internal breakpoints still active?) (%d %d)", gid, p.SelectedGoroutine().ID)
|
t.Fatalf("step did not step into function call (but internal breakpoints still active?) (%d %d)", gid, p.SelectedGoroutine().ID)
|
||||||
}
|
}
|
||||||
@ -2705,7 +2695,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
|
|||||||
gs, err := proc.GoroutinesInfo(p)
|
gs, err := proc.GoroutinesInfo(p)
|
||||||
assertNoError(err, t, "GoroutinesInfo()")
|
assertNoError(err, t, "GoroutinesInfo()")
|
||||||
for _, th := range p.ThreadList() {
|
for _, th := range p.ThreadList() {
|
||||||
if bp, _, _ := th.Breakpoint(); bp == nil {
|
if bp := th.Breakpoint(); bp.Breakpoint == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3140,3 +3130,23 @@ func TestAttachStripped(t *testing.T) {
|
|||||||
}
|
}
|
||||||
os.Remove(fixture.Path)
|
os.Remove(fixture.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIssue844(t *testing.T) {
|
||||||
|
// Conditional breakpoints should not prevent next from working if their
|
||||||
|
// condition isn't met.
|
||||||
|
withTestProcess("nextcond", t, func(p proc.Process, fixture protest.Fixture) {
|
||||||
|
setFileBreakpoint(p, t, fixture, 9)
|
||||||
|
condbp := setFileBreakpoint(p, t, fixture, 10)
|
||||||
|
condbp.Cond = &ast.BinaryExpr{
|
||||||
|
Op: token.EQL,
|
||||||
|
X: &ast.Ident{Name: "n"},
|
||||||
|
Y: &ast.BasicLit{Kind: token.INT, Value: "11"},
|
||||||
|
}
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue")
|
||||||
|
assertNoError(proc.Next(p), t, "Next")
|
||||||
|
f, l := currentLineNumber(p, t)
|
||||||
|
if l != 10 {
|
||||||
|
t.Fatalf("continued to wrong location %s:%d (expected 10)", f, l)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -86,7 +86,7 @@ func TestScope(t *testing.T) {
|
|||||||
}
|
}
|
||||||
assertNoError(err, t, "Continue()")
|
assertNoError(err, t, "Continue()")
|
||||||
}
|
}
|
||||||
bp, _, _ := p.CurrentThread().Breakpoint()
|
bp := p.CurrentThread().Breakpoint()
|
||||||
|
|
||||||
scopeCheck := findScopeCheck(scopeChecks, bp.Line)
|
scopeCheck := findScopeCheck(scopeChecks, bp.Line)
|
||||||
if scopeCheck == nil {
|
if scopeCheck == nil {
|
||||||
|
@ -19,11 +19,7 @@ type Thread interface {
|
|||||||
Location() (*Location, error)
|
Location() (*Location, error)
|
||||||
// Breakpoint will return the breakpoint that this thread is stopped at or
|
// Breakpoint will return the breakpoint that this thread is stopped at or
|
||||||
// nil if the thread is not stopped at any breakpoint.
|
// nil if the thread is not stopped at any breakpoint.
|
||||||
// Active will be true if the thread is stopped at a breakpoint and the
|
Breakpoint() BreakpointState
|
||||||
// breakpoint's condition is met.
|
|
||||||
// If there was an error evaluating the breakpoint's condition it will be
|
|
||||||
// returned as condErr
|
|
||||||
Breakpoint() (breakpoint *Breakpoint, active bool, condErr error)
|
|
||||||
ThreadID() int
|
ThreadID() int
|
||||||
Registers(floatingPoint bool) (Registers, error)
|
Registers(floatingPoint bool) (Registers, error)
|
||||||
Arch() Arch
|
Arch() Arch
|
||||||
@ -261,7 +257,7 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
// there it's ok.
|
// there it's ok.
|
||||||
}
|
}
|
||||||
|
|
||||||
if bp, _, _ := curthread.Breakpoint(); bp == nil {
|
if bp := curthread.Breakpoint(); bp.Breakpoint == nil {
|
||||||
curthread.SetCurrentBreakpoint()
|
curthread.SetCurrentBreakpoint()
|
||||||
}
|
}
|
||||||
success = true
|
success = true
|
||||||
@ -414,7 +410,7 @@ func onRuntimeBreakpoint(thread Thread) bool {
|
|||||||
func onNextGoroutine(thread Thread, breakpoints *BreakpointMap) (bool, error) {
|
func onNextGoroutine(thread Thread, breakpoints *BreakpointMap) (bool, error) {
|
||||||
var bp *Breakpoint
|
var bp *Breakpoint
|
||||||
for i := range breakpoints.M {
|
for i := range breakpoints.M {
|
||||||
if breakpoints.M[i].Internal() && breakpoints.M[i].Cond != nil {
|
if breakpoints.M[i].Kind != UserBreakpoint && breakpoints.M[i].internalCond != nil {
|
||||||
bp = breakpoints.M[i]
|
bp = breakpoints.M[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -432,7 +428,7 @@ func onNextGoroutine(thread Thread, breakpoints *BreakpointMap) (bool, error) {
|
|||||||
// runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z)
|
// runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z)
|
||||||
// Here we are only interested in testing the runtime.curg.goid clause.
|
// Here we are only interested in testing the runtime.curg.goid clause.
|
||||||
w := onNextGoroutineWalker{thread: thread}
|
w := onNextGoroutineWalker{thread: thread}
|
||||||
ast.Walk(&w, bp.Cond)
|
ast.Walk(&w, bp.internalCond)
|
||||||
return w.ret, w.err
|
return w.ret, w.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,8 +64,8 @@ func ConvertThread(th proc.Thread) *Thread {
|
|||||||
|
|
||||||
var bp *Breakpoint
|
var bp *Breakpoint
|
||||||
|
|
||||||
if b, active, _ := th.Breakpoint(); active {
|
if b := th.Breakpoint(); b.Active {
|
||||||
bp = ConvertBreakpoint(b)
|
bp = ConvertBreakpoint(b.Breakpoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
if g, _ := proc.GetG(th); g != nil {
|
if g, _ := proc.GetG(th); g != nil {
|
||||||
|
@ -264,12 +264,7 @@ func (d *Debugger) state() (*api.DebuggerState, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bp := range d.target.Breakpoints().M {
|
state.NextInProgress = d.target.Breakpoints().HasInternalBreakpoints()
|
||||||
if bp.Internal() {
|
|
||||||
state.NextInProgress = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if recorded, _ := d.target.Recorded(); recorded {
|
if recorded, _ := d.target.Recorded(); recorded {
|
||||||
state.When, _ = d.target.When()
|
state.When, _ = d.target.When()
|
||||||
@ -399,10 +394,9 @@ func (d *Debugger) Breakpoints() []*api.Breakpoint {
|
|||||||
func (d *Debugger) breakpoints() []*api.Breakpoint {
|
func (d *Debugger) breakpoints() []*api.Breakpoint {
|
||||||
bps := []*api.Breakpoint{}
|
bps := []*api.Breakpoint{}
|
||||||
for _, bp := range d.target.Breakpoints().M {
|
for _, bp := range d.target.Breakpoints().M {
|
||||||
if bp.Internal() {
|
if bp.IsUser() {
|
||||||
continue
|
bps = append(bps, api.ConvertBreakpoint(bp))
|
||||||
}
|
}
|
||||||
bps = append(bps, api.ConvertBreakpoint(bp))
|
|
||||||
}
|
}
|
||||||
return bps
|
return bps
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user