github.com/undoio/delve@v1.9.0/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/undoio/delve/pkg/logflags" 13 "github.com/undoio/delve/pkg/proc" 14 "github.com/undoio/delve/pkg/proc/gdbserial" 15 protest "github.com/undoio/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(t *proc.Target, 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 p, tracedir, err := gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{}, [3]string{}) 34 if err != nil { 35 t.Fatal("Launch():", err) 36 } 37 t.Logf("replaying %q", tracedir) 38 39 defer p.Detach(true) 40 41 fn(p, 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(p *proc.Target, fixture protest.Fixture) { 73 setFunctionBreakpoint(p, t, "main.main") 74 assertNoError(p.Continue(), t, "Continue") 75 loc, err := p.CurrentThread().Location() 76 assertNoError(err, t, "CurrentThread().Location()") 77 err = p.Continue() 78 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 79 t.Fatalf("program did not exit: %v", err) 80 } 81 82 assertNoError(p.Restart(""), t, "Restart") 83 84 assertNoError(p.Continue(), t, "Continue (after restart)") 85 loc2, err := p.CurrentThread().Location() 86 assertNoError(err, t, "CurrentThread().Location() (after restart)") 87 if loc2.Line != loc.Line { 88 t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line) 89 } 90 err = p.Continue() 91 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 92 t.Fatalf("program did not exit (after exit): %v", err) 93 } 94 }) 95 } 96 97 func TestRestartDuringStop(t *testing.T) { 98 protest.AllowRecording(t) 99 withTestRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) { 100 setFunctionBreakpoint(p, t, "main.main") 101 assertNoError(p.Continue(), t, "Continue") 102 loc, err := p.CurrentThread().Location() 103 assertNoError(err, t, "CurrentThread().Location()") 104 105 assertNoError(p.Restart(""), t, "Restart") 106 107 assertNoError(p.Continue(), t, "Continue (after restart)") 108 loc2, err := p.CurrentThread().Location() 109 assertNoError(err, t, "CurrentThread().Location() (after restart)") 110 if loc2.Line != loc.Line { 111 t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line) 112 } 113 err = p.Continue() 114 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 115 t.Fatalf("program did not exit (after exit): %v", err) 116 } 117 }) 118 } 119 120 func setFileBreakpoint(p *proc.Target, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint { 121 _, f, l, _ := runtime.Caller(1) 122 f = filepath.Base(f) 123 124 addrs, err := proc.FindFileLocation(p, fixture.Source, lineno) 125 if err != nil { 126 t.Fatalf("%s:%d: FindFileLocation(%s, %d): %v", f, l, fixture.Source, lineno, err) 127 } 128 if len(addrs) != 1 { 129 t.Fatalf("%s:%d: setFileLineBreakpoint(%s, %d): too many results %v", f, l, fixture.Source, lineno, addrs) 130 } 131 bp, err := p.SetBreakpoint(int(addrs[0]), addrs[0], proc.UserBreakpoint, nil) 132 if err != nil { 133 t.Fatalf("%s:%d: SetBreakpoint: %v", f, l, err) 134 } 135 return bp 136 } 137 138 func TestReverseBreakpointCounts(t *testing.T) { 139 protest.AllowRecording(t) 140 withTestRecording("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) { 141 endbp := setFileBreakpoint(p, t, fixture, 28) 142 assertNoError(p.Continue(), t, "Continue()") 143 loc, _ := p.CurrentThread().Location() 144 if loc.PC != endbp.Addr { 145 t.Fatalf("did not reach end of main.main function: %s:%d (%#x)", loc.File, loc.Line, loc.PC) 146 } 147 148 p.ClearBreakpoint(endbp.Addr) 149 assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction") 150 bp := setFileBreakpoint(p, t, fixture, 12) 151 startbp := setFileBreakpoint(p, t, fixture, 20) 152 153 countLoop: 154 for { 155 assertNoError(p.Continue(), t, "Continue()") 156 loc, _ := p.CurrentThread().Location() 157 switch loc.PC { 158 case startbp.Addr: 159 break countLoop 160 case bp.Addr: 161 // ok 162 default: 163 t.Fatalf("unexpected stop location %s:%d %#x", loc.File, loc.Line, loc.PC) 164 } 165 } 166 167 t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount) 168 if bp.Logical.TotalHitCount != 200 { 169 t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount) 170 } 171 172 if len(bp.Logical.HitCount) != 2 { 173 t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount)) 174 } 175 176 for _, v := range bp.Logical.HitCount { 177 if v != 100 { 178 t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount) 179 } 180 } 181 }) 182 } 183 184 func getPosition(p *proc.Target, t *testing.T) (when string, loc *proc.Location) { 185 var err error 186 when, err = p.When() 187 assertNoError(err, t, "When") 188 loc, err = p.CurrentThread().Location() 189 assertNoError(err, t, "Location") 190 return 191 } 192 193 func TestCheckpoints(t *testing.T) { 194 protest.AllowRecording(t) 195 withTestRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { 196 // Continues until start of main.main, record output of 'when' 197 bp := setFunctionBreakpoint(p, t, "main.main") 198 assertNoError(p.Continue(), t, "Continue") 199 when0, loc0 := getPosition(p, t) 200 t.Logf("when0: %q (%#x) %x", when0, loc0.PC, p.CurrentThread().ThreadID()) 201 202 // Create a checkpoint and check that the list of checkpoints reflects this 203 cpid, err := p.Checkpoint("checkpoint1") 204 if cpid != 1 { 205 t.Errorf("unexpected checkpoint id %d", cpid) 206 } 207 assertNoError(err, t, "Checkpoint") 208 checkpoints, err := p.Checkpoints() 209 assertNoError(err, t, "Checkpoints") 210 if len(checkpoints) != 1 { 211 t.Fatalf("wrong number of checkpoints %v (one expected)", checkpoints) 212 } 213 214 // Move forward with next, check that the output of 'when' changes 215 assertNoError(p.Next(), t, "First Next") 216 assertNoError(p.Next(), t, "Second Next") 217 when1, loc1 := getPosition(p, t) 218 t.Logf("when1: %q (%#x) %x", when1, loc1.PC, p.CurrentThread().ThreadID()) 219 if loc0.PC == loc1.PC { 220 t.Fatalf("next did not move process %#x", loc0.PC) 221 } 222 if when0 == when1 { 223 t.Fatalf("output of when did not change after next: %q", when0) 224 } 225 226 // Move back to checkpoint, check that the output of 'when' is the same as 227 // what it was when we set the breakpoint 228 p.Restart(fmt.Sprintf("c%d", cpid)) 229 g, _ := proc.FindGoroutine(p, 1) 230 p.SwitchGoroutine(g) 231 when2, loc2 := getPosition(p, t) 232 t.Logf("when2: %q (%#x) %x", when2, loc2.PC, p.CurrentThread().ThreadID()) 233 if loc2.PC != loc0.PC { 234 t.Fatalf("PC address mismatch %#x != %#x", loc0.PC, loc2.PC) 235 } 236 if when0 != when2 { 237 t.Fatalf("output of when mismatched %q != %q", when0, when2) 238 } 239 240 // Move forward with next again, check that the output of 'when' matches 241 assertNoError(p.Next(), t, "First Next") 242 assertNoError(p.Next(), t, "Second Next") 243 when3, loc3 := getPosition(p, t) 244 t.Logf("when3: %q (%#x)", when3, loc3.PC) 245 if loc3.PC != loc1.PC { 246 t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc3.PC) 247 } 248 if when3 != when1 { 249 t.Fatalf("when output mismatch %q != %q", when1, when3) 250 } 251 252 // Delete breakpoint, move back to checkpoint then next twice and check 253 // output of 'when' again 254 err = p.ClearBreakpoint(bp.Addr) 255 assertNoError(err, t, "ClearBreakpoint") 256 p.Restart(fmt.Sprintf("c%d", cpid)) 257 g, _ = proc.FindGoroutine(p, 1) 258 p.SwitchGoroutine(g) 259 assertNoError(p.Next(), t, "First Next") 260 assertNoError(p.Next(), t, "Second Next") 261 when4, loc4 := getPosition(p, t) 262 t.Logf("when4: %q (%#x)", when4, loc4.PC) 263 if loc4.PC != loc1.PC { 264 t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc4.PC) 265 } 266 if when4 != when1 { 267 t.Fatalf("when output mismatch %q != %q", when1, when4) 268 } 269 270 // Delete checkpoint, check that the list of checkpoints is updated 271 assertNoError(p.ClearCheckpoint(cpid), t, "ClearCheckpoint") 272 checkpoints, err = p.Checkpoints() 273 assertNoError(err, t, "Checkpoints") 274 if len(checkpoints) != 0 { 275 t.Fatalf("wrong number of checkpoints %v (zero expected)", checkpoints) 276 } 277 }) 278 } 279 280 func TestIssue1376(t *testing.T) { 281 // Backward Continue should terminate when it encounters the start of the process. 282 protest.AllowRecording(t) 283 withTestRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { 284 bp := setFunctionBreakpoint(p, t, "main.main") 285 assertNoError(p.Continue(), t, "Continue (forward)") 286 err := p.ClearBreakpoint(bp.Addr) 287 assertNoError(err, t, "ClearBreakpoint") 288 assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction") 289 assertNoError(p.Continue(), t, "Continue (backward)") 290 }) 291 }