service: fix breakpoint IDs after Restart with disabled breakpoints (#2425)

When restarting we must take care of setting breakpoint IDs correctly
so that enabled breakpoints do not end up having the same ID as a
disabled breakpoint, also we must make sure that breakpoints created
after restart will not get an ID already used by a disabled breakpoint.
This commit is contained in:
Alessandro Arzilli
2021-04-12 23:59:43 +02:00
committed by GitHub
parent f3d7b25fdf
commit 781ad72d62
3 changed files with 56 additions and 6 deletions

View File

@ -359,3 +359,8 @@ func (t *Target) createFatalThrowBreakpoint() {
func (t *Target) CurrentThread() Thread { func (t *Target) CurrentThread() Thread {
return t.currentThread return t.currentThread
} }
// SetNextBreakpointID sets the breakpoint ID of the next breakpoint
func (t *Target) SetNextBreakpointID(id int) {
t.Breakpoints().breakpointIDCounter = id
}

View File

@ -509,23 +509,27 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
discarded := []api.DiscardedBreakpoint{} discarded := []api.DiscardedBreakpoint{}
breakpoints := api.ConvertBreakpoints(d.breakpoints()) breakpoints := api.ConvertBreakpoints(d.breakpoints())
d.target = p d.target = p
maxID := 0
for _, oldBp := range breakpoints { for _, oldBp := range breakpoints {
if oldBp.ID < 0 { if oldBp.ID < 0 {
continue continue
} }
if oldBp.ID > maxID {
maxID = oldBp.ID
}
if len(oldBp.File) > 0 { if len(oldBp.File) > 0 {
addrs, err := proc.FindFileLocation(p, oldBp.File, oldBp.Line) addrs, err := proc.FindFileLocation(p, oldBp.File, oldBp.Line)
if err != nil { if err != nil {
discarded = append(discarded, api.DiscardedBreakpoint{Breakpoint: oldBp, Reason: err.Error()}) discarded = append(discarded, api.DiscardedBreakpoint{Breakpoint: oldBp, Reason: err.Error()})
continue continue
} }
createLogicalBreakpoint(d, addrs, oldBp) createLogicalBreakpoint(d, addrs, oldBp, oldBp.ID)
} else { } else {
// Avoid setting a breakpoint based on address when rebuilding // Avoid setting a breakpoint based on address when rebuilding
if rebuild { if rebuild {
continue continue
} }
newBp, err := p.SetBreakpoint(oldBp.Addr, proc.UserBreakpoint, nil) newBp, err := p.SetBreakpointWithID(oldBp.ID, oldBp.Addr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -534,6 +538,12 @@ func (d *Debugger) Restart(rerecord bool, pos string, resetArgs bool, newArgs []
} }
} }
} }
for _, bp := range d.disabledBreakpoints {
if bp.ID > maxID {
maxID = bp.ID
}
}
d.target.SetNextBreakpointID(maxID)
return discarded, nil return discarded, nil
} }
@ -652,7 +662,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
return nil, err return nil, err
} }
createdBp, err := createLogicalBreakpoint(d, addrs, requestedBp) createdBp, err := createLogicalBreakpoint(d, addrs, requestedBp, 0)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -662,7 +672,7 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
// createLogicalBreakpoint creates one physical breakpoint for each address // createLogicalBreakpoint creates one physical breakpoint for each address
// in addrs and associates all of them with the same logical breakpoint. // in addrs and associates all of them with the same logical breakpoint.
func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Breakpoint) (*api.Breakpoint, error) { func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Breakpoint, id int) (*api.Breakpoint, error) {
p := d.target p := d.target
if dbp, ok := d.disabledBreakpoints[requestedBp.ID]; ok { if dbp, ok := d.disabledBreakpoints[requestedBp.ID]; ok {
@ -672,7 +682,11 @@ func createLogicalBreakpoint(d *Debugger, addrs []uint64, requestedBp *api.Break
bps := make([]*proc.Breakpoint, len(addrs)) bps := make([]*proc.Breakpoint, len(addrs))
var err error var err error
for i := range addrs { 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) bps[i], err = p.SetBreakpoint(addrs[i], proc.UserBreakpoint, nil)
}
if err != nil { if err != nil {
break break
} }
@ -871,7 +885,7 @@ func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
func (d *Debugger) findBreakpoint(id int) []*proc.Breakpoint { func (d *Debugger) findBreakpoint(id int) []*proc.Breakpoint {
var bps []*proc.Breakpoint var bps []*proc.Breakpoint
for _, bp := range d.target.Breakpoints().M { for _, bp := range d.target.Breakpoints().M {
if bp.LogicalID == id { if bp.IsUser() && bp.LogicalID == id {
bps = append(bps, bp) bps = append(bps, bp)
} }
} }

View File

@ -2277,3 +2277,34 @@ func TestDetachLeaveRunning(t *testing.T) {
defer server.Stop() defer server.Stop()
assertNoError(client.Detach(false), t, "Detach") assertNoError(client.Detach(false), t, "Detach")
} }
func assertNoDuplicateBreakpoints(t *testing.T, c service.Client) {
t.Helper()
bps, _ := c.ListBreakpoints()
seen := make(map[int]bool)
for _, bp := range bps {
t.Logf("%#v\n", bp)
if seen[bp.ID] {
t.Fatalf("duplicate breakpoint ID %d", bp.ID)
}
seen[bp.ID] = true
}
}
func TestToggleBreakpointRestart(t *testing.T) {
// Checks that breakpoints IDs do not overlap after Restart if there are disabled breakpoints.
withTestClient2("testtoggle", t, func(c service.Client) {
bp1, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 1, Name: "firstbreakpoint"})
assertNoError(err, t, "CreateBreakpoint 1")
_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 2, Name: "secondbreakpoint"})
assertNoError(err, t, "CreateBreakpoint 2")
_, err = c.ToggleBreakpoint(bp1.ID)
assertNoError(err, t, "ToggleBreakpoint")
_, err = c.Restart(false)
assertNoError(err, t, "Restart")
assertNoDuplicateBreakpoints(t, c)
_, err = c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.main", Line: 3, Name: "thirdbreakpoint"})
assertNoError(err, t, "CreateBreakpoint 3")
assertNoDuplicateBreakpoints(t, c)
})
}