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  }