github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/cmd/dlv/dlv_test.go (about)

     1  package main_test
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"flag"
     7  	"fmt"
     8  	"go/ast"
     9  	"go/token"
    10  	"go/types"
    11  	"io"
    12  	"io/ioutil"
    13  	"net"
    14  	"os"
    15  	"os/exec"
    16  	"os/user"
    17  	"path/filepath"
    18  	"runtime"
    19  	"strconv"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/go-delve/delve/pkg/goversion"
    25  	protest "github.com/go-delve/delve/pkg/proc/test"
    26  	"github.com/go-delve/delve/pkg/terminal"
    27  	"github.com/go-delve/delve/service/dap"
    28  	"github.com/go-delve/delve/service/dap/daptest"
    29  	"github.com/go-delve/delve/service/rpc2"
    30  	godap "github.com/google/go-dap"
    31  	"golang.org/x/tools/go/packages"
    32  )
    33  
    34  var testBackend string
    35  var ldFlags string
    36  
    37  func init() {
    38  	ldFlags = os.Getenv("CGO_LDFLAGS")
    39  }
    40  
    41  func TestMain(m *testing.M) {
    42  	flag.StringVar(&testBackend, "backend", "", "selects backend")
    43  	flag.Parse()
    44  	if testBackend == "" {
    45  		testBackend = os.Getenv("PROCTEST")
    46  		if testBackend == "" {
    47  			testBackend = "native"
    48  			if runtime.GOOS == "darwin" {
    49  				testBackend = "lldb"
    50  			}
    51  		}
    52  	}
    53  	os.Exit(protest.RunTestsWithFixtures(m))
    54  }
    55  
    56  func assertNoError(err error, t testing.TB, s string) {
    57  	t.Helper()
    58  	if err != nil {
    59  		_, file, line, _ := runtime.Caller(1)
    60  		fname := filepath.Base(file)
    61  		t.Fatalf("failed assertion at %s:%d: %s - %s\n", fname, line, s, err)
    62  	}
    63  }
    64  
    65  func projectRoot() string {
    66  	wd, err := os.Getwd()
    67  	if err != nil {
    68  		panic(err)
    69  	}
    70  
    71  	gopaths := strings.FieldsFunc(os.Getenv("GOPATH"), func(r rune) bool { return r == os.PathListSeparator })
    72  	for _, curpath := range gopaths {
    73  		// Detects "gopath mode" when GOPATH contains several paths ex. "d:\\dir\\gopath;f:\\dir\\gopath2"
    74  		if strings.Contains(wd, curpath) {
    75  			return filepath.Join(curpath, "src", "github.com", "go-delve", "delve")
    76  		}
    77  	}
    78  	val, err := exec.Command("go", "list", "-mod=", "-m", "-f", "{{ .Dir }}").Output()
    79  	if err != nil {
    80  		panic(err) // the Go tool was tested to work earlier
    81  	}
    82  	return strings.TrimSuffix(string(val), "\n")
    83  }
    84  
    85  func TestBuild(t *testing.T) {
    86  	const listenAddr = "127.0.0.1:40573"
    87  	var err error
    88  
    89  	cmd := exec.Command("go", "run", "_scripts/make.go", "build")
    90  	cmd.Dir = projectRoot()
    91  	out, err := cmd.CombinedOutput()
    92  	if err != nil {
    93  		t.Fatalf("makefile error: %v\noutput %s\n", err, string(out))
    94  	}
    95  
    96  	dlvbin := filepath.Join(cmd.Dir, "dlv")
    97  	defer os.Remove(dlvbin)
    98  
    99  	fixtures := protest.FindFixturesDir()
   100  
   101  	buildtestdir := filepath.Join(fixtures, "buildtest")
   102  
   103  	cmd = exec.Command(dlvbin, "debug", "--headless=true", "--listen="+listenAddr, "--api-version=2", "--backend="+testBackend, "--log", "--log-output=debugger,rpc")
   104  	cmd.Dir = buildtestdir
   105  	stderr, err := cmd.StderrPipe()
   106  	assertNoError(err, t, "stderr pipe")
   107  	defer stderr.Close()
   108  
   109  	assertNoError(cmd.Start(), t, "dlv debug")
   110  
   111  	scan := bufio.NewScanner(stderr)
   112  	// wait for the debugger to start
   113  	for scan.Scan() {
   114  		text := scan.Text()
   115  		t.Log(text)
   116  		if strings.Contains(text, "API server pid = ") {
   117  			break
   118  		}
   119  	}
   120  	go func() {
   121  		for scan.Scan() {
   122  			t.Log(scan.Text())
   123  			// keep pipe empty
   124  		}
   125  	}()
   126  
   127  	client := rpc2.NewClient(listenAddr)
   128  	state := <-client.Continue()
   129  
   130  	if !state.Exited {
   131  		t.Fatal("Program did not exit")
   132  	}
   133  
   134  	client.Detach(true)
   135  	cmd.Wait()
   136  }
   137  
   138  func testOutput(t *testing.T, dlvbin, output string, delveCmds []string) (stdout, stderr []byte) {
   139  	var stdoutBuf, stderrBuf bytes.Buffer
   140  	buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
   141  
   142  	c := []string{dlvbin, "debug", "--allow-non-terminal-interactive=true"}
   143  	debugbin := filepath.Join(buildtestdir, "__debug_bin")
   144  	if output != "" {
   145  		c = append(c, "--output", output)
   146  		if filepath.IsAbs(output) {
   147  			debugbin = output
   148  		} else {
   149  			debugbin = filepath.Join(buildtestdir, output)
   150  		}
   151  	}
   152  	cmd := exec.Command(c[0], c[1:]...)
   153  	cmd.Dir = buildtestdir
   154  	stdin, err := cmd.StdinPipe()
   155  	assertNoError(err, t, "stdin pipe")
   156  	defer stdin.Close()
   157  
   158  	cmd.Stdout = &stdoutBuf
   159  	cmd.Stderr = &stderrBuf
   160  
   161  	assertNoError(cmd.Start(), t, "dlv debug with output")
   162  
   163  	// Give delve some time to compile and write the binary.
   164  	foundIt := false
   165  	for wait := 0; wait < 30; wait++ {
   166  		_, err = os.Stat(debugbin)
   167  		if err == nil {
   168  			foundIt = true
   169  			break
   170  		}
   171  
   172  		time.Sleep(1 * time.Second)
   173  	}
   174  	if !foundIt {
   175  		t.Errorf("running %q: file not created: %v", delveCmds, err)
   176  	}
   177  
   178  	for _, c := range delveCmds {
   179  		fmt.Fprintf(stdin, "%s\n", c)
   180  	}
   181  
   182  	// ignore "dlv debug" command error, it returns
   183  	// errors even after successful debug session.
   184  	cmd.Wait()
   185  	stdout, stderr = stdoutBuf.Bytes(), stderrBuf.Bytes()
   186  
   187  	_, err = os.Stat(debugbin)
   188  	if err == nil {
   189  		// Sometimes delve on Windows can't remove the built binary before
   190  		// exiting and gets an "Access is denied" error when trying.
   191  		// See: https://travis-ci.com/go-delve/delve/jobs/296325131)
   192  		// We have added a delay to gobuild.Remove, but to avoid any test
   193  		// flakiness, we guard against this failure here as well.
   194  		if runtime.GOOS != "windows" || !strings.Contains(err.Error(), "Access is denied") {
   195  			t.Errorf("running %q: file %v was not deleted\nstdout is %q, stderr is %q", delveCmds, debugbin, stdout, stderr)
   196  		}
   197  		return
   198  	}
   199  	if !os.IsNotExist(err) {
   200  		t.Errorf("running %q: %v\nstdout is %q, stderr is %q", delveCmds, err, stdout, stderr)
   201  		return
   202  	}
   203  	return
   204  }
   205  
   206  func getDlvBin(t *testing.T) string {
   207  	// In case this was set in the environment
   208  	// from getDlvBinEBPF lets clear it here so
   209  	// we can ensure we don't get build errors
   210  	// depending on the test ordering.
   211  	os.Setenv("CGO_LDFLAGS", ldFlags)
   212  	var tags string
   213  	if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
   214  		tags = "-tags=exp.winarm64"
   215  	}
   216  	if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" {
   217  		tags = "-tags=exp.linuxppc64le"
   218  	}
   219  	return getDlvBinInternal(t, tags)
   220  }
   221  
   222  func getDlvBinEBPF(t *testing.T) string {
   223  	return getDlvBinInternal(t, "-tags", "ebpf")
   224  }
   225  
   226  func getDlvBinInternal(t *testing.T, goflags ...string) string {
   227  	dlvbin := filepath.Join(t.TempDir(), "dlv.exe")
   228  	args := append([]string{"build", "-o", dlvbin}, goflags...)
   229  	args = append(args, "github.com/go-delve/delve/cmd/dlv")
   230  
   231  	out, err := exec.Command("go", args...).CombinedOutput()
   232  	if err != nil {
   233  		t.Fatalf("go build -o %v github.com/go-delve/delve/cmd/dlv: %v\n%s", dlvbin, err, string(out))
   234  	}
   235  
   236  	return dlvbin
   237  }
   238  
   239  // TestOutput verifies that the debug executable is created in the correct path
   240  // and removed after exit.
   241  func TestOutput(t *testing.T) {
   242  	dlvbin := getDlvBin(t)
   243  
   244  	for _, output := range []string{"__debug_bin", "myownname", filepath.Join(t.TempDir(), "absolute.path")} {
   245  		testOutput(t, dlvbin, output, []string{"exit"})
   246  
   247  		const hello = "hello world!"
   248  		stdout, _ := testOutput(t, dlvbin, output, []string{"continue", "exit"})
   249  		if !strings.Contains(string(stdout), hello) {
   250  			t.Errorf("stdout %q should contain %q", stdout, hello)
   251  		}
   252  	}
   253  }
   254  
   255  // TestContinue verifies that the debugged executable starts immediately with --continue
   256  func TestContinue(t *testing.T) {
   257  	const listenAddr = "127.0.0.1:40573"
   258  
   259  	dlvbin := getDlvBin(t)
   260  
   261  	buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
   262  	cmd := exec.Command(dlvbin, "debug", "--headless", "--continue", "--accept-multiclient", "--listen", listenAddr)
   263  	cmd.Dir = buildtestdir
   264  	stdout, err := cmd.StdoutPipe()
   265  	assertNoError(err, t, "stdout pipe")
   266  	defer stdout.Close()
   267  
   268  	assertNoError(cmd.Start(), t, "start headless instance")
   269  
   270  	scan := bufio.NewScanner(stdout)
   271  	// wait for the debugger to start
   272  	for scan.Scan() {
   273  		t.Log(scan.Text())
   274  		if scan.Text() == "hello world!" {
   275  			break
   276  		}
   277  	}
   278  
   279  	// and detach from and kill the headless instance
   280  	client := rpc2.NewClient(listenAddr)
   281  	if err := client.Detach(true); err != nil {
   282  		t.Fatalf("error detaching from headless instance: %v", err)
   283  	}
   284  	cmd.Wait()
   285  }
   286  
   287  // TestRedirect verifies that redirecting stdin works
   288  func TestRedirect(t *testing.T) {
   289  	const listenAddr = "127.0.0.1:40573"
   290  
   291  	dlvbin := getDlvBin(t)
   292  
   293  	catfixture := filepath.Join(protest.FindFixturesDir(), "cat.go")
   294  	cmd := exec.Command(dlvbin, "debug", "--headless", "--continue", "--accept-multiclient", "--listen", listenAddr, "-r", catfixture, catfixture)
   295  	stdout, err := cmd.StdoutPipe()
   296  	assertNoError(err, t, "stdout pipe")
   297  	defer stdout.Close()
   298  
   299  	assertNoError(cmd.Start(), t, "start headless instance")
   300  
   301  	scan := bufio.NewScanner(stdout)
   302  	// wait for the debugger to start
   303  	for scan.Scan() {
   304  		t.Log(scan.Text())
   305  		if scan.Text() == "read \"}\"" {
   306  			break
   307  		}
   308  	}
   309  
   310  	// and detach from and kill the headless instance
   311  	client := rpc2.NewClient(listenAddr)
   312  	_ = client.Detach(true)
   313  	cmd.Wait()
   314  }
   315  
   316  const checkAutogenDocLongOutput = false
   317  
   318  func checkAutogenDoc(t *testing.T, filename, gencommand string, generated []byte) {
   319  	saved := slurpFile(t, filepath.Join(projectRoot(), filename))
   320  
   321  	saved = bytes.ReplaceAll(saved, []byte("\r\n"), []byte{'\n'})
   322  	generated = bytes.ReplaceAll(generated, []byte("\r\n"), []byte{'\n'})
   323  
   324  	if len(saved) != len(generated) {
   325  		if checkAutogenDocLongOutput {
   326  			t.Logf("generated %q saved %q\n", generated, saved)
   327  		}
   328  		diffMaybe(t, filename, generated)
   329  		t.Fatalf("%s: needs to be regenerated; run %s", filename, gencommand)
   330  	}
   331  
   332  	for i := range saved {
   333  		if saved[i] != generated[i] {
   334  			if checkAutogenDocLongOutput {
   335  				t.Logf("generated %q saved %q\n", generated, saved)
   336  			}
   337  			diffMaybe(t, filename, generated)
   338  			t.Fatalf("%s: needs to be regenerated; run %s", filename, gencommand)
   339  		}
   340  	}
   341  }
   342  
   343  func slurpFile(t *testing.T, filename string) []byte {
   344  	saved, err := ioutil.ReadFile(filename)
   345  	if err != nil {
   346  		t.Fatalf("Could not read %s: %v", filename, err)
   347  	}
   348  	return saved
   349  }
   350  
   351  func diffMaybe(t *testing.T, filename string, generated []byte) {
   352  	_, err := exec.LookPath("diff")
   353  	if err != nil {
   354  		return
   355  	}
   356  	cmd := exec.Command("diff", filename, "-")
   357  	cmd.Dir = projectRoot()
   358  	stdin, _ := cmd.StdinPipe()
   359  	go func() {
   360  		stdin.Write(generated)
   361  		stdin.Close()
   362  	}()
   363  	out, _ := cmd.CombinedOutput()
   364  	t.Logf("diff:\n%s", string(out))
   365  }
   366  
   367  // TestGeneratedDoc tests that the autogenerated documentation has been
   368  // updated.
   369  func TestGeneratedDoc(t *testing.T) {
   370  	if strings.ToLower(os.Getenv("TRAVIS")) == "true" && runtime.GOOS == "windows" {
   371  		t.Skip("skipping test on Windows in CI")
   372  	}
   373  	if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
   374  		//TODO(qmuntal): investigate further when the Windows ARM64 backend is more stable.
   375  		t.Skip("skipping test on Windows in CI")
   376  	}
   377  	if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" {
   378  		//TODO(alexsaezm): finish CI integration
   379  		t.Skip("skipping test on Linux/PPC64LE in CI")
   380  	}
   381  	// Checks gen-cli-docs.go
   382  	var generatedBuf bytes.Buffer
   383  	commands := terminal.DebugCommands(nil)
   384  	commands.WriteMarkdown(&generatedBuf)
   385  	checkAutogenDoc(t, "Documentation/cli/README.md", "_scripts/gen-cli-docs.go", generatedBuf.Bytes())
   386  
   387  	// Checks gen-usage-docs.go
   388  	tempDir := t.TempDir()
   389  	cmd := exec.Command("go", "run", "_scripts/gen-usage-docs.go", tempDir)
   390  	cmd.Dir = projectRoot()
   391  	err := cmd.Run()
   392  	assertNoError(err, t, "go run _scripts/gen-usage-docs.go")
   393  	entries, err := ioutil.ReadDir(tempDir)
   394  	assertNoError(err, t, "ReadDir")
   395  	for _, doc := range entries {
   396  		docFilename := "Documentation/usage/" + doc.Name()
   397  		checkAutogenDoc(t, docFilename, "_scripts/gen-usage-docs.go", slurpFile(t, tempDir+"/"+doc.Name()))
   398  	}
   399  
   400  	runScript := func(args ...string) []byte {
   401  		a := []string{"run"}
   402  		a = append(a, args...)
   403  		cmd := exec.Command("go", a...)
   404  		cmd.Dir = projectRoot()
   405  		out, err := cmd.CombinedOutput()
   406  		if err != nil {
   407  			t.Fatalf("could not run script %v: %v (output: %q)", args, err, string(out))
   408  		}
   409  		return out
   410  	}
   411  
   412  	checkAutogenDoc(t, "Documentation/backend_test_health.md", "go run _scripts/gen-backend_test_health.go", runScript("_scripts/gen-backend_test_health.go", "-"))
   413  	checkAutogenDoc(t, "pkg/terminal/starbind/starlark_mapping.go", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "go", "-"))
   414  	checkAutogenDoc(t, "Documentation/cli/starlark.md", "'go generate' inside pkg/terminal/starbind", runScript("_scripts/gen-starlark-bindings.go", "doc/dummy", "Documentation/cli/starlark.md"))
   415  	checkAutogenDoc(t, "Documentation/faq.md", "'go run _scripts/gen-faq-toc.go Documentation/faq.md Documentation/faq.md'", runScript("_scripts/gen-faq-toc.go", "Documentation/faq.md", "-"))
   416  	if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) {
   417  		checkAutogenDoc(t, "_scripts/rtype-out.txt", "go run _scripts/rtype.go report _scripts/rtype-out.txt", runScript("_scripts/rtype.go", "report"))
   418  		runScript("_scripts/rtype.go", "check")
   419  	}
   420  }
   421  
   422  func TestExitInInit(t *testing.T) {
   423  	dlvbin := getDlvBin(t)
   424  
   425  	buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
   426  	exitInit := filepath.Join(protest.FindFixturesDir(), "exit.init")
   427  	cmd := exec.Command(dlvbin, "--init", exitInit, "debug")
   428  	cmd.Dir = buildtestdir
   429  	out, err := cmd.CombinedOutput()
   430  	t.Logf("%q %v\n", string(out), err)
   431  	// dlv will exit anyway because stdin is not a tty but it will print the
   432  	// prompt once if the init file didn't call exit successfully.
   433  	if strings.Contains(string(out), "(dlv)") {
   434  		t.Fatal("init did not cause dlv to exit")
   435  	}
   436  }
   437  
   438  func getMethods(pkg *types.Package, typename string) map[string]*types.Func {
   439  	r := make(map[string]*types.Func)
   440  	mset := types.NewMethodSet(types.NewPointer(pkg.Scope().Lookup(typename).Type()))
   441  	for i := 0; i < mset.Len(); i++ {
   442  		fn := mset.At(i).Obj().(*types.Func)
   443  		r[fn.Name()] = fn
   444  	}
   445  	return r
   446  }
   447  
   448  func publicMethodOf(decl ast.Decl, receiver string) *ast.FuncDecl {
   449  	fndecl, isfunc := decl.(*ast.FuncDecl)
   450  	if !isfunc {
   451  		return nil
   452  	}
   453  	if fndecl.Name.Name[0] >= 'a' && fndecl.Name.Name[0] <= 'z' {
   454  		return nil
   455  	}
   456  	if fndecl.Recv == nil || len(fndecl.Recv.List) != 1 {
   457  		return nil
   458  	}
   459  	starexpr, isstar := fndecl.Recv.List[0].Type.(*ast.StarExpr)
   460  	if !isstar {
   461  		return nil
   462  	}
   463  	identexpr, isident := starexpr.X.(*ast.Ident)
   464  	if !isident || identexpr.Name != receiver {
   465  		return nil
   466  	}
   467  	if fndecl.Body == nil {
   468  		return nil
   469  	}
   470  	return fndecl
   471  }
   472  
   473  func findCallCall(fndecl *ast.FuncDecl) *ast.CallExpr {
   474  	for _, stmt := range fndecl.Body.List {
   475  		var x ast.Expr = nil
   476  
   477  		switch s := stmt.(type) {
   478  		case *ast.AssignStmt:
   479  			if len(s.Rhs) == 1 {
   480  				x = s.Rhs[0]
   481  			}
   482  		case *ast.ReturnStmt:
   483  			if len(s.Results) == 1 {
   484  				x = s.Results[0]
   485  			}
   486  		case *ast.ExprStmt:
   487  			x = s.X
   488  		}
   489  
   490  		callx, iscall := x.(*ast.CallExpr)
   491  		if !iscall {
   492  			continue
   493  		}
   494  		fun, issel := callx.Fun.(*ast.SelectorExpr)
   495  		if !issel || fun.Sel.Name != "call" {
   496  			continue
   497  		}
   498  		return callx
   499  	}
   500  	return nil
   501  }
   502  
   503  func qf(*types.Package) string {
   504  	return ""
   505  }
   506  
   507  func TestTypecheckRPC(t *testing.T) {
   508  	fset := &token.FileSet{}
   509  	cfg := &packages.Config{
   510  		Mode: packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedName | packages.NeedCompiledGoFiles | packages.NeedTypes,
   511  		Fset: fset,
   512  	}
   513  	pkgs, err := packages.Load(cfg, "github.com/go-delve/delve/service/rpc2")
   514  	if err != nil {
   515  		t.Fatal(err)
   516  	}
   517  	var clientAst *ast.File
   518  	var serverMethods map[string]*types.Func
   519  	var info *types.Info
   520  	packages.Visit(pkgs, func(pkg *packages.Package) bool {
   521  		if pkg.PkgPath != "github.com/go-delve/delve/service/rpc2" {
   522  			return true
   523  		}
   524  		t.Logf("package found: %v", pkg.PkgPath)
   525  		serverMethods = getMethods(pkg.Types, "RPCServer")
   526  		info = pkg.TypesInfo
   527  		for i := range pkg.Syntax {
   528  			t.Logf("file %q", pkg.CompiledGoFiles[i])
   529  			if strings.HasSuffix(pkg.CompiledGoFiles[i], string(os.PathSeparator)+"client.go") {
   530  				clientAst = pkg.Syntax[i]
   531  				break
   532  			}
   533  		}
   534  		return true
   535  	}, nil)
   536  
   537  	errcount := 0
   538  
   539  	for _, decl := range clientAst.Decls {
   540  		fndecl := publicMethodOf(decl, "RPCClient")
   541  		if fndecl == nil {
   542  			continue
   543  		}
   544  
   545  		switch fndecl.Name.Name {
   546  		case "Continue", "Rewind":
   547  			// wrappers over continueDir
   548  			continue
   549  		case "SetReturnValuesLoadConfig", "Disconnect":
   550  			// support functions
   551  			continue
   552  		}
   553  
   554  		if fndecl.Name.Name == "Continue" || fndecl.Name.Name == "Rewind" || fndecl.Name.Name == "DirectionCongruentContinue" {
   555  			// using continueDir
   556  			continue
   557  		}
   558  
   559  		callx := findCallCall(fndecl)
   560  
   561  		if callx == nil {
   562  			t.Errorf("%s: could not find RPC call", fset.Position(fndecl.Pos()))
   563  			errcount++
   564  			continue
   565  		}
   566  
   567  		if len(callx.Args) != 3 {
   568  			t.Errorf("%s: wrong number of arguments for RPC call", fset.Position(callx.Pos()))
   569  			errcount++
   570  			continue
   571  		}
   572  
   573  		arg0, arg0islit := callx.Args[0].(*ast.BasicLit)
   574  		arg1 := callx.Args[1]
   575  		arg2 := callx.Args[2]
   576  		if !arg0islit || arg0.Kind != token.STRING {
   577  			continue
   578  		}
   579  		name, _ := strconv.Unquote(arg0.Value)
   580  		serverMethod := serverMethods[name]
   581  		if serverMethod == nil {
   582  			t.Errorf("%s: could not find RPC method %q", fset.Position(callx.Pos()), name)
   583  			errcount++
   584  			continue
   585  		}
   586  
   587  		params := serverMethod.Type().(*types.Signature).Params()
   588  
   589  		if a, e := info.TypeOf(arg1), params.At(0).Type(); !types.AssignableTo(a, e) {
   590  			t.Errorf("%s: wrong type of first argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
   591  			errcount++
   592  			continue
   593  		}
   594  
   595  		if !strings.HasSuffix(params.At(1).Type().String(), "/service.RPCCallback") {
   596  			if a, e := info.TypeOf(arg2), params.At(1).Type(); !types.AssignableTo(a, e) {
   597  				t.Errorf("%s: wrong type of second argument %s, expected %s", fset.Position(callx.Pos()), types.TypeString(a, qf), types.TypeString(e, qf))
   598  				errcount++
   599  				continue
   600  			}
   601  		}
   602  
   603  		if clit, ok := arg1.(*ast.CompositeLit); ok {
   604  			typ := params.At(0).Type()
   605  			st := typ.Underlying().(*types.Struct)
   606  			if len(clit.Elts) != st.NumFields() && types.TypeString(typ, qf) != "DebuggerCommand" {
   607  				t.Errorf("%s: wrong number of fields in first argument's literal %d, expected %d", fset.Position(callx.Pos()), len(clit.Elts), st.NumFields())
   608  				errcount++
   609  				continue
   610  			}
   611  		}
   612  	}
   613  
   614  	if errcount > 0 {
   615  		t.Errorf("%d errors", errcount)
   616  	}
   617  }
   618  
   619  // TestDAPCmd verifies that a dap server can be started and shut down.
   620  func TestDAPCmd(t *testing.T) {
   621  	const listenAddr = "127.0.0.1:40575"
   622  
   623  	dlvbin := getDlvBin(t)
   624  
   625  	cmd := exec.Command(dlvbin, "dap", "--log-output=dap", "--log", "--listen", listenAddr)
   626  	stdout, err := cmd.StdoutPipe()
   627  	assertNoError(err, t, "stdout pipe")
   628  	defer stdout.Close()
   629  	stderr, err := cmd.StderrPipe()
   630  	assertNoError(err, t, "stderr pipe")
   631  	defer stderr.Close()
   632  
   633  	assertNoError(cmd.Start(), t, "start dap instance")
   634  
   635  	scanOut := bufio.NewScanner(stdout)
   636  	scanErr := bufio.NewScanner(stderr)
   637  	// Wait for the debug server to start
   638  	scanOut.Scan()
   639  	listening := "DAP server listening at: " + listenAddr
   640  	if scanOut.Text() != listening {
   641  		cmd.Process.Kill() // release the port
   642  		t.Fatalf("Unexpected stdout:\ngot  %q\nwant %q", scanOut.Text(), listening)
   643  	}
   644  	go func() {
   645  		for scanErr.Scan() {
   646  			t.Log(scanErr.Text())
   647  		}
   648  	}()
   649  
   650  	// Connect a client and request shutdown.
   651  	client := daptest.NewClient(listenAddr)
   652  	client.DisconnectRequest()
   653  	client.ExpectDisconnectResponse(t)
   654  	client.ExpectTerminatedEvent(t)
   655  	_, err = client.ReadMessage()
   656  	if runtime.GOOS == "windows" {
   657  		if err == nil {
   658  			t.Errorf("got %q, want non-nil\n", err)
   659  		}
   660  	} else {
   661  		if err != io.EOF {
   662  			t.Errorf("got %q, want \"EOF\"\n", err)
   663  		}
   664  	}
   665  	client.Close()
   666  	cmd.Wait()
   667  }
   668  
   669  func newDAPRemoteClient(t *testing.T, addr string, isDlvAttach bool, isMulti bool) *daptest.Client {
   670  	c := daptest.NewClient(addr)
   671  	c.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
   672  	if isDlvAttach || isMulti {
   673  		c.ExpectCapabilitiesEventSupportTerminateDebuggee(t)
   674  	}
   675  	c.ExpectInitializedEvent(t)
   676  	c.ExpectAttachResponse(t)
   677  	c.ConfigurationDoneRequest()
   678  	c.ExpectStoppedEvent(t)
   679  	c.ExpectConfigurationDoneResponse(t)
   680  	return c
   681  }
   682  
   683  func TestRemoteDAPClient(t *testing.T) {
   684  	const listenAddr = "127.0.0.1:40576"
   685  
   686  	dlvbin := getDlvBin(t)
   687  
   688  	buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
   689  	cmd := exec.Command(dlvbin, "debug", "--headless", "--log-output=dap", "--log", "--listen", listenAddr)
   690  	cmd.Dir = buildtestdir
   691  	stdout, err := cmd.StdoutPipe()
   692  	assertNoError(err, t, "stdout pipe")
   693  	defer stdout.Close()
   694  	stderr, err := cmd.StderrPipe()
   695  	assertNoError(err, t, "stderr pipe")
   696  	defer stderr.Close()
   697  	assertNoError(cmd.Start(), t, "start headless instance")
   698  
   699  	scanOut := bufio.NewScanner(stdout)
   700  	scanErr := bufio.NewScanner(stderr)
   701  	// Wait for the debug server to start
   702  	scanOut.Scan()
   703  	t.Log(scanOut.Text())
   704  	go func() { // Capture logging
   705  		for scanErr.Scan() {
   706  			t.Log(scanErr.Text())
   707  		}
   708  	}()
   709  
   710  	client := newDAPRemoteClient(t, listenAddr, false, false)
   711  	client.ContinueRequest(1)
   712  	client.ExpectContinueResponse(t)
   713  	client.ExpectTerminatedEvent(t)
   714  
   715  	client.DisconnectRequest()
   716  	client.ExpectOutputEventProcessExited(t, 0)
   717  	client.ExpectOutputEventDetaching(t)
   718  	client.ExpectDisconnectResponse(t)
   719  	client.ExpectTerminatedEvent(t)
   720  	if _, err := client.ReadMessage(); err == nil {
   721  		t.Error("expected read error upon shutdown")
   722  	}
   723  	client.Close()
   724  	cmd.Wait()
   725  }
   726  
   727  func closeDAPRemoteMultiClient(t *testing.T, c *daptest.Client, expectStatus string) {
   728  	c.DisconnectRequest()
   729  	c.ExpectOutputEventClosingClient(t, expectStatus)
   730  	c.ExpectDisconnectResponse(t)
   731  	c.ExpectTerminatedEvent(t)
   732  	c.Close()
   733  	time.Sleep(10 * time.Millisecond)
   734  }
   735  
   736  func TestRemoteDAPClientMulti(t *testing.T) {
   737  	const listenAddr = "127.0.0.1:40577"
   738  
   739  	dlvbin := getDlvBin(t)
   740  
   741  	buildtestdir := filepath.Join(protest.FindFixturesDir(), "buildtest")
   742  	cmd := exec.Command(dlvbin, "debug", "--headless", "--accept-multiclient", "--log-output=debugger", "--log", "--listen", listenAddr)
   743  	cmd.Dir = buildtestdir
   744  	stdout, err := cmd.StdoutPipe()
   745  	assertNoError(err, t, "stdout pipe")
   746  	defer stdout.Close()
   747  	stderr, err := cmd.StderrPipe()
   748  	assertNoError(err, t, "stderr pipe")
   749  	defer stderr.Close()
   750  	assertNoError(cmd.Start(), t, "start headless instance")
   751  
   752  	scanOut := bufio.NewScanner(stdout)
   753  	scanErr := bufio.NewScanner(stderr)
   754  	// Wait for the debug server to start
   755  	scanOut.Scan()
   756  	t.Log(scanOut.Text())
   757  	go func() { // Capture logging
   758  		for scanErr.Scan() {
   759  			t.Log(scanErr.Text())
   760  		}
   761  	}()
   762  
   763  	// Client 0 connects but with the wrong attach request
   764  	dapclient0 := daptest.NewClient(listenAddr)
   765  	dapclient0.AttachRequest(map[string]interface{}{"mode": "local"})
   766  	dapclient0.ExpectErrorResponse(t)
   767  
   768  	// Client 1 connects and continues to main.main
   769  	dapclient := newDAPRemoteClient(t, listenAddr, false, true)
   770  	dapclient.SetFunctionBreakpointsRequest([]godap.FunctionBreakpoint{{Name: "main.main"}})
   771  	dapclient.ExpectSetFunctionBreakpointsResponse(t)
   772  	dapclient.ContinueRequest(1)
   773  	dapclient.ExpectContinueResponse(t)
   774  	dapclient.ExpectStoppedEvent(t)
   775  	dapclient.CheckStopLocation(t, 1, "main.main", 5)
   776  	closeDAPRemoteMultiClient(t, dapclient, "halted")
   777  
   778  	// Client 2 reconnects at main.main and continues to process exit
   779  	dapclient2 := newDAPRemoteClient(t, listenAddr, false, true)
   780  	dapclient2.CheckStopLocation(t, 1, "main.main", 5)
   781  	dapclient2.ContinueRequest(1)
   782  	dapclient2.ExpectContinueResponse(t)
   783  	dapclient2.ExpectTerminatedEvent(t)
   784  	closeDAPRemoteMultiClient(t, dapclient2, "exited")
   785  
   786  	// Attach to exited processes is an error
   787  	dapclient3 := daptest.NewClient(listenAddr)
   788  	dapclient3.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
   789  	dapclient3.ExpectErrorResponseWith(t, dap.FailedToAttach, `Process \d+ has exited with status 0`, true)
   790  	closeDAPRemoteMultiClient(t, dapclient3, "exited")
   791  
   792  	// But rpc clients can still connect and restart
   793  	rpcclient := rpc2.NewClient(listenAddr)
   794  	if _, err := rpcclient.Restart(false); err != nil {
   795  		t.Errorf("error restarting with rpc client: %v", err)
   796  	}
   797  	if err := rpcclient.Detach(true); err != nil {
   798  		t.Fatalf("error detaching from headless instance: %v", err)
   799  	}
   800  	cmd.Wait()
   801  }
   802  
   803  func TestRemoteDAPClientAfterContinue(t *testing.T) {
   804  	const listenAddr = "127.0.0.1:40578"
   805  
   806  	dlvbin := getDlvBin(t)
   807  
   808  	fixture := protest.BuildFixture("loopprog", 0)
   809  	cmd := exec.Command(dlvbin, "exec", fixture.Path, "--headless", "--continue", "--accept-multiclient", "--log-output=debugger,dap", "--log", "--listen", listenAddr)
   810  	stdout, err := cmd.StdoutPipe()
   811  	assertNoError(err, t, "stdout pipe")
   812  	defer stdout.Close()
   813  	stderr, err := cmd.StderrPipe()
   814  	assertNoError(err, t, "stderr pipe")
   815  	defer stderr.Close()
   816  	assertNoError(cmd.Start(), t, "start headless instance")
   817  
   818  	scanOut := bufio.NewScanner(stdout)
   819  	scanErr := bufio.NewScanner(stderr)
   820  	// Wait for the debug server to start
   821  	scanOut.Scan() // "API server listening...""
   822  	t.Log(scanOut.Text())
   823  	// Wait for the program to start
   824  	scanOut.Scan() // "past main"
   825  	t.Log(scanOut.Text())
   826  
   827  	go func() { // Capture logging
   828  		for scanErr.Scan() {
   829  			text := scanErr.Text()
   830  			if strings.Contains(text, "Internal Error") {
   831  				t.Error("ERROR", text)
   832  			} else {
   833  				t.Log(text)
   834  			}
   835  		}
   836  	}()
   837  
   838  	c := newDAPRemoteClient(t, listenAddr, false, true)
   839  	c.ContinueRequest(1)
   840  	c.ExpectContinueResponse(t)
   841  	c.DisconnectRequest()
   842  	c.ExpectOutputEventClosingClient(t, "running")
   843  	c.ExpectDisconnectResponse(t)
   844  	c.ExpectTerminatedEvent(t)
   845  	c.Close()
   846  
   847  	c = newDAPRemoteClient(t, listenAddr, false, true)
   848  	c.DisconnectRequestWithKillOption(true)
   849  	c.ExpectOutputEventDetachingKill(t)
   850  	c.ExpectDisconnectResponse(t)
   851  	c.ExpectTerminatedEvent(t)
   852  	if _, err := c.ReadMessage(); err == nil {
   853  		t.Error("expected read error upon shutdown")
   854  	}
   855  	c.Close()
   856  	cmd.Wait()
   857  }
   858  
   859  // TestDAPCmdWithClient tests dlv dap --client-addr can be started and shut down.
   860  func TestDAPCmdWithClient(t *testing.T) {
   861  	listener, err := net.Listen("tcp", ":0")
   862  	if err != nil {
   863  		t.Fatalf("cannot setup listener required for testing: %v", err)
   864  	}
   865  	defer listener.Close()
   866  
   867  	dlvbin := getDlvBin(t)
   868  
   869  	cmd := exec.Command(dlvbin, "dap", "--log-output=dap", "--log", "--client-addr", listener.Addr().String())
   870  	buf := &bytes.Buffer{}
   871  	cmd.Stdin = buf
   872  	cmd.Stdout = buf
   873  	assertNoError(cmd.Start(), t, "start dlv dap process with --client-addr flag")
   874  
   875  	// Wait for the connection.
   876  	conn, err := listener.Accept()
   877  	if err != nil {
   878  		cmd.Process.Kill() // release the port
   879  		t.Fatalf("Failed to get connection: %v", err)
   880  	}
   881  	t.Log("dlv dap process dialed in successfully")
   882  
   883  	client := daptest.NewClientFromConn(conn)
   884  	client.InitializeRequest()
   885  	client.ExpectInitializeResponse(t)
   886  
   887  	// Close the connection.
   888  	if err := conn.Close(); err != nil {
   889  		cmd.Process.Kill()
   890  		t.Fatalf("Failed to get connection: %v", err)
   891  	}
   892  
   893  	// Connection close should trigger dlv-reverse command's normal exit.
   894  	if err := cmd.Wait(); err != nil {
   895  		cmd.Process.Kill()
   896  		t.Fatalf("command failed: %v\n%s\n%v", err, buf.Bytes(), cmd.Process.Pid)
   897  	}
   898  }
   899  
   900  func TestTrace(t *testing.T) {
   901  	dlvbin := getDlvBin(t)
   902  
   903  	expected := []byte("> goroutine(1): main.foo(99, 9801)\n>> goroutine(1): => (9900)\n")
   904  
   905  	fixtures := protest.FindFixturesDir()
   906  	cmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "issue573.go"), "foo")
   907  	rdr, err := cmd.StderrPipe()
   908  	assertNoError(err, t, "stderr pipe")
   909  	defer rdr.Close()
   910  
   911  	cmd.Dir = filepath.Join(fixtures, "buildtest")
   912  
   913  	assertNoError(cmd.Start(), t, "running trace")
   914  
   915  	output, err := ioutil.ReadAll(rdr)
   916  	assertNoError(err, t, "ReadAll")
   917  
   918  	if !bytes.Contains(output, expected) {
   919  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
   920  	}
   921  	cmd.Wait()
   922  }
   923  
   924  func TestTrace2(t *testing.T) {
   925  	dlvbin := getDlvBin(t)
   926  
   927  	expected := []byte("> goroutine(1): main.callme(2)\n>> goroutine(1): => (4)\n")
   928  
   929  	fixtures := protest.FindFixturesDir()
   930  	cmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "traceprog.go"), "callme")
   931  	rdr, err := cmd.StderrPipe()
   932  	assertNoError(err, t, "stderr pipe")
   933  	defer rdr.Close()
   934  
   935  	cmd.Dir = filepath.Join(fixtures, "buildtest")
   936  
   937  	assertNoError(cmd.Start(), t, "running trace")
   938  
   939  	output, err := ioutil.ReadAll(rdr)
   940  	assertNoError(err, t, "ReadAll")
   941  
   942  	if !bytes.Contains(output, expected) {
   943  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
   944  	}
   945  	assertNoError(cmd.Wait(), t, "cmd.Wait()")
   946  }
   947  
   948  func TestTraceMultipleGoroutines(t *testing.T) {
   949  	dlvbin := getDlvBin(t)
   950  
   951  	// TODO(derekparker) this test has to be a bit vague to avoid flakyness.
   952  	// I think a future improvement could be to use regexp captures to match the
   953  	// goroutine IDs at function entry and exit.
   954  	expected := []byte("main.callme(0, \"five\")\n")
   955  	expected2 := []byte("=> (0)\n")
   956  
   957  	fixtures := protest.FindFixturesDir()
   958  	cmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "goroutines-trace.go"), "callme")
   959  	rdr, err := cmd.StderrPipe()
   960  	assertNoError(err, t, "stderr pipe")
   961  	defer rdr.Close()
   962  
   963  	cmd.Dir = filepath.Join(fixtures, "buildtest")
   964  
   965  	assertNoError(cmd.Start(), t, "running trace")
   966  
   967  	output, err := ioutil.ReadAll(rdr)
   968  	assertNoError(err, t, "ReadAll")
   969  
   970  	if !bytes.Contains(output, expected) {
   971  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
   972  	}
   973  	if !bytes.Contains(output, expected2) {
   974  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
   975  	}
   976  	cmd.Wait()
   977  }
   978  
   979  func TestTracePid(t *testing.T) {
   980  	if runtime.GOOS == "linux" {
   981  		bs, _ := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
   982  		if bs == nil || strings.TrimSpace(string(bs)) != "0" {
   983  			t.Logf("can not run TestAttachDetach: %v\n", bs)
   984  			return
   985  		}
   986  	}
   987  
   988  	dlvbin := getDlvBin(t)
   989  
   990  	expected := []byte("goroutine(1): main.A()\n>> goroutine(1): => ()\n")
   991  
   992  	// make process run
   993  	fix := protest.BuildFixture("issue2023", 0)
   994  	targetCmd := exec.Command(fix.Path)
   995  	assertNoError(targetCmd.Start(), t, "execute issue2023")
   996  
   997  	if targetCmd.Process == nil || targetCmd.Process.Pid == 0 {
   998  		t.Fatal("expected target process running")
   999  	}
  1000  	defer targetCmd.Process.Kill()
  1001  
  1002  	// dlv attach the process by pid
  1003  	cmd := exec.Command(dlvbin, "trace", "-p", strconv.Itoa(targetCmd.Process.Pid), "main.A")
  1004  	rdr, err := cmd.StderrPipe()
  1005  	assertNoError(err, t, "stderr pipe")
  1006  	defer rdr.Close()
  1007  
  1008  	assertNoError(cmd.Start(), t, "running trace")
  1009  
  1010  	output, err := ioutil.ReadAll(rdr)
  1011  	assertNoError(err, t, "ReadAll")
  1012  
  1013  	if !bytes.Contains(output, expected) {
  1014  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
  1015  	}
  1016  
  1017  	cmd.Wait()
  1018  }
  1019  
  1020  func TestTraceBreakpointExists(t *testing.T) {
  1021  	dlvbin := getDlvBin(t)
  1022  
  1023  	fixtures := protest.FindFixturesDir()
  1024  	// We always set breakpoints on some runtime functions at startup, so this would return with
  1025  	// a breakpoints exists error.
  1026  	// TODO: Perhaps we shouldn't be setting these default breakpoints in trace mode, however.
  1027  	cmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "issue573.go"), "runtime.*")
  1028  	rdr, err := cmd.StderrPipe()
  1029  	assertNoError(err, t, "stderr pipe")
  1030  	defer rdr.Close()
  1031  
  1032  	cmd.Dir = filepath.Join(fixtures, "buildtest")
  1033  
  1034  	assertNoError(cmd.Start(), t, "running trace")
  1035  
  1036  	defer cmd.Wait()
  1037  
  1038  	output, err := ioutil.ReadAll(rdr)
  1039  	assertNoError(err, t, "ReadAll")
  1040  
  1041  	if bytes.Contains(output, []byte("Breakpoint exists")) {
  1042  		t.Fatal("Breakpoint exists errors should be ignored")
  1043  	}
  1044  }
  1045  
  1046  func TestTracePrintStack(t *testing.T) {
  1047  	dlvbin := getDlvBin(t)
  1048  
  1049  	fixtures := protest.FindFixturesDir()
  1050  	cmd := exec.Command(dlvbin, "trace", "--output", filepath.Join(t.TempDir(), "__debug"), "--stack", "2", filepath.Join(fixtures, "issue573.go"), "foo")
  1051  	rdr, err := cmd.StderrPipe()
  1052  	assertNoError(err, t, "stderr pipe")
  1053  	defer rdr.Close()
  1054  
  1055  	cmd.Dir = filepath.Join(fixtures, "buildtest")
  1056  	assertNoError(cmd.Start(), t, "running trace")
  1057  
  1058  	defer cmd.Wait()
  1059  
  1060  	output, err := ioutil.ReadAll(rdr)
  1061  	assertNoError(err, t, "ReadAll")
  1062  
  1063  	if !bytes.Contains(output, []byte("Stack:")) && !bytes.Contains(output, []byte("main.main")) {
  1064  		t.Fatal("stacktrace not printed")
  1065  	}
  1066  }
  1067  
  1068  func TestTraceEBPF(t *testing.T) {
  1069  	if os.Getenv("CI") == "true" {
  1070  		t.Skip("cannot run test in CI, requires kernel compiled with btf support")
  1071  	}
  1072  	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
  1073  		t.Skip("not implemented on non linux/amd64 systems")
  1074  	}
  1075  	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
  1076  		t.Skip("requires at least Go 1.16 to run test")
  1077  	}
  1078  	usr, err := user.Current()
  1079  	if err != nil {
  1080  		t.Fatal(err)
  1081  	}
  1082  	if usr.Uid != "0" {
  1083  		t.Skip("test must be run as root")
  1084  	}
  1085  
  1086  	dlvbin := getDlvBinEBPF(t)
  1087  
  1088  	expected := []byte("> (1) main.foo(99, 9801)\n=> \"9900\"")
  1089  
  1090  	fixtures := protest.FindFixturesDir()
  1091  	cmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "issue573.go"), "foo")
  1092  	rdr, err := cmd.StderrPipe()
  1093  	assertNoError(err, t, "stderr pipe")
  1094  	defer rdr.Close()
  1095  
  1096  	assertNoError(cmd.Start(), t, "running trace")
  1097  
  1098  	output, err := ioutil.ReadAll(rdr)
  1099  	assertNoError(err, t, "ReadAll")
  1100  
  1101  	cmd.Wait()
  1102  	if !bytes.Contains(output, expected) {
  1103  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
  1104  	}
  1105  }
  1106  
  1107  func TestTraceEBPF2(t *testing.T) {
  1108  	if os.Getenv("CI") == "true" {
  1109  		t.Skip("cannot run test in CI, requires kernel compiled with btf support")
  1110  	}
  1111  	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
  1112  		t.Skip("not implemented on non linux/amd64 systems")
  1113  	}
  1114  	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
  1115  		t.Skip("requires at least Go 1.16 to run test")
  1116  	}
  1117  	usr, err := user.Current()
  1118  	if err != nil {
  1119  		t.Fatal(err)
  1120  	}
  1121  	if usr.Uid != "0" {
  1122  		t.Skip("test must be run as root")
  1123  	}
  1124  
  1125  	dlvbin := getDlvBinEBPF(t)
  1126  
  1127  	expected := []byte(`> (1) main.callme(10)
  1128  > (1) main.callme(9)
  1129  > (1) main.callme(8)
  1130  > (1) main.callme(7)
  1131  > (1) main.callme(6)
  1132  > (1) main.callme(5)
  1133  > (1) main.callme(4)
  1134  > (1) main.callme(3)
  1135  > (1) main.callme(2)
  1136  > (1) main.callme(1)
  1137  > (1) main.callme(0)
  1138  => "100"
  1139  => "100"
  1140  => "100"
  1141  => "100"
  1142  => "100"
  1143  => "100"
  1144  => "100"
  1145  => "100"
  1146  => "100"
  1147  => "100"
  1148  => "100"`)
  1149  
  1150  	fixtures := protest.FindFixturesDir()
  1151  	cmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "ebpf_trace.go"), "main.callme")
  1152  	rdr, err := cmd.StderrPipe()
  1153  	assertNoError(err, t, "stderr pipe")
  1154  	defer rdr.Close()
  1155  
  1156  	assertNoError(cmd.Start(), t, "running trace")
  1157  
  1158  	output, err := ioutil.ReadAll(rdr)
  1159  	assertNoError(err, t, "ReadAll")
  1160  
  1161  	cmd.Wait()
  1162  	if !bytes.Contains(output, expected) {
  1163  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
  1164  	}
  1165  }
  1166  
  1167  func TestTraceEBPF3(t *testing.T) {
  1168  	if os.Getenv("CI") == "true" {
  1169  		t.Skip("cannot run test in CI, requires kernel compiled with btf support")
  1170  	}
  1171  	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
  1172  		t.Skip("not implemented on non linux/amd64 systems")
  1173  	}
  1174  	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
  1175  		t.Skip("requires at least Go 1.16 to run test")
  1176  	}
  1177  	usr, err := user.Current()
  1178  	if err != nil {
  1179  		t.Fatal(err)
  1180  	}
  1181  	if usr.Uid != "0" {
  1182  		t.Skip("test must be run as root")
  1183  	}
  1184  
  1185  	dlvbin := getDlvBinEBPF(t)
  1186  
  1187  	expected := []byte(`> (1) main.tracedFunction(0)
  1188  > (1) main.tracedFunction(1)
  1189  > (1) main.tracedFunction(2)
  1190  > (1) main.tracedFunction(3)
  1191  > (1) main.tracedFunction(4)
  1192  > (1) main.tracedFunction(5)
  1193  > (1) main.tracedFunction(6)
  1194  > (1) main.tracedFunction(7)
  1195  > (1) main.tracedFunction(8)
  1196  > (1) main.tracedFunction(9)`)
  1197  
  1198  	fixtures := protest.FindFixturesDir()
  1199  	cmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "ebpf_trace2.go"), "main.traced")
  1200  	rdr, err := cmd.StderrPipe()
  1201  	assertNoError(err, t, "stderr pipe")
  1202  	defer rdr.Close()
  1203  
  1204  	assertNoError(cmd.Start(), t, "running trace")
  1205  
  1206  	output, err := ioutil.ReadAll(rdr)
  1207  	assertNoError(err, t, "ReadAll")
  1208  
  1209  	cmd.Wait()
  1210  	if !bytes.Contains(output, expected) {
  1211  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
  1212  	}
  1213  }
  1214  
  1215  func TestTraceEBPF4(t *testing.T) {
  1216  	if os.Getenv("CI") == "true" {
  1217  		t.Skip("cannot run test in CI, requires kernel compiled with btf support")
  1218  	}
  1219  	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
  1220  		t.Skip("not implemented on non linux/amd64 systems")
  1221  	}
  1222  	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) {
  1223  		t.Skip("requires at least Go 1.16 to run test")
  1224  	}
  1225  	usr, err := user.Current()
  1226  	if err != nil {
  1227  		t.Fatal(err)
  1228  	}
  1229  	if usr.Uid != "0" {
  1230  		t.Skip("test must be run as root")
  1231  	}
  1232  
  1233  	dlvbin := getDlvBinEBPF(t)
  1234  
  1235  	expected := []byte(`> (1) main.tracedFunction(0, true, 97)
  1236  > (1) main.tracedFunction(1, false, 98)
  1237  > (1) main.tracedFunction(2, false, 99)
  1238  > (1) main.tracedFunction(3, false, 100)
  1239  > (1) main.tracedFunction(4, false, 101)
  1240  > (1) main.tracedFunction(5, true, 102)
  1241  > (1) main.tracedFunction(6, false, 103)
  1242  > (1) main.tracedFunction(7, false, 104)
  1243  > (1) main.tracedFunction(8, false, 105)
  1244  > (1) main.tracedFunction(9, false, 106)`)
  1245  
  1246  	fixtures := protest.FindFixturesDir()
  1247  	cmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "ebpf_trace3.go"), "main.traced")
  1248  	rdr, err := cmd.StderrPipe()
  1249  	assertNoError(err, t, "stderr pipe")
  1250  	defer rdr.Close()
  1251  
  1252  	assertNoError(cmd.Start(), t, "running trace")
  1253  
  1254  	output, err := ioutil.ReadAll(rdr)
  1255  	assertNoError(err, t, "ReadAll")
  1256  
  1257  	cmd.Wait()
  1258  	if !bytes.Contains(output, expected) {
  1259  		t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output))
  1260  	}
  1261  }
  1262  
  1263  func TestDlvTestChdir(t *testing.T) {
  1264  	dlvbin := getDlvBin(t)
  1265  
  1266  	fixtures := protest.FindFixturesDir()
  1267  
  1268  	dotest := func(testargs []string) {
  1269  		t.Helper()
  1270  
  1271  		args := []string{"--allow-non-terminal-interactive=true", "test"}
  1272  		args = append(args, testargs...)
  1273  		args = append(args, "--", "-test.v")
  1274  		t.Logf("dlv test %s", args)
  1275  		cmd := exec.Command(dlvbin, args...)
  1276  		cmd.Stdin = strings.NewReader("continue\nexit\n")
  1277  		out, err := cmd.CombinedOutput()
  1278  		if err != nil {
  1279  			t.Fatalf("error executing Delve: %v", err)
  1280  		}
  1281  		t.Logf("output: %q", out)
  1282  
  1283  		p, _ := filepath.Abs(filepath.Join(fixtures, "buildtest"))
  1284  		tgt := "current directory: " + p
  1285  		if !strings.Contains(string(out), tgt) {
  1286  			t.Errorf("output did not contain expected string %q", tgt)
  1287  		}
  1288  	}
  1289  
  1290  	dotest([]string{filepath.Join(fixtures, "buildtest")})
  1291  	files, _ := filepath.Glob(filepath.Join(fixtures, "buildtest", "*.go"))
  1292  	dotest(files)
  1293  }
  1294  
  1295  func TestVersion(t *testing.T) {
  1296  	dlvbin := getDlvBin(t)
  1297  
  1298  	got, err := exec.Command(dlvbin, "version", "-v").CombinedOutput()
  1299  	if err != nil {
  1300  		t.Fatalf("error executing `dlv version`: %v\n%s\n", err, got)
  1301  	}
  1302  	want1 := []byte("mod\tgithub.com/go-delve/delve")
  1303  	want2 := []byte("dep\tgithub.com/google/go-dap")
  1304  	if !bytes.Contains(got, want1) || !bytes.Contains(got, want2) {
  1305  		t.Errorf("got %s\nwant %v and %v in the output", got, want1, want2)
  1306  	}
  1307  }
  1308  
  1309  func TestStaticcheck(t *testing.T) {
  1310  	_, err := exec.LookPath("staticcheck")
  1311  	if err != nil {
  1312  		t.Skip("staticcheck not installed")
  1313  	}
  1314  	// default checks minus SA1019 which complains about deprecated identifiers, which change between versions of Go.
  1315  	args := []string{"-tests=false", "-checks=all,-SA1019,-ST1000,-ST1003,-ST1016,-S1021,-ST1023", "github.com/go-delve/delve/..."}
  1316  	// * SA1019 is disabled because new deprecations get added on every version
  1317  	//   of Go making the output of staticcheck inconsistent depending on the
  1318  	//   version of Go used to run it.
  1319  	// * ST1000,ST1003,ST1016 are disabled in the default
  1320  	//   staticcheck configuration
  1321  	// * S1021 "Merge variable declaration and assignment" is disabled because
  1322  	//   where we don't do this it is a deliberate style choice.
  1323  	// * ST1023 "Redundant type in variable declaration" same as S1021.
  1324  	cmd := exec.Command("staticcheck", args...)
  1325  	cmd.Dir = projectRoot()
  1326  	cmd.Env = append(os.Environ(), "GOOS=linux", "GOARCH=amd64")
  1327  	out, _ := cmd.CombinedOutput()
  1328  	checkAutogenDoc(t, "_scripts/staticcheck-out.txt", fmt.Sprintf("staticcheck %s > _scripts/staticcheck-out.txt", strings.Join(args, " ")), out)
  1329  }
  1330  
  1331  func TestDefaultBinary(t *testing.T) {
  1332  	// Check that when delve is run twice in the same directory simultaneously
  1333  	// it will pick different default output binary paths.
  1334  	dlvbin := getDlvBin(t)
  1335  	fixture := filepath.Join(protest.FindFixturesDir(), "testargs.go")
  1336  
  1337  	startOne := func() (io.WriteCloser, func() error, *bytes.Buffer) {
  1338  		cmd := exec.Command(dlvbin, "debug", "--allow-non-terminal-interactive=true", fixture, "--", "test")
  1339  		stdin, _ := cmd.StdinPipe()
  1340  		stdoutBuf := new(bytes.Buffer)
  1341  		cmd.Stdout = stdoutBuf
  1342  
  1343  		assertNoError(cmd.Start(), t, "dlv debug")
  1344  		return stdin, cmd.Wait, stdoutBuf
  1345  	}
  1346  
  1347  	stdin1, wait1, stdoutBuf1 := startOne()
  1348  	defer stdin1.Close()
  1349  
  1350  	stdin2, wait2, stdoutBuf2 := startOne()
  1351  	defer stdin2.Close()
  1352  
  1353  	fmt.Fprintf(stdin1, "continue\nquit\n")
  1354  	fmt.Fprintf(stdin2, "continue\nquit\n")
  1355  
  1356  	wait1()
  1357  	wait2()
  1358  
  1359  	out1, out2 := stdoutBuf1.String(), stdoutBuf2.String()
  1360  	t.Logf("%q", out1)
  1361  	t.Logf("%q", out2)
  1362  	if out1 == out2 {
  1363  		t.Errorf("outputs match")
  1364  	}
  1365  }