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  }