2018-04-18 07:36:40 +00:00
package main_test
2016-09-25 15:26:59 +00:00
import (
"bufio"
2017-07-26 18:51:44 +00:00
"bytes"
2017-02-10 14:11:40 +00:00
"flag"
2017-07-26 18:51:44 +00:00
"fmt"
2018-11-20 15:13:09 +00:00
"go/ast"
"go/token"
"go/types"
2020-02-28 17:48:59 +00:00
"io"
2021-10-13 18:43:47 +00:00
"net"
2016-09-25 15:26:59 +00:00
"os"
"os/exec"
2021-08-24 12:53:27 +00:00
"os/user"
2016-09-25 15:26:59 +00:00
"path/filepath"
"runtime"
2018-11-20 15:13:09 +00:00
"strconv"
2017-03-13 17:59:34 +00:00
"strings"
2016-09-25 15:26:59 +00:00
"testing"
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
"time"
2016-09-25 15:26:59 +00:00
2021-08-24 12:53:27 +00:00
"github.com/go-delve/delve/pkg/goversion"
2019-01-04 18:39:25 +00:00
protest "github.com/go-delve/delve/pkg/proc/test"
"github.com/go-delve/delve/pkg/terminal"
2021-10-25 19:39:28 +00:00
"github.com/go-delve/delve/service/dap"
2020-02-28 17:48:59 +00:00
"github.com/go-delve/delve/service/dap/daptest"
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/service/rpc2"
2021-10-25 19:39:28 +00:00
godap "github.com/google/go-dap"
2018-11-20 15:13:09 +00:00
"golang.org/x/tools/go/packages"
2016-09-25 15:26:59 +00:00
)
2017-02-10 14:11:40 +00:00
var testBackend string
func TestMain ( m * testing . M ) {
flag . StringVar ( & testBackend , "backend" , "" , "selects backend" )
flag . Parse ( )
if testBackend == "" {
testBackend = os . Getenv ( "PROCTEST" )
if testBackend == "" {
testBackend = "native"
2018-11-12 22:52:13 +00:00
if runtime . GOOS == "darwin" {
testBackend = "lldb"
}
2017-02-10 14:11:40 +00:00
}
}
2024-11-12 16:03:50 +00:00
protest . RunTestsWithFixtures ( m )
2017-02-10 14:11:40 +00:00
}
2016-09-25 15:26:59 +00:00
func assertNoError ( err error , t testing . TB , s string ) {
2021-10-25 19:39:28 +00:00
t . Helper ( )
2016-09-25 15:26:59 +00:00
if err != nil {
_ , file , line , _ := runtime . Caller ( 1 )
fname := filepath . Base ( file )
t . Fatalf ( "failed assertion at %s:%d: %s - %s\n" , fname , line , s , err )
}
}
func TestBuild ( t * testing . T ) {
2019-07-30 15:38:25 +00:00
const listenAddr = "127.0.0.1:40573"
2018-05-28 18:28:39 +00:00
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2016-09-25 15:26:59 +00:00
defer os . Remove ( dlvbin )
fixtures := protest . FindFixturesDir ( )
buildtestdir := filepath . Join ( fixtures , "buildtest" )
2024-10-14 18:04:03 +00:00
cmd := exec . Command ( dlvbin , "debug" , "--headless=true" , "--listen=" + listenAddr , "--api-version=2" , "--backend=" + testBackend , "--log" , "--log-output=debugger,rpc" )
2016-09-25 15:26:59 +00:00
cmd . Dir = buildtestdir
2018-06-14 18:12:11 +00:00
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
2020-10-19 21:44:04 +00:00
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "dlv debug" )
2016-09-25 15:26:59 +00:00
2018-06-14 18:12:11 +00:00
scan := bufio . NewScanner ( stderr )
2016-09-25 15:26:59 +00:00
// wait for the debugger to start
2022-08-15 16:40:25 +00:00
for scan . Scan ( ) {
text := scan . Text ( )
t . Log ( text )
if strings . Contains ( text , "API server pid = " ) {
break
}
}
2016-09-25 15:26:59 +00:00
go func ( ) {
for scan . Scan ( ) {
2018-11-12 22:52:13 +00:00
t . Log ( scan . Text ( ) )
2016-09-25 15:26:59 +00:00
// keep pipe empty
}
} ( )
client := rpc2 . NewClient ( listenAddr )
state := <- client . Continue ( )
if ! state . Exited {
t . Fatal ( "Program did not exit" )
}
2017-06-26 18:45:13 +00:00
client . Detach ( true )
cmd . Wait ( )
2016-09-25 15:26:59 +00:00
}
2017-07-26 18:51:44 +00:00
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
func testOutput ( t * testing . T , dlvbin , output string , delveCmds [ ] string ) ( stdout , stderr [ ] byte ) {
2017-07-26 18:51:44 +00:00
var stdoutBuf , stderrBuf bytes . Buffer
buildtestdir := filepath . Join ( protest . FindFixturesDir ( ) , "buildtest" )
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
2020-08-21 14:14:02 +00:00
c := [ ] string { dlvbin , "debug" , "--allow-non-terminal-interactive=true" }
2019-05-31 17:26:19 +00:00
debugbin := filepath . Join ( buildtestdir , "__debug_bin" )
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
if output != "" {
c = append ( c , "--output" , output )
if filepath . IsAbs ( output ) {
debugbin = output
} else {
debugbin = filepath . Join ( buildtestdir , output )
}
}
cmd := exec . Command ( c [ 0 ] , c [ 1 : ] ... )
2017-07-26 18:51:44 +00:00
cmd . Dir = buildtestdir
stdin , err := cmd . StdinPipe ( )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "stdin pipe" )
defer stdin . Close ( )
2017-07-26 18:51:44 +00:00
cmd . Stdout = & stdoutBuf
cmd . Stderr = & stderrBuf
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
2020-10-19 21:44:04 +00:00
assertNoError ( cmd . Start ( ) , t , "dlv debug with output" )
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
// Give delve some time to compile and write the binary.
foundIt := false
for wait := 0 ; wait < 30 ; wait ++ {
_ , err = os . Stat ( debugbin )
if err == nil {
foundIt = true
break
}
time . Sleep ( 1 * time . Second )
}
if ! foundIt {
t . Errorf ( "running %q: file not created: %v" , delveCmds , err )
}
for _ , c := range delveCmds {
2017-07-26 18:51:44 +00:00
fmt . Fprintf ( stdin , "%s\n" , c )
}
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
2017-07-26 18:51:44 +00:00
// ignore "dlv debug" command error, it returns
// errors even after successful debug session.
cmd . Wait ( )
stdout , stderr = stdoutBuf . Bytes ( ) , stderrBuf . Bytes ( )
_ , err = os . Stat ( debugbin )
if err == nil {
2021-02-21 19:17:14 +00:00
// Sometimes delve on Windows can't remove the built binary before
// exiting and gets an "Access is denied" error when trying.
2023-11-03 16:22:02 +00:00
// See: https://travis-ci.com/go-delve/delve/jobs/296325131.
2021-03-15 16:34:26 +00:00
// We have added a delay to gobuild.Remove, but to avoid any test
// flakiness, we guard against this failure here as well.
2023-11-03 16:22:02 +00:00
if runtime . GOOS != "windows" {
2021-03-15 16:34:26 +00:00
t . Errorf ( "running %q: file %v was not deleted\nstdout is %q, stderr is %q" , delveCmds , debugbin , stdout , stderr )
}
2017-07-26 18:51:44 +00:00
return
}
if ! os . IsNotExist ( err ) {
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
t . Errorf ( "running %q: %v\nstdout is %q, stderr is %q" , delveCmds , err , stdout , stderr )
2017-07-26 18:51:44 +00:00
return
}
return
}
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
// TestOutput verifies that the debug executable is created in the correct path
// and removed after exit.
func TestOutput ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2017-07-26 18:51:44 +00:00
2023-05-16 16:36:15 +00:00
for _ , output := range [ ] string { "__debug_bin" , "myownname" , filepath . Join ( t . TempDir ( ) , "absolute.path" ) } {
Support --output for debug, trace, and test commands (#1028)
* Support --output for debug, trace, and test commands
With the `--output` parameter you can configure the output binary. For
example:
dlv debug --output /tmp/xxx
Will build the binary to `/tmp/xxx`, instead of always putting it as
`debug` in the current directory.
This ensures that the command always works (even if there is already a
file or directory named `debug`) and doesn't write to the source
directory. Especially for things like Delve/Vim integration this is a
good thing to have, I think.
* Address PR feedback and add a test
- We don't need to use `filepath.IsAbs()` on startup; I added that
because it previously did `"./" + debugname` everywhere, but I don't
think that's needed at all, since `pathname` without a leading `./`
implies the current directory.
- Repurpose the existing `TestIssue398` to also test the `--output`
flag. Also fix an issue where tests wouldn't work if `GOPATH` has
multiple entries (e..g `GOPATH=$HOME/go:$HOME/mygocode`).
- Print an error if we can't remove the debug binary on exit instead of
failing silently. Not strictly related to this PR, but a good change
to add I think.
* Also warn when delve can't remove the binary in test/trace
I only added that to debug, but good to issue this warning consistently.
2017-11-28 18:51:30 +00:00
testOutput ( t , dlvbin , output , [ ] string { "exit" } )
const hello = "hello world!"
stdout , _ := testOutput ( t , dlvbin , output , [ ] string { "continue" , "exit" } )
if ! strings . Contains ( string ( stdout ) , hello ) {
t . Errorf ( "stdout %q should contain %q" , stdout , hello )
}
2017-07-26 18:51:44 +00:00
}
}
2018-04-18 07:36:40 +00:00
2024-06-12 19:31:46 +00:00
// TestUnattendedBreakpoint tests whether dlv will print a message to stderr when the client that sends continue is disconnected
// or not.
func TestUnattendedBreakpoint ( t * testing . T ) {
const listenAddr = "127.0.0.1:40573"
fixturePath := filepath . Join ( protest . FindFixturesDir ( ) , "panic.go" )
2024-10-31 17:19:08 +00:00
cmd := exec . Command ( protest . GetDlvBinary ( t ) , "debug" , "--continue" , "--headless" , "--accept-multiclient" , "--listen" , listenAddr , fixturePath )
2024-06-12 19:31:46 +00:00
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stdout pipe" )
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "start headless instance" )
scan := bufio . NewScanner ( stderr )
for scan . Scan ( ) {
t . Log ( scan . Text ( ) )
if strings . Contains ( scan . Text ( ) , "execution is paused because your program is panicking" ) {
break
}
}
// and detach from and kill the headless instance
client := rpc2 . NewClient ( listenAddr )
if err := client . Detach ( true ) ; err != nil {
t . Fatalf ( "error detaching from headless instance: %v" , err )
}
cmd . Wait ( )
}
2019-07-19 17:59:38 +00:00
// TestContinue verifies that the debugged executable starts immediately with --continue
func TestContinue ( t * testing . T ) {
2019-07-30 15:38:25 +00:00
const listenAddr = "127.0.0.1:40573"
2019-07-19 17:59:38 +00:00
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2019-07-19 17:59:38 +00:00
buildtestdir := filepath . Join ( protest . FindFixturesDir ( ) , "buildtest" )
cmd := exec . Command ( dlvbin , "debug" , "--headless" , "--continue" , "--accept-multiclient" , "--listen" , listenAddr )
cmd . Dir = buildtestdir
stdout , err := cmd . StdoutPipe ( )
2021-09-01 17:31:37 +00:00
assertNoError ( err , t , "stdout pipe" )
2020-10-19 21:44:04 +00:00
defer stdout . Close ( )
assertNoError ( cmd . Start ( ) , t , "start headless instance" )
2019-07-19 17:59:38 +00:00
scan := bufio . NewScanner ( stdout )
// wait for the debugger to start
for scan . Scan ( ) {
t . Log ( scan . Text ( ) )
if scan . Text ( ) == "hello world!" {
break
2019-08-01 23:28:38 +00:00
}
2019-07-19 17:59:38 +00:00
}
// and detach from and kill the headless instance
client := rpc2 . NewClient ( listenAddr )
if err := client . Detach ( true ) ; err != nil {
t . Fatalf ( "error detaching from headless instance: %v" , err )
}
cmd . Wait ( )
}
2020-11-09 08:31:12 +00:00
// TestRedirect verifies that redirecting stdin works
func TestRedirect ( t * testing . T ) {
const listenAddr = "127.0.0.1:40573"
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-11-09 08:31:12 +00:00
catfixture := filepath . Join ( protest . FindFixturesDir ( ) , "cat.go" )
cmd := exec . Command ( dlvbin , "debug" , "--headless" , "--continue" , "--accept-multiclient" , "--listen" , listenAddr , "-r" , catfixture , catfixture )
stdout , err := cmd . StdoutPipe ( )
2021-09-01 17:31:37 +00:00
assertNoError ( err , t , "stdout pipe" )
2020-11-09 08:31:12 +00:00
defer stdout . Close ( )
assertNoError ( cmd . Start ( ) , t , "start headless instance" )
scan := bufio . NewScanner ( stdout )
// wait for the debugger to start
for scan . Scan ( ) {
t . Log ( scan . Text ( ) )
if scan . Text ( ) == "read \"}\"" {
break
}
}
// and detach from and kill the headless instance
client := rpc2 . NewClient ( listenAddr )
2023-11-03 16:22:02 +00:00
client . Detach ( true )
2020-11-09 08:31:12 +00:00
cmd . Wait ( )
}
2021-07-01 18:25:33 +00:00
const checkAutogenDocLongOutput = false
2018-04-18 07:36:40 +00:00
func checkAutogenDoc ( t * testing . T , filename , gencommand string , generated [ ] byte ) {
2024-10-31 17:19:08 +00:00
saved := slurpFile ( t , filepath . Join ( protest . ProjectRoot ( ) , filename ) )
2018-04-18 07:36:40 +00:00
2021-01-20 17:40:03 +00:00
saved = bytes . ReplaceAll ( saved , [ ] byte ( "\r\n" ) , [ ] byte { '\n' } )
generated = bytes . ReplaceAll ( generated , [ ] byte ( "\r\n" ) , [ ] byte { '\n' } )
2018-04-18 07:36:40 +00:00
if len ( saved ) != len ( generated ) {
2021-07-01 18:25:33 +00:00
if checkAutogenDocLongOutput {
t . Logf ( "generated %q saved %q\n" , generated , saved )
}
2021-09-24 22:31:54 +00:00
diffMaybe ( t , filename , generated )
2018-04-19 08:43:23 +00:00
t . Fatalf ( "%s: needs to be regenerated; run %s" , filename , gencommand )
2018-04-18 07:36:40 +00:00
}
for i := range saved {
if saved [ i ] != generated [ i ] {
2021-07-01 18:25:33 +00:00
if checkAutogenDocLongOutput {
t . Logf ( "generated %q saved %q\n" , generated , saved )
}
2021-09-24 22:31:54 +00:00
diffMaybe ( t , filename , generated )
2018-04-18 07:36:40 +00:00
t . Fatalf ( "%s: needs to be regenerated; run %s" , filename , gencommand )
}
}
}
func slurpFile ( t * testing . T , filename string ) [ ] byte {
2023-09-25 18:41:59 +00:00
saved , err := os . ReadFile ( filename )
2018-04-18 07:36:40 +00:00
if err != nil {
t . Fatalf ( "Could not read %s: %v" , filename , err )
}
return saved
}
2021-09-24 22:31:54 +00:00
func diffMaybe ( t * testing . T , filename string , generated [ ] byte ) {
_ , err := exec . LookPath ( "diff" )
if err != nil {
return
}
cmd := exec . Command ( "diff" , filename , "-" )
2024-10-31 17:19:08 +00:00
cmd . Dir = protest . ProjectRoot ( )
2021-09-24 22:31:54 +00:00
stdin , _ := cmd . StdinPipe ( )
go func ( ) {
stdin . Write ( generated )
stdin . Close ( )
} ( )
out , _ := cmd . CombinedOutput ( )
t . Logf ( "diff:\n%s" , string ( out ) )
}
2018-04-18 07:36:40 +00:00
// TestGeneratedDoc tests that the autogenerated documentation has been
// updated.
func TestGeneratedDoc ( t * testing . T ) {
2022-12-05 23:21:52 +00:00
if runtime . GOOS == "windows" && runtime . GOARCH == "arm64" {
//TODO(qmuntal): investigate further when the Windows ARM64 backend is more stable.
t . Skip ( "skipping test on Windows in CI" )
}
2023-07-07 16:30:38 +00:00
if runtime . GOOS == "linux" && runtime . GOARCH == "ppc64le" {
//TODO(alexsaezm): finish CI integration
t . Skip ( "skipping test on Linux/PPC64LE in CI" )
}
2024-10-01 18:00:52 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 24 ) {
t . Skip ( "disabled due to export format changes" )
}
2018-04-18 07:36:40 +00:00
// Checks gen-cli-docs.go
var generatedBuf bytes . Buffer
commands := terminal . DebugCommands ( nil )
commands . WriteMarkdown ( & generatedBuf )
2020-03-28 18:18:55 +00:00
checkAutogenDoc ( t , "Documentation/cli/README.md" , "_scripts/gen-cli-docs.go" , generatedBuf . Bytes ( ) )
2018-04-18 07:36:40 +00:00
// Checks gen-usage-docs.go
2023-01-30 23:04:55 +00:00
tempDir := t . TempDir ( )
2020-03-28 18:18:55 +00:00
cmd := exec . Command ( "go" , "run" , "_scripts/gen-usage-docs.go" , tempDir )
2024-10-31 17:19:08 +00:00
cmd . Dir = protest . ProjectRoot ( )
2023-01-30 23:04:55 +00:00
err := cmd . Run ( )
2020-05-04 17:00:33 +00:00
assertNoError ( err , t , "go run _scripts/gen-usage-docs.go" )
2023-09-25 18:41:59 +00:00
entries , err := os . ReadDir ( tempDir )
2018-04-18 07:36:40 +00:00
assertNoError ( err , t , "ReadDir" )
for _ , doc := range entries {
docFilename := "Documentation/usage/" + doc . Name ( )
2020-03-28 18:18:55 +00:00
checkAutogenDoc ( t , docFilename , "_scripts/gen-usage-docs.go" , slurpFile ( t , tempDir + "/" + doc . Name ( ) ) )
2018-04-18 07:36:40 +00:00
}
2019-07-02 17:55:27 +00:00
runScript := func ( args ... string ) [ ] byte {
a := [ ] string { "run" }
a = append ( a , args ... )
cmd := exec . Command ( "go" , a ... )
2024-10-31 17:19:08 +00:00
cmd . Dir = protest . ProjectRoot ( )
2019-07-02 17:55:27 +00:00
out , err := cmd . CombinedOutput ( )
if err != nil {
t . Fatalf ( "could not run script %v: %v (output: %q)" , args , err , string ( out ) )
}
return out
}
2022-12-05 23:21:52 +00:00
checkAutogenDoc ( t , "Documentation/backend_test_health.md" , "go run _scripts/gen-backend_test_health.go" , runScript ( "_scripts/gen-backend_test_health.go" , "-" ) )
2020-03-28 18:18:55 +00:00
checkAutogenDoc ( t , "pkg/terminal/starbind/starlark_mapping.go" , "'go generate' inside pkg/terminal/starbind" , runScript ( "_scripts/gen-starlark-bindings.go" , "go" , "-" ) )
checkAutogenDoc ( t , "Documentation/cli/starlark.md" , "'go generate' inside pkg/terminal/starbind" , runScript ( "_scripts/gen-starlark-bindings.go" , "doc/dummy" , "Documentation/cli/starlark.md" ) )
2023-01-03 17:23:26 +00:00
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" , "-" ) )
2022-09-26 17:10:51 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 18 ) {
checkAutogenDoc ( t , "_scripts/rtype-out.txt" , "go run _scripts/rtype.go report _scripts/rtype-out.txt" , runScript ( "_scripts/rtype.go" , "report" ) )
runScript ( "_scripts/rtype.go" , "check" )
}
2018-04-18 07:36:40 +00:00
}
2018-11-30 08:53:29 +00:00
func TestExitInInit ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2018-11-30 08:53:29 +00:00
buildtestdir := filepath . Join ( protest . FindFixturesDir ( ) , "buildtest" )
exitInit := filepath . Join ( protest . FindFixturesDir ( ) , "exit.init" )
cmd := exec . Command ( dlvbin , "--init" , exitInit , "debug" )
cmd . Dir = buildtestdir
out , err := cmd . CombinedOutput ( )
t . Logf ( "%q %v\n" , string ( out ) , err )
2023-11-03 16:22:02 +00:00
// dlv will exit anyway because stdin is not a tty, but it will print the
2018-11-30 08:53:29 +00:00
// prompt once if the init file didn't call exit successfully.
if strings . Contains ( string ( out ) , "(dlv)" ) {
t . Fatal ( "init did not cause dlv to exit" )
}
}
2018-11-20 15:13:09 +00:00
func getMethods ( pkg * types . Package , typename string ) map [ string ] * types . Func {
r := make ( map [ string ] * types . Func )
mset := types . NewMethodSet ( types . NewPointer ( pkg . Scope ( ) . Lookup ( typename ) . Type ( ) ) )
for i := 0 ; i < mset . Len ( ) ; i ++ {
fn := mset . At ( i ) . Obj ( ) . ( * types . Func )
r [ fn . Name ( ) ] = fn
}
return r
}
func publicMethodOf ( decl ast . Decl , receiver string ) * ast . FuncDecl {
fndecl , isfunc := decl . ( * ast . FuncDecl )
if ! isfunc {
return nil
}
if fndecl . Name . Name [ 0 ] >= 'a' && fndecl . Name . Name [ 0 ] <= 'z' {
return nil
}
if fndecl . Recv == nil || len ( fndecl . Recv . List ) != 1 {
return nil
}
starexpr , isstar := fndecl . Recv . List [ 0 ] . Type . ( * ast . StarExpr )
if ! isstar {
return nil
}
identexpr , isident := starexpr . X . ( * ast . Ident )
if ! isident || identexpr . Name != receiver {
return nil
}
if fndecl . Body == nil {
return nil
}
return fndecl
}
func findCallCall ( fndecl * ast . FuncDecl ) * ast . CallExpr {
for _ , stmt := range fndecl . Body . List {
var x ast . Expr = nil
switch s := stmt . ( type ) {
case * ast . AssignStmt :
if len ( s . Rhs ) == 1 {
x = s . Rhs [ 0 ]
}
case * ast . ReturnStmt :
if len ( s . Results ) == 1 {
x = s . Results [ 0 ]
}
case * ast . ExprStmt :
x = s . X
}
callx , iscall := x . ( * ast . CallExpr )
if ! iscall {
continue
}
fun , issel := callx . Fun . ( * ast . SelectorExpr )
if ! issel || fun . Sel . Name != "call" {
continue
}
return callx
}
return nil
}
func qf ( * types . Package ) string {
return ""
}
func TestTypecheckRPC ( t * testing . T ) {
2024-12-05 03:14:47 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 24 ) {
t . Skip ( "disabled due to export format changes" )
}
2018-11-20 15:13:09 +00:00
fset := & token . FileSet { }
cfg := & packages . Config {
2020-03-19 17:10:08 +00:00
Mode : packages . NeedSyntax | packages . NeedTypesInfo | packages . NeedName | packages . NeedCompiledGoFiles | packages . NeedTypes ,
2018-11-20 15:13:09 +00:00
Fset : fset ,
}
pkgs , err := packages . Load ( cfg , "github.com/go-delve/delve/service/rpc2" )
if err != nil {
t . Fatal ( err )
}
var clientAst * ast . File
var serverMethods map [ string ] * types . Func
var info * types . Info
packages . Visit ( pkgs , func ( pkg * packages . Package ) bool {
if pkg . PkgPath != "github.com/go-delve/delve/service/rpc2" {
return true
}
t . Logf ( "package found: %v" , pkg . PkgPath )
serverMethods = getMethods ( pkg . Types , "RPCServer" )
info = pkg . TypesInfo
for i := range pkg . Syntax {
t . Logf ( "file %q" , pkg . CompiledGoFiles [ i ] )
if strings . HasSuffix ( pkg . CompiledGoFiles [ i ] , string ( os . PathSeparator ) + "client.go" ) {
clientAst = pkg . Syntax [ i ]
break
}
}
return true
} , nil )
errcount := 0
for _ , decl := range clientAst . Decls {
fndecl := publicMethodOf ( decl , "RPCClient" )
if fndecl == nil {
continue
}
switch fndecl . Name . Name {
case "Continue" , "Rewind" :
// wrappers over continueDir
continue
case "SetReturnValuesLoadConfig" , "Disconnect" :
// support functions
continue
}
proc,terminal: Implement reverse step, next and stepout (#1785)
* proc: move defer breakpoint code into a function
Moves the code that sets a breakpoint on the first deferred function,
used by both next and StepOut, to its function.
* proc: implement reverse step/next/stepout
When the direction of execution is reversed (on a recording) Step, Next and
StepOut will behave similarly to their forward version. However there are
some subtle interactions between their behavior, prologue skipping, deferred
calls and normal calls. Specifically:
- when stepping backwards we need to set a breakpoint on the first
instruction after each CALL instruction, once this breakpoint is reached we
need to execute a single StepInstruction operation to reverse step into the
CALL.
- to insure that the prologue is skipped reverse next needs to check if it
is on the first instruction after the prologue, and if it is behave like
reverse stepout.
- there is no reason to set breakpoints on deferred calls when reverse
nexting or reverse stepping out, they will never be hit.
- reverse step out should generally place its breakpoint on the CALL
instruction that created the current stack frame (which will be the CALL
instruction immediately preceding the instruction at the return address).
- reverse step out needs to treat panic calls and deferreturn calls
specially.
* service,terminal: implement reverse step, next, stepout
2020-03-11 22:40:41 +00:00
if fndecl . Name . Name == "Continue" || fndecl . Name . Name == "Rewind" || fndecl . Name . Name == "DirectionCongruentContinue" {
2018-11-20 15:13:09 +00:00
// using continueDir
continue
}
callx := findCallCall ( fndecl )
if callx == nil {
t . Errorf ( "%s: could not find RPC call" , fset . Position ( fndecl . Pos ( ) ) )
errcount ++
continue
}
if len ( callx . Args ) != 3 {
t . Errorf ( "%s: wrong number of arguments for RPC call" , fset . Position ( callx . Pos ( ) ) )
errcount ++
continue
}
arg0 , arg0islit := callx . Args [ 0 ] . ( * ast . BasicLit )
arg1 := callx . Args [ 1 ]
arg2 := callx . Args [ 2 ]
if ! arg0islit || arg0 . Kind != token . STRING {
continue
}
name , _ := strconv . Unquote ( arg0 . Value )
serverMethod := serverMethods [ name ]
if serverMethod == nil {
t . Errorf ( "%s: could not find RPC method %q" , fset . Position ( callx . Pos ( ) ) , name )
errcount ++
continue
}
params := serverMethod . Type ( ) . ( * types . Signature ) . Params ( )
if a , e := info . TypeOf ( arg1 ) , params . At ( 0 ) . Type ( ) ; ! types . AssignableTo ( a , e ) {
t . Errorf ( "%s: wrong type of first argument %s, expected %s" , fset . Position ( callx . Pos ( ) ) , types . TypeString ( a , qf ) , types . TypeString ( e , qf ) )
errcount ++
continue
}
if ! strings . HasSuffix ( params . At ( 1 ) . Type ( ) . String ( ) , "/service.RPCCallback" ) {
if a , e := info . TypeOf ( arg2 ) , params . At ( 1 ) . Type ( ) ; ! types . AssignableTo ( a , e ) {
t . Errorf ( "%s: wrong type of second argument %s, expected %s" , fset . Position ( callx . Pos ( ) ) , types . TypeString ( a , qf ) , types . TypeString ( e , qf ) )
errcount ++
continue
}
}
if clit , ok := arg1 . ( * ast . CompositeLit ) ; ok {
typ := params . At ( 0 ) . Type ( )
st := typ . Underlying ( ) . ( * types . Struct )
if len ( clit . Elts ) != st . NumFields ( ) && types . TypeString ( typ , qf ) != "DebuggerCommand" {
t . Errorf ( "%s: wrong number of fields in first argument's literal %d, expected %d" , fset . Position ( callx . Pos ( ) ) , len ( clit . Elts ) , st . NumFields ( ) )
errcount ++
continue
}
}
}
if errcount > 0 {
t . Errorf ( "%d errors" , errcount )
}
}
2020-02-28 17:48:59 +00:00
2021-10-25 19:39:28 +00:00
// TestDAPCmd verifies that a dap server can be started and shut down.
func TestDAPCmd ( t * testing . T ) {
2020-02-28 17:48:59 +00:00
const listenAddr = "127.0.0.1:40575"
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-02-28 17:48:59 +00:00
cmd := exec . Command ( dlvbin , "dap" , "--log-output=dap" , "--log" , "--listen" , listenAddr )
stdout , err := cmd . StdoutPipe ( )
assertNoError ( err , t , "stdout pipe" )
2020-10-19 21:44:04 +00:00
defer stdout . Close ( )
2020-02-28 17:48:59 +00:00
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
2020-10-19 21:44:04 +00:00
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "start dap instance" )
2020-02-28 17:48:59 +00:00
scanOut := bufio . NewScanner ( stdout )
scanErr := bufio . NewScanner ( stderr )
// Wait for the debug server to start
scanOut . Scan ( )
listening := "DAP server listening at: " + listenAddr
if scanOut . Text ( ) != listening {
cmd . Process . Kill ( ) // release the port
t . Fatalf ( "Unexpected stdout:\ngot %q\nwant %q" , scanOut . Text ( ) , listening )
}
go func ( ) {
for scanErr . Scan ( ) {
t . Log ( scanErr . Text ( ) )
}
} ( )
// Connect a client and request shutdown.
client := daptest . NewClient ( listenAddr )
client . DisconnectRequest ( )
client . ExpectDisconnectResponse ( t )
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2022-12-05 17:02:22 +00:00
_ , err = client . ReadMessage ( )
if runtime . GOOS == "windows" {
if err == nil {
t . Errorf ( "got %q, want non-nil\n" , err )
}
} else {
if err != io . EOF {
t . Errorf ( "got %q, want \"EOF\"\n" , err )
}
2020-02-28 17:48:59 +00:00
}
client . Close ( )
cmd . Wait ( )
}
2020-05-13 06:38:10 +00:00
2022-05-18 10:23:47 +00:00
func newDAPRemoteClient ( t * testing . T , addr string , isDlvAttach bool , isMulti bool ) * daptest . Client {
2021-10-25 19:39:28 +00:00
c := daptest . NewClient ( addr )
c . AttachRequest ( map [ string ] interface { } { "mode" : "remote" , "stopOnEntry" : true } )
2022-05-18 10:23:47 +00:00
if isDlvAttach || isMulti {
c . ExpectCapabilitiesEventSupportTerminateDebuggee ( t )
}
2021-10-25 19:39:28 +00:00
c . ExpectInitializedEvent ( t )
c . ExpectAttachResponse ( t )
c . ConfigurationDoneRequest ( )
c . ExpectStoppedEvent ( t )
c . ExpectConfigurationDoneResponse ( t )
return c
}
func TestRemoteDAPClient ( t * testing . T ) {
const listenAddr = "127.0.0.1:40576"
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2021-10-25 19:39:28 +00:00
buildtestdir := filepath . Join ( protest . FindFixturesDir ( ) , "buildtest" )
cmd := exec . Command ( dlvbin , "debug" , "--headless" , "--log-output=dap" , "--log" , "--listen" , listenAddr )
cmd . Dir = buildtestdir
stdout , err := cmd . StdoutPipe ( )
assertNoError ( err , t , "stdout pipe" )
defer stdout . Close ( )
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "start headless instance" )
scanOut := bufio . NewScanner ( stdout )
scanErr := bufio . NewScanner ( stderr )
// Wait for the debug server to start
scanOut . Scan ( )
t . Log ( scanOut . Text ( ) )
go func ( ) { // Capture logging
for scanErr . Scan ( ) {
t . Log ( scanErr . Text ( ) )
}
} ( )
2022-05-18 10:23:47 +00:00
client := newDAPRemoteClient ( t , listenAddr , false , false )
2021-10-25 19:39:28 +00:00
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectTerminatedEvent ( t )
client . DisconnectRequest ( )
client . ExpectOutputEventProcessExited ( t , 0 )
client . ExpectOutputEventDetaching ( t )
client . ExpectDisconnectResponse ( t )
client . ExpectTerminatedEvent ( t )
if _ , err := client . ReadMessage ( ) ; err == nil {
t . Error ( "expected read error upon shutdown" )
}
client . Close ( )
cmd . Wait ( )
}
2021-12-07 17:23:26 +00:00
func closeDAPRemoteMultiClient ( t * testing . T , c * daptest . Client , expectStatus string ) {
2021-10-25 19:39:28 +00:00
c . DisconnectRequest ( )
2021-12-07 17:23:26 +00:00
c . ExpectOutputEventClosingClient ( t , expectStatus )
2021-10-25 19:39:28 +00:00
c . ExpectDisconnectResponse ( t )
c . ExpectTerminatedEvent ( t )
c . Close ( )
time . Sleep ( 10 * time . Millisecond )
}
func TestRemoteDAPClientMulti ( t * testing . T ) {
const listenAddr = "127.0.0.1:40577"
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2021-10-25 19:39:28 +00:00
buildtestdir := filepath . Join ( protest . FindFixturesDir ( ) , "buildtest" )
cmd := exec . Command ( dlvbin , "debug" , "--headless" , "--accept-multiclient" , "--log-output=debugger" , "--log" , "--listen" , listenAddr )
cmd . Dir = buildtestdir
stdout , err := cmd . StdoutPipe ( )
assertNoError ( err , t , "stdout pipe" )
defer stdout . Close ( )
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "start headless instance" )
scanOut := bufio . NewScanner ( stdout )
scanErr := bufio . NewScanner ( stderr )
// Wait for the debug server to start
scanOut . Scan ( )
t . Log ( scanOut . Text ( ) )
go func ( ) { // Capture logging
for scanErr . Scan ( ) {
t . Log ( scanErr . Text ( ) )
}
} ( )
2021-12-07 17:23:26 +00:00
// Client 0 connects but with the wrong attach request
dapclient0 := daptest . NewClient ( listenAddr )
dapclient0 . AttachRequest ( map [ string ] interface { } { "mode" : "local" } )
dapclient0 . ExpectErrorResponse ( t )
2021-10-25 19:39:28 +00:00
// Client 1 connects and continues to main.main
2022-05-18 10:23:47 +00:00
dapclient := newDAPRemoteClient ( t , listenAddr , false , true )
2021-10-25 19:39:28 +00:00
dapclient . SetFunctionBreakpointsRequest ( [ ] godap . FunctionBreakpoint { { Name : "main.main" } } )
dapclient . ExpectSetFunctionBreakpointsResponse ( t )
dapclient . ContinueRequest ( 1 )
dapclient . ExpectContinueResponse ( t )
dapclient . ExpectStoppedEvent ( t )
dapclient . CheckStopLocation ( t , 1 , "main.main" , 5 )
2021-12-07 17:23:26 +00:00
closeDAPRemoteMultiClient ( t , dapclient , "halted" )
2021-10-25 19:39:28 +00:00
// Client 2 reconnects at main.main and continues to process exit
2022-05-18 10:23:47 +00:00
dapclient2 := newDAPRemoteClient ( t , listenAddr , false , true )
2021-10-25 19:39:28 +00:00
dapclient2 . CheckStopLocation ( t , 1 , "main.main" , 5 )
dapclient2 . ContinueRequest ( 1 )
dapclient2 . ExpectContinueResponse ( t )
dapclient2 . ExpectTerminatedEvent ( t )
2021-12-07 17:23:26 +00:00
closeDAPRemoteMultiClient ( t , dapclient2 , "exited" )
2021-10-25 19:39:28 +00:00
2022-03-23 15:10:00 +00:00
// Attach to exited processes is an error
2021-10-25 19:39:28 +00:00
dapclient3 := daptest . NewClient ( listenAddr )
dapclient3 . AttachRequest ( map [ string ] interface { } { "mode" : "remote" , "stopOnEntry" : true } )
dapclient3 . ExpectErrorResponseWith ( t , dap . FailedToAttach , ` Process \d+ has exited with status 0 ` , true )
2021-12-07 17:23:26 +00:00
closeDAPRemoteMultiClient ( t , dapclient3 , "exited" )
2021-10-25 19:39:28 +00:00
// But rpc clients can still connect and restart
rpcclient := rpc2 . NewClient ( listenAddr )
if _ , err := rpcclient . Restart ( false ) ; err != nil {
t . Errorf ( "error restarting with rpc client: %v" , err )
}
if err := rpcclient . Detach ( true ) ; err != nil {
t . Fatalf ( "error detaching from headless instance: %v" , err )
}
cmd . Wait ( )
}
func TestRemoteDAPClientAfterContinue ( t * testing . T ) {
const listenAddr = "127.0.0.1:40578"
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2021-10-25 19:39:28 +00:00
fixture := protest . BuildFixture ( "loopprog" , 0 )
cmd := exec . Command ( dlvbin , "exec" , fixture . Path , "--headless" , "--continue" , "--accept-multiclient" , "--log-output=debugger,dap" , "--log" , "--listen" , listenAddr )
stdout , err := cmd . StdoutPipe ( )
assertNoError ( err , t , "stdout pipe" )
defer stdout . Close ( )
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "start headless instance" )
scanOut := bufio . NewScanner ( stdout )
scanErr := bufio . NewScanner ( stderr )
// Wait for the debug server to start
scanOut . Scan ( ) // "API server listening...""
t . Log ( scanOut . Text ( ) )
// Wait for the program to start
scanOut . Scan ( ) // "past main"
t . Log ( scanOut . Text ( ) )
go func ( ) { // Capture logging
for scanErr . Scan ( ) {
2021-11-09 08:24:26 +00:00
text := scanErr . Text ( )
if strings . Contains ( text , "Internal Error" ) {
t . Error ( "ERROR" , text )
} else {
t . Log ( text )
}
2021-10-25 19:39:28 +00:00
}
} ( )
2022-05-18 10:23:47 +00:00
c := newDAPRemoteClient ( t , listenAddr , false , true )
2021-11-09 08:24:26 +00:00
c . ContinueRequest ( 1 )
c . ExpectContinueResponse ( t )
c . DisconnectRequest ( )
2021-12-08 10:37:35 +00:00
c . ExpectOutputEventClosingClient ( t , "running" )
2021-11-09 08:24:26 +00:00
c . ExpectDisconnectResponse ( t )
c . ExpectTerminatedEvent ( t )
c . Close ( )
2022-05-18 10:23:47 +00:00
c = newDAPRemoteClient ( t , listenAddr , false , true )
2021-10-25 19:39:28 +00:00
c . DisconnectRequestWithKillOption ( true )
c . ExpectOutputEventDetachingKill ( t )
c . ExpectDisconnectResponse ( t )
c . ExpectTerminatedEvent ( t )
if _ , err := c . ReadMessage ( ) ; err == nil {
t . Error ( "expected read error upon shutdown" )
}
c . Close ( )
cmd . Wait ( )
}
// TestDAPCmdWithClient tests dlv dap --client-addr can be started and shut down.
func TestDAPCmdWithClient ( t * testing . T ) {
2021-10-13 18:43:47 +00:00
listener , err := net . Listen ( "tcp" , ":0" )
if err != nil {
t . Fatalf ( "cannot setup listener required for testing: %v" , err )
}
defer listener . Close ( )
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2021-10-13 18:43:47 +00:00
cmd := exec . Command ( dlvbin , "dap" , "--log-output=dap" , "--log" , "--client-addr" , listener . Addr ( ) . String ( ) )
buf := & bytes . Buffer { }
cmd . Stdin = buf
cmd . Stdout = buf
assertNoError ( cmd . Start ( ) , t , "start dlv dap process with --client-addr flag" )
// Wait for the connection.
conn , err := listener . Accept ( )
if err != nil {
cmd . Process . Kill ( ) // release the port
t . Fatalf ( "Failed to get connection: %v" , err )
}
t . Log ( "dlv dap process dialed in successfully" )
2024-09-30 18:20:48 +00:00
client := daptest . NewClientFromConn ( conn )
client . InitializeRequest ( )
client . ExpectInitializeResponse ( t )
// Close the connection.
if err := conn . Close ( ) ; err != nil {
cmd . Process . Kill ( )
t . Fatalf ( "Failed to get connection: %v" , err )
}
// Connection close should trigger dlv-reverse command's normal exit.
if err := cmd . Wait ( ) ; err != nil {
cmd . Process . Kill ( )
t . Fatalf ( "command failed: %v\n%s\n%v" , err , buf . Bytes ( ) , cmd . Process . Pid )
}
}
// TestDAPCmdWithUnixClient tests dlv dap --client-addr can be started with unix domain socket and shut down.
func TestDAPCmdWithUnixClient ( t * testing . T ) {
tmpdir := os . TempDir ( )
if tmpdir == "" {
return
}
listenPath := filepath . Join ( tmpdir , "dap_test" )
listener , err := net . Listen ( "unix" , listenPath )
if err != nil {
t . Fatalf ( "cannot setup listener required for testing: %v" , err )
}
defer listener . Close ( )
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2024-09-30 18:20:48 +00:00
cmd := exec . Command ( dlvbin , "dap" , "--log-output=dap" , "--log" , "--client-addr=unix:" + listener . Addr ( ) . String ( ) )
buf := & bytes . Buffer { }
cmd . Stdin = buf
cmd . Stdout = buf
assertNoError ( cmd . Start ( ) , t , "start dlv dap process with --client-addr flag" )
// Wait for the connection.
conn , err := listener . Accept ( )
if err != nil {
cmd . Process . Kill ( ) // release the socket file
t . Fatalf ( "Failed to get connection: %v" , err )
}
t . Log ( "dlv dap process dialed in successfully" )
2021-10-13 18:43:47 +00:00
client := daptest . NewClientFromConn ( conn )
client . InitializeRequest ( )
client . ExpectInitializeResponse ( t )
// Close the connection.
if err := conn . Close ( ) ; err != nil {
cmd . Process . Kill ( )
t . Fatalf ( "Failed to get connection: %v" , err )
}
// Connection close should trigger dlv-reverse command's normal exit.
if err := cmd . Wait ( ) ; err != nil {
cmd . Process . Kill ( )
t . Fatalf ( "command failed: %v\n%s\n%v" , err , buf . Bytes ( ) , cmd . Process . Pid )
}
}
2020-05-13 06:38:10 +00:00
func TestTrace ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-05-13 06:38:10 +00:00
2024-04-23 21:22:17 +00:00
expected := [ ] byte ( "> goroutine(1): main.foo(99, 9801)\n>> goroutine(1): main.foo => (9900)\n" )
2020-05-13 06:38:10 +00:00
fixtures := protest . FindFixturesDir ( )
2023-01-30 23:04:55 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "issue573.go" ) , "foo" )
2020-05-13 06:38:10 +00:00
rdr , err := cmd . StderrPipe ( )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
2020-05-13 06:38:10 +00:00
cmd . Dir = filepath . Join ( fixtures , "buildtest" )
2020-10-19 21:44:04 +00:00
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "ReadAll" )
2020-05-13 06:38:10 +00:00
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
cmd . Wait ( )
}
2023-01-24 14:56:05 +00:00
func TestTrace2 ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2023-01-24 14:56:05 +00:00
2024-04-23 21:22:17 +00:00
expected := [ ] byte ( "> goroutine(1): main.callme(2)\n>> goroutine(1): main.callme => (4)\n" )
2023-01-24 14:56:05 +00:00
fixtures := protest . FindFixturesDir ( )
2023-01-30 23:04:55 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "traceprog.go" ) , "callme" )
2023-01-24 14:56:05 +00:00
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
cmd . Dir = filepath . Join ( fixtures , "buildtest" )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2023-01-24 14:56:05 +00:00
assertNoError ( err , t , "ReadAll" )
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
2023-02-02 10:11:31 +00:00
assertNoError ( cmd . Wait ( ) , t , "cmd.Wait()" )
2023-01-24 14:56:05 +00:00
}
2024-06-12 19:35:48 +00:00
func TestTraceDirRecursion ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2024-06-12 19:35:48 +00:00
expected := [ ] byte ( "> goroutine(1):frame(1) main.A(5, 5)\n > goroutine(1):frame(2) main.A(4, 4)\n > goroutine(1):frame(3) main.A(3, 3)\n > goroutine(1):frame(4) main.A(2, 2)\n > goroutine(1):frame(5) main.A(1, 1)\n >> goroutine(1):frame(5) main.A => (1)\n >> goroutine(1):frame(4) main.A => (2)\n >> goroutine(1):frame(3) main.A => (6)\n >> goroutine(1):frame(2) main.A => (24)\n>> goroutine(1):frame(1) main.A => (120)\n" )
fixtures := protest . FindFixturesDir ( )
cmd := exec . Command ( dlvbin , "trace" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "leafrec.go" ) , "main.A" , "--follow-calls" , "4" )
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
cmd . Dir = filepath . Join ( fixtures , "buildtest" )
assertNoError ( cmd . Start ( ) , t , "running trace" )
// Parse output to ignore calls to morestack_noctxt for comparison
scan := bufio . NewScanner ( rdr )
text := ""
outputtext := ""
for scan . Scan ( ) {
text = scan . Text ( )
if ! strings . Contains ( text , "morestack_noctxt" ) {
outputtext += text
outputtext += "\n"
}
}
output := [ ] byte ( outputtext )
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
assertNoError ( cmd . Wait ( ) , t , "cmd.Wait()" )
}
2022-08-04 08:10:54 +00:00
func TestTraceMultipleGoroutines ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2022-08-04 08:10:54 +00:00
2023-11-06 13:55:44 +00:00
// TODO(derekparker) this test has to be a bit vague to avoid flakiness.
2022-08-04 08:10:54 +00:00
// I think a future improvement could be to use regexp captures to match the
// goroutine IDs at function entry and exit.
expected := [ ] byte ( "main.callme(0, \"five\")\n" )
2024-04-23 21:22:17 +00:00
expected2 := [ ] byte ( "main.callme => (0)\n" )
2022-08-04 08:10:54 +00:00
fixtures := protest . FindFixturesDir ( )
2023-01-30 23:04:55 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "goroutines-trace.go" ) , "callme" )
2022-08-04 08:10:54 +00:00
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
cmd . Dir = filepath . Join ( fixtures , "buildtest" )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2022-08-04 08:10:54 +00:00
assertNoError ( err , t , "ReadAll" )
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
if ! bytes . Contains ( output , expected2 ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
cmd . Wait ( )
}
2020-06-02 19:07:03 +00:00
func TestTracePid ( t * testing . T ) {
if runtime . GOOS == "linux" {
2023-09-25 18:41:59 +00:00
bs , _ := os . ReadFile ( "/proc/sys/kernel/yama/ptrace_scope" )
2020-06-02 19:07:03 +00:00
if bs == nil || strings . TrimSpace ( string ( bs ) ) != "0" {
t . Logf ( "can not run TestAttachDetach: %v\n" , bs )
return
}
}
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-06-02 19:07:03 +00:00
2024-04-23 21:22:17 +00:00
expected := [ ] byte ( "goroutine(1): main.A()\n>> goroutine(1): main.A => ()\n" )
2020-06-02 19:07:03 +00:00
// make process run
fix := protest . BuildFixture ( "issue2023" , 0 )
targetCmd := exec . Command ( fix . Path )
2020-10-19 21:44:04 +00:00
assertNoError ( targetCmd . Start ( ) , t , "execute issue2023" )
2020-06-02 19:07:03 +00:00
if targetCmd . Process == nil || targetCmd . Process . Pid == 0 {
2023-08-29 12:44:18 +00:00
t . Fatal ( "expected target process running" )
2020-06-02 19:07:03 +00:00
}
defer targetCmd . Process . Kill ( )
// dlv attach the process by pid
cmd := exec . Command ( dlvbin , "trace" , "-p" , strconv . Itoa ( targetCmd . Process . Pid ) , "main.A" )
rdr , err := cmd . StderrPipe ( )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "ReadAll" )
2020-06-02 19:07:03 +00:00
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
cmd . Wait ( )
}
2020-05-13 06:38:10 +00:00
func TestTraceBreakpointExists ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-05-13 06:38:10 +00:00
fixtures := protest . FindFixturesDir ( )
// We always set breakpoints on some runtime functions at startup, so this would return with
// a breakpoints exists error.
// TODO: Perhaps we shouldn't be setting these default breakpoints in trace mode, however.
2024-07-01 18:23:39 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "issue573.go" ) , "runtime.*panic" )
2020-05-13 06:38:10 +00:00
rdr , err := cmd . StderrPipe ( )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
2020-05-13 06:38:10 +00:00
cmd . Dir = filepath . Join ( fixtures , "buildtest" )
2020-10-19 21:44:04 +00:00
assertNoError ( cmd . Start ( ) , t , "running trace" )
2020-05-13 06:38:10 +00:00
defer cmd . Wait ( )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "ReadAll" )
2020-05-13 06:38:10 +00:00
if bytes . Contains ( output , [ ] byte ( "Breakpoint exists" ) ) {
t . Fatal ( "Breakpoint exists errors should be ignored" )
}
}
func TestTracePrintStack ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-05-13 06:38:10 +00:00
fixtures := protest . FindFixturesDir ( )
2023-01-30 23:04:55 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , "--stack" , "2" , filepath . Join ( fixtures , "issue573.go" ) , "foo" )
2020-05-13 06:38:10 +00:00
rdr , err := cmd . StderrPipe ( )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
2020-05-13 06:38:10 +00:00
cmd . Dir = filepath . Join ( fixtures , "buildtest" )
2020-10-19 21:44:04 +00:00
assertNoError ( cmd . Start ( ) , t , "running trace" )
2020-05-13 06:38:10 +00:00
defer cmd . Wait ( )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2020-10-19 21:44:04 +00:00
assertNoError ( err , t , "ReadAll" )
2020-05-13 06:38:10 +00:00
if ! bytes . Contains ( output , [ ] byte ( "Stack:" ) ) && ! bytes . Contains ( output , [ ] byte ( "main.main" ) ) {
t . Fatal ( "stacktrace not printed" )
}
}
2020-08-31 17:51:10 +00:00
2021-08-24 12:53:27 +00:00
func TestTraceEBPF ( t * testing . T ) {
if os . Getenv ( "CI" ) == "true" {
t . Skip ( "cannot run test in CI, requires kernel compiled with btf support" )
}
if runtime . GOOS != "linux" || runtime . GOARCH != "amd64" {
t . Skip ( "not implemented on non linux/amd64 systems" )
}
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 16 ) {
t . Skip ( "requires at least Go 1.16 to run test" )
}
usr , err := user . Current ( )
if err != nil {
t . Fatal ( err )
}
if usr . Uid != "0" {
t . Skip ( "test must be run as root" )
}
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinaryEBPF ( t )
2021-08-24 12:53:27 +00:00
2021-10-25 19:37:36 +00:00
expected := [ ] byte ( "> (1) main.foo(99, 9801)\n=> \"9900\"" )
2021-08-24 12:53:27 +00:00
fixtures := protest . FindFixturesDir ( )
2023-01-30 23:04:55 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--ebpf" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "issue573.go" ) , "foo" )
2021-08-24 12:53:27 +00:00
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2021-08-24 12:53:27 +00:00
assertNoError ( err , t , "ReadAll" )
2023-08-18 17:56:44 +00:00
cmd . Wait ( )
2021-08-24 12:53:27 +00:00
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
}
2022-07-29 10:00:32 +00:00
func TestTraceEBPF2 ( t * testing . T ) {
if os . Getenv ( "CI" ) == "true" {
t . Skip ( "cannot run test in CI, requires kernel compiled with btf support" )
}
if runtime . GOOS != "linux" || runtime . GOARCH != "amd64" {
t . Skip ( "not implemented on non linux/amd64 systems" )
}
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 16 ) {
t . Skip ( "requires at least Go 1.16 to run test" )
}
usr , err := user . Current ( )
if err != nil {
t . Fatal ( err )
}
if usr . Uid != "0" {
t . Skip ( "test must be run as root" )
}
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinaryEBPF ( t )
2022-07-29 10:00:32 +00:00
expected := [ ] byte ( ` > ( 1 ) main . callme ( 10 )
> ( 1 ) main . callme ( 9 )
> ( 1 ) main . callme ( 8 )
> ( 1 ) main . callme ( 7 )
> ( 1 ) main . callme ( 6 )
> ( 1 ) main . callme ( 5 )
> ( 1 ) main . callme ( 4 )
> ( 1 ) main . callme ( 3 )
> ( 1 ) main . callme ( 2 )
> ( 1 ) main . callme ( 1 )
> ( 1 ) main . callme ( 0 )
= > "100"
= > "100"
= > "100"
= > "100"
= > "100"
= > "100"
= > "100"
= > "100"
= > "100"
= > "100"
= > "100" ` )
fixtures := protest . FindFixturesDir ( )
2023-01-30 23:04:55 +00:00
cmd := exec . Command ( dlvbin , "trace" , "--ebpf" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "ebpf_trace.go" ) , "main.callme" )
2022-07-29 10:00:32 +00:00
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2022-07-29 10:00:32 +00:00
assertNoError ( err , t , "ReadAll" )
2023-08-18 17:56:44 +00:00
cmd . Wait ( )
2022-07-29 10:00:32 +00:00
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
}
2023-06-15 10:07:32 +00:00
func TestTraceEBPF3 ( t * testing . T ) {
if os . Getenv ( "CI" ) == "true" {
t . Skip ( "cannot run test in CI, requires kernel compiled with btf support" )
}
if runtime . GOOS != "linux" || runtime . GOARCH != "amd64" {
t . Skip ( "not implemented on non linux/amd64 systems" )
}
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 16 ) {
t . Skip ( "requires at least Go 1.16 to run test" )
}
usr , err := user . Current ( )
if err != nil {
t . Fatal ( err )
}
if usr . Uid != "0" {
t . Skip ( "test must be run as root" )
}
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinaryEBPF ( t )
2023-06-15 10:07:32 +00:00
expected := [ ] byte ( ` > ( 1 ) main . tracedFunction ( 0 )
> ( 1 ) main . tracedFunction ( 1 )
> ( 1 ) main . tracedFunction ( 2 )
> ( 1 ) main . tracedFunction ( 3 )
> ( 1 ) main . tracedFunction ( 4 )
> ( 1 ) main . tracedFunction ( 5 )
> ( 1 ) main . tracedFunction ( 6 )
> ( 1 ) main . tracedFunction ( 7 )
> ( 1 ) main . tracedFunction ( 8 )
> ( 1 ) main . tracedFunction ( 9 ) ` )
fixtures := protest . FindFixturesDir ( )
cmd := exec . Command ( dlvbin , "trace" , "--ebpf" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "ebpf_trace2.go" ) , "main.traced" )
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2023-06-15 10:07:32 +00:00
assertNoError ( err , t , "ReadAll" )
2023-08-18 17:56:44 +00:00
cmd . Wait ( )
2023-06-15 10:07:32 +00:00
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
2023-08-18 17:56:44 +00:00
}
func TestTraceEBPF4 ( t * testing . T ) {
if os . Getenv ( "CI" ) == "true" {
t . Skip ( "cannot run test in CI, requires kernel compiled with btf support" )
}
if runtime . GOOS != "linux" || runtime . GOARCH != "amd64" {
t . Skip ( "not implemented on non linux/amd64 systems" )
}
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 16 ) {
t . Skip ( "requires at least Go 1.16 to run test" )
}
usr , err := user . Current ( )
if err != nil {
t . Fatal ( err )
}
if usr . Uid != "0" {
t . Skip ( "test must be run as root" )
}
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinaryEBPF ( t )
2023-08-18 17:56:44 +00:00
expected := [ ] byte ( ` > ( 1 ) main . tracedFunction ( 0 , true , 97 )
> ( 1 ) main . tracedFunction ( 1 , false , 98 )
> ( 1 ) main . tracedFunction ( 2 , false , 99 )
> ( 1 ) main . tracedFunction ( 3 , false , 100 )
> ( 1 ) main . tracedFunction ( 4 , false , 101 )
> ( 1 ) main . tracedFunction ( 5 , true , 102 )
> ( 1 ) main . tracedFunction ( 6 , false , 103 )
> ( 1 ) main . tracedFunction ( 7 , false , 104 )
> ( 1 ) main . tracedFunction ( 8 , false , 105 )
> ( 1 ) main . tracedFunction ( 9 , false , 106 ) ` )
fixtures := protest . FindFixturesDir ( )
cmd := exec . Command ( dlvbin , "trace" , "--ebpf" , "--output" , filepath . Join ( t . TempDir ( ) , "__debug" ) , filepath . Join ( fixtures , "ebpf_trace3.go" ) , "main.traced" )
rdr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer rdr . Close ( )
assertNoError ( cmd . Start ( ) , t , "running trace" )
2023-09-25 18:41:59 +00:00
output , err := io . ReadAll ( rdr )
2023-08-18 17:56:44 +00:00
assertNoError ( err , t , "ReadAll" )
2023-06-15 10:07:32 +00:00
cmd . Wait ( )
2023-08-18 17:56:44 +00:00
if ! bytes . Contains ( output , expected ) {
t . Fatalf ( "expected:\n%s\ngot:\n%s" , string ( expected ) , string ( output ) )
}
2023-06-15 10:07:32 +00:00
}
2020-08-31 17:51:10 +00:00
func TestDlvTestChdir ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2020-08-31 17:51:10 +00:00
fixtures := protest . FindFixturesDir ( )
2023-01-04 18:47:27 +00:00
dotest := func ( testargs [ ] string ) {
t . Helper ( )
args := [ ] string { "--allow-non-terminal-interactive=true" , "test" }
args = append ( args , testargs ... )
args = append ( args , "--" , "-test.v" )
t . Logf ( "dlv test %s" , args )
cmd := exec . Command ( dlvbin , args ... )
cmd . Stdin = strings . NewReader ( "continue\nexit\n" )
out , err := cmd . CombinedOutput ( )
if err != nil {
t . Fatalf ( "error executing Delve: %v" , err )
}
t . Logf ( "output: %q" , out )
p , _ := filepath . Abs ( filepath . Join ( fixtures , "buildtest" ) )
tgt := "current directory: " + p
if ! strings . Contains ( string ( out ) , tgt ) {
t . Errorf ( "output did not contain expected string %q" , tgt )
}
2020-08-31 17:51:10 +00:00
}
2023-01-04 18:47:27 +00:00
dotest ( [ ] string { filepath . Join ( fixtures , "buildtest" ) } )
files , _ := filepath . Glob ( filepath . Join ( fixtures , "buildtest" , "*.go" ) )
dotest ( files )
2020-08-31 17:51:10 +00:00
}
2021-07-27 16:38:48 +00:00
func TestVersion ( t * testing . T ) {
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2021-07-27 16:38:48 +00:00
got , err := exec . Command ( dlvbin , "version" , "-v" ) . CombinedOutput ( )
if err != nil {
t . Fatalf ( "error executing `dlv version`: %v\n%s\n" , err , got )
}
want1 := [ ] byte ( "mod\tgithub.com/go-delve/delve" )
want2 := [ ] byte ( "dep\tgithub.com/google/go-dap" )
if ! bytes . Contains ( got , want1 ) || ! bytes . Contains ( got , want2 ) {
t . Errorf ( "got %s\nwant %v and %v in the output" , got , want1 , want2 )
}
}
2021-09-24 22:31:54 +00:00
func TestStaticcheck ( t * testing . T ) {
2024-12-05 03:14:47 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 24 ) {
t . Skip ( "disabled due to export format changes" )
}
2021-09-24 22:31:54 +00:00
_ , err := exec . LookPath ( "staticcheck" )
if err != nil {
t . Skip ( "staticcheck not installed" )
}
// default checks minus SA1019 which complains about deprecated identifiers, which change between versions of Go.
2021-09-28 19:07:42 +00:00
args := [ ] string { "-tests=false" , "-checks=all,-SA1019,-ST1000,-ST1003,-ST1016,-S1021,-ST1023" , "github.com/go-delve/delve/..." }
// * SA1019 is disabled because new deprecations get added on every version
// of Go making the output of staticcheck inconsistent depending on the
// version of Go used to run it.
// * ST1000,ST1003,ST1016 are disabled in the default
// staticcheck configuration
// * S1021 "Merge variable declaration and assignment" is disabled because
// where we don't do this it is a deliberate style choice.
// * ST1023 "Redundant type in variable declaration" same as S1021.
2021-09-24 22:31:54 +00:00
cmd := exec . Command ( "staticcheck" , args ... )
2024-10-31 17:19:08 +00:00
cmd . Dir = protest . ProjectRoot ( )
2021-09-24 22:31:54 +00:00
cmd . Env = append ( os . Environ ( ) , "GOOS=linux" , "GOARCH=amd64" )
out , _ := cmd . CombinedOutput ( )
checkAutogenDoc ( t , "_scripts/staticcheck-out.txt" , fmt . Sprintf ( "staticcheck %s > _scripts/staticcheck-out.txt" , strings . Join ( args , " " ) ) , out )
}
2023-05-16 16:36:15 +00:00
func TestDefaultBinary ( t * testing . T ) {
// Check that when delve is run twice in the same directory simultaneously
// it will pick different default output binary paths.
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2023-05-16 16:36:15 +00:00
fixture := filepath . Join ( protest . FindFixturesDir ( ) , "testargs.go" )
startOne := func ( ) ( io . WriteCloser , func ( ) error , * bytes . Buffer ) {
cmd := exec . Command ( dlvbin , "debug" , "--allow-non-terminal-interactive=true" , fixture , "--" , "test" )
stdin , _ := cmd . StdinPipe ( )
stdoutBuf := new ( bytes . Buffer )
cmd . Stdout = stdoutBuf
assertNoError ( cmd . Start ( ) , t , "dlv debug" )
return stdin , cmd . Wait , stdoutBuf
}
stdin1 , wait1 , stdoutBuf1 := startOne ( )
defer stdin1 . Close ( )
stdin2 , wait2 , stdoutBuf2 := startOne ( )
defer stdin2 . Close ( )
fmt . Fprintf ( stdin1 , "continue\nquit\n" )
fmt . Fprintf ( stdin2 , "continue\nquit\n" )
wait1 ( )
wait2 ( )
out1 , out2 := stdoutBuf1 . String ( ) , stdoutBuf2 . String ( )
t . Logf ( "%q" , out1 )
t . Logf ( "%q" , out2 )
if out1 == out2 {
t . Errorf ( "outputs match" )
}
}
2024-02-07 19:04:45 +00:00
func TestUnixDomainSocket ( t * testing . T ) {
tmpdir := os . TempDir ( )
if tmpdir == "" {
return
}
listenPath := filepath . Join ( tmpdir , "delve_test" )
2024-10-31 17:19:08 +00:00
dlvbin := protest . GetDlvBinary ( t )
2024-02-07 19:04:45 +00:00
defer os . Remove ( dlvbin )
fixtures := protest . FindFixturesDir ( )
buildtestdir := filepath . Join ( fixtures , "buildtest" )
2024-10-14 18:04:03 +00:00
cmd := exec . Command ( dlvbin , "debug" , "--headless=true" , "--listen=unix:" + listenPath , "--api-version=2" , "--backend=" + testBackend , "--log" , "--log-output=debugger,rpc" )
2024-02-07 19:04:45 +00:00
cmd . Dir = buildtestdir
stderr , err := cmd . StderrPipe ( )
assertNoError ( err , t , "stderr pipe" )
defer stderr . Close ( )
assertNoError ( cmd . Start ( ) , t , "dlv debug" )
scan := bufio . NewScanner ( stderr )
// wait for the debugger to start
for scan . Scan ( ) {
text := scan . Text ( )
t . Log ( text )
if strings . Contains ( text , "API server pid = " ) {
break
}
}
go func ( ) {
for scan . Scan ( ) {
t . Log ( scan . Text ( ) )
// keep pipe empty
}
} ( )
conn , err := net . Dial ( "unix" , listenPath )
assertNoError ( err , t , "dialing" )
client := rpc2 . NewClientFromConn ( conn )
state := <- client . Continue ( )
if ! state . Exited {
t . Fatal ( "Program did not exit" )
}
client . Detach ( true )
cmd . Wait ( )
}