diff --git a/debug/debug_test.go b/debug/debug_test.go new file mode 100644 index 0000000..566ab30 --- /dev/null +++ b/debug/debug_test.go @@ -0,0 +1,166 @@ +// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) +// SPDX-License-Identifier: BSD-3-Clause + +//go:build linux && amd64 + +package debug + +import "testing" + +func TestConditionEval(t *testing.T) { + regs := &Regs{ + RAX: 42, + RBX: 0, + RCX: 100, + RIP: 0x1000, + RSP: 0x2000, + R8: 8, + R15: 15, + } + + tests := []struct { + cond Condition + want bool + }{ + {Condition{Reg: "rax", Op: "==", Value: 42}, true}, + {Condition{Reg: "rax", Op: "==", Value: 43}, false}, + {Condition{Reg: "rax", Op: "!=", Value: 43}, true}, + {Condition{Reg: "rax", Op: "!=", Value: 42}, false}, + {Condition{Reg: "rax", Op: "<", Value: 50}, true}, + {Condition{Reg: "rax", Op: "<", Value: 40}, false}, + {Condition{Reg: "rax", Op: ">", Value: 40}, true}, + {Condition{Reg: "rax", Op: ">", Value: 50}, false}, + {Condition{Reg: "rax", Op: "<=", Value: 42}, true}, + {Condition{Reg: "rax", Op: ">=", Value: 42}, true}, + {Condition{Reg: "rbx", Op: "==", Value: 0}, true}, + {Condition{Reg: "rcx", Op: ">", Value: 50}, true}, + {Condition{Reg: "rip", Op: "==", Value: 0x1000}, true}, + {Condition{Reg: "rsp", Op: ">", Value: 0x1000}, true}, + {Condition{Reg: "r8", Op: "==", Value: 8}, true}, + {Condition{Reg: "r15", Op: "==", Value: 15}, true}, + {Condition{Reg: "eax", Op: "==", Value: 42}, true}, // 32-bit alias + {Condition{Reg: "ax", Op: "==", Value: 42}, true}, // 16-bit alias + {Condition{Reg: "unknown", Op: "==", Value: 0}, true}, // unknown reg → don't block + {Condition{Reg: "rax", Op: "??", Value: 0}, true}, // unknown op → don't block + } + + for _, tt := range tests { + got := tt.cond.Eval(regs) + if got != tt.want { + t.Errorf("Condition{%q %q %d}.Eval() = %v, want %v", + tt.cond.Reg, tt.cond.Op, tt.cond.Value, got, tt.want) + } + } +} + +func TestLineAt(t *testing.T) { + lines := []SourceLine{ + {Offset: 0, Line: 5}, + {Offset: 5, Line: 6}, + {Offset: 10, Line: 7}, + {Offset: 15, Line: 8}, + } + + tests := []struct { + offset int + want int + }{ + {0, 5}, + {1, 5}, + {4, 5}, + {5, 6}, + {7, 6}, + {10, 7}, + {12, 7}, + {15, 8}, + {20, 8}, + } + + for _, tt := range tests { + got := lineAt(lines, tt.offset) + if got != tt.want { + t.Errorf("lineAt(lines, %d) = %d, want %d", tt.offset, got, tt.want) + } + } + + // Empty table. + if lineAt(nil, 5) != 0 { + t.Error("lineAt(nil, 5) should return 0") + } +} + +func TestOffsetForLine(t *testing.T) { + lines := []SourceLine{ + {Offset: 0, Line: 5}, + {Offset: 5, Line: 6}, + {Offset: 10, Line: 7}, + } + + tests := []struct { + line int + want int + }{ + {5, 0}, + {6, 5}, + {7, 10}, + {99, -1}, // not found + {0, -1}, // not found + } + + for _, tt := range tests { + got := offsetForLine(lines, tt.line) + if got != tt.want { + t.Errorf("offsetForLine(lines, %d) = %d, want %d", tt.line, got, tt.want) + } + } +} + +func TestDecodeRflags(t *testing.T) { + tests := []struct { + flags uint64 + want string + }{ + {0x202, "IF"}, // only IF set (bit 9) + {0x246, "PF ZF IF"}, // PF(2) + ZF(6) + IF(9) + {0x001, "CF"}, // carry flag + {0x080, "SF"}, // sign flag + {0x800, "OF"}, // overflow flag + {0x000, "none"}, // no flags + {0x202 | 0x001, "CF IF"}, // CF + IF + {0x3F7, "CF PF AF ZF SF TF IF"}, // all arithmetic flags + } + + for _, tt := range tests { + got := decodeRflags(tt.flags) + if got != tt.want { + t.Errorf("decodeRflags(%#x) = %q, want %q", tt.flags, got, tt.want) + } + } +} + +func TestNearestLabel(t *testing.T) { + labels := []Label{ + {Name: "start", Offset: 0}, + {Name: "loop", Offset: 10}, + {Name: "done", Offset: 20}, + } + + tests := []struct { + offset int + want string + }{ + {0, "start"}, + {5, "start"}, + {10, "loop"}, + {15, "loop"}, + {20, "done"}, + {25, "done"}, + } + + for _, tt := range tests { + got := nearestLabel(labels, tt.offset) + if got != tt.want { + t.Errorf("nearestLabel(labels, %d) = %q, want %q", tt.offset, got, tt.want) + } + } +} diff --git a/debug/repl.go b/debug/repl.go index 3198198..9a5768f 100644 --- a/debug/repl.go +++ b/debug/repl.go @@ -538,7 +538,7 @@ func decodeRflags(f uint64) string { flags += "OF " } if flags == "" { - flags = "none" + return "none" } return flags[:len(flags)-1] // trim trailing space }