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  }