fix(debug): handle mapping edges, stray traps and dying debuggees
Assisted-by: GLM 5.3
This commit is contained in:
+30
-6
@@ -7,7 +7,11 @@ package debug
|
||||
|
||||
import "strings"
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
// Breakpoint is one software breakpoint in the debuggee.
|
||||
type Breakpoint struct {
|
||||
@@ -149,15 +153,16 @@ func (bm *Breakpoints) SetWithCond(addr uint64, label string, cond *Condition) (
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// Info returns a formatted list of all breakpoints.
|
||||
// Info returns a formatted list of all breakpoints, ordered by address so
|
||||
// the numbering is stable across calls (map iteration order is not).
|
||||
func (bm *Breakpoints) Info() string {
|
||||
if len(bm.bps) == 0 {
|
||||
return "no breakpoints set\n"
|
||||
}
|
||||
var result strings.Builder
|
||||
i := 0
|
||||
for _, bp := range bm.bps {
|
||||
i++
|
||||
bps := bm.All()
|
||||
slices.SortFunc(bps, func(a, b *Breakpoint) int { return cmp.Compare(a.Addr, b.Addr) })
|
||||
for i, bp := range bps {
|
||||
status := "enabled"
|
||||
if !bp.Enabled {
|
||||
status = "disabled"
|
||||
@@ -170,7 +175,7 @@ func (bm *Breakpoints) Info() string {
|
||||
if bp.Cond != nil {
|
||||
cond = " if " + bp.Cond.String()
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" %d: %s at %#x [%s, %d hits]%s\n", i, label, bp.Addr, status, bp.hits, cond))
|
||||
result.WriteString(fmt.Sprintf(" %d: %s at %#x [%s, %d hits]%s\n", i+1, label, bp.Addr, status, bp.hits, cond))
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
@@ -238,6 +243,25 @@ func (bm *Breakpoints) All() []*Breakpoint {
|
||||
// Hits returns how many times the breakpoint has been hit.
|
||||
func (bp *Breakpoint) Hits() int { return bp.hits }
|
||||
|
||||
// TrapStray reports whether a stop is a breakpoint-class trap that matches
|
||||
// no breakpoint of ours and cannot be resumed: the PC still stands on the
|
||||
// trapping instruction (the kernel's own BRK, EBREAK or break, on an
|
||||
// architecture that reports the trap in place), so the next resume would
|
||||
// re-execute it and trap forever. trapPC is the PC the stop reported,
|
||||
// before any HandleTrap rewinding; reason is the stop's StopInfo class.
|
||||
// The debuggee's SIGSTOP barriers also stop without PC movement, and they
|
||||
// never carry the breakpoint class, so they are unaffected.
|
||||
func TrapStray(s *Session, reason StopReason, trapPC uint64) bool {
|
||||
if reason != StopBreakpoint {
|
||||
return false
|
||||
}
|
||||
after, err := s.GetRegs()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return after.GetPC() <= trapPC-uint64(breakpointPCAdjust)
|
||||
}
|
||||
|
||||
func (bm *Breakpoints) HandleTrap(regs *Regs) *Breakpoint {
|
||||
// On amd64 the kernel reports the trap with RIP past the INT3; on the
|
||||
// other supported architectures the PC still stands on the trap
|
||||
|
||||
Reference in New Issue
Block a user