terminal/command: add -per-g-hitcount option to condition command (#3055)
Support use per goroutine hitcount as hintcond operand. Fixes #3050 Co-authored-by: roketyyang <roketyyang@tencent.com>
This commit is contained in:
parent
6aa54c5c9f
commit
278e4d10c8
@ -180,6 +180,7 @@ Set breakpoint condition.
|
||||
|
||||
condition <breakpoint name or id> <boolean expression>.
|
||||
condition -hitcount <breakpoint name or id> <operator> <argument>.
|
||||
condition -per-g-hitcount <breakpoint name or id> <operator> <argument>.
|
||||
condition -clear <breakpoint name or id>.
|
||||
|
||||
Specifies that the breakpoint, tracepoint or watchpoint should break only if the boolean expression is true.
|
||||
@ -196,6 +197,8 @@ With the -hitcount option a condition on the breakpoint hit count can be set, th
|
||||
condition -hitcount bp != n
|
||||
condition -hitcount bp % n
|
||||
|
||||
The -per-g-hitcount option works like -hitcount, but use per goroutine hitcount to compare with n.
|
||||
|
||||
With the -clear option a condtion on the breakpoint can removed.
|
||||
|
||||
The '% n' form means we should stop at the breakpoint when the hitcount is a multiple of n.
|
||||
|
25
_fixtures/condperghitcount.go
Normal file
25
_fixtures/condperghitcount.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
for i := 0; i < 2; i++ {
|
||||
go func() {
|
||||
j := 0
|
||||
for {
|
||||
j++
|
||||
if j > 10 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
@ -264,14 +264,16 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
|
||||
|
||||
switch breaklet.Kind {
|
||||
case UserBreakpoint:
|
||||
var goroutineID int
|
||||
lbp := bpstate.Breakpoint.Logical
|
||||
if lbp != nil {
|
||||
if g, err := GetG(thread); err == nil {
|
||||
lbp.HitCount[g.ID]++
|
||||
goroutineID = g.ID
|
||||
lbp.HitCount[goroutineID]++
|
||||
}
|
||||
lbp.TotalHitCount++
|
||||
}
|
||||
active = checkHitCond(lbp)
|
||||
active = checkHitCond(lbp, goroutineID)
|
||||
|
||||
case StepBreakpoint, NextBreakpoint, NextDeferBreakpoint:
|
||||
nextDeferOk := true
|
||||
@ -318,26 +320,30 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
|
||||
}
|
||||
|
||||
// checkHitCond evaluates bp's hit condition on thread.
|
||||
func checkHitCond(lbp *LogicalBreakpoint) bool {
|
||||
func checkHitCond(lbp *LogicalBreakpoint, goroutineID int) bool {
|
||||
if lbp == nil || lbp.HitCond == nil {
|
||||
return true
|
||||
}
|
||||
hitCount := int(lbp.TotalHitCount)
|
||||
if lbp.HitCondPerG && goroutineID > 0 {
|
||||
hitCount = int(lbp.HitCount[goroutineID])
|
||||
}
|
||||
// Evaluate the breakpoint condition.
|
||||
switch lbp.HitCond.Op {
|
||||
case token.EQL:
|
||||
return int(lbp.TotalHitCount) == lbp.HitCond.Val
|
||||
return hitCount == lbp.HitCond.Val
|
||||
case token.NEQ:
|
||||
return int(lbp.TotalHitCount) != lbp.HitCond.Val
|
||||
return hitCount != lbp.HitCond.Val
|
||||
case token.GTR:
|
||||
return int(lbp.TotalHitCount) > lbp.HitCond.Val
|
||||
return hitCount > lbp.HitCond.Val
|
||||
case token.LSS:
|
||||
return int(lbp.TotalHitCount) < lbp.HitCond.Val
|
||||
return hitCount < lbp.HitCond.Val
|
||||
case token.GEQ:
|
||||
return int(lbp.TotalHitCount) >= lbp.HitCond.Val
|
||||
return hitCount >= lbp.HitCond.Val
|
||||
case token.LEQ:
|
||||
return int(lbp.TotalHitCount) <= lbp.HitCond.Val
|
||||
return hitCount <= lbp.HitCond.Val
|
||||
case token.REM:
|
||||
return int(lbp.TotalHitCount)%lbp.HitCond.Val == 0
|
||||
return hitCount%lbp.HitCond.Val == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -971,6 +977,7 @@ type LogicalBreakpoint struct {
|
||||
|
||||
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
|
||||
HitCondPerG bool // Use per goroutine hitcount as HitCond operand, instead of total hitcount
|
||||
|
||||
// HitCond: if not nil the breakpoint will be triggered only if the evaluated HitCond returns
|
||||
// true with the TotalHitCount.
|
||||
|
@ -451,6 +451,7 @@ The command 'on x -edit' can be used to edit the list of commands executed when
|
||||
|
||||
condition <breakpoint name or id> <boolean expression>.
|
||||
condition -hitcount <breakpoint name or id> <operator> <argument>.
|
||||
condition -per-g-hitcount <breakpoint name or id> <operator> <argument>.
|
||||
condition -clear <breakpoint name or id>.
|
||||
|
||||
Specifies that the breakpoint, tracepoint or watchpoint should break only if the boolean expression is true.
|
||||
@ -467,6 +468,8 @@ With the -hitcount option a condition on the breakpoint hit count can be set, th
|
||||
condition -hitcount bp != n
|
||||
condition -hitcount bp % n
|
||||
|
||||
The -per-g-hitcount option works like -hitcount, but use per goroutine hitcount to compare with n.
|
||||
|
||||
With the -clear option a condtion on the breakpoint can removed.
|
||||
|
||||
The '% n' form means we should stop at the breakpoint when the hitcount is a multiple of n.
|
||||
@ -1654,7 +1657,11 @@ func formatBreakpointAttrs(prefix string, bp *api.Breakpoint, includeTrace bool)
|
||||
attrs = append(attrs, fmt.Sprintf("%scond %s", prefix, bp.Cond))
|
||||
}
|
||||
if bp.HitCond != "" {
|
||||
attrs = append(attrs, fmt.Sprintf("%scond -hitcount %s", prefix, bp.HitCond))
|
||||
if bp.HitCondPerG {
|
||||
attrs = append(attrs, fmt.Sprintf("%scond -per-g-hitcount %s", prefix, bp.HitCond))
|
||||
} else {
|
||||
attrs = append(attrs, fmt.Sprintf("%scond -hitcount %s", prefix, bp.HitCond))
|
||||
}
|
||||
}
|
||||
if bp.Stacktrace > 0 {
|
||||
attrs = append(attrs, fmt.Sprintf("%sstack %d", prefix, bp.Stacktrace))
|
||||
@ -2814,11 +2821,13 @@ func conditionCmd(t *Term, ctx callContext, argstr string) error {
|
||||
return fmt.Errorf("not enough arguments")
|
||||
}
|
||||
|
||||
if args[0] == "-hitcount" {
|
||||
hitCondPerG := args[0] == "-per-g-hitcount"
|
||||
if args[0] == "-hitcount" || hitCondPerG {
|
||||
// hitcount breakpoint
|
||||
|
||||
if ctx.Prefix == onPrefix {
|
||||
ctx.Breakpoint.HitCond = args[1]
|
||||
ctx.Breakpoint.HitCondPerG = hitCondPerG
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -2833,6 +2842,7 @@ func conditionCmd(t *Term, ctx callContext, argstr string) error {
|
||||
}
|
||||
|
||||
bp.HitCond = args[1]
|
||||
bp.HitCondPerG = hitCondPerG
|
||||
|
||||
return t.client.AmendBreakpoint(bp)
|
||||
}
|
||||
|
@ -1158,6 +1158,26 @@ func TestHitCondBreakpoint(t *testing.T) {
|
||||
t.Fatalf("wrong value of i")
|
||||
}
|
||||
})
|
||||
|
||||
withTestTerminal("condperghitcount", t, func(term *FakeTerminal) {
|
||||
term.MustExec("break bp1 main.main:8")
|
||||
term.MustExec("condition -per-g-hitcount bp1 == 2")
|
||||
listIsAt(t, term, "continue", 16, -1, -1)
|
||||
// first g hit
|
||||
out := term.MustExec("print j")
|
||||
t.Logf("%q", out)
|
||||
if !strings.Contains(out, "2\n") {
|
||||
t.Fatalf("wrong value of j")
|
||||
}
|
||||
term.MustExec("toggle bp1")
|
||||
listIsAt(t, term, "continue", 16, -1, -1)
|
||||
// second g hit
|
||||
out = term.MustExec("print j")
|
||||
t.Logf("%q", out)
|
||||
if !strings.Contains(out, "2\n") {
|
||||
t.Fatalf("wrong value of j")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestClearCondBreakpoint(t *testing.T) {
|
||||
|
@ -43,6 +43,7 @@ func ConvertLogicalBreakpoint(lbp *proc.LogicalBreakpoint) *Breakpoint {
|
||||
|
||||
if lbp.HitCond != nil {
|
||||
b.HitCond = fmt.Sprintf("%s %d", lbp.HitCond.Op.String(), lbp.HitCond.Val)
|
||||
b.HitCondPerG = lbp.HitCondPerG
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
@ -92,6 +92,8 @@ type Breakpoint struct {
|
||||
// Breakpoint hit count condition.
|
||||
// Supported hit count conditions are "NUMBER" and "OP NUMBER".
|
||||
HitCond string
|
||||
// HitCondPerG use per goroutine hitcount as HitCond operand, instead of total hitcount
|
||||
HitCondPerG bool
|
||||
|
||||
// Tracepoint flag, signifying this is a tracepoint.
|
||||
Tracepoint bool `json:"continue"`
|
||||
|
@ -900,6 +900,7 @@ func copyLogicalBreakpointInfo(lbp *proc.LogicalBreakpoint, requested *api.Break
|
||||
Op token.Token
|
||||
Val int
|
||||
}{opTok, val}
|
||||
lbp.HitCondPerG = requested.HitCondPerG
|
||||
}
|
||||
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user