proc,debugger: move breakpoint ID counter to service/debugger (#2913)

Moves breakpoindIDCounter out of BreakpointsMap and into
service/debugger.Debuggger to simplify proc.Target's API and aid with
implementing fork follow mode, where we'll have to debug multiple
processes simultaneously.
This commit is contained in:
Alessandro Arzilli 2022-02-25 10:09:53 +01:00 committed by GitHub
parent 1418cfd385
commit 4c5b111abb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 72 deletions

@ -471,8 +471,6 @@ type BreakpointMap struct {
// WatchOutOfScope is the list of watchpoints that went out of scope during
// the last resume operation
WatchOutOfScope []*Breakpoint
breakpointIDCounter int
}
// NewBreakpointMap creates a new BreakpointMap.
@ -484,8 +482,8 @@ func NewBreakpointMap() BreakpointMap {
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
// break point table.
func (t *Target) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
return t.setBreakpointInternal(addr, kind, 0, cond)
func (t *Target) SetBreakpoint(logicalID int, addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
return t.setBreakpointInternal(logicalID, addr, kind, 0, cond)
}
// SetEBPFTracepoint will attach a uprobe to the function
@ -587,7 +585,7 @@ func (t *Target) setEBPFTracepointOnFunc(fn *Function, goidOffset int64) error {
// SetWatchpoint sets a data breakpoint at addr and stores it in the
// process wide break point table.
func (t *Target) SetWatchpoint(scope *EvalScope, expr string, wtype WatchType, cond ast.Expr) (*Breakpoint, error) {
func (t *Target) SetWatchpoint(logicalID int, scope *EvalScope, expr string, wtype WatchType, cond ast.Expr) (*Breakpoint, error) {
if (wtype&WatchWrite == 0) && (wtype&WatchRead == 0) {
return nil, errors.New("at least one of read and write must be set for watchpoint")
}
@ -626,7 +624,7 @@ func (t *Target) SetWatchpoint(scope *EvalScope, expr string, wtype WatchType, c
return nil, errors.New("can not watch stack allocated variable for reads")
}
bp, err := t.setBreakpointInternal(xv.Addr, UserBreakpoint, wtype.withSize(uint8(sz)), cond)
bp, err := t.setBreakpointInternal(logicalID, xv.Addr, UserBreakpoint, wtype.withSize(uint8(sz)), cond)
if err != nil {
return bp, err
}
@ -643,7 +641,7 @@ func (t *Target) SetWatchpoint(scope *EvalScope, expr string, wtype WatchType, c
return bp, nil
}
func (t *Target) setBreakpointInternal(addr uint64, kind BreakpointKind, wtype WatchType, cond ast.Expr) (*Breakpoint, error) {
func (t *Target) setBreakpointInternal(logicalID int, addr uint64, kind BreakpointKind, wtype WatchType, cond ast.Expr) (*Breakpoint, error) {
if valid, err := t.Valid(); !valid {
recorded, _ := t.Recorded()
if !recorded {
@ -654,8 +652,7 @@ func (t *Target) setBreakpointInternal(addr uint64, kind BreakpointKind, wtype W
newBreaklet := &Breaklet{Kind: kind, Cond: cond}
if kind == UserBreakpoint {
newBreaklet.HitCount = map[int]uint64{}
bpmap.breakpointIDCounter++
newBreaklet.LogicalID = bpmap.breakpointIDCounter
newBreaklet.LogicalID = logicalID
}
if bp, ok := bpmap.M[addr]; ok {
if !bp.canOverlap(kind) {
@ -717,22 +714,6 @@ func (t *Target) setBreakpointInternal(addr uint64, kind BreakpointKind, wtype W
return newBreakpoint, nil
}
// SetBreakpointWithID creates a breakpoint at addr, with the specified logical ID.
func (t *Target) SetBreakpointWithID(id int, addr uint64) (*Breakpoint, error) {
bpmap := t.Breakpoints()
bp, err := t.SetBreakpoint(addr, UserBreakpoint, nil)
if err == nil {
for _, breaklet := range bp.Breaklets {
if breaklet.Kind == UserBreakpoint {
breaklet.LogicalID = id
bpmap.breakpointIDCounter--
break
}
}
}
return bp, err
}
// canOverlap returns true if a breakpoint of kind can be overlapped to the
// already existing breaklets in bp.
// At most one user breakpoint can be set but multiple internal breakpoints are allowed.

@ -60,7 +60,7 @@ func setFunctionBreakpoint(p *proc.Target, t *testing.T, fname string) *proc.Bre
if len(addrs) != 1 {
t.Fatalf("%s:%d: setFunctionBreakpoint(%s): too many results %v", f, l, fname, addrs)
}
bp, err := p.SetBreakpoint(addrs[0], proc.UserBreakpoint, nil)
bp, err := p.SetBreakpoint(0, addrs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
}
@ -128,7 +128,7 @@ func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, li
if len(addrs) != 1 {
t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many results %v", f, l, fixture.Source, lineno, addrs)
}
bp, err := p.SetBreakpoint(addrs[0], proc.UserBreakpoint, nil)
bp, err := p.SetBreakpoint(0, addrs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err)
}

@ -218,7 +218,7 @@ func setFunctionBreakpoint(p *proc.Target, t testing.TB, fname string) *proc.Bre
if len(addrs) != 1 {
t.Fatalf("%s:%d: setFunctionBreakpoint(%s): too many results %v", f, l, fname, addrs)
}
bp, err := p.SetBreakpoint(addrs[0], proc.UserBreakpoint, nil)
bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
}
@ -236,7 +236,7 @@ func setFileBreakpoint(p *proc.Target, t testing.TB, path string, lineno int) *p
if len(addrs) != 1 {
t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many (or not enough) results %v", f, l, path, lineno, addrs)
}
bp, err := p.SetBreakpoint(addrs[0], proc.UserBreakpoint, nil)
bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err)
}
@ -350,7 +350,7 @@ func TestBreakpointInSeparateGoRoutine(t *testing.T) {
func TestBreakpointWithNonExistantFunction(t *testing.T) {
withTestProcess("testprog", t, func(p *proc.Target, fixture protest.Fixture) {
_, err := p.SetBreakpoint(0, proc.UserBreakpoint, nil)
_, err := p.SetBreakpoint(0, 0, proc.UserBreakpoint, nil)
if err == nil {
t.Fatal("Should not be able to break at non existant function")
}
@ -3874,7 +3874,7 @@ func TestInlinedStacktraceAndVariables(t *testing.T) {
}
for _, pc := range pcs {
t.Logf("setting breakpoint at %#x\n", pc)
_, err := p.SetBreakpoint(pc, proc.UserBreakpoint, nil)
_, err := p.SetBreakpoint(0, pc, proc.UserBreakpoint, nil)
assertNoError(err, t, fmt.Sprintf("SetBreakpoint(%#x)", pc))
}
@ -4029,7 +4029,7 @@ func TestInlineBreakpoint(t *testing.T) {
if fn.Name != expectedFn {
t.Fatalf("incorrect function returned, expected %s, got %s", expectedFn, fn.Name)
}
_, err = p.SetBreakpoint(pcs[0], proc.UserBreakpoint, nil)
_, err = p.SetBreakpoint(0, pcs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("unable to set breakpoint: %v", err)
}
@ -5466,7 +5466,7 @@ func TestWatchpointsBasic(t *testing.T) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
bp, err := p.SetWatchpoint(scope, "globalvar1", proc.WatchWrite, nil)
bp, err := p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite, nil)
assertNoError(err, t, "SetDataBreakpoint(write-only)")
assertNoError(p.Continue(), t, "Continue 1")
@ -5481,7 +5481,7 @@ func TestWatchpointsBasic(t *testing.T) {
assertNoError(p.Continue(), t, "Continue 2")
assertLineNumber(p, t, 21, "Continue 2") // Position 2
_, err = p.SetWatchpoint(scope, "globalvar1", proc.WatchWrite|proc.WatchRead, nil)
_, err = p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite|proc.WatchRead, nil)
assertNoError(err, t, "SetDataBreakpoint(read-write)")
assertNoError(p.Continue(), t, "Continue 3")
@ -5493,7 +5493,7 @@ func TestWatchpointsBasic(t *testing.T) {
assertLineNumber(p, t, 27, "Continue 4") // Position 4
t.Logf("setting final breakpoint")
_, err = p.SetWatchpoint(scope, "globalvar1", proc.WatchWrite, nil)
_, err = p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite, nil)
assertNoError(err, t, "SetDataBreakpoint(write-only, again)")
assertNoError(p.Continue(), t, "Continue 5")
@ -5514,7 +5514,7 @@ func TestWatchpointCounts(t *testing.T) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
bp, err := p.SetWatchpoint(scope, "globalvar1", proc.WatchWrite, nil)
bp, err := p.SetWatchpoint(0, scope, "globalvar1", proc.WatchWrite, nil)
assertNoError(err, t, "SetWatchpoint(write-only)")
for {
@ -5638,7 +5638,7 @@ func TestWatchpointStack(t *testing.T) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
_, err = p.SetWatchpoint(scope, "w", proc.WatchWrite, nil)
_, err = p.SetWatchpoint(0, scope, "w", proc.WatchWrite, nil)
assertNoError(err, t, "SetDataBreakpoint(write-only)")
watchbpnum := 3
@ -5666,7 +5666,7 @@ func TestWatchpointStack(t *testing.T) {
// instruction preceding the return address, this does not matter for this
// test.
_, err = p.SetBreakpoint(retaddr, proc.UserBreakpoint, nil)
_, err = p.SetBreakpoint(0, retaddr, proc.UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint")
if len(p.Breakpoints().M) != clearlen+watchbpnum {
@ -5714,7 +5714,7 @@ func TestWatchpointStackBackwardsOutOfScope(t *testing.T) {
scope, err := proc.GoroutineScope(p, p.CurrentThread())
assertNoError(err, t, "GoroutineScope")
_, err = p.SetWatchpoint(scope, "w", proc.WatchWrite, nil)
_, err = p.SetWatchpoint(0, scope, "w", proc.WatchWrite, nil)
assertNoError(err, t, "SetDataBreakpoint(write-only)")
assertNoError(p.Continue(), t, "Continue 1")

@ -53,7 +53,7 @@ func (t *Target) setStackWatchBreakpoints(scope *EvalScope, watchpoint *Breakpoi
}
}
if deferpc != 0 && deferpc != topframe.Current.PC {
deferbp, err := t.SetBreakpoint(deferpc, WatchOutOfScopeBreakpoint, sameGCond)
deferbp, err := t.SetBreakpoint(0, deferpc, WatchOutOfScopeBreakpoint, sameGCond)
if err != nil {
return err
}
@ -63,7 +63,7 @@ func (t *Target) setStackWatchBreakpoints(scope *EvalScope, watchpoint *Breakpoi
deferbreaklet.callback = woos
}
retbp, err := t.SetBreakpoint(retframe.Current.PC, WatchOutOfScopeBreakpoint, retFrameCond)
retbp, err := t.SetBreakpoint(0, retframe.Current.PC, WatchOutOfScopeBreakpoint, retFrameCond)
if err != nil {
return err
}
@ -82,7 +82,7 @@ func (t *Target) setStackWatchBreakpoints(scope *EvalScope, watchpoint *Breakpoi
}
for i, instr := range callerText {
if instr.Loc.PC == retframe.Current.PC && i > 0 {
retbp2, err := t.SetBreakpoint(callerText[i-1].Loc.PC, WatchOutOfScopeBreakpoint, retFrameCond)
retbp2, err := t.SetBreakpoint(0, callerText[i-1].Loc.PC, WatchOutOfScopeBreakpoint, retFrameCond)
if err != nil {
return err
}
@ -116,7 +116,7 @@ func (t *Target) setStackWatchBreakpoints(scope *EvalScope, watchpoint *Breakpoi
if retpc == 0 {
return errors.New("could not find return instruction in runtime.copystack")
}
rszbp, err := t.SetBreakpoint(retpc, StackResizeBreakpoint, sameGCond)
rszbp, err := t.SetBreakpoint(0, retpc, StackResizeBreakpoint, sameGCond)
if err != nil {
return err
}

@ -393,7 +393,7 @@ func (t *Target) createUnrecoveredPanicBreakpoint() {
panicpcs, err = FindFunctionLocation(t.Process, "runtime.fatalpanic", 0)
}
if err == nil {
bp, err := t.SetBreakpointWithID(unrecoveredPanicID, panicpcs[0])
bp, err := t.SetBreakpoint(unrecoveredPanicID, panicpcs[0], UserBreakpoint, nil)
if err == nil {
bp.Name = UnrecoveredPanic
bp.Variables = []string{"runtime.curg._panic.arg"}
@ -405,7 +405,7 @@ func (t *Target) createUnrecoveredPanicBreakpoint() {
func (t *Target) createFatalThrowBreakpoint() {
fatalpcs, err := FindFunctionLocation(t.Process, "runtime.throw", 0)
if err == nil {
bp, err := t.SetBreakpointWithID(fatalThrowID, fatalpcs[0])
bp, err := t.SetBreakpoint(fatalThrowID, fatalpcs[0], UserBreakpoint, nil)
if err == nil {
bp.Name = FatalThrow
}
@ -464,11 +464,6 @@ func (t *Target) GetBufferedTracepoints() []*UProbeTraceResult {
return results
}
// SetNextBreakpointID sets the breakpoint ID of the next breakpoint
func (t *Target) SetNextBreakpointID(id int) {
t.Breakpoints().breakpointIDCounter = id
}
const (
FakeAddressBase = 0xbeef000000000000
fakeAddressUnresolv = 0xbeed000000000000 // this address never resloves to memory

@ -374,7 +374,7 @@ func (dbp *Target) StepOut() error {
if topframe.Ret != 0 {
topframe, retframe := skipAutogeneratedWrappersOut(selg, curthread, &topframe, &retframe)
retFrameCond := astutil.And(sameGCond, frameoffCondition(retframe))
bp, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(retframe.Current.PC, NextBreakpoint, retFrameCond))
bp, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, retframe.Current.PC, NextBreakpoint, retFrameCond))
if err != nil {
return err
}
@ -401,7 +401,7 @@ func (dbp *Target) StepInstruction() (err error) {
if g != nil {
if g.Thread == nil {
// Step called on parked goroutine
if _, err := dbp.SetBreakpoint(g.PC, NextBreakpoint,
if _, err := dbp.SetBreakpoint(0, g.PC, NextBreakpoint,
sameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
return err
}
@ -594,7 +594,7 @@ func next(dbp *Target, stepInto, inlinedStepOut bool) error {
}
for _, pc := range pcs {
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(pc, NextBreakpoint, sameFrameCond)); err != nil {
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, pc, NextBreakpoint, sameFrameCond)); err != nil {
dbp.ClearSteppingBreakpoints()
return err
}
@ -615,7 +615,7 @@ func next(dbp *Target, stepInto, inlinedStepOut bool) error {
// For inlined functions there is no need to do this, the set of PCs
// returned by the AllPCsBetween call above already cover all instructions
// of the containing function.
bp, _ := dbp.SetBreakpoint(retframe.Current.PC, NextBreakpoint, retFrameCond)
bp, _ := dbp.SetBreakpoint(0, retframe.Current.PC, NextBreakpoint, retFrameCond)
// Return address could be wrong, if we are unable to set a breakpoint
// there it's ok.
if bp != nil {
@ -642,7 +642,7 @@ func setStepIntoBreakpoints(dbp *Target, curfn *Function, text []AsmInstruction,
}
} else {
// Non-absolute call instruction, set a StepBreakpoint here
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, sameGCond)); err != nil {
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, instr.Loc.PC, StepBreakpoint, sameGCond)); err != nil {
return err
}
}
@ -665,7 +665,7 @@ func setStepIntoBreakpointsReverse(dbp *Target, text []AsmInstruction, topframe
if nextIdx := i + 1; nextIdx < len(text) {
_, ok := bpmap.M[text[nextIdx].Loc.PC]
if !ok {
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(text[nextIdx].Loc.PC, StepBreakpoint, sameGCond)); err != nil {
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, text[nextIdx].Loc.PC, StepBreakpoint, sameGCond)); err != nil {
return err
}
}
@ -766,7 +766,7 @@ func setStepIntoBreakpoint(dbp *Target, curfn *Function, text []AsmInstruction,
}
// Set a breakpoint after the function's prologue
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(pc, NextBreakpoint, cond)); err != nil {
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, pc, NextBreakpoint, cond)); err != nil {
return err
}
@ -905,7 +905,7 @@ func setDeferBreakpoint(p *Target, text []AsmInstruction, topframe Stackframe, s
}
}
if deferpc != 0 && deferpc != topframe.Current.PC {
bp, err := allowDuplicateBreakpoint(p.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond))
bp, err := allowDuplicateBreakpoint(p.SetBreakpoint(0, deferpc, NextDeferBreakpoint, sameGCond))
if err != nil {
return 0, err
}
@ -1004,7 +1004,7 @@ func stepOutReverse(p *Target, topframe, retframe Stackframe, sameGCond ast.Expr
}
_, err = allowDuplicateBreakpoint(p.SetBreakpoint(callpc, NextBreakpoint, sameGCond))
_, err = allowDuplicateBreakpoint(p.SetBreakpoint(0, callpc, NextBreakpoint, sameGCond))
return err
}

@ -76,6 +76,8 @@ type Debugger struct {
// so lower layers like proc doesn't need to deal
// with them
disabledBreakpoints map[int]*api.Breakpoint
breakpointIDCounter int
}
type ExecuteKind int
@ -541,7 +543,7 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
discarded = append(discarded, api.DiscardedBreakpoint{Breakpoint: oldBp, Reason: "can not recreate address breakpoints on restart"})
continue
}
newBp, err := p.SetBreakpointWithID(oldBp.ID, oldBp.Addr)
newBp, err := p.SetBreakpoint(oldBp.ID, oldBp.Addr, proc.UserBreakpoint, nil)
if err != nil {
return nil, err
}
@ -555,7 +557,7 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
maxID = bp.ID
}
}
d.target.SetNextBreakpointID(maxID)
d.breakpointIDCounter = maxID
return discarded, nil
}
@ -722,14 +724,11 @@ func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Break
bps := make([]*proc.Breakpoint, len(addrs))
var err error
for i := range addrs {
if id > 0 {
bps[i], err = p.SetBreakpointWithID(id, addrs[i])
} else {
bps[i], err = p.SetBreakpoint(addrs[i], proc.UserBreakpoint, nil)
if err == nil {
id = bps[i].LogicalID()
}
if id <= 0 {
d.breakpointIDCounter++
id = d.breakpointIDCounter
}
bps[i], err = p.SetBreakpoint(id, addrs[i], proc.UserBreakpoint, nil)
if err != nil {
break
}
@ -785,7 +784,7 @@ func (d *Debugger) amendBreakpoint(amend *api.Breakpoint) error {
return fmt.Errorf("no breakpoint with ID %d", amend.ID)
}
if !amend.Disabled && disabled { // enable the breakpoint
bp, err := d.target.SetBreakpointWithID(amend.ID, amend.Addr)
bp, err := d.target.SetBreakpoint(amend.ID, amend.Addr, proc.UserBreakpoint, nil)
if err != nil {
return err
}
@ -1111,7 +1110,8 @@ func (d *Debugger) CreateWatchpoint(goid, frame, deferredCall int, expr string,
if err != nil {
return nil, err
}
bp, err := d.target.SetWatchpoint(s, expr, proc.WatchType(wtype), nil)
d.breakpointIDCounter++
bp, err := d.target.SetWatchpoint(d.breakpointIDCounter, s, expr, proc.WatchType(wtype), nil)
if err != nil {
return nil, err
}

@ -1150,7 +1150,7 @@ func setFunctionBreakpoint(p *proc.Target, t testing.TB, fname string) *proc.Bre
if len(addrs) != 1 {
t.Fatalf("%s:%d: setFunctionBreakpoint(%s): too many results %v", f, l, fname, addrs)
}
bp, err := p.SetBreakpoint(addrs[0], proc.UserBreakpoint, nil)
bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err)
}
@ -1480,7 +1480,7 @@ func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, li
if len(addrs) != 1 {
t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many results %v", f, l, fixture.Source, lineno, addrs)
}
bp, err := p.SetBreakpoint(addrs[0], proc.UserBreakpoint, nil)
bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil)
if err != nil {
t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err)
}