github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/terminal/command_test.go (about)

     1  package terminal
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net"
     9  	"net/http"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"regexp"
    14  	"runtime"
    15  	"strconv"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/go-delve/delve/pkg/config"
    21  	"github.com/go-delve/delve/pkg/goversion"
    22  	"github.com/go-delve/delve/pkg/logflags"
    23  	"github.com/go-delve/delve/pkg/proc/test"
    24  	"github.com/go-delve/delve/service"
    25  	"github.com/go-delve/delve/service/api"
    26  	"github.com/go-delve/delve/service/debugger"
    27  	"github.com/go-delve/delve/service/rpc2"
    28  	"github.com/go-delve/delve/service/rpccommon"
    29  )
    30  
    31  var testBackend, buildMode string
    32  
    33  func TestMain(m *testing.M) {
    34  	flag.StringVar(&testBackend, "backend", "", "selects backend")
    35  	flag.StringVar(&buildMode, "test-buildmode", "", "selects build mode")
    36  	var logConf string
    37  	flag.StringVar(&logConf, "log", "", "configures logging")
    38  	flag.Parse()
    39  	test.DefaultTestBackend(&testBackend)
    40  	if buildMode != "" && buildMode != "pie" {
    41  		fmt.Fprintf(os.Stderr, "unknown build mode %q", buildMode)
    42  		os.Exit(1)
    43  	}
    44  	logflags.Setup(logConf != "", logConf, "")
    45  	os.Exit(test.RunTestsWithFixtures(m))
    46  }
    47  
    48  type FakeTerminal struct {
    49  	*Term
    50  	t testing.TB
    51  }
    52  
    53  const logCommandOutput = false
    54  
    55  func (ft *FakeTerminal) Exec(cmdstr string) (outstr string, err error) {
    56  	var buf bytes.Buffer
    57  	ft.Term.stdout.pw.w = &buf
    58  	ft.Term.starlarkEnv.Redirect(ft.Term.stdout)
    59  	err = ft.cmds.Call(cmdstr, ft.Term)
    60  	outstr = buf.String()
    61  	if logCommandOutput {
    62  		ft.t.Logf("command %q -> %q", cmdstr, outstr)
    63  	}
    64  	ft.Term.stdout.Flush()
    65  	return
    66  }
    67  
    68  func (ft *FakeTerminal) ExecStarlark(starlarkProgram string) (outstr string, err error) {
    69  	var buf bytes.Buffer
    70  	ft.Term.stdout.pw.w = &buf
    71  	ft.Term.starlarkEnv.Redirect(ft.Term.stdout)
    72  	_, err = ft.Term.starlarkEnv.Execute("<stdin>", starlarkProgram, "main", nil)
    73  	outstr = buf.String()
    74  	if logCommandOutput {
    75  		ft.t.Logf("command %q -> %q", starlarkProgram, outstr)
    76  	}
    77  	ft.Term.stdout.Flush()
    78  	return
    79  }
    80  
    81  func (ft *FakeTerminal) MustExec(cmdstr string) string {
    82  	ft.t.Helper()
    83  	outstr, err := ft.Exec(cmdstr)
    84  	if err != nil {
    85  		ft.t.Errorf("output of %q: %q", cmdstr, outstr)
    86  		ft.t.Fatalf("Error executing <%s>: %v", cmdstr, err)
    87  	}
    88  	return outstr
    89  }
    90  
    91  func (ft *FakeTerminal) MustExecStarlark(starlarkProgram string) string {
    92  	outstr, err := ft.ExecStarlark(starlarkProgram)
    93  	if err != nil {
    94  		ft.t.Errorf("output of %q: %q", starlarkProgram, outstr)
    95  		ft.t.Fatalf("Error executing <%s>: %v", starlarkProgram, err)
    96  	}
    97  	return outstr
    98  }
    99  
   100  func (ft *FakeTerminal) AssertExec(cmdstr, tgt string) {
   101  	out := ft.MustExec(cmdstr)
   102  	if out != tgt {
   103  		ft.t.Fatalf("Error executing %q, expected %q got %q", cmdstr, tgt, out)
   104  	}
   105  }
   106  
   107  func (ft *FakeTerminal) AssertExecError(cmdstr, tgterr string) {
   108  	_, err := ft.Exec(cmdstr)
   109  	if err == nil {
   110  		ft.t.Fatalf("Expected error executing %q", cmdstr)
   111  	}
   112  	if err.Error() != tgterr {
   113  		ft.t.Fatalf("Expected error %q executing %q, got error %q", tgterr, cmdstr, err.Error())
   114  	}
   115  }
   116  
   117  func withTestTerminal(name string, t testing.TB, fn func(*FakeTerminal)) {
   118  	withTestTerminalBuildFlags(name, t, 0, fn)
   119  }
   120  
   121  func withTestTerminalBuildFlags(name string, t testing.TB, buildFlags test.BuildFlags, fn func(*FakeTerminal)) {
   122  	if testBackend == "rr" {
   123  		test.MustHaveRecordingAllowed(t)
   124  	}
   125  	os.Setenv("TERM", "dumb")
   126  	listener, err := net.Listen("tcp", "127.0.0.1:0")
   127  	if err != nil {
   128  		t.Fatalf("couldn't start listener: %s\n", err)
   129  	}
   130  	defer listener.Close()
   131  	if buildMode == "pie" {
   132  		buildFlags |= test.BuildModePIE
   133  	}
   134  	server := rpccommon.NewServer(&service.Config{
   135  		Listener:    listener,
   136  		ProcessArgs: []string{test.BuildFixture(name, buildFlags).Path},
   137  		Debugger: debugger.Config{
   138  			Backend: testBackend,
   139  		},
   140  	})
   141  	if err := server.Run(); err != nil {
   142  		t.Fatal(err)
   143  	}
   144  	client := rpc2.NewClient(listener.Addr().String())
   145  	defer func() {
   146  		client.Detach(true)
   147  	}()
   148  
   149  	ft := &FakeTerminal{
   150  		t:    t,
   151  		Term: New(client, &config.Config{}),
   152  	}
   153  	fn(ft)
   154  }
   155  
   156  func TestCommandDefault(t *testing.T) {
   157  	var (
   158  		cmds = Commands{}
   159  		cmd  = cmds.Find("non-existent-command", noPrefix).cmdFn
   160  	)
   161  
   162  	err := cmd(nil, callContext{}, "")
   163  	if err == nil {
   164  		t.Fatal("cmd() did not default")
   165  	}
   166  
   167  	if err.Error() != "command not available" {
   168  		t.Fatal("wrong command output")
   169  	}
   170  }
   171  
   172  func TestCommandReplayWithoutPreviousCommand(t *testing.T) {
   173  	var (
   174  		cmds = DebugCommands(nil)
   175  		cmd  = cmds.Find("", noPrefix).cmdFn
   176  		err  = cmd(nil, callContext{}, "")
   177  	)
   178  
   179  	if err != nil {
   180  		t.Error("Null command not returned", err)
   181  	}
   182  }
   183  
   184  func TestCommandThread(t *testing.T) {
   185  	var (
   186  		cmds = DebugCommands(nil)
   187  		cmd  = cmds.Find("thread", noPrefix).cmdFn
   188  	)
   189  
   190  	err := cmd(nil, callContext{}, "")
   191  	if err == nil {
   192  		t.Fatal("thread terminal command did not default")
   193  	}
   194  
   195  	if err.Error() != "you must specify a thread" {
   196  		t.Fatal("wrong command output: ", err.Error())
   197  	}
   198  }
   199  
   200  func TestExecuteFile(t *testing.T) {
   201  	breakCount := 0
   202  	traceCount := 0
   203  	c := &Commands{
   204  		client: nil,
   205  		cmds: []command{
   206  			{aliases: []string{"trace"}, cmdFn: func(t *Term, ctx callContext, args string) error {
   207  				traceCount++
   208  				return nil
   209  			}},
   210  			{aliases: []string{"break"}, cmdFn: func(t *Term, ctx callContext, args string) error {
   211  				breakCount++
   212  				return nil
   213  			}},
   214  		},
   215  	}
   216  
   217  	fixturesDir := test.FindFixturesDir()
   218  	err := c.executeFile(nil, filepath.Join(fixturesDir, "bpfile"))
   219  	if err != nil {
   220  		t.Fatalf("executeFile: %v", err)
   221  	}
   222  
   223  	if breakCount != 1 || traceCount != 1 {
   224  		t.Fatalf("Wrong counts break: %d trace: %d\n", breakCount, traceCount)
   225  	}
   226  }
   227  
   228  func TestIssue354(t *testing.T) {
   229  	printStack(&Term{}, os.Stdout, []api.Stackframe{}, "", false)
   230  	printStack(&Term{}, os.Stdout, []api.Stackframe{
   231  		{Location: api.Location{PC: 0, File: "irrelevant.go", Line: 10, Function: nil},
   232  			Bottom: true}}, "", false)
   233  }
   234  
   235  func TestIssue411(t *testing.T) {
   236  	test.AllowRecording(t)
   237  	withTestTerminal("math", t, func(term *FakeTerminal) {
   238  		term.MustExec("break _fixtures/math.go:8")
   239  		term.MustExec("trace _fixtures/math.go:9")
   240  		term.MustExec("continue")
   241  		out := term.MustExec("next")
   242  		if !strings.HasPrefix(out, "> goroutine(1): main.main()") {
   243  			t.Fatalf("Wrong output for next: <%s>", out)
   244  		}
   245  	})
   246  }
   247  
   248  func TestTrace(t *testing.T) {
   249  	test.AllowRecording(t)
   250  	withTestTerminal("issue573", t, func(term *FakeTerminal) {
   251  		term.MustExec("trace foo")
   252  		out, _ := term.Exec("continue")
   253  		// The output here is a little strange, but we don't filter stdout vs stderr so it gets jumbled.
   254  		// Therefore we assert about the call and return values separately.
   255  		if !strings.Contains(out, "> goroutine(1): main.foo(99, 9801)") {
   256  			t.Fatalf("Wrong output for tracepoint: %s", out)
   257  		}
   258  		if !strings.Contains(out, "=> (9900)") {
   259  			t.Fatalf("Wrong output for tracepoint return value: %s", out)
   260  		}
   261  	})
   262  }
   263  
   264  func TestTraceWithName(t *testing.T) {
   265  	test.AllowRecording(t)
   266  	withTestTerminal("issue573", t, func(term *FakeTerminal) {
   267  		term.MustExec("trace foobar foo")
   268  		out, _ := term.Exec("continue")
   269  		// The output here is a little strange, but we don't filter stdout vs stderr so it gets jumbled.
   270  		// Therefore we assert about the call and return values separately.
   271  		if !strings.Contains(out, "> goroutine(1): [foobar] main.foo(99, 9801)") {
   272  			t.Fatalf("Wrong output for tracepoint: %s", out)
   273  		}
   274  		if !strings.Contains(out, "=> (9900)") {
   275  			t.Fatalf("Wrong output for tracepoint return value: %s", out)
   276  		}
   277  	})
   278  }
   279  
   280  func TestTraceOnNonFunctionEntry(t *testing.T) {
   281  	test.AllowRecording(t)
   282  	withTestTerminal("issue573", t, func(term *FakeTerminal) {
   283  		term.MustExec("trace foobar issue573.go:19")
   284  		out, _ := term.Exec("continue")
   285  		if !strings.Contains(out, "> goroutine(1): [foobar] main.foo(99, 9801)") {
   286  			t.Fatalf("Wrong output for tracepoint: %s", out)
   287  		}
   288  		if strings.Contains(out, "=> (9900)") {
   289  			t.Fatalf("Tracepoint on non-function locspec should not have return value:\n%s", out)
   290  		}
   291  	})
   292  }
   293  
   294  func TestExitStatus(t *testing.T) {
   295  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
   296  		term.Exec("continue")
   297  		status, err := term.handleExit()
   298  		if err != nil {
   299  			t.Fatal(err)
   300  		}
   301  		if status != 0 {
   302  			t.Fatalf("incorrect exit status, expected 0, got %d", status)
   303  		}
   304  	})
   305  }
   306  
   307  func TestScopePrefix(t *testing.T) {
   308  	if runtime.GOARCH == "ppc64le" && buildMode == "pie" {
   309  		t.Skip("pie mode broken on ppc64le")
   310  	}
   311  	const goroutinesLinePrefix = "  Goroutine "
   312  	const goroutinesCurLinePrefix = "* Goroutine "
   313  	test.AllowRecording(t)
   314  
   315  	lenient := 0
   316  	if runtime.GOOS == "windows" {
   317  		lenient = 1
   318  	}
   319  
   320  	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
   321  		term.MustExec("b stacktraceme")
   322  		term.MustExec("continue")
   323  
   324  		goroutinesOut := strings.Split(term.MustExec("goroutines"), "\n")
   325  		agoroutines := []int{}
   326  		nonagoroutines := []int{}
   327  		curgid := -1
   328  
   329  		for _, line := range goroutinesOut {
   330  			iscur := strings.HasPrefix(line, goroutinesCurLinePrefix)
   331  			if !iscur && !strings.HasPrefix(line, goroutinesLinePrefix) {
   332  				continue
   333  			}
   334  
   335  			dash := strings.Index(line, " - ")
   336  			if dash < 0 {
   337  				continue
   338  			}
   339  
   340  			gid, err := strconv.Atoi(line[len(goroutinesLinePrefix):dash])
   341  			if err != nil {
   342  				continue
   343  			}
   344  
   345  			if iscur {
   346  				curgid = gid
   347  			}
   348  
   349  			if idx := strings.Index(line, " main.agoroutine "); idx < 0 {
   350  				nonagoroutines = append(nonagoroutines, gid)
   351  				continue
   352  			}
   353  
   354  			agoroutines = append(agoroutines, gid)
   355  		}
   356  
   357  		if len(agoroutines) > 10 {
   358  			t.Fatalf("Output of goroutines did not have 10 goroutines stopped on main.agoroutine (%d found): %q", len(agoroutines), goroutinesOut)
   359  		}
   360  
   361  		if len(agoroutines) < 10 {
   362  			extraAgoroutines := 0
   363  			for _, gid := range nonagoroutines {
   364  				stackOut := strings.Split(term.MustExec(fmt.Sprintf("goroutine %d stack", gid)), "\n")
   365  				for _, line := range stackOut {
   366  					if strings.HasSuffix(line, " main.agoroutine") {
   367  						extraAgoroutines++
   368  						break
   369  					}
   370  				}
   371  			}
   372  			if len(agoroutines)+extraAgoroutines < 10-lenient {
   373  				t.Fatalf("Output of goroutines did not have 10 goroutines stopped on main.agoroutine (%d+%d found): %q", len(agoroutines), extraAgoroutines, goroutinesOut)
   374  			}
   375  		}
   376  
   377  		if curgid < 0 {
   378  			t.Fatalf("Could not find current goroutine in output of goroutines: %q", goroutinesOut)
   379  		}
   380  
   381  		seen := make([]bool, 10)
   382  		for _, gid := range agoroutines {
   383  			stackOut := strings.Split(term.MustExec(fmt.Sprintf("goroutine %d stack", gid)), "\n")
   384  			fid := -1
   385  			for _, line := range stackOut {
   386  				line = strings.TrimLeft(line, " ")
   387  				space := strings.Index(line, " ")
   388  				if space < 0 {
   389  					continue
   390  				}
   391  				curfid, err := strconv.Atoi(line[:space])
   392  				if err != nil {
   393  					continue
   394  				}
   395  
   396  				if idx := strings.Index(line, " main.agoroutine"); idx >= 0 {
   397  					fid = curfid
   398  					break
   399  				}
   400  			}
   401  			if fid < 0 {
   402  				t.Fatalf("Could not find frame for goroutine %d: %q", gid, stackOut)
   403  			}
   404  			term.AssertExec(fmt.Sprintf("goroutine     %d    frame     %d     locals", gid, fid), "(no locals)\n")
   405  			argsOut := strings.Split(term.MustExec(fmt.Sprintf("goroutine %d frame %d args", gid, fid)), "\n")
   406  			if len(argsOut) != 4 || argsOut[3] != "" {
   407  				t.Fatalf("Wrong number of arguments in goroutine %d frame %d: %v", gid, fid, argsOut)
   408  			}
   409  			out := term.MustExec(fmt.Sprintf("goroutine %d frame %d p i", gid, fid))
   410  			ival, err := strconv.Atoi(out[:len(out)-1])
   411  			if err != nil {
   412  				t.Fatalf("could not parse value %q of i for goroutine %d frame %d: %v", out, gid, fid, err)
   413  			}
   414  			seen[ival] = true
   415  		}
   416  
   417  		for i := range seen {
   418  			if !seen[i] {
   419  				if lenient > 0 {
   420  					lenient--
   421  				} else {
   422  					t.Fatalf("goroutine %d not found", i)
   423  				}
   424  			}
   425  		}
   426  
   427  		term.MustExec("c")
   428  
   429  		term.AssertExecError("frame", "not enough arguments")
   430  		term.AssertExecError(fmt.Sprintf("goroutine %d frame 10 locals", curgid), fmt.Sprintf("Frame 10 does not exist in goroutine %d", curgid))
   431  		term.AssertExecError("goroutine 9000 locals", "unknown goroutine 9000")
   432  
   433  		term.AssertExecError("print n", "could not find symbol value for n")
   434  		term.AssertExec("frame 1 print n", "3\n")
   435  		term.AssertExec("frame 2 print n", "2\n")
   436  		term.AssertExec("frame 3 print n", "1\n")
   437  		term.AssertExec("frame 4 print n", "0\n")
   438  		term.AssertExecError("frame 5 print n", "could not find symbol value for n")
   439  
   440  		term.MustExec("frame 2")
   441  		term.AssertExec("print n", "2\n")
   442  		term.MustExec("frame 4")
   443  		term.AssertExec("print n", "0\n")
   444  		term.MustExec("down")
   445  		term.AssertExec("print n", "1\n")
   446  		term.MustExec("down 2")
   447  		term.AssertExec("print n", "3\n")
   448  		term.AssertExecError("down 2", "Invalid frame -1")
   449  		term.AssertExec("print n", "3\n")
   450  		term.MustExec("up 2")
   451  		term.AssertExec("print n", "1\n")
   452  		term.AssertExecError("up 100", "Invalid frame 103")
   453  		term.AssertExec("print n", "1\n")
   454  
   455  		term.MustExec("step")
   456  		term.AssertExecError("print n", "could not find symbol value for n")
   457  		term.MustExec("frame 2")
   458  		term.AssertExec("print n", "2\n")
   459  	})
   460  }
   461  
   462  func TestOnPrefix(t *testing.T) {
   463  	const prefix = "\ti: "
   464  	test.AllowRecording(t)
   465  	lenient := false
   466  	if runtime.GOOS == "windows" {
   467  		lenient = true
   468  	}
   469  	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
   470  		term.MustExec("b agobp main.agoroutine")
   471  		term.MustExec("on agobp print i")
   472  
   473  		seen := make([]bool, 10)
   474  
   475  		for {
   476  			outstr, err := term.Exec("continue")
   477  			if err != nil {
   478  				if !strings.Contains(err.Error(), "exited") {
   479  					t.Fatalf("Unexpected error executing 'continue': %v", err)
   480  				}
   481  				break
   482  			}
   483  			out := strings.Split(outstr, "\n")
   484  
   485  			for i := range out {
   486  				if !strings.HasPrefix(out[i], prefix) {
   487  					continue
   488  				}
   489  				id, err := strconv.Atoi(out[i][len(prefix):])
   490  				if err != nil {
   491  					continue
   492  				}
   493  				if seen[id] {
   494  					t.Fatalf("Goroutine %d seen twice\n", id)
   495  				}
   496  				seen[id] = true
   497  			}
   498  		}
   499  
   500  		for i := range seen {
   501  			if !seen[i] {
   502  				if lenient {
   503  					lenient = false
   504  				} else {
   505  					t.Fatalf("Goroutine %d not seen\n", i)
   506  				}
   507  			}
   508  		}
   509  	})
   510  }
   511  
   512  func TestNoVars(t *testing.T) {
   513  	test.AllowRecording(t)
   514  	withTestTerminal("locationsUpperCase", t, func(term *FakeTerminal) {
   515  		term.MustExec("b main.main")
   516  		term.MustExec("continue")
   517  		term.AssertExec("args", "(no args)\n")
   518  		term.AssertExec("locals", "(no locals)\n")
   519  		term.AssertExec("vars filterThatMatchesNothing", "(no vars)\n")
   520  	})
   521  }
   522  
   523  func TestOnPrefixLocals(t *testing.T) {
   524  	const prefix = "\ti: "
   525  	test.AllowRecording(t)
   526  	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
   527  		term.MustExec("b agobp main.agoroutine")
   528  		term.MustExec("on agobp args -v")
   529  
   530  		seen := make([]bool, 10)
   531  
   532  		for {
   533  			outstr, err := term.Exec("continue")
   534  			if err != nil {
   535  				if !strings.Contains(err.Error(), "exited") {
   536  					t.Fatalf("Unexpected error executing 'continue': %v", err)
   537  				}
   538  				break
   539  			}
   540  			out := strings.Split(outstr, "\n")
   541  
   542  			for i := range out {
   543  				if !strings.HasPrefix(out[i], prefix) {
   544  					continue
   545  				}
   546  				id, err := strconv.Atoi(out[i][len(prefix):])
   547  				if err != nil {
   548  					continue
   549  				}
   550  				if seen[id] {
   551  					t.Fatalf("Goroutine %d seen twice\n", id)
   552  				}
   553  				seen[id] = true
   554  			}
   555  		}
   556  
   557  		for i := range seen {
   558  			if !seen[i] {
   559  				t.Fatalf("Goroutine %d not seen\n", i)
   560  			}
   561  		}
   562  	})
   563  }
   564  
   565  func listIsAt(t *testing.T, term *FakeTerminal, listcmd string, cur, start, end int) {
   566  	t.Helper()
   567  	outstr := term.MustExec(listcmd)
   568  	lines := strings.Split(outstr, "\n")
   569  
   570  	t.Logf("%q: %q", listcmd, outstr)
   571  
   572  	if cur >= 0 && !strings.Contains(lines[0], fmt.Sprintf(":%d", cur)) {
   573  		t.Fatalf("Could not find current line number in first output line: %q", lines[0])
   574  	}
   575  
   576  	re := regexp.MustCompile(`(=>)?\s+(\d+):`)
   577  
   578  	outStart, outEnd := 0, 0
   579  
   580  	for _, line := range lines[1:] {
   581  		if line == "" {
   582  			continue
   583  		}
   584  		v := re.FindStringSubmatch(line)
   585  		if len(v) != 3 {
   586  			continue
   587  		}
   588  		curline, _ := strconv.Atoi(v[2])
   589  		if v[1] == "=>" {
   590  			if cur != curline {
   591  				t.Fatalf("Wrong current line, got %d expected %d", curline, cur)
   592  			}
   593  		}
   594  		if outStart == 0 {
   595  			outStart = curline
   596  		}
   597  		outEnd = curline
   598  	}
   599  
   600  	if start != -1 || end != -1 {
   601  		if outStart != start || outEnd != end {
   602  			t.Fatalf("Wrong output range, got %d:%d expected %d:%d", outStart, outEnd, start, end)
   603  		}
   604  	}
   605  }
   606  
   607  func TestListCmd(t *testing.T) {
   608  	withTestTerminal("testvariables", t, func(term *FakeTerminal) {
   609  		term.MustExec("continue")
   610  		term.MustExec("continue")
   611  		listIsAt(t, term, "list", 27, 22, 32)
   612  		listIsAt(t, term, "list 69", 69, 64, 74)
   613  		listIsAt(t, term, "frame 1 list", 66, 61, 71)
   614  		listIsAt(t, term, "frame 1 list 69", 69, 64, 74)
   615  		_, err := term.Exec("frame 50 list")
   616  		if err == nil {
   617  			t.Fatalf("Expected error requesting 50th frame")
   618  		}
   619  		listIsAt(t, term, "list testvariables.go:1", -1, 1, 6)
   620  		listIsAt(t, term, "list testvariables.go:10000", -1, 0, 0)
   621  	})
   622  }
   623  
   624  func TestReverseContinue(t *testing.T) {
   625  	test.AllowRecording(t)
   626  	if testBackend != "rr" {
   627  		return
   628  	}
   629  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
   630  		term.MustExec("break main.main")
   631  		term.MustExec("break main.sayhi")
   632  		listIsAt(t, term, "continue", 16, -1, -1)
   633  		listIsAt(t, term, "continue", 12, -1, -1)
   634  		listIsAt(t, term, "rewind", 16, -1, -1)
   635  	})
   636  }
   637  
   638  func TestCheckpoints(t *testing.T) {
   639  	test.AllowRecording(t)
   640  	if testBackend != "rr" {
   641  		return
   642  	}
   643  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
   644  		term.MustExec("break main.main")
   645  		listIsAt(t, term, "continue", 16, -1, -1)
   646  		term.MustExec("checkpoint")
   647  		term.MustExec("checkpoints")
   648  		listIsAt(t, term, "next", 17, -1, -1)
   649  		listIsAt(t, term, "next", 18, -1, -1)
   650  		term.MustExec("restart c1")
   651  		term.MustExec("goroutine 1")
   652  		listIsAt(t, term, "list", 16, -1, -1)
   653  	})
   654  }
   655  
   656  func TestNextWithCount(t *testing.T) {
   657  	test.AllowRecording(t)
   658  	withTestTerminal("nextcond", t, func(term *FakeTerminal) {
   659  		term.MustExec("break main.main")
   660  		listIsAt(t, term, "continue", 8, -1, -1)
   661  		listIsAt(t, term, "next 2", 10, -1, -1)
   662  	})
   663  }
   664  
   665  func TestRestart(t *testing.T) {
   666  	withTestTerminal("restartargs", t, func(term *FakeTerminal) {
   667  		term.MustExec("break main.printArgs")
   668  		term.MustExec("continue")
   669  		if out := term.MustExec("print main.args"); !strings.Contains(out, ", []") {
   670  			t.Fatalf("wrong args: %q", out)
   671  		}
   672  		// Reset the arg list
   673  		term.MustExec("restart hello")
   674  		term.MustExec("continue")
   675  		if out := term.MustExec("print main.args"); !strings.Contains(out, ", [\"hello\"]") {
   676  			t.Fatalf("wrong args: %q ", out)
   677  		}
   678  		// Restart w/o arg should retain the current args.
   679  		term.MustExec("restart")
   680  		term.MustExec("continue")
   681  		if out := term.MustExec("print main.args"); !strings.Contains(out, ", [\"hello\"]") {
   682  			t.Fatalf("wrong args: %q ", out)
   683  		}
   684  		// Empty arg list
   685  		term.MustExec("restart -noargs")
   686  		term.MustExec("continue")
   687  		if out := term.MustExec("print main.args"); !strings.Contains(out, ", []") {
   688  			t.Fatalf("wrong args: %q ", out)
   689  		}
   690  	})
   691  }
   692  
   693  func TestIssue827(t *testing.T) {
   694  	// switching goroutines when the current thread isn't running any goroutine
   695  	// causes nil pointer dereference.
   696  	withTestTerminal("notify-v2", t, func(term *FakeTerminal) {
   697  		go func() {
   698  			time.Sleep(1 * time.Second)
   699  			resp, err := http.Get("http://127.0.0.1:8888/test")
   700  			if err == nil {
   701  				resp.Body.Close()
   702  			}
   703  			time.Sleep(1 * time.Second)
   704  			term.client.Halt()
   705  		}()
   706  		term.MustExec("continue")
   707  		term.MustExec("goroutine 1")
   708  	})
   709  }
   710  
   711  func findCmdName(c *Commands, cmdstr string, prefix cmdPrefix) string {
   712  	for _, v := range c.cmds {
   713  		if v.match(cmdstr) {
   714  			if prefix != noPrefix && v.allowedPrefixes&prefix == 0 {
   715  				continue
   716  			}
   717  			return v.aliases[0]
   718  		}
   719  	}
   720  	return ""
   721  }
   722  
   723  func assertNoError(t *testing.T, err error, str string) {
   724  	t.Helper()
   725  	if err != nil {
   726  		t.Fatalf("%s: %v", str, err)
   727  	}
   728  }
   729  
   730  func assertNoErrorConfigureCmd(t *testing.T, term *Term, cmdstr string) {
   731  	t.Helper()
   732  	err := configureCmd(term, callContext{}, cmdstr)
   733  	assertNoError(t, err, fmt.Sprintf("error executing configureCmd(%s)", cmdstr))
   734  }
   735  
   736  func assertSubstitutePath(t *testing.T, sp config.SubstitutePathRules, v ...string) {
   737  	t.Helper()
   738  	if len(sp) != len(v)/2 {
   739  		t.Fatalf("wrong number of substitute path rules (expected: %d): %#v", len(v)/2, sp)
   740  	}
   741  	for i := range sp {
   742  		if sp[i].From != v[i*2] || sp[i].To != v[i*2+1] {
   743  			t.Fatalf("wrong substitute path rule %#v expected (from: %q to %q)", sp[i], v[i*2], v[i*2+1])
   744  		}
   745  	}
   746  }
   747  
   748  func assertDebugInfoDirs(t *testing.T, got []string, tgt ...string) {
   749  	if len(got) != len(tgt) {
   750  		t.Fatalf("wrong number of debug info directories (got %d expected %d)", len(got), len(tgt))
   751  	}
   752  	for i := range got {
   753  		if got[i] != tgt[i] {
   754  			t.Fatalf("debug info directories mismatch got: %v expected: %v", got, tgt)
   755  		}
   756  	}
   757  }
   758  
   759  func TestConfig(t *testing.T) {
   760  	var buf bytes.Buffer
   761  	var term Term
   762  	term.conf = &config.Config{}
   763  	term.cmds = DebugCommands(nil)
   764  	term.stdout = &transcriptWriter{pw: &pagingWriter{w: &buf}}
   765  
   766  	err := configureCmd(&term, callContext{}, "nonexistent-parameter 10")
   767  	if err == nil {
   768  		t.Fatalf("expected error executing configureCmd(nonexistent-parameter)")
   769  	}
   770  
   771  	assertNoErrorConfigureCmd(t, &term, "max-string-len 10")
   772  	if term.conf.MaxStringLen == nil {
   773  		t.Fatalf("expected MaxStringLen 10, got nil")
   774  	}
   775  	if *term.conf.MaxStringLen != 10 {
   776  		t.Fatalf("expected MaxStringLen 10, got: %d", *term.conf.MaxStringLen)
   777  	}
   778  	assertNoErrorConfigureCmd(t, &term, "show-location-expr   true")
   779  	if term.conf.ShowLocationExpr != true {
   780  		t.Fatalf("expected ShowLocationExpr true, got false")
   781  	}
   782  
   783  	assertNoErrorConfigureCmd(t, &term, "max-variable-recurse 4")
   784  	if term.conf.MaxVariableRecurse == nil {
   785  		t.Fatalf("expected MaxVariableRecurse 4, got nil")
   786  	}
   787  	if *term.conf.MaxVariableRecurse != 4 {
   788  		t.Fatalf("expected MaxVariableRecurse 4, got: %d", *term.conf.MaxVariableRecurse)
   789  	}
   790  
   791  	assertNoErrorConfigureCmd(t, &term, "substitute-path a b")
   792  	assertSubstitutePath(t, term.conf.SubstitutePath, "a", "b")
   793  
   794  	assertNoErrorConfigureCmd(t, &term, "substitute-path a")
   795  	assertSubstitutePath(t, term.conf.SubstitutePath)
   796  
   797  	assertNoErrorConfigureCmd(t, &term, "alias print blah")
   798  	if len(term.conf.Aliases["print"]) != 1 {
   799  		t.Fatalf("aliases not changed after configure command %v", term.conf.Aliases)
   800  	}
   801  	if findCmdName(term.cmds, "blah", noPrefix) != "print" {
   802  		t.Fatalf("new alias not found")
   803  	}
   804  
   805  	assertNoErrorConfigureCmd(t, &term, "alias blah")
   806  	if len(term.conf.Aliases["print"]) != 0 {
   807  		t.Fatalf("alias not removed after configure command %v", term.conf.Aliases)
   808  	}
   809  	if findCmdName(term.cmds, "blah", noPrefix) != "" {
   810  		t.Fatalf("new alias found after delete")
   811  	}
   812  
   813  	err = configureCmd(&term, callContext{}, "show-location-expr")
   814  	if err == nil {
   815  		t.Fatalf("no error form configureCmd(show-location-expr)")
   816  	}
   817  	if !term.conf.ShowLocationExpr {
   818  		t.Fatalf("ShowLocationExpr not set to true")
   819  	}
   820  
   821  	assertNoErrorConfigureCmd(t, &term, "show-location-expr false")
   822  	if term.conf.ShowLocationExpr {
   823  		t.Fatalf("ShowLocationExpr set to true")
   824  	}
   825  
   826  	assertNoErrorConfigureCmd(t, &term, "substitute-path a b")
   827  	assertNoErrorConfigureCmd(t, &term, "substitute-path c d")
   828  	assertSubstitutePath(t, term.conf.SubstitutePath, "a", "b", "c", "d")
   829  
   830  	buf.Reset()
   831  	assertNoErrorConfigureCmd(t, &term, "substitute-path")
   832  	t.Logf("current substitute-path: %q", buf.String())
   833  	if buf.String() != "\"a\" → \"b\"\n\"c\" → \"d\"\n" {
   834  		t.Fatalf("wrong substitute-path value")
   835  	}
   836  
   837  	assertNoErrorConfigureCmd(t, &term, "substitute-path -clear c")
   838  	assertSubstitutePath(t, term.conf.SubstitutePath, "a", "b")
   839  
   840  	assertNoErrorConfigureCmd(t, &term, "substitute-path -clear")
   841  	assertSubstitutePath(t, term.conf.SubstitutePath)
   842  
   843  	assertNoErrorConfigureCmd(t, &term, "substitute-path \"\" something")
   844  	assertSubstitutePath(t, term.conf.SubstitutePath, "", "something")
   845  
   846  	assertNoErrorConfigureCmd(t, &term, "substitute-path somethingelse \"\"")
   847  	assertSubstitutePath(t, term.conf.SubstitutePath, "", "something", "somethingelse", "")
   848  
   849  	assertDebugInfoDirs(t, term.conf.DebugInfoDirectories)
   850  
   851  	assertNoErrorConfigureCmd(t, &term, "debug-info-directories -add a")
   852  	assertDebugInfoDirs(t, term.conf.DebugInfoDirectories, "a")
   853  	assertNoErrorConfigureCmd(t, &term, "debug-info-directories -add b")
   854  	assertDebugInfoDirs(t, term.conf.DebugInfoDirectories, "a", "b")
   855  	assertNoErrorConfigureCmd(t, &term, "debug-info-directories -add c")
   856  	assertDebugInfoDirs(t, term.conf.DebugInfoDirectories, "a", "b", "c")
   857  	assertNoErrorConfigureCmd(t, &term, "debug-info-directories -rm b")
   858  	assertDebugInfoDirs(t, term.conf.DebugInfoDirectories, "a", "c")
   859  	assertNoErrorConfigureCmd(t, &term, "debug-info-directories -clear")
   860  	assertDebugInfoDirs(t, term.conf.DebugInfoDirectories)
   861  }
   862  
   863  func TestIssue1090(t *testing.T) {
   864  	// Exit while executing 'next' should report the "Process exited" error
   865  	// message instead of crashing.
   866  	withTestTerminal("math", t, func(term *FakeTerminal) {
   867  		term.MustExec("break main.main")
   868  		term.MustExec("continue")
   869  		for {
   870  			_, err := term.Exec("next")
   871  			if err != nil && strings.Contains(err.Error(), " has exited with status ") {
   872  				break
   873  			}
   874  		}
   875  	})
   876  }
   877  
   878  func TestPrintContextParkedGoroutine(t *testing.T) {
   879  	if runtime.GOARCH == "ppc64le" && buildMode == "pie" {
   880  		t.Skip("pie mode broken on ppc64le")
   881  	}
   882  	withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
   883  		term.MustExec("break stacktraceme")
   884  		term.MustExec("continue")
   885  
   886  		// pick a goroutine that isn't running on a thread
   887  		gid := ""
   888  		gout := strings.Split(term.MustExec("goroutines"), "\n")
   889  		t.Logf("goroutines -> %q", gout)
   890  		for _, gline := range gout {
   891  			if !strings.Contains(gline, "thread ") && strings.Contains(gline, "agoroutine") {
   892  				if dash := strings.Index(gline, " - "); dash > 0 {
   893  					gid = gline[len("  Goroutine "):dash]
   894  					break
   895  				}
   896  			}
   897  		}
   898  
   899  		t.Logf("picked %q", gid)
   900  		term.MustExec(fmt.Sprintf("goroutine %s", gid))
   901  
   902  		frameout := strings.Split(term.MustExec("frame 0"), "\n")
   903  		t.Logf("frame 0 -> %q", frameout)
   904  		if strings.Contains(frameout[0], "stacktraceme") {
   905  			t.Fatal("bad output for `frame 0` command on a parked goorutine")
   906  		}
   907  
   908  		listout := strings.Split(term.MustExec("list"), "\n")
   909  		t.Logf("list -> %q", listout)
   910  		if strings.Contains(listout[0], "stacktraceme") {
   911  			t.Fatal("bad output for list command on a parked goroutine")
   912  		}
   913  	})
   914  }
   915  
   916  func TestStepOutReturn(t *testing.T) {
   917  	ver, _ := goversion.Parse(runtime.Version())
   918  	if ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
   919  		t.Skip("return variables aren't marked on 1.9 or earlier")
   920  	}
   921  	withTestTerminal("stepoutret", t, func(term *FakeTerminal) {
   922  		term.MustExec("break main.stepout")
   923  		term.MustExec("continue")
   924  		out := term.MustExec("stepout")
   925  		t.Logf("output: %q", out)
   926  		if !strings.Contains(out, "num: ") || !strings.Contains(out, "str: ") {
   927  			t.Fatal("could not find parameter")
   928  		}
   929  	})
   930  }
   931  
   932  func TestOptimizationCheck(t *testing.T) {
   933  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
   934  		term.MustExec("break main.main")
   935  		out := term.MustExec("continue")
   936  		t.Logf("output %q", out)
   937  		if strings.Contains(out, optimizedFunctionWarning) {
   938  			t.Fatal("optimized function warning")
   939  		}
   940  	})
   941  
   942  	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) {
   943  		withTestTerminalBuildFlags("continuetestprog", t, test.EnableOptimization|test.EnableInlining, func(term *FakeTerminal) {
   944  			term.MustExec("break main.main")
   945  			out := term.MustExec("continue")
   946  			t.Logf("output %q", out)
   947  			if !strings.Contains(out, optimizedFunctionWarning) {
   948  				t.Fatal("optimized function warning missing")
   949  			}
   950  		})
   951  	}
   952  }
   953  
   954  func TestTruncateStacktrace(t *testing.T) {
   955  	if runtime.GOARCH == "ppc64le" && buildMode == "pie" {
   956  		t.Skip("pie mode broken on ppc64le")
   957  	}
   958  	const stacktraceTruncatedMessage = "(truncated)"
   959  	withTestTerminal("stacktraceprog", t, func(term *FakeTerminal) {
   960  		term.MustExec("break main.stacktraceme")
   961  		term.MustExec("continue")
   962  		out1 := term.MustExec("stack")
   963  		t.Logf("untruncated output %q", out1)
   964  		if strings.Contains(out1, stacktraceTruncatedMessage) {
   965  			t.Fatalf("stacktrace was truncated")
   966  		}
   967  		out2 := term.MustExec("stack 1")
   968  		t.Logf("truncated output %q", out2)
   969  		if !strings.Contains(out2, stacktraceTruncatedMessage) {
   970  			t.Fatalf("stacktrace was not truncated")
   971  		}
   972  	})
   973  }
   974  
   975  func TestIssue1493(t *testing.T) {
   976  	// The 'regs' command without the '-a' option should only return
   977  	// general purpose registers.
   978  	if runtime.GOARCH == "ppc64le" {
   979  		t.Skip("skipping, some registers such as vector registers are currently not loaded")
   980  	}
   981  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
   982  		r := term.MustExec("regs")
   983  		nr := len(strings.Split(r, "\n"))
   984  		t.Logf("regs: %s", r)
   985  		ra := term.MustExec("regs -a")
   986  		nra := len(strings.Split(ra, "\n"))
   987  		t.Logf("regs -a: %s", ra)
   988  		if nr > nra/2+1 {
   989  			t.Fatalf("'regs' returned too many registers (%d) compared to 'regs -a' (%d)", nr, nra)
   990  		}
   991  	})
   992  }
   993  
   994  func findStarFile(name string) string {
   995  	return filepath.Join(test.FindFixturesDir(), name+".star")
   996  }
   997  
   998  func TestIssue1598(t *testing.T) {
   999  	test.MustSupportFunctionCalls(t, testBackend)
  1000  	withTestTerminal("issue1598", t, func(term *FakeTerminal) {
  1001  		term.MustExec("break issue1598.go:5")
  1002  		term.MustExec("continue")
  1003  		term.MustExec("config max-string-len 500")
  1004  		r := term.MustExec("call x()")
  1005  		t.Logf("result %q", r)
  1006  		if !strings.Contains(r, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut \\nlabore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut") {
  1007  			t.Fatalf("wrong value returned")
  1008  		}
  1009  	})
  1010  }
  1011  
  1012  func TestExamineMemoryCmd(t *testing.T) {
  1013  	withTestTerminal("examinememory", t, func(term *FakeTerminal) {
  1014  		term.MustExec("break examinememory.go:19")
  1015  		term.MustExec("break examinememory.go:24")
  1016  		term.MustExec("continue")
  1017  
  1018  		addressStr := strings.TrimSpace(term.MustExec("p bspUintptr"))
  1019  		address, err := strconv.ParseInt(addressStr, 0, 64)
  1020  		if err != nil {
  1021  			t.Fatalf("could convert %s into int64, err %s", addressStr, err)
  1022  		}
  1023  
  1024  		res := term.MustExec("examinemem  -count 52 -fmt hex " + addressStr)
  1025  		t.Logf("the result of examining memory \n%s", res)
  1026  		// check first line
  1027  		firstLine := fmt.Sprintf("%#x:   0x0a   0x0b   0x0c   0x0d   0x0e   0x0f   0x10   0x11", address)
  1028  		if !strings.Contains(res, firstLine) {
  1029  			t.Fatalf("expected first line: %s", firstLine)
  1030  		}
  1031  
  1032  		// check last line
  1033  		lastLine := fmt.Sprintf("%#x:   0x3a   0x3b   0x3c   0x00", address+6*8)
  1034  		if !strings.Contains(res, lastLine) {
  1035  			t.Fatalf("expected last line: %s", lastLine)
  1036  		}
  1037  
  1038  		// second examining memory
  1039  		term.MustExec("continue")
  1040  		res = term.MustExec("x -count 52 -fmt bin " + addressStr)
  1041  		t.Logf("the second result of examining memory result \n%s", res)
  1042  
  1043  		// check first line
  1044  		firstLine = fmt.Sprintf("%#x:   11111111   00001011   00001100   00001101", address)
  1045  		if !strings.Contains(res, firstLine) {
  1046  			t.Fatalf("expected first line: %s", firstLine)
  1047  		}
  1048  
  1049  		// third examining memory: -x addr
  1050  		res = term.MustExec("examinemem -x " + addressStr)
  1051  		t.Logf("the third result of examining memory result \n%s", res)
  1052  		firstLine = fmt.Sprintf("%#x:   0xff", address)
  1053  		if !strings.Contains(res, firstLine) {
  1054  			t.Fatalf("expected first line: %s", firstLine)
  1055  		}
  1056  
  1057  		// fourth examining memory: -x addr + offset
  1058  		res = term.MustExec("examinemem -x " + addressStr + " + 8")
  1059  		t.Logf("the fourth result of examining memory result \n%s", res)
  1060  		firstLine = fmt.Sprintf("%#x:   0x12", address+8)
  1061  		if !strings.Contains(res, firstLine) {
  1062  			t.Fatalf("expected first line: %s", firstLine)
  1063  		}
  1064  		// fifth examining memory: -x &var
  1065  		res = term.MustExec("examinemem -x &bs[0]")
  1066  		t.Logf("the fifth result of examining memory result \n%s", res)
  1067  		firstLine = fmt.Sprintf("%#x:   0xff", address)
  1068  		if !strings.Contains(res, firstLine) {
  1069  			t.Fatalf("expected first line: %s", firstLine)
  1070  		}
  1071  
  1072  		// sixth examining memory: -fmt and double spaces
  1073  		res = term.MustExec("examinemem -fmt  hex  -x &bs[0]")
  1074  		t.Logf("the sixth result of examining memory result \n%s", res)
  1075  		firstLine = fmt.Sprintf("%#x:   0xff", address)
  1076  		if !strings.Contains(res, firstLine) {
  1077  			t.Fatalf("expected first line: %s", firstLine)
  1078  		}
  1079  	})
  1080  
  1081  	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
  1082  		tests := []struct {
  1083  			Expr string
  1084  			Want int
  1085  		}{
  1086  			{Expr: "&i1", Want: 1},
  1087  			{Expr: "&i2", Want: 2},
  1088  			{Expr: "p1", Want: 1},
  1089  			{Expr: "*pp1", Want: 1},
  1090  			{Expr: "&str1[1]", Want: '1'},
  1091  			{Expr: "c1.pb", Want: 1},
  1092  			{Expr: "&c1.pb.a", Want: 1},
  1093  			{Expr: "&c1.pb.a.A", Want: 1},
  1094  			{Expr: "&c1.pb.a.B", Want: 2},
  1095  		}
  1096  		term.MustExec("continue")
  1097  		for _, test := range tests {
  1098  			res := term.MustExec("examinemem -fmt dec -x " + test.Expr)
  1099  			// strip addr from output, e.g. "0xc0000160b8:   023" -> "023"
  1100  			res = strings.TrimSpace(strings.Split(res, ":")[1])
  1101  			got, err := strconv.Atoi(res)
  1102  			if err != nil {
  1103  				t.Fatalf("expr=%q err=%s", test.Expr, err)
  1104  			} else if got != test.Want {
  1105  				t.Errorf("expr=%q got=%d want=%d", test.Expr, got, test.Want)
  1106  			}
  1107  		}
  1108  	})
  1109  }
  1110  
  1111  func TestPrintOnTracepoint(t *testing.T) {
  1112  	withTestTerminal("increment", t, func(term *FakeTerminal) {
  1113  		term.MustExec("trace main.Increment")
  1114  		term.MustExec("on 1 print y+1")
  1115  		out, _ := term.Exec("continue")
  1116  		if !strings.Contains(out, "y+1: 4") || !strings.Contains(out, "y+1: 2") || !strings.Contains(out, "y+1: 1") {
  1117  			t.Errorf("output did not contain breakpoint information: %q", out)
  1118  		}
  1119  	})
  1120  }
  1121  
  1122  func TestPrintCastToInterface(t *testing.T) {
  1123  	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
  1124  		term.MustExec("continue")
  1125  		out := term.MustExec(`p (*"interface {}")(uintptr(&iface2))`)
  1126  		t.Logf("%q", out)
  1127  	})
  1128  }
  1129  
  1130  func TestParseNewArgv(t *testing.T) {
  1131  	testCases := []struct {
  1132  		in       string
  1133  		tgtargs  string
  1134  		tgtredir string
  1135  		tgterr   string
  1136  	}{
  1137  		{"-noargs", "", " |  | ", ""},
  1138  		{"-noargs arg1", "", "", "too many arguments to restart"},
  1139  		{"arg1 arg2", "arg1 | arg2", " |  | ", ""},
  1140  		{"arg1 arg2 <input.txt", "arg1 | arg2", "input.txt |  | ", ""},
  1141  		{"arg1 arg2 < input.txt", "arg1 | arg2", "input.txt |  | ", ""},
  1142  		{"<input.txt", "", "input.txt |  | ", ""},
  1143  		{"< input.txt", "", "input.txt |  | ", ""},
  1144  		{"arg1 < input.txt > output.txt 2> error.txt", "arg1", "input.txt | output.txt | error.txt", ""},
  1145  		{"< input.txt > output.txt 2> error.txt", "", "input.txt | output.txt | error.txt", ""},
  1146  		{"arg1 <input.txt >output.txt 2>error.txt", "arg1", "input.txt | output.txt | error.txt", ""},
  1147  		{"<input.txt >output.txt 2>error.txt", "", "input.txt | output.txt | error.txt", ""},
  1148  		{"<input.txt <input2.txt", "", "", "redirect error: stdin redirected twice"},
  1149  	}
  1150  
  1151  	for _, tc := range testCases {
  1152  		resetArgs, newArgv, newRedirects, err := parseNewArgv(tc.in)
  1153  		t.Logf("%q -> %q %q %v\n", tc.in, newArgv, newRedirects, err)
  1154  		if tc.tgterr != "" {
  1155  			if err == nil {
  1156  				t.Errorf("Expected error %q, got no error", tc.tgterr)
  1157  			} else if errstr := err.Error(); errstr != tc.tgterr {
  1158  				t.Errorf("Expected error %q, got error %q", tc.tgterr, errstr)
  1159  			}
  1160  		} else {
  1161  			if !resetArgs {
  1162  				t.Errorf("parse error, resetArgs is false")
  1163  				continue
  1164  			}
  1165  			argvstr := strings.Join(newArgv, " | ")
  1166  			if argvstr != tc.tgtargs {
  1167  				t.Errorf("Expected new arguments %q, got %q", tc.tgtargs, argvstr)
  1168  			}
  1169  			redirstr := strings.Join(newRedirects[:], " | ")
  1170  			if redirstr != tc.tgtredir {
  1171  				t.Errorf("Expected new redirects %q, got %q", tc.tgtredir, redirstr)
  1172  			}
  1173  		}
  1174  	}
  1175  }
  1176  
  1177  func TestContinueUntil(t *testing.T) {
  1178  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
  1179  		if runtime.GOARCH != "386" {
  1180  			listIsAt(t, term, "continue main.main", 16, -1, -1)
  1181  		} else {
  1182  			listIsAt(t, term, "continue main.main", 17, -1, -1)
  1183  		}
  1184  		listIsAt(t, term, "continue main.sayhi", 12, -1, -1)
  1185  	})
  1186  }
  1187  
  1188  func TestContinueUntilExistingBreakpoint(t *testing.T) {
  1189  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
  1190  		term.MustExec("break main.main")
  1191  		if runtime.GOARCH != "386" {
  1192  			listIsAt(t, term, "continue main.main", 16, -1, -1)
  1193  		} else {
  1194  			listIsAt(t, term, "continue main.main", 17, -1, -1)
  1195  		}
  1196  		listIsAt(t, term, "continue main.sayhi", 12, -1, -1)
  1197  	})
  1198  }
  1199  
  1200  func TestPrintFormat(t *testing.T) {
  1201  	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
  1202  		term.MustExec("continue")
  1203  		out := term.MustExec("print %#x m2[1].B")
  1204  		if !strings.Contains(out, "0xb\n") {
  1205  			t.Fatalf("output did not contain '0xb': %q", out)
  1206  		}
  1207  	})
  1208  }
  1209  
  1210  func TestHitCondBreakpoint(t *testing.T) {
  1211  	withTestTerminal("break", t, func(term *FakeTerminal) {
  1212  		term.MustExec("break bp1 main.main:4")
  1213  		term.MustExec("condition -hitcount bp1 > 2")
  1214  		listIsAt(t, term, "continue", 7, -1, -1)
  1215  		out := term.MustExec("print i")
  1216  		t.Logf("%q", out)
  1217  		if !strings.Contains(out, "3\n") {
  1218  			t.Fatalf("wrong value of i")
  1219  		}
  1220  	})
  1221  
  1222  	withTestTerminal("condperghitcount", t, func(term *FakeTerminal) {
  1223  		term.MustExec("break bp1 main.main:8")
  1224  		term.MustExec("condition -per-g-hitcount bp1 == 2")
  1225  		listIsAt(t, term, "continue", 16, -1, -1)
  1226  		// first g hit
  1227  		out := term.MustExec("print j")
  1228  		t.Logf("%q", out)
  1229  		if !strings.Contains(out, "2\n") {
  1230  			t.Fatalf("wrong value of j")
  1231  		}
  1232  		term.MustExec("toggle bp1")
  1233  		listIsAt(t, term, "continue", 16, -1, -1)
  1234  		// second g hit
  1235  		out = term.MustExec("print j")
  1236  		t.Logf("%q", out)
  1237  		if !strings.Contains(out, "2\n") {
  1238  			t.Fatalf("wrong value of j")
  1239  		}
  1240  	})
  1241  }
  1242  
  1243  func TestClearCondBreakpoint(t *testing.T) {
  1244  	withTestTerminal("break", t, func(term *FakeTerminal) {
  1245  		term.MustExec("break main.main:4")
  1246  		term.MustExec("condition 1 i%3==2")
  1247  		listIsAt(t, term, "continue", 7, -1, -1)
  1248  		out := term.MustExec("print i")
  1249  		t.Logf("%q", out)
  1250  		if !strings.Contains(out, "2\n") {
  1251  			t.Fatalf("wrong value of i")
  1252  		}
  1253  		term.MustExec("condition -clear 1")
  1254  		listIsAt(t, term, "continue", 7, -1, -1)
  1255  		out = term.MustExec("print i")
  1256  		t.Logf("%q", out)
  1257  		if !strings.Contains(out, "3\n") {
  1258  			t.Fatalf("wrong value of i")
  1259  		}
  1260  	})
  1261  }
  1262  
  1263  func TestBreakpointEditing(t *testing.T) {
  1264  	term := &FakeTerminal{
  1265  		t:    t,
  1266  		Term: New(nil, &config.Config{}),
  1267  	}
  1268  	_ = term
  1269  
  1270  	var testCases = []struct {
  1271  		inBp    *api.Breakpoint
  1272  		inBpStr string
  1273  		edit    string
  1274  		outBp   *api.Breakpoint
  1275  	}{
  1276  		{ // tracepoint -> breakpoint
  1277  			&api.Breakpoint{Tracepoint: true},
  1278  			"trace",
  1279  			"",
  1280  			&api.Breakpoint{}},
  1281  		{ // breakpoint -> tracepoint
  1282  			&api.Breakpoint{Variables: []string{"a"}},
  1283  			"print a",
  1284  			"print a\ntrace",
  1285  			&api.Breakpoint{Tracepoint: true, Variables: []string{"a"}}},
  1286  		{ // add print var
  1287  			&api.Breakpoint{Variables: []string{"a"}},
  1288  			"print a",
  1289  			"print b\nprint a\n",
  1290  			&api.Breakpoint{Variables: []string{"b", "a"}}},
  1291  		{ // add goroutine flag
  1292  			&api.Breakpoint{},
  1293  			"",
  1294  			"goroutine",
  1295  			&api.Breakpoint{Goroutine: true}},
  1296  		{ // remove goroutine flag
  1297  			&api.Breakpoint{Goroutine: true},
  1298  			"goroutine",
  1299  			"",
  1300  			&api.Breakpoint{}},
  1301  		{ // add stack directive
  1302  			&api.Breakpoint{},
  1303  			"",
  1304  			"stack 10",
  1305  			&api.Breakpoint{Stacktrace: 10}},
  1306  		{ // remove stack directive
  1307  			&api.Breakpoint{Stacktrace: 20},
  1308  			"stack 20",
  1309  			"print a",
  1310  			&api.Breakpoint{Variables: []string{"a"}}},
  1311  		{ // add condition
  1312  			&api.Breakpoint{Variables: []string{"a"}},
  1313  			"print a",
  1314  			"print a\ncond a < b",
  1315  			&api.Breakpoint{Variables: []string{"a"}, Cond: "a < b"}},
  1316  		{ // remove condition
  1317  			&api.Breakpoint{Cond: "a < b"},
  1318  			"cond a < b",
  1319  			"",
  1320  			&api.Breakpoint{}},
  1321  		{ // change condition
  1322  			&api.Breakpoint{Cond: "a < b"},
  1323  			"cond a < b",
  1324  			"cond a < 5",
  1325  			&api.Breakpoint{Cond: "a < 5"}},
  1326  		{ // change hitcount condition
  1327  			&api.Breakpoint{HitCond: "% 2"},
  1328  			"cond -hitcount % 2",
  1329  			"cond -hitcount = 2",
  1330  			&api.Breakpoint{HitCond: "= 2"}},
  1331  	}
  1332  
  1333  	for _, tc := range testCases {
  1334  		bp := *tc.inBp
  1335  		bpStr := strings.Join(formatBreakpointAttrs("", &bp, true), "\n")
  1336  		if bpStr != tc.inBpStr {
  1337  			t.Errorf("Expected %q got %q for:\n%#v", tc.inBpStr, bpStr, tc.inBp)
  1338  		}
  1339  		ctx := callContext{Prefix: onPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: 0, DeferredCall: 0}, Breakpoint: &bp}
  1340  		err := term.cmds.parseBreakpointAttrs(nil, ctx, strings.NewReader(tc.edit))
  1341  		if err != nil {
  1342  			t.Errorf("Unexpected error during edit %q", tc.edit)
  1343  		}
  1344  		if !reflect.DeepEqual(bp, *tc.outBp) {
  1345  			t.Errorf("mismatch after edit\nexpected: %#v\ngot: %#v", tc.outBp, bp)
  1346  		}
  1347  	}
  1348  }
  1349  
  1350  func TestTranscript(t *testing.T) {
  1351  	withTestTerminal("math", t, func(term *FakeTerminal) {
  1352  		term.MustExec("break main.main")
  1353  		out := term.MustExec("continue")
  1354  		if !strings.HasPrefix(out, "> main.main()") {
  1355  			t.Fatalf("Wrong output for next: <%s>", out)
  1356  		}
  1357  		fh, err := ioutil.TempFile("", "test-transcript-*")
  1358  		if err != nil {
  1359  			t.Fatalf("TempFile: %v", err)
  1360  		}
  1361  		name := fh.Name()
  1362  		fh.Close()
  1363  		t.Logf("output to %q", name)
  1364  
  1365  		slurp := func() string {
  1366  			b, err := ioutil.ReadFile(name)
  1367  			if err != nil {
  1368  				t.Fatalf("could not read transcript file: %v", err)
  1369  			}
  1370  			return string(b)
  1371  		}
  1372  
  1373  		term.MustExec(fmt.Sprintf("transcript %s", name))
  1374  		out = term.MustExec("list")
  1375  		//term.MustExec("transcript -off")
  1376  		if out != slurp() {
  1377  			t.Logf("output of list %s", out)
  1378  			t.Logf("contents of transcript: %s", slurp())
  1379  			t.Errorf("transcript and command out differ")
  1380  		}
  1381  
  1382  		term.MustExec(fmt.Sprintf("transcript -t -x %s", name))
  1383  		out = term.MustExec(`print "hello"`)
  1384  		if out != "" {
  1385  			t.Errorf("output of print is %q but should have been suppressed by transcript", out)
  1386  		}
  1387  		if slurp() != "\"hello\"\n" {
  1388  			t.Errorf("wrong contents of transcript: %q", slurp())
  1389  		}
  1390  
  1391  		os.Remove(name)
  1392  	})
  1393  }
  1394  
  1395  func TestDisassPosCmd(t *testing.T) {
  1396  	if runtime.GOARCH == "ppc64le" && buildMode == "pie" {
  1397  		t.Skip("pie mode broken on ppc64le")
  1398  	}
  1399  	withTestTerminal("testvariables2", t, func(term *FakeTerminal) {
  1400  		term.MustExec("continue")
  1401  		out := term.MustExec("step-instruction")
  1402  		t.Logf("%q\n", out)
  1403  		if !strings.Contains(out, "call $runtime.Breakpoint") && !strings.Contains(out, "CALL runtime.Breakpoint(SB)") {
  1404  			t.Errorf("output doesn't look like disassembly")
  1405  		}
  1406  	})
  1407  }
  1408  
  1409  func TestCreateBreakpointByLocExpr(t *testing.T) {
  1410  	withTestTerminal("math", t, func(term *FakeTerminal) {
  1411  		out := term.MustExec("break main.main")
  1412  		position1 := strings.Split(out, " set at ")[1]
  1413  		term.MustExec("continue")
  1414  		term.MustExec("clear 1")
  1415  		out = term.MustExec("break +0")
  1416  		position2 := strings.Split(out, " set at ")[1]
  1417  		if position1 != position2 {
  1418  			t.Fatalf("mismatched positions %q and %q\n", position1, position2)
  1419  		}
  1420  	})
  1421  }
  1422  
  1423  func TestRestartBreakpoints(t *testing.T) {
  1424  	// Tests that breakpoints set using just a line number and with a line
  1425  	// offset are preserved after restart. See issue #3423.
  1426  	withTestTerminal("continuetestprog", t, func(term *FakeTerminal) {
  1427  		term.MustExec("break main.main")
  1428  		term.MustExec("continue")
  1429  		term.MustExec("break 9")
  1430  		term.MustExec("break +1")
  1431  		out := term.MustExec("breakpoints")
  1432  		t.Log("breakpoints before:\n", out)
  1433  		term.MustExec("restart")
  1434  		out = term.MustExec("breakpoints")
  1435  		t.Log("breakpoints after:\n", out)
  1436  		bps, err := term.client.ListBreakpoints(false)
  1437  		assertNoError(t, err, "ListBreakpoints")
  1438  		for _, bp := range bps {
  1439  			if bp.ID < 0 {
  1440  				continue
  1441  			}
  1442  			if bp.Addr == 0 {
  1443  				t.Fatalf("breakpoint %d has address 0", bp.ID)
  1444  			}
  1445  		}
  1446  	})
  1447  }