fix(debug): hardware watchpoints, signal stops and breakpoint restore
Assisted-by: GLM 5.3
This commit is contained in:
+74
-18
@@ -7,9 +7,10 @@ package debug
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"cmp"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -93,7 +94,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
regs, _ := s.GetRegs()
|
||||
pc := regs.GetPC()
|
||||
text, instLen, _ := s.Disassemble(pc)
|
||||
if strings.HasPrefix(strings.ToLower(text), "call") || strings.HasPrefix(strings.ToLower(text), "bl") {
|
||||
if isCallInsn(text) {
|
||||
afterAddr := pc + uint64(instLen)
|
||||
_, err := bm.Set(afterAddr, "(next)")
|
||||
if err != nil {
|
||||
@@ -108,6 +109,21 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
bm.Clear(afterAddr)
|
||||
continue
|
||||
}
|
||||
if s.Exited() {
|
||||
bm.Clear(afterAddr)
|
||||
fmt.Println("debuggee exited")
|
||||
continue
|
||||
}
|
||||
if sig := s.LastSignal(); sig != 0 {
|
||||
bm.Clear(afterAddr)
|
||||
regs, _ := s.GetRegs()
|
||||
fmt.Printf("stopped on signal %v at %#x\n", sig, regs.GetPC())
|
||||
continue
|
||||
}
|
||||
// Fetch the registers after the stop: the trap must be
|
||||
// evaluated against the real PC, not the pre-Continue
|
||||
// snapshot, and a stale SetRegs would clobber live state.
|
||||
regs, _ = s.GetRegs()
|
||||
bm.HandleTrap(®s)
|
||||
bm.Clear(afterAddr)
|
||||
} else {
|
||||
@@ -143,9 +159,21 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
bm.Clear(retAddr)
|
||||
continue
|
||||
}
|
||||
if !s.Exited() {
|
||||
bm.HandleTrap(®s)
|
||||
if s.Exited() {
|
||||
bm.Clear(retAddr)
|
||||
fmt.Println("debuggee exited")
|
||||
continue
|
||||
}
|
||||
if sig := s.LastSignal(); sig != 0 {
|
||||
bm.Clear(retAddr)
|
||||
regs, _ := s.GetRegs()
|
||||
fmt.Printf("stopped on signal %v at %#x\n", sig, regs.GetPC())
|
||||
continue
|
||||
}
|
||||
// Fetch the registers after the stop, as the continue case
|
||||
// does: HandleTrap must see the PC the trap left behind.
|
||||
regs, _ = s.GetRegs()
|
||||
bm.HandleTrap(®s)
|
||||
bm.Clear(retAddr)
|
||||
if s.Exited() {
|
||||
fmt.Println("debuggee exited")
|
||||
@@ -171,6 +199,15 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
fmt.Println("debuggee exited")
|
||||
break
|
||||
}
|
||||
if sig := s.LastSignal(); sig != 0 {
|
||||
// A genuine signal-delivery-stop (a fault): report it
|
||||
// and return to the prompt. Continuing would restart
|
||||
// the faulting instruction and fault forever.
|
||||
regs, _ := s.GetRegs()
|
||||
fmt.Printf("stopped on signal %v at %#x (func+%#x)\n",
|
||||
sig, regs.GetPC(), regs.GetPC()-codeBase-uint64(funcOffset))
|
||||
break
|
||||
}
|
||||
reason, wpAddr := s.StopInfo()
|
||||
if reason == StopWatchpoint {
|
||||
fmt.Printf("watchpoint hit at %#x\n", wpAddr)
|
||||
@@ -221,13 +258,27 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
reg := strings.ToLower(parts[3])
|
||||
op := parts[4]
|
||||
operand := parts[5]
|
||||
if val, err := strconv.ParseUint(operand, 0, 64); err == nil {
|
||||
cond = &Condition{Reg: reg, Op: op, Value: val}
|
||||
} else {
|
||||
cond = &Condition{Reg: reg, Op: op, Reg2: strings.ToLower(operand)}
|
||||
switch {
|
||||
case strings.HasPrefix(operand, "*"):
|
||||
// Memory operand: compare against the 8-byte word at
|
||||
// the address, resolved in the debuggee when the
|
||||
// breakpoint is evaluated.
|
||||
addr, err := strconv.ParseUint(strings.TrimPrefix(operand, "*"), 0, 64)
|
||||
if err != nil {
|
||||
fmt.Printf("invalid memory operand: %s\n", operand)
|
||||
continue
|
||||
}
|
||||
cond = &Condition{Reg: reg, Op: op, MemAddr: addr}
|
||||
default:
|
||||
val, err := strconv.ParseUint(operand, 0, 64)
|
||||
if err == nil {
|
||||
cond = &Condition{Reg: reg, Op: op, Value: val}
|
||||
} else {
|
||||
cond = &Condition{Reg: reg, Op: op, Reg2: strings.ToLower(operand)}
|
||||
}
|
||||
}
|
||||
} else if len(parts) >= 4 && parts[2] == "if" {
|
||||
fmt.Println("usage: break <label|addr> if <reg> <op> <value|reg>")
|
||||
fmt.Println("usage: break <label|addr> if <reg> <op> <value|reg|*addr>")
|
||||
continue
|
||||
}
|
||||
bp, err := bm.SetWithCond(addr, label, cond)
|
||||
@@ -237,7 +288,7 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
}
|
||||
condStr := ""
|
||||
if cond != nil {
|
||||
condStr = fmt.Sprintf(" if %s %s %#x", cond.Reg, cond.Op, cond.Value)
|
||||
condStr = " if " + cond.String()
|
||||
}
|
||||
fmt.Printf("breakpoint set: %s at %#x (func+%#x)%s\n", bp.Label, bp.Addr, bp.Addr-codeBase-uint64(funcOffset), condStr)
|
||||
|
||||
@@ -277,7 +328,11 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
addr, _ = resolveAddr(parts[1], codeBase, uint64(funcOffset), labels)
|
||||
}
|
||||
if len(parts) > 2 {
|
||||
length, _ = strconv.Atoi(parts[2])
|
||||
// A malformed or non-positive length would panic
|
||||
// ReadMemory's make; fall back to the default instead.
|
||||
if n, err := strconv.Atoi(parts[2]); err == nil && n > 0 {
|
||||
length = n
|
||||
}
|
||||
}
|
||||
mem, err := s.ReadMemory(addr, length)
|
||||
if err != nil {
|
||||
@@ -336,10 +391,8 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
}
|
||||
|
||||
case "labels", "l":
|
||||
sorted := make([]Label, len(labels))
|
||||
copy(sorted, labels)
|
||||
sort.Slice(sorted, func(i, j int) bool { return sorted[i].Offset < sorted[j].Offset })
|
||||
for _, l := range sorted {
|
||||
slices.SortFunc(labels, func(a, b Label) int { return cmp.Compare(a.Offset, b.Offset) })
|
||||
for _, l := range labels {
|
||||
fmt.Printf(" func+%#04x %s\n", l.Offset, l.Name)
|
||||
}
|
||||
|
||||
@@ -369,7 +422,10 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
fmt.Println()
|
||||
|
||||
case "help", "h", "?":
|
||||
fmt.Printf(` break <label|addr> [if <reg> <op> <val>] set a breakpoint
|
||||
fmt.Printf(` break <label|addr> [if <reg> <op> <val|reg|*addr>]
|
||||
set a breakpoint, optionally conditional on a
|
||||
register compared to a constant, a register, or the
|
||||
8-byte word at *addr
|
||||
delete <label|addr> remove a breakpoint
|
||||
info break list all breakpoints
|
||||
watch <addr> [r|w] [size] set a hardware watchpoint (write by default)
|
||||
@@ -463,8 +519,8 @@ func REPL(s *Session, bm *Breakpoints, codeBase uint64, funcOffset, funcSize, ar
|
||||
case "unwatch":
|
||||
if len(parts) >= 2 {
|
||||
slot, err := strconv.Atoi(parts[1])
|
||||
if err != nil || slot < 0 || slot > 3 {
|
||||
fmt.Println("usage: unwatch [<slot>]")
|
||||
if err != nil || slot < 0 || slot >= maxWatchpoints() {
|
||||
fmt.Printf("usage: unwatch [<slot 0-%d>]\n", maxWatchpoints()-1)
|
||||
continue
|
||||
}
|
||||
if err := s.ClearWatchpoint(slot); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user