Skip to content

Commit 6fe02a9

Browse files
committed
proc,service: represent logical breakpoints explicitly
Adds a LogicalBreakpoint type to represent logical breakpoints explicitly. Until now logical breakpoints were constructed implicitly by grouping physical breakpoints together by their LogicalID. Having logical breakpoints represented explicitly allows for a simpler implementation of disabled breakpoints, as well as allowing a simple implementation of delayed breakpoints (go-delve#1653, go-delve#2551) and in general of breakpoints spanning multiple processes if we implement debugging process trees (go-delve#2551). Updates go-delve#1653 Updates go-delve#2551
1 parent 398d0a5 commit 6fe02a9

File tree

9 files changed

+390
-408
lines changed

9 files changed

+390
-408
lines changed

pkg/proc/breakpoints.go

Lines changed: 93 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ type Breakpoint struct {
4747

4848
Addr uint64 // Address breakpoint is set for.
4949
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
50-
Name string // User defined name of the breakpoint
5150

5251
WatchExpr string
5352
WatchType WatchType
@@ -59,14 +58,7 @@ type Breakpoint struct {
5958
Breaklets []*Breaklet
6059

6160
// Breakpoint information
62-
Tracepoint bool // Tracepoint flag
63-
TraceReturn bool
64-
Goroutine bool // Retrieve goroutine information
65-
Stacktrace int // Number of stack frames to retrieve
66-
Variables []string // Variables to evaluate
67-
LoadArgs *LoadConfig
68-
LoadLocals *LoadConfig
69-
UserData interface{} // Any additional information about the breakpoint
61+
Logical *LogicalBreakpoint
7062

7163
// ReturnInfo describes how to collect return variables when this
7264
// breakpoint is hit as a return breakpoint.
@@ -85,9 +77,6 @@ type Breaklet struct {
8577
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
8678
Cond ast.Expr
8779

88-
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
89-
TotalHitCount uint64 // Number of times a breakpoint has been reached
90-
9180
// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
9281
// will also check if the caller is runtime.gopanic or if the return
9382
// address is in the DeferReturns array.
@@ -99,13 +88,6 @@ type Breaklet struct {
9988
// the function, not when the function is called directly
10089
DeferReturns []uint64
10190

102-
// HitCond: if not nil the breakpoint will be triggered only if the evaluated HitCond returns
103-
// true with the TotalHitCount.
104-
HitCond *struct {
105-
Op token.Token
106-
Val int
107-
}
108-
10991
// checkPanicCall checks that the breakpoint happened while the function was
11092
// called by a panic. It is only checked for WatchOutOfScopeBreakpoint Kind.
11193
checkPanicCall bool
@@ -206,10 +188,12 @@ func (bp *Breakpoint) VerboseDescr() []string {
206188
r = append(r, fmt.Sprintf("HWBreakIndex=%#x watchStackOff=%#x", bp.HWBreakIndex, bp.watchStackOff))
207189
}
208190

191+
lbp := bp.Logical
192+
209193
for _, breaklet := range bp.Breaklets {
210194
switch breaklet.Kind {
211195
case UserBreakpoint:
212-
r = append(r, fmt.Sprintf("User Cond=%q HitCond=%v", exprToString(breaklet.Cond), breaklet.HitCond))
196+
r = append(r, fmt.Sprintf("User Cond=%q HitCond=%v", exprToString(breaklet.Cond), lbp.HitCond))
213197
case NextBreakpoint:
214198
r = append(r, fmt.Sprintf("Next Cond=%q", exprToString(breaklet.Cond)))
215199
case NextDeferBreakpoint:
@@ -280,11 +264,14 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
280264

281265
switch breaklet.Kind {
282266
case UserBreakpoint:
283-
if g, err := GetG(thread); err == nil {
284-
breaklet.HitCount[g.ID]++
267+
lbp := bpstate.Breakpoint.Logical
268+
if lbp != nil {
269+
if g, err := GetG(thread); err == nil {
270+
lbp.HitCount[g.ID]++
271+
}
272+
lbp.TotalHitCount++
285273
}
286-
breaklet.TotalHitCount++
287-
active = checkHitCond(breaklet)
274+
active = checkHitCond(lbp)
288275

289276
case StepBreakpoint, NextBreakpoint, NextDeferBreakpoint:
290277
nextDeferOk := true
@@ -331,26 +318,26 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
331318
}
332319

333320
// checkHitCond evaluates bp's hit condition on thread.
334-
func checkHitCond(breaklet *Breaklet) bool {
335-
if breaklet.HitCond == nil {
321+
func checkHitCond(lbp *LogicalBreakpoint) bool {
322+
if lbp == nil || lbp.HitCond == nil {
336323
return true
337324
}
338325
// Evaluate the breakpoint condition.
339-
switch breaklet.HitCond.Op {
326+
switch lbp.HitCond.Op {
340327
case token.EQL:
341-
return int(breaklet.TotalHitCount) == breaklet.HitCond.Val
328+
return int(lbp.TotalHitCount) == lbp.HitCond.Val
342329
case token.NEQ:
343-
return int(breaklet.TotalHitCount) != breaklet.HitCond.Val
330+
return int(lbp.TotalHitCount) != lbp.HitCond.Val
344331
case token.GTR:
345-
return int(breaklet.TotalHitCount) > breaklet.HitCond.Val
332+
return int(lbp.TotalHitCount) > lbp.HitCond.Val
346333
case token.LSS:
347-
return int(breaklet.TotalHitCount) < breaklet.HitCond.Val
334+
return int(lbp.TotalHitCount) < lbp.HitCond.Val
348335
case token.GEQ:
349-
return int(breaklet.TotalHitCount) >= breaklet.HitCond.Val
336+
return int(lbp.TotalHitCount) >= lbp.HitCond.Val
350337
case token.LEQ:
351-
return int(breaklet.TotalHitCount) <= breaklet.HitCond.Val
338+
return int(lbp.TotalHitCount) <= lbp.HitCond.Val
352339
case token.REM:
353-
return int(breaklet.TotalHitCount)%breaklet.HitCond.Val == 0
340+
return int(lbp.TotalHitCount)%lbp.HitCond.Val == 0
354341
}
355342
return false
356343
}
@@ -468,6 +455,9 @@ func (nbp NoBreakpointError) Error() string {
468455
type BreakpointMap struct {
469456
M map[uint64]*Breakpoint
470457

458+
// Logical is a map of logical breakpoints.
459+
Logical map[int]*LogicalBreakpoint
460+
471461
// WatchOutOfScope is the list of watchpoints that went out of scope during
472462
// the last resume operation
473463
WatchOutOfScope []*Breakpoint
@@ -651,23 +641,42 @@ func (t *Target) setBreakpointInternal(logicalID int, addr uint64, kind Breakpoi
651641
bpmap := t.Breakpoints()
652642
newBreaklet := &Breaklet{Kind: kind, Cond: cond}
653643
if kind == UserBreakpoint {
654-
newBreaklet.HitCount = map[int]uint64{}
655644
newBreaklet.LogicalID = logicalID
656645
}
646+
647+
setLogicalBreakpoint := func(bp *Breakpoint) {
648+
if kind != UserBreakpoint || bp.Logical != nil {
649+
return
650+
}
651+
if bpmap.Logical == nil {
652+
bpmap.Logical = make(map[int]*LogicalBreakpoint)
653+
}
654+
lbp := bpmap.Logical[logicalID]
655+
if lbp == nil {
656+
lbp = &LogicalBreakpoint{LogicalID: logicalID}
657+
lbp.HitCount = make(map[int]uint64)
658+
lbp.Enabled = true
659+
bpmap.Logical[logicalID] = lbp
660+
}
661+
bp.Logical = lbp
662+
breaklet := bp.UserBreaklet()
663+
if breaklet != nil && breaklet.Cond == nil {
664+
breaklet.Cond = lbp.Cond
665+
}
666+
lbp.File = bp.File
667+
lbp.Line = bp.Line
668+
fn := t.BinInfo().PCToFunc(bp.Addr)
669+
if fn != nil {
670+
lbp.FunctionName = fn.NameWithoutTypeParams()
671+
}
672+
}
673+
657674
if bp, ok := bpmap.M[addr]; ok {
658675
if !bp.canOverlap(kind) {
659676
return bp, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
660677
}
661-
if kind == UserBreakpoint {
662-
bp.Tracepoint = false
663-
bp.TraceReturn = false
664-
bp.Goroutine = false
665-
bp.Stacktrace = 0
666-
bp.Variables = nil
667-
bp.LoadArgs = nil
668-
bp.LoadLocals = nil
669-
}
670678
bp.Breaklets = append(bp.Breaklets, newBreaklet)
679+
setLogicalBreakpoint(bp)
671680
return bp, nil
672681
}
673682

@@ -708,6 +717,7 @@ func (t *Target) setBreakpointInternal(logicalID int, addr uint64, kind Breakpoi
708717
}
709718

710719
newBreakpoint.Breaklets = append(newBreakpoint.Breaklets, newBreaklet)
720+
setLogicalBreakpoint(newBreakpoint)
711721

712722
bpmap.M[addr] = newBreakpoint
713723

@@ -741,6 +751,9 @@ func (t *Target) ClearBreakpoint(addr uint64) error {
741751
for i := range bp.Breaklets {
742752
if bp.Breaklets[i].Kind == UserBreakpoint {
743753
bp.Breaklets[i] = nil
754+
if bp.WatchExpr == "" {
755+
bp.Logical = nil
756+
}
744757
}
745758
}
746759

@@ -805,6 +818,9 @@ func (t *Target) finishClearBreakpoint(bp *Breakpoint) (bool, error) {
805818
}
806819

807820
delete(t.Breakpoints().M, bp.Addr)
821+
if bp.WatchExpr != "" && bp.Logical != nil {
822+
delete(t.Breakpoints().Logical, bp.Logical.LogicalID)
823+
}
808824
return true, nil
809825
}
810826

@@ -925,3 +941,36 @@ func returnInfoError(descr string, err error, mem MemoryReadWriter) []*Variable
925941
v.Name = "return value read error"
926942
return []*Variable{v}
927943
}
944+
945+
// LogicalBreakpoint represents a breakpoint set by a user.
946+
type LogicalBreakpoint struct {
947+
LogicalID int
948+
Name string
949+
FunctionName string
950+
File string
951+
Line int
952+
Enabled bool
953+
954+
Tracepoint bool // Tracepoint flag
955+
TraceReturn bool
956+
Goroutine bool // Retrieve goroutine information
957+
Stacktrace int // Number of stack frames to retrieve
958+
Variables []string // Variables to evaluate
959+
LoadArgs *LoadConfig
960+
LoadLocals *LoadConfig
961+
962+
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
963+
TotalHitCount uint64 // Number of times a breakpoint has been reached
964+
965+
// HitCond: if not nil the breakpoint will be triggered only if the evaluated HitCond returns
966+
// true with the TotalHitCount.
967+
HitCond *struct {
968+
Op token.Token
969+
Val int
970+
}
971+
972+
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
973+
Cond ast.Expr
974+
975+
UserData interface{} // Any additional information about the breakpoint
976+
}

pkg/proc/gdbserial/rr_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, li
128128
if len(addrs) != 1 {
129129
t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many results %v", f, l, fixture.Source, lineno, addrs)
130130
}
131-
bp, err := p.SetBreakpoint(0, addrs[0], proc.UserBreakpoint, nil)
131+
bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil)
132132
if err != nil {
133133
t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err)
134134
}
@@ -164,18 +164,18 @@ func TestReverseBreakpointCounts(t *testing.T) {
164164
}
165165
}
166166

167-
t.Logf("TotalHitCount: %d", bp.UserBreaklet().TotalHitCount)
168-
if bp.UserBreaklet().TotalHitCount != 200 {
169-
t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.UserBreaklet().TotalHitCount)
167+
t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount)
168+
if bp.Logical.TotalHitCount != 200 {
169+
t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount)
170170
}
171171

172-
if len(bp.UserBreaklet().HitCount) != 2 {
173-
t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.UserBreaklet().HitCount))
172+
if len(bp.Logical.HitCount) != 2 {
173+
t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount))
174174
}
175175

176-
for _, v := range bp.UserBreaklet().HitCount {
176+
for _, v := range bp.Logical.HitCount {
177177
if v != 100 {
178-
t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.UserBreaklet().HitCount)
178+
t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount)
179179
}
180180
}
181181
})

0 commit comments

Comments
 (0)