github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/gdbserial/rr_test.go (about) 1 package gdbserial_test 2 3 import ( 4 "flag" 5 "fmt" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 "testing" 11 12 "github.com/go-delve/delve/pkg/logflags" 13 "github.com/go-delve/delve/pkg/proc" 14 "github.com/go-delve/delve/pkg/proc/gdbserial" 15 protest "github.com/go-delve/delve/pkg/proc/test" 16 ) 17 18 func TestMain(m *testing.M) { 19 var logConf string 20 flag.StringVar(&logConf, "log", "", "configures logging") 21 flag.Parse() 22 logflags.Setup(logConf != "", logConf, "") 23 os.Exit(protest.RunTestsWithFixtures(m)) 24 } 25 26 func withTestRecording(name string, t testing.TB, fn func(grp *proc.TargetGroup, fixture protest.Fixture)) { 27 fixture := protest.BuildFixture(name, 0) 28 protest.MustHaveRecordingAllowed(t) 29 if path, _ := exec.LookPath("rr"); path == "" { 30 t.Skip("test skipped, rr not found") 31 } 32 t.Log("recording") 33 grp, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{}, "", proc.OutputRedirect{}, proc.OutputRedirect{}) 34 if err != nil { 35 t.Fatal("Launch():", err) 36 } 37 t.Logf("replaying %q", tracedir) 38 39 defer grp.Detach(true) 40 41 fn(grp, fixture) 42 } 43 44 func assertNoError(err error, t testing.TB, s string) { 45 if err != nil { 46 _, file, line, _ := runtime.Caller(1) 47 fname := filepath.Base(file) 48 t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err) 49 } 50 } 51 52 func setFunctionBreakpoint(p *proc.Target, t *testing.T, fname string) *proc.Breakpoint { 53 _, f, l, _ := runtime.Caller(1) 54 f = filepath.Base(f) 55 56 addrs, err := proc.FindFunctionLocation(p, fname, 0) 57 if err != nil { 58 t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err) 59 } 60 if len(addrs) != 1 { 61 t.Fatalf("%s:%d: setFunctionBreakpoint(%s): too many results %v", f, l, fname, addrs) 62 } 63 bp, err := p.SetBreakpoint(0, addrs[0], proc.UserBreakpoint, nil) 64 if err != nil { 65 t.Fatalf("%s:%d: FindFunctionLocation(%s): %v", f, l, fname, err) 66 } 67 return bp 68 } 69 70 func TestRestartAfterExit(t *testing.T) { 71 protest.AllowRecording(t) 72 withTestRecording("testnextprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) { 73 p := grp.Selected 74 setFunctionBreakpoint(p, t, "main.main") 75 assertNoError(grp.Continue(), t, "Continue") 76 loc, err := p.CurrentThread().Location() 77 assertNoError(err, t, "CurrentThread().Location()") 78 err = grp.Continue() 79 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 80 t.Fatalf("program did not exit: %v", err) 81 } 82 83 assertNoError(grp.Restart(""), t, "Restart") 84 85 assertNoError(grp.Continue(), t, "Continue (after restart)") 86 loc2, err := p.CurrentThread().Location() 87 assertNoError(err, t, "CurrentThread().Location() (after restart)") 88 if loc2.Line != loc.Line { 89 t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line) 90 } 91 err = grp.Continue() 92 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 93 t.Fatalf("program did not exit (after exit): %v", err) 94 } 95 }) 96 } 97 98 func TestRestartDuringStop(t *testing.T) { 99 protest.AllowRecording(t) 100 withTestRecording("testnextprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) { 101 p := grp.Selected 102 setFunctionBreakpoint(p, t, "main.main") 103 assertNoError(grp.Continue(), t, "Continue") 104 loc, err := p.CurrentThread().Location() 105 assertNoError(err, t, "CurrentThread().Location()") 106 107 assertNoError(grp.Restart(""), t, "Restart") 108 109 assertNoError(grp.Continue(), t, "Continue (after restart)") 110 loc2, err := p.CurrentThread().Location() 111 assertNoError(err, t, "CurrentThread().Location() (after restart)") 112 if loc2.Line != loc.Line { 113 t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line) 114 } 115 err = grp.Continue() 116 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 117 t.Fatalf("program did not exit (after exit): %v", err) 118 } 119 }) 120 } 121 122 func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint { 123 _, f, l, _ := runtime.Caller(1) 124 f = filepath.Base(f) 125 126 addrs, err := proc.FindFileLocation(p, fixture.Source, lineno) 127 if err != nil { 128 t.Fatalf("%s:%d: FindFileLocation(%s, %d): %v", f, l, fixture.Source, lineno, err) 129 } 130 if len(addrs) != 1 { 131 t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many results %v", f, l, fixture.Source, lineno, addrs) 132 } 133 bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil) 134 if err != nil { 135 t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err) 136 } 137 return bp 138 } 139 140 func TestReverseBreakpointCounts(t *testing.T) { 141 protest.AllowRecording(t) 142 withTestRecording("bpcountstest", t, func(grp *proc.TargetGroup, fixture protest.Fixture) { 143 p := grp.Selected 144 endbp := setFileBreakpoint(p, t, fixture, 28) 145 assertNoError(grp.Continue(), t, "Continue()") 146 loc, _ := p.CurrentThread().Location() 147 if loc.PC != endbp.Addr { 148 t.Fatalf("did not reach end of main.main function: %s:%d (%#x)", loc.File, loc.Line, loc.PC) 149 } 150 151 p.ClearBreakpoint(endbp.Addr) 152 assertNoError(grp.ChangeDirection(proc.Backward), t, "Switching to backward direction") 153 bp := setFileBreakpoint(p, t, fixture, 12) 154 startbp := setFileBreakpoint(p, t, fixture, 20) 155 156 countLoop: 157 for { 158 assertNoError(grp.Continue(), t, "Continue()") 159 loc, _ := p.CurrentThread().Location() 160 switch loc.PC { 161 case startbp.Addr: 162 break countLoop 163 case bp.Addr: 164 // ok 165 default: 166 t.Fatalf("unexpected stop location %s:%d %#x", loc.File, loc.Line, loc.PC) 167 } 168 } 169 170 t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount) 171 if bp.Logical.TotalHitCount != 200 { 172 t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount) 173 } 174 175 if len(bp.Logical.HitCount) != 2 { 176 t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount)) 177 } 178 179 for _, v := range bp.Logical.HitCount { 180 if v != 100 { 181 t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount) 182 } 183 } 184 }) 185 } 186 187 func getPosition(grp *proc.TargetGroup, t *testing.T) (when string, loc *proc.Location) { 188 var err error 189 when, err = grp.When() 190 assertNoError(err, t, "When") 191 loc, err = grp.Selected.CurrentThread().Location() 192 assertNoError(err, t, "Location") 193 return 194 } 195 196 func TestCheckpoints(t *testing.T) { 197 protest.AllowRecording(t) 198 withTestRecording("continuetestprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) { 199 p := grp.Selected 200 // Continues until start of main.main, record output of 'when' 201 bp := setFunctionBreakpoint(p, t, "main.main") 202 assertNoError(grp.Continue(), t, "Continue") 203 when0, loc0 := getPosition(grp, t) 204 t.Logf("when0: %q (%#x) %x", when0, loc0.PC, p.CurrentThread().ThreadID()) 205 206 // Create a checkpoint and check that the list of checkpoints reflects this 207 cpid, err := grp.Checkpoint("checkpoint1") 208 if cpid != 1 { 209 t.Errorf("unexpected checkpoint id %d", cpid) 210 } 211 assertNoError(err, t, "Checkpoint") 212 checkpoints, err := grp.Checkpoints() 213 assertNoError(err, t, "Checkpoints") 214 if len(checkpoints) != 1 { 215 t.Fatalf("wrong number of checkpoints %v (one expected)", checkpoints) 216 } 217 218 // Move forward with next, check that the output of 'when' changes 219 assertNoError(grp.Next(), t, "First Next") 220 assertNoError(grp.Next(), t, "Second Next") 221 when1, loc1 := getPosition(grp, t) 222 t.Logf("when1: %q (%#x) %x", when1, loc1.PC, p.CurrentThread().ThreadID()) 223 if loc0.PC == loc1.PC { 224 t.Fatalf("next did not move process %#x", loc0.PC) 225 } 226 if when0 == when1 { 227 t.Fatalf("output of when did not change after next: %q", when0) 228 } 229 230 // Move back to checkpoint, check that the output of 'when' is the same as 231 // what it was when we set the breakpoint 232 grp.Restart(fmt.Sprintf("c%d", cpid)) 233 g, _ := proc.FindGoroutine(p, 1) 234 p.SwitchGoroutine(g) 235 when2, loc2 := getPosition(grp, t) 236 t.Logf("when2: %q (%#x) %x", when2, loc2.PC, p.CurrentThread().ThreadID()) 237 if loc2.PC != loc0.PC { 238 t.Fatalf("PC address mismatch %#x != %#x", loc0.PC, loc2.PC) 239 } 240 if when0 != when2 { 241 t.Fatalf("output of when mismatched %q != %q", when0, when2) 242 } 243 244 // Move forward with next again, check that the output of 'when' matches 245 assertNoError(grp.Next(), t, "First Next") 246 assertNoError(grp.Next(), t, "Second Next") 247 when3, loc3 := getPosition(grp, t) 248 t.Logf("when3: %q (%#x)", when3, loc3.PC) 249 if loc3.PC != loc1.PC { 250 t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc3.PC) 251 } 252 if when3 != when1 { 253 t.Fatalf("when output mismatch %q != %q", when1, when3) 254 } 255 256 // Delete breakpoint, move back to checkpoint then next twice and check 257 // output of 'when' again 258 err = p.ClearBreakpoint(bp.Addr) 259 assertNoError(err, t, "ClearBreakpoint") 260 grp.Restart(fmt.Sprintf("c%d", cpid)) 261 g, _ = proc.FindGoroutine(p, 1) 262 p.SwitchGoroutine(g) 263 assertNoError(grp.Next(), t, "First Next") 264 assertNoError(grp.Next(), t, "Second Next") 265 when4, loc4 := getPosition(grp, t) 266 t.Logf("when4: %q (%#x)", when4, loc4.PC) 267 if loc4.PC != loc1.PC { 268 t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc4.PC) 269 } 270 if when4 != when1 { 271 t.Fatalf("when output mismatch %q != %q", when1, when4) 272 } 273 274 // Delete checkpoint, check that the list of checkpoints is updated 275 assertNoError(grp.ClearCheckpoint(cpid), t, "ClearCheckpoint") 276 checkpoints, err = grp.Checkpoints() 277 assertNoError(err, t, "Checkpoints") 278 if len(checkpoints) != 0 { 279 t.Fatalf("wrong number of checkpoints %v (zero expected)", checkpoints) 280 } 281 }) 282 } 283 284 func TestIssue1376(t *testing.T) { 285 // Backward Continue should terminate when it encounters the start of the process. 286 protest.AllowRecording(t) 287 withTestRecording("continuetestprog", t, func(grp *proc.TargetGroup, fixture protest.Fixture) { 288 p := grp.Selected 289 bp := setFunctionBreakpoint(p, t, "main.main") 290 assertNoError(grp.Continue(), t, "Continue (forward)") 291 err := p.ClearBreakpoint(bp.Addr) 292 assertNoError(err, t, "ClearBreakpoint") 293 assertNoError(grp.ChangeDirection(proc.Backward), t, "Switching to backward direction") 294 assertNoError(grp.Continue(), t, "Continue (backward)") 295 }) 296 }