github.com/undoio/delve@v1.9.0/pkg/proc/gdbserial/undo_test.go (about) 1 package gdbserial_test 2 3 import ( 4 "fmt" 5 "os" 6 "testing" 7 8 "github.com/undoio/delve/pkg/proc" 9 "github.com/undoio/delve/pkg/proc/gdbserial" 10 protest "github.com/undoio/delve/pkg/proc/test" 11 ) 12 13 func withUndoRecording(name string, t testing.TB, fn func(p *proc.Target, fixture protest.Fixture)) { 14 fixture := protest.BuildFixture(name, 0) 15 protest.MustHaveRecordingAllowed(t) 16 if err := gdbserial.UndoIsAvailable(); err != nil { 17 t.Skip("test skipped, Undo tools not found") 18 } 19 t.Log("recording") 20 p, recording, err := gdbserial.UndoRecordAndReplay([]string{fixture.Path}, ".", true, []string{}, [3]string{}) 21 if err != nil { 22 t.Fatal("Launch():", err) 23 } 24 t.Logf("replaying %q", recording) 25 26 defer func() { 27 p.Detach(true) 28 if recording != "" { 29 os.Remove(recording) 30 } 31 }() 32 33 fn(p, fixture) 34 } 35 36 func TestUndoRestartAfterExit(t *testing.T) { 37 protest.AllowRecording(t) 38 withUndoRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) { 39 setFunctionBreakpoint(p, t, "main.main") 40 assertNoError(p.Continue(), t, "Continue") 41 loc, err := p.CurrentThread().Location() 42 assertNoError(err, t, "CurrentThread().Location()") 43 err = p.Continue() 44 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 45 t.Fatalf("program did not exit: %v", err) 46 } 47 48 assertNoError(p.Restart(""), t, "Restart") 49 50 assertNoError(p.Continue(), t, "Continue (after restart)") 51 loc2, err := p.CurrentThread().Location() 52 assertNoError(err, t, "CurrentThread().Location() (after restart)") 53 if loc2.Line != loc.Line { 54 t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line) 55 } 56 err = p.Continue() 57 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 58 t.Fatalf("program did not exit (after exit): %v", err) 59 } 60 }) 61 } 62 63 func TestUndoRestartDuringStop(t *testing.T) { 64 protest.AllowRecording(t) 65 withUndoRecording("testnextprog", t, func(p *proc.Target, fixture protest.Fixture) { 66 setFunctionBreakpoint(p, t, "main.main") 67 assertNoError(p.Continue(), t, "Continue") 68 loc, err := p.CurrentThread().Location() 69 assertNoError(err, t, "CurrentThread().Location()") 70 71 assertNoError(p.Restart(""), t, "Restart") 72 73 assertNoError(p.Continue(), t, "Continue (after restart)") 74 loc2, err := p.CurrentThread().Location() 75 assertNoError(err, t, "CurrentThread().Location() (after restart)") 76 if loc2.Line != loc.Line { 77 t.Fatalf("stopped at %d (expected %d)", loc2.Line, loc.Line) 78 } 79 err = p.Continue() 80 if _, isexited := err.(proc.ErrProcessExited); err == nil || !isexited { 81 t.Fatalf("program did not exit (after exit): %v", err) 82 } 83 }) 84 } 85 86 func TestUndoReverseBreakpointCounts(t *testing.T) { 87 protest.AllowRecording(t) 88 withUndoRecording("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) { 89 endbp := setFileBreakpoint(p, t, fixture, 28) 90 assertNoError(p.Continue(), t, "Continue()") 91 loc, _ := p.CurrentThread().Location() 92 if loc.PC != endbp.Addr { 93 t.Fatalf("did not reach end of main.main function: %s:%d (%#x)", loc.File, loc.Line, loc.PC) 94 } 95 96 p.ClearBreakpoint(endbp.Addr) 97 assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction") 98 bp := setFileBreakpoint(p, t, fixture, 12) 99 startbp := setFileBreakpoint(p, t, fixture, 20) 100 101 countLoop: 102 for { 103 assertNoError(p.Continue(), t, "Continue()") 104 loc, _ := p.CurrentThread().Location() 105 switch loc.PC { 106 case startbp.Addr: 107 break countLoop 108 case bp.Addr: 109 // ok 110 default: 111 t.Fatalf("unexpected stop location %s:%d %#x", loc.File, loc.Line, loc.PC) 112 } 113 } 114 115 t.Logf("TotalHitCount: %d", bp.Logical.TotalHitCount) 116 if bp.Logical.TotalHitCount != 200 { 117 t.Fatalf("Wrong TotalHitCount for the breakpoint (%d)", bp.Logical.TotalHitCount) 118 } 119 120 if len(bp.Logical.HitCount) != 2 { 121 t.Fatalf("Wrong number of goroutines for breakpoint (%d)", len(bp.Logical.HitCount)) 122 } 123 124 for _, v := range bp.Logical.HitCount { 125 if v != 100 { 126 t.Fatalf("Wrong HitCount for breakpoint (%v)", bp.Logical.HitCount) 127 } 128 } 129 }) 130 } 131 132 func TestUndoCheckpoints(t *testing.T) { 133 protest.AllowRecording(t) 134 withUndoRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { 135 // Continues until start of main.main, record output of 'when' 136 bp := setFunctionBreakpoint(p, t, "main.main") 137 assertNoError(p.Continue(), t, "Continue") 138 when0, loc0 := getPosition(p, t) 139 t.Logf("when0: %q (%#x) %x", when0, loc0.PC, p.CurrentThread().ThreadID()) 140 141 // Create a checkpoint and check that the list of checkpoints reflects this 142 cpid, err := p.Checkpoint("checkpoint1") 143 if cpid != 1 { 144 t.Errorf("unexpected checkpoint id %d", cpid) 145 } 146 assertNoError(err, t, "Checkpoint") 147 checkpoints, err := p.Checkpoints() 148 assertNoError(err, t, "Checkpoints") 149 if len(checkpoints) != 1 { 150 t.Fatalf("wrong number of checkpoints %v (one expected)", checkpoints) 151 } 152 153 // Move forward with next, check that the output of 'when' changes 154 assertNoError(p.Next(), t, "First Next") 155 assertNoError(p.Next(), t, "Second Next") 156 when1, loc1 := getPosition(p, t) 157 t.Logf("when1: %q (%#x) %x", when1, loc1.PC, p.CurrentThread().ThreadID()) 158 if loc0.PC == loc1.PC { 159 t.Fatalf("next did not move process %#x", loc0.PC) 160 } 161 if when0 == when1 { 162 t.Fatalf("output of when did not change after next: %q", when0) 163 } 164 165 // Move back to checkpoint, check that the output of 'when' is the same as 166 // what it was when we set the breakpoint 167 p.Restart(fmt.Sprintf("c%d", cpid)) 168 g, _ := proc.FindGoroutine(p, 1) 169 p.SwitchGoroutine(g) 170 when2, loc2 := getPosition(p, t) 171 t.Logf("when2: %q (%#x) %x", when2, loc2.PC, p.CurrentThread().ThreadID()) 172 if loc2.PC != loc0.PC { 173 t.Fatalf("PC address mismatch %#x != %#x", loc0.PC, loc2.PC) 174 } 175 if when0 != when2 { 176 t.Fatalf("output of when mismatched %q != %q", when0, when2) 177 } 178 179 // Move forward with next again, check that the output of 'when' matches 180 assertNoError(p.Next(), t, "First Next") 181 assertNoError(p.Next(), t, "Second Next") 182 when3, loc3 := getPosition(p, t) 183 t.Logf("when3: %q (%#x)", when3, loc3.PC) 184 if loc3.PC != loc1.PC { 185 t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc3.PC) 186 } 187 if when3 != when1 { 188 t.Fatalf("when output mismatch %q != %q", when1, when3) 189 } 190 191 // Delete breakpoint, move back to checkpoint then next twice and check 192 // output of 'when' again 193 err = p.ClearBreakpoint(bp.Addr) 194 assertNoError(err, t, "ClearBreakpoint") 195 p.Restart(fmt.Sprintf("c%d", cpid)) 196 g, _ = proc.FindGoroutine(p, 1) 197 p.SwitchGoroutine(g) 198 assertNoError(p.Next(), t, "First Next") 199 assertNoError(p.Next(), t, "Second Next") 200 when4, loc4 := getPosition(p, t) 201 t.Logf("when4: %q (%#x)", when4, loc4.PC) 202 if loc4.PC != loc1.PC { 203 t.Fatalf("PC address mismatch %#x != %#x", loc1.PC, loc4.PC) 204 } 205 if when4 != when1 { 206 t.Fatalf("when output mismatch %q != %q", when1, when4) 207 } 208 209 // Delete checkpoint, check that the list of checkpoints is updated 210 assertNoError(p.ClearCheckpoint(cpid), t, "ClearCheckpoint") 211 checkpoints, err = p.Checkpoints() 212 assertNoError(err, t, "Checkpoints") 213 if len(checkpoints) != 0 { 214 t.Fatalf("wrong number of checkpoints %v (zero expected)", checkpoints) 215 } 216 }) 217 } 218 219 func TestUndoIssue1376(t *testing.T) { 220 // Backward Continue should terminate when it encounters the start of the process. 221 protest.AllowRecording(t) 222 withUndoRecording("continuetestprog", t, func(p *proc.Target, fixture protest.Fixture) { 223 bp := setFunctionBreakpoint(p, t, "main.main") 224 assertNoError(p.Continue(), t, "Continue (forward)") 225 err := p.ClearBreakpoint(bp.Addr) 226 assertNoError(err, t, "ClearBreakpoint") 227 assertNoError(p.ChangeDirection(proc.Backward), t, "Switching to backward direction") 228 assertNoError(p.Continue(), t, "Continue (backward)") 229 }) 230 }