2017-04-19 02:56:20 +00:00
package service_test
2015-03-20 21:11:11 +00:00
import (
2017-02-10 14:11:40 +00:00
"flag"
2015-06-28 15:00:56 +00:00
"fmt"
2020-06-05 18:03:09 +00:00
"io/ioutil"
2016-02-15 14:39:29 +00:00
"math/rand"
2015-03-20 21:11:11 +00:00
"net"
2019-06-04 21:33:19 +00:00
"net/rpc"
"net/rpc/jsonrpc"
2016-05-18 07:07:19 +00:00
"os"
2015-05-08 20:16:22 +00:00
"path/filepath"
2018-05-11 12:51:15 +00:00
"reflect"
2015-06-13 04:47:30 +00:00
"runtime"
2015-06-28 15:00:56 +00:00
"strconv"
2016-01-30 21:27:08 +00:00
"strings"
2015-03-20 21:11:11 +00:00
"testing"
2016-02-15 14:39:29 +00:00
"time"
2015-03-20 21:11:11 +00:00
2019-01-04 18:39:25 +00:00
protest "github.com/go-delve/delve/pkg/proc/test"
2020-04-13 18:07:15 +00:00
"github.com/go-delve/delve/service/debugger"
2015-06-21 03:47:44 +00:00
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/api"
"github.com/go-delve/delve/service/rpc2"
"github.com/go-delve/delve/service/rpccommon"
2015-03-20 21:11:11 +00:00
)
2020-03-18 16:25:32 +00:00
var normalLoadConfig = api . LoadConfig {
FollowPointers : true ,
MaxVariableRecurse : 1 ,
MaxStringLen : 64 ,
MaxArrayValues : 64 ,
MaxStructFields : - 1 ,
}
2018-05-29 15:01:51 +00:00
var testBackend , buildMode string
2016-04-24 17:15:39 +00:00
func TestMain ( m * testing . M ) {
2017-02-10 14:11:40 +00:00
flag . StringVar ( & testBackend , "backend" , "" , "selects backend" )
2018-05-29 15:01:51 +00:00
flag . StringVar ( & buildMode , "test-buildmode" , "" , "selects build mode" )
2018-05-04 17:31:45 +00:00
var logOutput string
flag . StringVar ( & logOutput , "log-output" , "" , "configures log output" )
2017-02-10 14:11:40 +00:00
flag . Parse ( )
2018-10-01 08:19:06 +00:00
protest . DefaultTestBackend ( & testBackend )
2018-05-29 15:01:51 +00:00
if buildMode != "" && buildMode != "pie" {
fmt . Fprintf ( os . Stderr , "unknown build mode %q" , buildMode )
os . Exit ( 1 )
}
2019-03-27 21:58:36 +00:00
logflags . Setup ( logOutput != "" , logOutput , "" )
2016-04-24 17:15:39 +00:00
os . Exit ( protest . RunTestsWithFixtures ( m ) )
}
2016-04-18 19:20:20 +00:00
func withTestClient2 ( name string , t * testing . T , fn func ( c service . Client ) ) {
2020-08-21 14:14:02 +00:00
withTestClient2Extended ( name , t , 0 , [ 3 ] string { } , func ( c service . Client , fixture protest . Fixture ) {
2018-05-29 15:01:51 +00:00
fn ( c )
} )
}
2020-08-21 14:14:02 +00:00
func startServer ( name string , buildFlags protest . BuildFlags , t * testing . T , redirects [ 3 ] string ) ( clientConn net . Conn , fixture protest . Fixture ) {
2017-05-05 22:17:52 +00:00
if testBackend == "rr" {
protest . MustHaveRecordingAllowed ( t )
}
2018-09-03 08:00:30 +00:00
listener , clientConn := service . ListenerPipe ( )
2018-05-29 15:01:51 +00:00
defer listener . Close ( )
if buildMode == "pie" {
2020-03-24 09:47:36 +00:00
buildFlags |= protest . BuildModePIE
2018-05-29 15:01:51 +00:00
}
2019-06-04 21:33:19 +00:00
fixture = protest . BuildFixture ( name , buildFlags )
2020-08-21 14:14:02 +00:00
for i := range redirects {
if redirects [ i ] != "" {
redirects [ i ] = filepath . Join ( fixture . BuildDir , redirects [ i ] )
}
}
2016-06-19 06:43:29 +00:00
server := rpccommon . NewServer ( & service . Config {
2020-04-13 18:07:15 +00:00
Listener : listener ,
ProcessArgs : [ ] string { fixture . Path } ,
Debugger : debugger . Config {
Backend : testBackend ,
CheckGoVersion : true ,
2020-06-05 18:03:09 +00:00
Packages : [ ] string { fixture . Source } ,
BuildFlags : "" , // build flags can be an empty string here because the only test that uses it, does not set special flags.
ExecuteKind : debugger . ExecutingGeneratedFile ,
2020-08-21 14:14:02 +00:00
Redirects : redirects ,
2020-04-13 18:07:15 +00:00
} ,
2018-06-14 18:12:11 +00:00
} )
2015-06-27 04:05:15 +00:00
if err := server . Run ( ) ; err != nil {
t . Fatal ( err )
}
2019-06-04 21:33:19 +00:00
return clientConn , fixture
}
2020-08-21 14:14:02 +00:00
func withTestClient2Extended ( name string , t * testing . T , buildFlags protest . BuildFlags , redirects [ 3 ] string , fn func ( c service . Client , fixture protest . Fixture ) ) {
clientConn , fixture := startServer ( name , buildFlags , t , redirects )
2018-09-03 08:00:30 +00:00
client := rpc2 . NewClientFromConn ( clientConn )
2015-06-24 14:44:24 +00:00
defer func ( ) {
client . Detach ( true )
} ( )
2018-05-29 15:01:51 +00:00
fn ( client , fixture )
2015-03-20 21:11:11 +00:00
}
2015-06-27 04:05:15 +00:00
func TestRunWithInvalidPath ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
if testBackend == "rr" {
// This test won't work because rr returns an error, after recording, when
// the recording failed but also when the recording succeeded but the
// inferior returned an error. Therefore we have to ignore errors from rr.
return
}
2019-07-30 15:38:25 +00:00
listener , err := net . Listen ( "tcp" , "127.0.0.1:0" )
2015-06-27 04:05:15 +00:00
if err != nil {
t . Fatalf ( "couldn't start listener: %s\n" , err )
}
defer listener . Close ( )
2016-06-19 06:43:29 +00:00
server := rpccommon . NewServer ( & service . Config {
2015-06-27 04:05:15 +00:00
Listener : listener ,
ProcessArgs : [ ] string { "invalid_path" } ,
2016-06-19 06:43:29 +00:00
APIVersion : 2 ,
2020-04-13 18:07:15 +00:00
Debugger : debugger . Config {
2020-06-05 18:03:09 +00:00
Backend : testBackend ,
ExecuteKind : debugger . ExecutingGeneratedFile ,
2020-04-13 18:07:15 +00:00
} ,
2018-06-14 18:12:11 +00:00
} )
2015-06-27 04:05:15 +00:00
if err := server . Run ( ) ; err == nil {
t . Fatal ( "Expected Run to return error for invalid program path" )
}
}
2015-07-03 20:35:22 +00:00
func TestRestart_afterExit ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
2015-07-03 20:35:22 +00:00
origPid := c . ProcessPid ( )
state := <- c . Continue ( )
if ! state . Exited {
t . Fatal ( "expected initial process to have exited" )
}
2020-06-05 18:03:09 +00:00
if _ , err := c . Restart ( false ) ; err != nil {
2015-07-03 20:35:22 +00:00
t . Fatal ( err )
}
if c . ProcessPid ( ) == origPid {
t . Fatal ( "did not spawn new process, has same PID" )
}
state = <- c . Continue ( )
if ! state . Exited {
2015-09-26 19:36:13 +00:00
t . Fatalf ( "expected restarted process to have exited %v" , state )
2015-07-03 20:35:22 +00:00
}
} )
}
2016-01-24 08:16:45 +00:00
func TestRestart_breakpointPreservation ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
2016-01-24 08:16:45 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 1 , Name : "firstbreakpoint" , Tracepoint : true } )
assertNoError ( err , t , "CreateBreakpoint()" )
stateCh := c . Continue ( )
2016-02-22 15:08:45 +00:00
state := <- stateCh
2016-01-24 08:16:45 +00:00
if state . CurrentThread . Breakpoint . Name != "firstbreakpoint" || ! state . CurrentThread . Breakpoint . Tracepoint {
t . Fatalf ( "Wrong breakpoint: %#v\n" , state . CurrentThread . Breakpoint )
}
2016-02-22 15:08:45 +00:00
state = <- stateCh
2016-01-24 08:16:45 +00:00
if ! state . Exited {
t . Fatal ( "Did not exit after first tracepoint" )
}
2016-02-22 15:08:45 +00:00
2016-01-24 08:16:45 +00:00
t . Log ( "Restart" )
2020-06-05 18:03:09 +00:00
c . Restart ( false )
2016-01-24 08:16:45 +00:00
stateCh = c . Continue ( )
2016-02-22 15:08:45 +00:00
state = <- stateCh
2016-01-24 08:16:45 +00:00
if state . CurrentThread . Breakpoint . Name != "firstbreakpoint" || ! state . CurrentThread . Breakpoint . Tracepoint {
t . Fatalf ( "Wrong breakpoint (after restart): %#v\n" , state . CurrentThread . Breakpoint )
}
2016-02-22 15:08:45 +00:00
state = <- stateCh
2016-01-24 08:16:45 +00:00
if ! state . Exited {
t . Fatal ( "Did not exit after first tracepoint (after restart)" )
}
} )
}
2015-07-03 20:35:22 +00:00
func TestRestart_duringStop ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
2015-07-03 20:35:22 +00:00
origPid := c . ProcessPid ( )
2015-08-07 16:50:14 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 1 } )
2015-07-03 20:35:22 +00:00
if err != nil {
t . Fatal ( err )
}
state := <- c . Continue ( )
2015-10-29 09:59:22 +00:00
if state . CurrentThread . Breakpoint == nil {
2015-07-03 20:35:22 +00:00
t . Fatal ( "did not hit breakpoint" )
}
2020-06-05 18:03:09 +00:00
if _ , err := c . Restart ( false ) ; err != nil {
2015-07-03 20:35:22 +00:00
t . Fatal ( err )
}
if c . ProcessPid ( ) == origPid {
t . Fatal ( "did not spawn new process, has same PID" )
}
bps , err := c . ListBreakpoints ( )
if err != nil {
t . Fatal ( err )
}
2015-08-02 05:08:48 +00:00
if len ( bps ) == 0 {
t . Fatal ( "breakpoints not preserved" )
2015-07-03 20:35:22 +00:00
}
} )
}
2020-06-05 18:03:09 +00:00
// This source is a slightly modified version of
// _fixtures/testenv.go. The only difference is that
// the name of the environment variable we are trying to
// read is named differently, so we can assert the code
// was actually changed in the test.
const modifiedSource = ` package main
import (
"fmt"
"os"
"runtime"
)
func main ( ) {
x := os . Getenv ( "SOMEMODIFIEDVAR" )
runtime . Breakpoint ( )
fmt . Printf ( "SOMEMODIFIEDVAR=%s\n" , x )
}
`
func TestRestart_rebuild ( t * testing . T ) {
// In the original fixture file the env var tested for is SOMEVAR.
os . Setenv ( "SOMEVAR" , "bah" )
2020-08-21 14:14:02 +00:00
withTestClient2Extended ( "testenv" , t , 0 , [ 3 ] string { } , func ( c service . Client , f protest . Fixture ) {
2020-06-05 18:03:09 +00:00
<- c . Continue ( )
var1 , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "x" , normalLoadConfig )
assertNoError ( err , t , "EvalVariable" )
if var1 . Value != "bah" {
t . Fatalf ( "expected 'bah' got %q" , var1 . Value )
}
fi , err := os . Stat ( f . Source )
assertNoError ( err , t , "Stat fixture.Source" )
originalSource , err := ioutil . ReadFile ( f . Source )
assertNoError ( err , t , "Reading original source" )
// Ensure we write the original source code back after the test exits.
defer ioutil . WriteFile ( f . Source , originalSource , fi . Mode ( ) )
// Write modified source code to the fixture file.
err = ioutil . WriteFile ( f . Source , [ ] byte ( modifiedSource ) , fi . Mode ( ) )
assertNoError ( err , t , "Writing modified source" )
// First set our new env var and ensure later that the
// modified source code picks it up.
os . Setenv ( "SOMEMODIFIEDVAR" , "foobar" )
// Restart the program, rebuilding from source.
_ , err = c . Restart ( true )
assertNoError ( err , t , "Restart(true)" )
<- c . Continue ( )
var1 , err = c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "x" , normalLoadConfig )
assertNoError ( err , t , "EvalVariable" )
if var1 . Value != "foobar" {
t . Fatalf ( "expected 'foobar' got %q" , var1 . Value )
}
} )
}
2015-03-20 21:11:11 +00:00
func TestClientServer_exit ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
2015-03-20 21:11:11 +00:00
state , err := c . GetState ( )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if e , a := false , state . Exited ; e != a {
t . Fatalf ( "Expected exited %v, got %v" , e , a )
}
2015-06-28 15:00:56 +00:00
state = <- c . Continue ( )
2015-07-03 19:18:03 +00:00
if state . Err == nil {
t . Fatalf ( "Error expected after continue" )
2015-06-21 18:08:14 +00:00
}
2015-06-28 15:00:56 +00:00
if ! state . Exited {
t . Fatalf ( "Expected exit after continue: %v" , state )
2015-06-21 18:08:14 +00:00
}
2017-06-29 18:15:59 +00:00
_ , err = c . GetState ( )
2015-10-09 23:30:28 +00:00
if err == nil {
t . Fatal ( "Expected error on querying state from exited process" )
2015-03-20 21:11:11 +00:00
}
} )
}
func TestClientServer_step ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testprog" , t , func ( c service . Client ) {
2017-02-07 21:07:18 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.helloworld" , Line : - 1 } )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-07-01 03:16:52 +00:00
stateBefore := <- c . Continue ( )
2015-06-28 15:00:56 +00:00
if stateBefore . Err != nil {
t . Fatalf ( "Unexpected error: %v" , stateBefore . Err )
2015-03-20 21:11:11 +00:00
}
stateAfter , err := c . Step ( )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if before , after := stateBefore . CurrentThread . PC , stateAfter . CurrentThread . PC ; before >= after {
2018-09-19 11:59:44 +00:00
t . Fatalf ( "Expected %#v to be greater than %#v" , after , before )
2015-03-20 21:11:11 +00:00
}
} )
}
2016-04-14 09:58:59 +00:00
func TestClientServer_stepout ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-14 09:58:59 +00:00
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.helloworld" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint()" )
stateBefore := <- c . Continue ( )
assertNoError ( stateBefore . Err , t , "Continue()" )
if stateBefore . CurrentThread . Line != 13 {
t . Fatalf ( "wrong line number %s:%d, expected %d" , stateBefore . CurrentThread . File , stateBefore . CurrentThread . Line , 13 )
}
stateAfter , err := c . StepOut ( )
assertNoError ( err , t , "StepOut()" )
if stateAfter . CurrentThread . Line != 35 {
t . Fatalf ( "wrong line number %s:%d, expected %d" , stateAfter . CurrentThread . File , stateAfter . CurrentThread . Line , 13 )
}
} )
}
2016-04-18 19:20:20 +00:00
func testnext2 ( testcases [ ] nextTest , initialLocation string , t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
bp , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : initialLocation , Line : - 1 } )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-06-28 15:00:56 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v" , state . Err )
2015-03-20 21:11:11 +00:00
}
2015-06-12 19:32:32 +00:00
_ , err = c . ClearBreakpoint ( bp . ID )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
for _ , tc := range testcases {
if state . CurrentThread . Line != tc . begin {
2015-04-27 15:51:02 +00:00
t . Fatalf ( "Program not stopped at correct spot expected %d was %d" , tc . begin , state . CurrentThread . Line )
2015-03-20 21:11:11 +00:00
}
t . Logf ( "Next for scenario %#v" , tc )
state , err = c . Next ( )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if state . CurrentThread . Line != tc . end {
2015-04-27 15:51:02 +00:00
t . Fatalf ( "Program did not continue to correct next location expected %d was %d" , tc . end , state . CurrentThread . Line )
2015-03-20 21:11:11 +00:00
}
}
} )
}
func TestNextGeneral ( t * testing . T ) {
2016-02-25 09:48:42 +00:00
var testcases [ ] nextTest
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
2016-02-25 09:48:42 +00:00
2020-03-18 16:25:32 +00:00
if ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVersion { Major : 1 , Minor : 7 , Rev : - 1 } ) {
2016-02-25 09:48:42 +00:00
testcases = [ ] nextTest {
{ 17 , 19 } ,
{ 19 , 20 } ,
{ 20 , 23 } ,
{ 23 , 24 } ,
{ 24 , 26 } ,
{ 26 , 31 } ,
{ 31 , 23 } ,
{ 23 , 24 } ,
{ 24 , 26 } ,
{ 26 , 31 } ,
{ 31 , 23 } ,
{ 23 , 24 } ,
{ 24 , 26 } ,
{ 26 , 27 } ,
{ 27 , 28 } ,
{ 28 , 34 } ,
}
} else {
testcases = [ ] nextTest {
{ 17 , 19 } ,
{ 19 , 20 } ,
{ 20 , 23 } ,
{ 23 , 24 } ,
{ 24 , 26 } ,
{ 26 , 31 } ,
{ 31 , 23 } ,
{ 23 , 24 } ,
{ 24 , 26 } ,
{ 26 , 31 } ,
{ 31 , 23 } ,
{ 23 , 24 } ,
{ 24 , 26 } ,
{ 26 , 27 } ,
{ 27 , 34 } ,
}
2015-03-20 21:11:11 +00:00
}
2016-02-25 09:48:42 +00:00
2016-04-14 09:58:59 +00:00
testnext2 ( testcases , "main.testnext" , t )
2015-03-20 21:11:11 +00:00
}
func TestNextFunctionReturn ( t * testing . T ) {
testcases := [ ] nextTest {
2016-02-11 07:18:39 +00:00
{ 13 , 14 } ,
2015-10-22 17:07:24 +00:00
{ 14 , 15 } ,
{ 15 , 35 } ,
2015-03-20 21:11:11 +00:00
}
2016-04-14 09:58:59 +00:00
testnext2 ( testcases , "main.helloworld" , t )
2015-03-20 21:11:11 +00:00
}
func TestClientServer_breakpointInMainThread ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testprog" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
bp , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.helloworld" , Line : 1 } )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-06-28 15:00:56 +00:00
state := <- c . Continue ( )
2020-01-13 17:01:39 +00:00
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
2015-03-20 21:11:11 +00:00
}
pc := state . CurrentThread . PC
if pc - 1 != bp . Addr && pc != bp . Addr {
f , l := state . CurrentThread . File , state . CurrentThread . Line
t . Fatalf ( "Break not respected:\nPC:%#v %s:%d\nFN:%#v \n" , pc , f , l , bp . Addr )
}
} )
}
func TestClientServer_breakpointInSeparateGoroutine ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testthreads" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.anotherthread" , Line : 1 } )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-06-28 15:00:56 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
2015-03-20 21:11:11 +00:00
}
f , l := state . CurrentThread . File , state . CurrentThread . Line
2015-07-10 15:16:03 +00:00
if f != "testthreads.go" && l != 9 {
2015-03-20 21:11:11 +00:00
t . Fatal ( "Program did not hit breakpoint" )
}
} )
}
func TestClientServer_breakAtNonexistentPoint ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testprog" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "nowhere" , Line : 1 } )
2015-03-20 21:11:11 +00:00
if err == nil {
t . Fatal ( "Should not be able to break at non existent function" )
}
} )
}
func TestClientServer_clearBreakpoint ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testprog" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
bp , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.sleepytime" , Line : 1 } )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2016-03-06 17:54:43 +00:00
if e , a := 1 , countBreakpoints ( t , c ) ; e != a {
2015-03-20 21:11:11 +00:00
t . Fatalf ( "Expected breakpoint count %d, got %d" , e , a )
}
2015-06-12 19:32:32 +00:00
deleted , err := c . ClearBreakpoint ( bp . ID )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if deleted . ID != bp . ID {
t . Fatalf ( "Expected deleted breakpoint ID %v, got %v" , bp . ID , deleted . ID )
}
2016-03-06 17:54:43 +00:00
if e , a := 0 , countBreakpoints ( t , c ) ; e != a {
2015-03-20 21:11:11 +00:00
t . Fatalf ( "Expected breakpoint count %d, got %d" , e , a )
}
} )
}
func TestClientServer_switchThread ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
2015-03-20 21:11:11 +00:00
// With invalid thread id
_ , err := c . SwitchThread ( - 1 )
if err == nil {
t . Fatal ( "Expected error for invalid thread id" )
}
2015-08-07 16:50:14 +00:00
_ , err = c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 1 } )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-06-28 15:00:56 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
2015-03-20 21:11:11 +00:00
}
var nt int
ct := state . CurrentThread . ID
threads , err := c . ListThreads ( )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
for _ , th := range threads {
if th . ID != ct {
nt = th . ID
break
}
}
if nt == 0 {
t . Fatal ( "could not find thread to switch to" )
}
// With valid thread id
state , err = c . SwitchThread ( nt )
if err != nil {
t . Fatal ( err )
}
if state . CurrentThread . ID != nt {
t . Fatal ( "Did not switch threads" )
}
} )
}
2015-05-08 20:16:22 +00:00
2015-06-28 15:00:56 +00:00
func TestClientServer_infoLocals ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
2015-06-28 15:00:56 +00:00
fp := testProgPath ( t , "testnextprog" )
2020-07-28 16:19:51 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { File : fp , Line : 24 } )
2015-05-08 20:16:22 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-06-28 15:00:56 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
2015-05-08 20:16:22 +00:00
}
2020-03-18 16:25:32 +00:00
locals , err := c . ListLocalVariables ( api . EvalScope { GoroutineID : - 1 } , normalLoadConfig )
2015-05-08 20:16:22 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if len ( locals ) != 3 {
t . Fatalf ( "Expected 3 locals, got %d %#v" , len ( locals ) , locals )
}
} )
}
func TestClientServer_infoArgs ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
2015-06-28 15:00:56 +00:00
fp := testProgPath ( t , "testnextprog" )
_ , err := c . CreateBreakpoint ( & api . Breakpoint { File : fp , Line : 47 } )
2015-05-08 20:16:22 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2015-06-28 15:00:56 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
2015-05-08 20:16:22 +00:00
}
2020-02-24 18:47:02 +00:00
regs , err := c . ListThreadRegisters ( 0 , false )
2015-06-19 07:20:10 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
2016-11-15 16:16:33 +00:00
if len ( regs ) == 0 {
2015-06-19 07:20:10 +00:00
t . Fatal ( "Expected string showing registers values, got empty string" )
}
2020-02-24 18:47:02 +00:00
regs , err = c . ListScopeRegisters ( api . EvalScope { GoroutineID : - 1 , Frame : 0 } , false )
assertNoError ( err , t , "ListScopeRegisters(-1, 0)" )
if len ( regs ) == 0 {
t . Fatal ( "Expected string showing registers values, got empty string" )
}
t . Logf ( "GoroutineID: -1, Frame: 0\n%s" , regs . String ( ) )
regs , err = c . ListScopeRegisters ( api . EvalScope { GoroutineID : - 1 , Frame : 1 } , false )
assertNoError ( err , t , "ListScopeRegisters(-1, 1)" )
if len ( regs ) == 0 {
t . Fatal ( "Expected string showing registers values, got empty string" )
}
t . Logf ( "GoroutineID: -1, Frame: 1\n%s" , regs . String ( ) )
2020-03-18 16:25:32 +00:00
locals , err := c . ListFunctionArgs ( api . EvalScope { GoroutineID : - 1 } , normalLoadConfig )
2015-05-08 20:16:22 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if len ( locals ) != 2 {
t . Fatalf ( "Expected 2 function args, got %d %#v" , len ( locals ) , locals )
}
} )
}
2015-06-28 15:00:56 +00:00
func TestClientServer_traceContinue ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "integrationprog" , t , func ( c service . Client ) {
2015-06-28 15:00:56 +00:00
fp := testProgPath ( t , "integrationprog" )
2015-07-01 03:16:52 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { File : fp , Line : 15 , Tracepoint : true , Goroutine : true , Stacktrace : 5 , Variables : [ ] string { "i" } } )
2015-06-28 15:00:56 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v\n" , err )
}
count := 0
2015-07-01 03:16:52 +00:00
contChan := c . Continue ( )
for state := range contChan {
2015-10-29 09:59:22 +00:00
if state . CurrentThread != nil && state . CurrentThread . Breakpoint != nil {
2015-06-28 15:00:56 +00:00
count ++
t . Logf ( "%v" , state )
2015-10-29 09:59:22 +00:00
bpi := state . CurrentThread . BreakpointInfo
2015-06-28 15:00:56 +00:00
if bpi . Goroutine == nil {
t . Fatalf ( "No goroutine information" )
}
if len ( bpi . Stacktrace ) <= 0 {
t . Fatalf ( "No stacktrace\n" )
}
if len ( bpi . Variables ) != 1 {
t . Fatalf ( "Wrong number of variables returned: %d" , len ( bpi . Variables ) )
}
if bpi . Variables [ 0 ] . Name != "i" {
t . Fatalf ( "Wrong variable returned %s" , bpi . Variables [ 0 ] . Name )
}
2015-10-18 17:37:13 +00:00
t . Logf ( "Variable i is %v" , bpi . Variables [ 0 ] )
n , err := strconv . Atoi ( bpi . Variables [ 0 ] . Value )
if err != nil || n != count - 1 {
t . Fatalf ( "Wrong variable value %q (%v %d)" , bpi . Variables [ 0 ] . Value , err , count )
2015-06-28 15:00:56 +00:00
}
}
if state . Exited {
continue
}
t . Logf ( "%v" , state )
if state . Err != nil {
t . Fatalf ( "Unexpected error during continue: %v\n" , state . Err )
}
}
if count != 3 {
t . Fatalf ( "Wrong number of continues hit: %d\n" , count )
}
} )
}
2015-07-09 16:41:03 +00:00
func TestClientServer_traceContinue2 ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "integrationprog" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
bp1 , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 1 , Tracepoint : true } )
2015-07-09 16:41:03 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v\n" , err )
}
2015-08-07 16:50:14 +00:00
bp2 , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.sayhi" , Line : 1 , Tracepoint : true } )
2015-07-09 16:41:03 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v\n" , err )
}
countMain := 0
countSayhi := 0
contChan := c . Continue ( )
for state := range contChan {
2015-10-29 09:59:22 +00:00
if state . CurrentThread != nil && state . CurrentThread . Breakpoint != nil {
switch state . CurrentThread . Breakpoint . ID {
2015-07-09 16:41:03 +00:00
case bp1 . ID :
countMain ++
case bp2 . ID :
countSayhi ++
}
t . Logf ( "%v" , state )
}
if state . Exited {
continue
}
if state . Err != nil {
t . Fatalf ( "Unexpected error during continue: %v\n" , state . Err )
}
}
if countMain != 1 {
t . Fatalf ( "Wrong number of continues (main.main) hit: %d\n" , countMain )
}
if countSayhi != 3 {
t . Fatalf ( "Wrong number of continues (main.sayhi) hit: %d\n" , countSayhi )
}
} )
}
2015-08-07 16:50:14 +00:00
func TestClientServer_FindLocations ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "locationsprog" , t , func ( c service . Client ) {
2016-02-11 07:18:39 +00:00
someFunctionCallAddr := findLocationHelper ( t , c , "locationsprog.go:26" , false , 1 , 0 ) [ 0 ]
someFunctionLine1 := findLocationHelper ( t , c , "locationsprog.go:27" , false , 1 , 0 ) [ 0 ]
findLocationHelper ( t , c , "anotherFunction:1" , false , 1 , someFunctionLine1 )
findLocationHelper ( t , c , "main.anotherFunction:1" , false , 1 , someFunctionLine1 )
2015-08-07 16:50:14 +00:00
findLocationHelper ( t , c , "anotherFunction" , false , 1 , someFunctionCallAddr )
findLocationHelper ( t , c , "main.anotherFunction" , false , 1 , someFunctionCallAddr )
findLocationHelper ( t , c , fmt . Sprintf ( "*0x%x" , someFunctionCallAddr ) , false , 1 , someFunctionCallAddr )
findLocationHelper ( t , c , "sprog.go:26" , true , 0 , 0 )
findLocationHelper ( t , c , "String" , true , 0 , 0 )
findLocationHelper ( t , c , "main.String" , true , 0 , 0 )
2016-02-11 07:18:39 +00:00
someTypeStringFuncAddr := findLocationHelper ( t , c , "locationsprog.go:14" , false , 1 , 0 ) [ 0 ]
otherTypeStringFuncAddr := findLocationHelper ( t , c , "locationsprog.go:18" , false , 1 , 0 ) [ 0 ]
2015-08-07 16:50:14 +00:00
findLocationHelper ( t , c , "SomeType.String" , false , 1 , someTypeStringFuncAddr )
findLocationHelper ( t , c , "(*SomeType).String" , false , 1 , someTypeStringFuncAddr )
findLocationHelper ( t , c , "main.SomeType.String" , false , 1 , someTypeStringFuncAddr )
findLocationHelper ( t , c , "main.(*SomeType).String" , false , 1 , someTypeStringFuncAddr )
2015-10-17 07:31:07 +00:00
// Issue #275
2015-12-12 14:01:35 +00:00
readfile := findLocationHelper ( t , c , "io/ioutil.ReadFile" , false , 1 , 0 ) [ 0 ]
// Issue #296
findLocationHelper ( t , c , "/io/ioutil.ReadFile" , false , 1 , readfile )
findLocationHelper ( t , c , "ioutil.ReadFile" , false , 1 , readfile )
2015-10-17 07:31:07 +00:00
2015-08-07 16:50:14 +00:00
stringAddrs := findLocationHelper ( t , c , "/^main.*Type.*String$/" , false , 2 , 0 )
if otherTypeStringFuncAddr != stringAddrs [ 0 ] && otherTypeStringFuncAddr != stringAddrs [ 1 ] {
t . Fatalf ( "Wrong locations returned for \"/.*Type.*String/\", got: %v expected: %v and %v\n" , stringAddrs , someTypeStringFuncAddr , otherTypeStringFuncAddr )
}
2016-05-29 19:20:09 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 4 , Tracepoint : false } )
2015-08-07 16:50:14 +00:00
if err != nil {
t . Fatalf ( "CreateBreakpoint(): %v\n" , err )
}
<- c . Continue ( )
2016-05-29 19:20:09 +00:00
locationsprog35Addr := findLocationHelper ( t , c , "locationsprog.go:35" , false , 1 , 0 ) [ 0 ]
findLocationHelper ( t , c , fmt . Sprintf ( "%s:35" , testProgPath ( t , "locationsprog" ) ) , false , 1 , locationsprog35Addr )
findLocationHelper ( t , c , "+1" , false , 1 , locationsprog35Addr )
findLocationHelper ( t , c , "35" , false , 1 , locationsprog35Addr )
findLocationHelper ( t , c , "-1" , false , 1 , findLocationHelper ( t , c , "locationsprog.go:33" , false , 1 , 0 ) [ 0 ] )
2017-07-26 18:52:51 +00:00
findLocationHelper ( t , c , ` *amap["k"] ` , false , 1 , findLocationHelper ( t , c , ` amap["k"] ` , false , 1 , 0 ) [ 0 ] )
2020-10-26 12:36:52 +00:00
locsNoSubst , _ := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "_fixtures/locationsprog.go:35" , false , nil )
sep := "/"
if strings . Contains ( locsNoSubst [ 0 ] . File , "\\" ) {
sep = "\\"
}
substRules := [ ] [ 2 ] string { [ 2 ] string { strings . Replace ( locsNoSubst [ 0 ] . File , "locationsprog.go" , "" , 1 ) , strings . Replace ( locsNoSubst [ 0 ] . File , "_fixtures" + sep + "locationsprog.go" , "nonexistent" , 1 ) } }
t . Logf ( "substitute rules: %q -> %q" , substRules [ 0 ] [ 0 ] , substRules [ 0 ] [ 1 ] )
locsSubst , err := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "nonexistent/locationsprog.go:35" , false , substRules )
if err != nil {
t . Fatalf ( "FindLocation(locationsprog.go:35) with substitute rules: %v" , err )
}
t . Logf ( "FindLocation(\"/nonexistent/path/locationsprog.go:35\") -> %#v" , locsSubst )
if locsNoSubst [ 0 ] . PC != locsSubst [ 0 ] . PC {
t . Fatalf ( "FindLocation with substitute rules mismatch %#v %#v" , locsNoSubst [ 0 ] , locsSubst [ 0 ] )
}
2015-08-07 16:50:14 +00:00
} )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testnextdefer" , t , func ( c service . Client ) {
2016-02-11 07:18:39 +00:00
firstMainLine := findLocationHelper ( t , c , "testnextdefer.go:5" , false , 1 , 0 ) [ 0 ]
2015-08-07 16:50:14 +00:00
findLocationHelper ( t , c , "main.main" , false , 1 , firstMainLine )
} )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "stacktraceprog" , t , func ( c service . Client ) {
2015-08-07 16:50:14 +00:00
stacktracemeAddr := findLocationHelper ( t , c , "stacktraceprog.go:4" , false , 1 , 0 ) [ 0 ]
findLocationHelper ( t , c , "main.stacktraceme" , false , 1 , stacktracemeAddr )
} )
2016-01-30 21:27:08 +00:00
2020-08-21 14:14:02 +00:00
withTestClient2Extended ( "locationsUpperCase" , t , 0 , [ 3 ] string { } , func ( c service . Client , fixture protest . Fixture ) {
2016-01-30 21:27:08 +00:00
// Upper case
findLocationHelper ( t , c , "locationsUpperCase.go:6" , false , 1 , 0 )
// Fully qualified path
2018-05-29 15:01:51 +00:00
findLocationHelper ( t , c , fixture . Source + ":6" , false , 1 , 0 )
bp , err := c . CreateBreakpoint ( & api . Breakpoint { File : fixture . Source , Line : 6 } )
2016-01-30 21:27:08 +00:00
if err != nil {
2018-05-29 15:01:51 +00:00
t . Fatalf ( "Could not set breakpoint in %s: %v\n" , fixture . Source , err )
2016-01-30 21:27:08 +00:00
}
c . ClearBreakpoint ( bp . ID )
// Allow `/` or `\` on Windows
if runtime . GOOS == "windows" {
2018-05-29 15:01:51 +00:00
findLocationHelper ( t , c , filepath . FromSlash ( fixture . Source ) + ":6" , false , 1 , 0 )
bp , err = c . CreateBreakpoint ( & api . Breakpoint { File : filepath . FromSlash ( fixture . Source ) , Line : 6 } )
2016-01-30 21:27:08 +00:00
if err != nil {
2018-05-29 15:01:51 +00:00
t . Fatalf ( "Could not set breakpoint in %s: %v\n" , filepath . FromSlash ( fixture . Source ) , err )
2016-01-30 21:27:08 +00:00
}
c . ClearBreakpoint ( bp . ID )
}
// Case-insensitive on Windows, case-sensitive otherwise
shouldWrongCaseBeError := true
numExpectedMatches := 0
if runtime . GOOS == "windows" {
shouldWrongCaseBeError = false
numExpectedMatches = 1
}
2018-05-29 15:01:51 +00:00
findLocationHelper ( t , c , strings . ToLower ( fixture . Source ) + ":6" , shouldWrongCaseBeError , numExpectedMatches , 0 )
bp , err = c . CreateBreakpoint ( & api . Breakpoint { File : strings . ToLower ( fixture . Source ) , Line : 6 } )
2016-01-30 21:27:08 +00:00
if ( err == nil ) == shouldWrongCaseBeError {
2018-05-29 15:01:51 +00:00
t . Fatalf ( "Could not set breakpoint in %s: %v\n" , strings . ToLower ( fixture . Source ) , err )
2016-01-30 21:27:08 +00:00
}
c . ClearBreakpoint ( bp . ID )
} )
2019-11-25 17:10:18 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 13 ) {
withTestClient2 ( "pkgrenames" , t , func ( c service . Client ) {
someFuncLoc := findLocationHelper ( t , c , "github.com/go-delve/delve/_fixtures/internal/dir%2eio.SomeFunction:0" , false , 1 , 0 ) [ 0 ]
findLocationHelper ( t , c , "dirio.SomeFunction:0" , false , 1 , someFuncLoc )
} )
}
2015-08-07 16:50:14 +00:00
}
2015-08-07 07:30:01 +00:00
2016-01-10 13:08:16 +00:00
func TestClientServer_FindLocationsAddr ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "locationsprog2" , t , func ( c service . Client ) {
2016-01-10 13:08:16 +00:00
<- c . Continue ( )
afunction := findLocationHelper ( t , c , "main.afunction" , false , 1 , 0 ) [ 0 ]
2016-02-11 07:18:39 +00:00
anonfunc := findLocationHelper ( t , c , "main.main.func1" , false , 1 , 0 ) [ 0 ]
2016-01-10 13:08:16 +00:00
findLocationHelper ( t , c , "*fn1" , false , 1 , afunction )
findLocationHelper ( t , c , "*fn3" , false , 1 , anonfunc )
} )
}
2016-10-22 05:04:03 +00:00
func TestClientServer_FindLocationsExactMatch ( t * testing . T ) {
// if an expression matches multiple functions but one of them is an exact
// match it should be used anyway.
// In this example "math/rand.Intn" would normally match "math/rand.Intn"
// and "math/rand.(*Rand).Intn" but since the first match is exact it
// should be prioritized.
withTestClient2 ( "locationsprog3" , t , func ( c service . Client ) {
<- c . Continue ( )
findLocationHelper ( t , c , "math/rand.Intn" , false , 1 , 0 )
} )
}
2015-08-28 20:06:29 +00:00
func TestClientServer_EvalVariable ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testvariables" , t , func ( c service . Client ) {
2015-08-07 07:30:01 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Continue(): %v\n" , state . Err )
}
2020-03-18 16:25:32 +00:00
var1 , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "a1" , normalLoadConfig )
2015-09-17 08:42:34 +00:00
assertNoError ( err , t , "EvalVariable" )
2015-08-07 07:30:01 +00:00
2015-10-18 17:37:13 +00:00
t . Logf ( "var1: %s" , var1 . SinglelineString ( ) )
2015-08-07 07:30:01 +00:00
if var1 . Value != "foofoofoofoofoofoo" {
2015-10-18 17:37:13 +00:00
t . Fatalf ( "Wrong variable value: %s" , var1 . Value )
2015-09-28 10:01:18 +00:00
}
} )
}
func TestClientServer_SetVariable ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testvariables" , t , func ( c service . Client ) {
2015-09-28 10:01:18 +00:00
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Continue(): %v\n" , state . Err )
}
2020-03-18 16:25:32 +00:00
assertNoError ( c . SetVariable ( api . EvalScope { GoroutineID : - 1 } , "a2" , "8" ) , t , "SetVariable()" )
2015-09-28 10:01:18 +00:00
2020-03-18 16:25:32 +00:00
a2 , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "a2" , normalLoadConfig )
2017-06-29 18:15:59 +00:00
if err != nil {
t . Fatalf ( "Could not evaluate variable: %v" , err )
}
2015-09-28 10:01:18 +00:00
2015-10-18 17:37:13 +00:00
t . Logf ( "a2: %v" , a2 )
2015-09-28 10:01:18 +00:00
2015-10-18 17:37:13 +00:00
n , err := strconv . Atoi ( a2 . Value )
if err != nil && n != 8 {
t . Fatalf ( "Wrong variable value: %v" , a2 )
2015-08-07 07:30:01 +00:00
}
} )
}
2015-09-17 08:42:34 +00:00
func TestClientServer_FullStacktrace ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "goroutinestackprog" , t , func ( c service . Client ) {
2015-09-17 08:42:34 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.stacktraceme" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint()" )
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Continue(): %v\n" , state . Err )
}
2018-11-19 14:18:10 +00:00
gs , _ , err := c . ListGoroutines ( 0 , 0 )
2015-09-17 08:42:34 +00:00
assertNoError ( err , t , "GoroutinesInfo()" )
found := make ( [ ] bool , 10 )
for _ , g := range gs {
2019-09-25 17:21:20 +00:00
frames , err := c . Stacktrace ( g . ID , 10 , 0 , & normalLoadConfig )
2015-09-17 08:42:34 +00:00
assertNoError ( err , t , fmt . Sprintf ( "Stacktrace(%d)" , g . ID ) )
for i , frame := range frames {
if frame . Function == nil {
continue
}
2018-07-06 07:37:31 +00:00
if frame . Function . Name ( ) != "main.agoroutine" {
2015-09-17 08:42:34 +00:00
continue
}
t . Logf ( "frame %d: %v" , i , frame )
for _ , arg := range frame . Arguments {
if arg . Name != "i" {
continue
}
2017-02-16 13:16:00 +00:00
t . Logf ( "frame %v, variable i is %v\n" , frame , arg )
2015-10-18 17:37:13 +00:00
argn , err := strconv . Atoi ( arg . Value )
if err == nil {
found [ argn ] = true
}
2015-09-17 08:42:34 +00:00
}
}
}
for i := range found {
if ! found [ i ] {
2020-11-09 19:28:40 +00:00
t . Fatalf ( "Goroutine %d not found" , i )
2015-09-17 08:42:34 +00:00
}
}
state = <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Continue(): %v\n" , state . Err )
}
2019-09-25 17:21:20 +00:00
frames , err := c . Stacktrace ( - 1 , 10 , 0 , & normalLoadConfig )
2015-09-17 08:42:34 +00:00
assertNoError ( err , t , "Stacktrace" )
cur := 3
for i , frame := range frames {
if i == 0 {
continue
}
t . Logf ( "frame %d: %v" , i , frame )
v := frame . Var ( "n" )
if v == nil {
t . Fatalf ( "Could not find value of variable n in frame %d" , i )
}
2015-10-18 17:37:13 +00:00
vn , err := strconv . Atoi ( v . Value )
if err != nil || vn != cur {
t . Fatalf ( "Expected value %d got %d (error: %v)" , cur , vn , err )
2015-09-17 08:42:34 +00:00
}
cur --
if cur < 0 {
break
}
}
} )
}
2016-01-30 06:23:14 +00:00
func TestIssue355 ( t * testing . T ) {
// After the target process has terminated should return an error but not crash
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
2016-01-30 06:23:14 +00:00
bp , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.sayhi" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint()" )
ch := c . Continue ( )
state := <- ch
tid := state . CurrentThread . ID
gid := state . SelectedGoroutine . ID
assertNoError ( state . Err , t , "First Continue()" )
ch = c . Continue ( )
state = <- ch
if ! state . Exited {
t . Fatalf ( "Target did not terminate after second continue" )
}
ch = c . Continue ( )
state = <- ch
assertError ( state . Err , t , "Continue()" )
_ , err = c . Next ( )
assertError ( err , t , "Next()" )
_ , err = c . Step ( )
assertError ( err , t , "Step()" )
_ , err = c . StepInstruction ( )
assertError ( err , t , "StepInstruction()" )
_ , err = c . SwitchThread ( tid )
assertError ( err , t , "SwitchThread()" )
_ , err = c . SwitchGoroutine ( gid )
assertError ( err , t , "SwitchGoroutine()" )
_ , err = c . Halt ( )
assertError ( err , t , "Halt()" )
_ , err = c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : - 1 } )
2017-05-05 22:17:52 +00:00
if testBackend != "rr" {
assertError ( err , t , "CreateBreakpoint()" )
}
2016-01-30 06:23:14 +00:00
_ , err = c . ClearBreakpoint ( bp . ID )
2017-05-05 22:17:52 +00:00
if testBackend != "rr" {
assertError ( err , t , "ClearBreakpoint()" )
}
2016-01-30 06:23:14 +00:00
_ , err = c . ListThreads ( )
assertError ( err , t , "ListThreads()" )
_ , err = c . GetThread ( tid )
assertError ( err , t , "GetThread()" )
2020-03-18 16:25:32 +00:00
assertError ( c . SetVariable ( api . EvalScope { GoroutineID : gid } , "a" , "10" ) , t , "SetVariable()" )
_ , err = c . ListLocalVariables ( api . EvalScope { GoroutineID : gid } , normalLoadConfig )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "ListLocalVariables()" )
2020-03-18 16:25:32 +00:00
_ , err = c . ListFunctionArgs ( api . EvalScope { GoroutineID : gid } , normalLoadConfig )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "ListFunctionArgs()" )
2020-02-24 18:47:02 +00:00
_ , err = c . ListThreadRegisters ( 0 , false )
assertError ( err , t , "ListThreadRegisters()" )
2020-03-18 16:25:32 +00:00
_ , err = c . ListScopeRegisters ( api . EvalScope { GoroutineID : gid } , false )
2020-02-24 18:47:02 +00:00
assertError ( err , t , "ListScopeRegisters()" )
2018-11-19 14:18:10 +00:00
_ , _ , err = c . ListGoroutines ( 0 , 0 )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "ListGoroutines()" )
2019-09-25 17:21:20 +00:00
_ , err = c . Stacktrace ( gid , 10 , 0 , & normalLoadConfig )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "Stacktrace()" )
2020-10-26 12:36:52 +00:00
_ , err = c . FindLocation ( api . EvalScope { GoroutineID : gid } , "+1" , false , nil )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "FindLocation()" )
2020-03-18 16:25:32 +00:00
_ , err = c . DisassemblePC ( api . EvalScope { GoroutineID : - 1 } , 0x40100 , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
assertError ( err , t , "DisassemblePC()" )
} )
}
func TestDisasm ( t * testing . T ) {
// Tests that disassembling by PC, range, and current PC all yeld similar results
// Tests that disassembly by current PC will return a disassembly containing the instruction at PC
// Tests that stepping on a calculated CALL instruction will yield a disassembly that contains the
// effective destination of the CALL instruction
2016-04-18 19:20:20 +00:00
withTestClient2 ( "locationsprog2" , t , func ( c service . Client ) {
2016-02-06 06:00:48 +00:00
ch := c . Continue ( )
state := <- ch
assertNoError ( state . Err , t , "Continue()" )
2020-10-26 12:36:52 +00:00
locs , err := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "main.main" , false , nil )
2016-02-06 06:00:48 +00:00
assertNoError ( err , t , "FindLocation()" )
if len ( locs ) != 1 {
t . Fatalf ( "wrong number of locations for main.main: %d" , len ( locs ) )
}
2020-03-18 16:25:32 +00:00
d1 , err := c . DisassemblePC ( api . EvalScope { GoroutineID : - 1 } , locs [ 0 ] . PC , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
assertNoError ( err , t , "DisassemblePC()" )
if len ( d1 ) < 2 {
t . Fatalf ( "wrong size of disassembly: %d" , len ( d1 ) )
}
pcstart := d1 [ 0 ] . Loc . PC
pcend := d1 [ len ( d1 ) - 1 ] . Loc . PC + uint64 ( len ( d1 [ len ( d1 ) - 1 ] . Bytes ) )
2020-02-19 16:46:03 +00:00
// start address should be less than end address
2020-03-18 16:25:32 +00:00
_ , err = c . DisassembleRange ( api . EvalScope { GoroutineID : - 1 } , pcend , pcstart , api . IntelFlavour )
2020-02-19 16:46:03 +00:00
assertError ( err , t , "DisassembleRange()" )
2020-03-18 16:25:32 +00:00
d2 , err := c . DisassembleRange ( api . EvalScope { GoroutineID : - 1 } , pcstart , pcend , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
assertNoError ( err , t , "DisassembleRange()" )
if len ( d1 ) != len ( d2 ) {
t . Logf ( "d1: %v" , d1 )
t . Logf ( "d2: %v" , d2 )
t . Fatal ( "mismatched length between disassemble pc and disassemble range" )
}
2020-03-18 16:25:32 +00:00
d3 , err := c . DisassemblePC ( api . EvalScope { GoroutineID : - 1 } , state . CurrentThread . PC , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
assertNoError ( err , t , "DisassemblePC() - second call" )
if len ( d1 ) != len ( d3 ) {
t . Logf ( "d1: %v" , d1 )
t . Logf ( "d3: %v" , d3 )
t . Fatal ( "mismatched length between the two calls of disassemble pc" )
}
// look for static call to afunction() on line 29
found := false
for i := range d3 {
2019-11-16 13:43:44 +00:00
if d3 [ i ] . Loc . Line == 29 && ( strings . HasPrefix ( d3 [ i ] . Text , "call" ) || strings . HasPrefix ( d3 [ i ] . Text , "CALL" ) ) && d3 [ i ] . DestLoc != nil && d3 [ i ] . DestLoc . Function != nil && d3 [ i ] . DestLoc . Function . Name ( ) == "main.afunction" {
2016-02-06 06:00:48 +00:00
found = true
break
}
}
if ! found {
t . Fatal ( "Could not find call to main.afunction on line 29" )
}
haspc := false
for i := range d3 {
if d3 [ i ] . AtPC {
haspc = true
break
}
}
if ! haspc {
t . Logf ( "d3: %v" , d3 )
t . Fatal ( "PC instruction not found" )
}
2020-03-10 16:34:40 +00:00
if runtime . GOARCH == "386" && buildMode == "pie" {
// Skip the rest of the test because on intel 386 with PIE build mode
// the compiler will insert calls to __x86.get_pc_thunk which do not have DIEs and we can't resolve.
return
}
2016-02-06 06:00:48 +00:00
2020-03-10 16:34:40 +00:00
startinstr := getCurinstr ( d3 )
2016-02-06 06:00:48 +00:00
count := 0
for {
if count > 20 {
t . Fatal ( "too many step instructions executed without finding a call instruction" )
}
state , err := c . StepInstruction ( )
assertNoError ( err , t , fmt . Sprintf ( "StepInstruction() %d" , count ) )
2020-03-18 16:25:32 +00:00
d3 , err = c . DisassemblePC ( api . EvalScope { GoroutineID : - 1 } , state . CurrentThread . PC , api . IntelFlavour )
2016-02-06 06:00:48 +00:00
assertNoError ( err , t , fmt . Sprintf ( "StepInstruction() %d" , count ) )
curinstr := getCurinstr ( d3 )
if curinstr == nil {
t . Fatalf ( "Could not find current instruction %d" , count )
}
if curinstr . Loc . Line != startinstr . Loc . Line {
t . Fatal ( "Calling StepInstruction() repeatedly did not find the call instruction" )
}
2019-11-16 13:43:44 +00:00
if strings . HasPrefix ( curinstr . Text , "call" ) || strings . HasPrefix ( curinstr . Text , "CALL" ) {
2016-02-06 06:00:48 +00:00
t . Logf ( "call: %v" , curinstr )
if curinstr . DestLoc == nil || curinstr . DestLoc . Function == nil {
t . Fatalf ( "Call instruction does not have destination: %v" , curinstr )
}
2018-07-06 07:37:31 +00:00
if curinstr . DestLoc . Function . Name ( ) != "main.afunction" {
2016-02-06 06:00:48 +00:00
t . Fatalf ( "Call instruction destination not main.afunction: %v" , curinstr )
}
break
}
count ++
}
2016-01-30 06:23:14 +00:00
} )
}
2016-02-02 11:26:29 +00:00
func TestNegativeStackDepthBug ( t * testing . T ) {
// After the target process has terminated should return an error but not crash
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
2016-02-02 11:26:29 +00:00
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.sayhi" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint()" )
ch := c . Continue ( )
state := <- ch
assertNoError ( state . Err , t , "Continue()" )
2019-09-25 17:21:20 +00:00
_ , err = c . Stacktrace ( - 1 , - 2 , 0 , & normalLoadConfig )
2016-02-02 11:26:29 +00:00
assertError ( err , t , "Stacktrace()" )
} )
}
2016-01-24 08:16:45 +00:00
func TestClientServer_CondBreakpoint ( t * testing . T ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "parallel_next" , t , func ( c service . Client ) {
2016-01-24 08:16:45 +00:00
bp , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.sayhi" , Line : 1 } )
assertNoError ( err , t , "CreateBreakpoint()" )
bp . Cond = "n == 7"
assertNoError ( c . AmendBreakpoint ( bp ) , t , "AmendBreakpoint() 1" )
bp , err = c . GetBreakpoint ( bp . ID )
assertNoError ( err , t , "GetBreakpoint() 1" )
bp . Variables = append ( bp . Variables , "n" )
assertNoError ( c . AmendBreakpoint ( bp ) , t , "AmendBreakpoint() 2" )
bp , err = c . GetBreakpoint ( bp . ID )
assertNoError ( err , t , "GetBreakpoint() 2" )
if bp . Cond == "" {
t . Fatalf ( "No condition set on breakpoint %#v" , bp )
}
if len ( bp . Variables ) != 1 {
t . Fatalf ( "Wrong number of expressions to evaluate on breakpoint %#v" , bp )
}
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
2020-03-18 16:25:32 +00:00
nvar , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "n" , normalLoadConfig )
2016-01-24 08:16:45 +00:00
assertNoError ( err , t , "EvalVariable()" )
if nvar . SinglelineString ( ) != "7" {
t . Fatalf ( "Stopped on wrong goroutine %s\n" , nvar . Value )
}
} )
}
2016-02-11 07:18:39 +00:00
2019-06-25 20:50:05 +00:00
func clientEvalVariable ( t * testing . T , c service . Client , expr string ) * api . Variable {
v , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , expr , normalLoadConfig )
assertNoError ( err , t , fmt . Sprintf ( "EvalVariable(%s)" , expr ) )
return v
}
2016-02-11 07:18:39 +00:00
func TestSkipPrologue ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "locationsprog2" , t , func ( c service . Client ) {
2016-02-11 07:18:39 +00:00
<- c . Continue ( )
afunction := findLocationHelper ( t , c , "main.afunction" , false , 1 , 0 ) [ 0 ]
findLocationHelper ( t , c , "*fn1" , false , 1 , afunction )
findLocationHelper ( t , c , "locationsprog2.go:8" , false , 1 , afunction )
2019-06-25 20:50:05 +00:00
afunction0 := uint64 ( clientEvalVariable ( t , c , "main.afunction" ) . Addr )
2016-02-11 07:18:39 +00:00
if afunction == afunction0 {
t . Fatal ( "Skip prologue failed" )
}
} )
}
func TestSkipPrologue2 ( t * testing . T ) {
2016-04-18 19:20:20 +00:00
withTestClient2 ( "callme" , t , func ( c service . Client ) {
2016-02-11 07:18:39 +00:00
callme := findLocationHelper ( t , c , "main.callme" , false , 1 , 0 ) [ 0 ]
2019-06-25 20:50:05 +00:00
callmeZ := uint64 ( clientEvalVariable ( t , c , "main.callme" ) . Addr )
2016-02-11 07:18:39 +00:00
findLocationHelper ( t , c , "callme.go:5" , false , 1 , callme )
if callme == callmeZ {
t . Fatal ( "Skip prologue failed" )
}
callme2 := findLocationHelper ( t , c , "main.callme2" , false , 1 , 0 ) [ 0 ]
2019-06-25 20:50:05 +00:00
callme2Z := uint64 ( clientEvalVariable ( t , c , "main.callme2" ) . Addr )
2016-02-11 07:18:39 +00:00
findLocationHelper ( t , c , "callme.go:12" , false , 1 , callme2 )
if callme2 == callme2Z {
t . Fatal ( "Skip prologue failed" )
}
callme3 := findLocationHelper ( t , c , "main.callme3" , false , 1 , 0 ) [ 0 ]
2019-06-25 20:50:05 +00:00
callme3Z := uint64 ( clientEvalVariable ( t , c , "main.callme3" ) . Addr )
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
2020-03-10 16:34:40 +00:00
if ( ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVer18Beta ) ) && runtime . GOARCH != "386" {
2017-02-07 21:07:18 +00:00
findLocationHelper ( t , c , "callme.go:19" , false , 1 , callme3 )
} else {
// callme3 does not have local variables therefore the first line of the
// function is immediately after the prologue
2020-03-10 16:34:40 +00:00
// This is only true before go1.8 or on Intel386 where frame pointer chaining
// introduced a bit of prologue even for functions without local variables
2017-02-07 21:07:18 +00:00
findLocationHelper ( t , c , "callme.go:19" , false , 1 , callme3Z )
}
2016-02-11 07:18:39 +00:00
if callme3 == callme3Z {
t . Fatal ( "Skip prologue failed" )
}
} )
}
2016-02-15 14:39:29 +00:00
func TestIssue419 ( t * testing . T ) {
// Calling service/rpc.(*Client).Halt could cause a crash because both Halt and Continue simultaneously
// try to read 'runtime.g' and debug/dwarf.Data.Type is not thread safe
2019-10-21 18:48:04 +00:00
finish := make ( chan struct { } )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "issue419" , t , func ( c service . Client ) {
2016-02-15 14:39:29 +00:00
go func ( ) {
2019-10-21 18:48:04 +00:00
defer close ( finish )
2016-02-15 14:39:29 +00:00
rand . Seed ( time . Now ( ) . Unix ( ) )
d := time . Duration ( rand . Intn ( 4 ) + 1 )
time . Sleep ( d * time . Second )
2019-10-21 18:48:04 +00:00
t . Logf ( "halt" )
2016-02-15 14:39:29 +00:00
_ , err := c . Halt ( )
assertNoError ( err , t , "RequestManualStop()" )
} ( )
statech := c . Continue ( )
state := <- statech
assertNoError ( state . Err , t , "Continue()" )
2019-10-21 18:48:04 +00:00
t . Logf ( "done" )
<- finish
2016-02-15 14:39:29 +00:00
} )
}
2016-02-22 15:08:45 +00:00
func TestTypesCommand ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "testvariables2" , t , func ( c service . Client ) {
2016-02-22 15:08:45 +00:00
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
types , err := c . ListTypes ( "" )
assertNoError ( err , t , "ListTypes()" )
found := false
for i := range types {
if types [ i ] == "main.astruct" {
found = true
break
}
}
if ! found {
t . Fatal ( "Type astruct not found in ListTypes output" )
}
2016-02-27 23:50:37 +00:00
types , err = c . ListTypes ( "^main.astruct$" )
2016-02-22 15:08:45 +00:00
assertNoError ( err , t , "ListTypes(\"main.astruct\")" )
2016-02-27 23:50:37 +00:00
if len ( types ) != 1 {
t . Fatalf ( "ListTypes(\"^main.astruct$\") did not filter properly, expected 1 got %d: %v" , len ( types ) , types )
2016-02-22 15:08:45 +00:00
}
} )
}
2016-04-18 19:20:20 +00:00
func TestIssue406 ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-04-18 19:20:20 +00:00
withTestClient2 ( "issue406" , t , func ( c service . Client ) {
2020-10-26 12:36:52 +00:00
locs , err := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "issue406.go:146" , false , nil )
2016-04-18 19:20:20 +00:00
assertNoError ( err , t , "FindLocation()" )
_ , err = c . CreateBreakpoint ( & api . Breakpoint { Addr : locs [ 0 ] . PC } )
assertNoError ( err , t , "CreateBreakpoint()" )
ch := c . Continue ( )
state := <- ch
assertNoError ( state . Err , t , "Continue()" )
2020-03-18 16:25:32 +00:00
v , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "cfgtree" , normalLoadConfig )
2016-04-18 19:20:20 +00:00
assertNoError ( err , t , "EvalVariable()" )
vs := v . MultilineString ( "" )
t . Logf ( "cfgtree formats to: %s\n" , vs )
} )
}
2016-04-24 17:15:39 +00:00
func TestEvalExprName ( t * testing . T ) {
withTestClient2 ( "testvariables2" , t , func ( c service . Client ) {
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
2020-03-18 16:25:32 +00:00
var1 , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "i1+1" , normalLoadConfig )
2016-04-24 17:15:39 +00:00
assertNoError ( err , t , "EvalVariable" )
const name = "i1+1"
t . Logf ( "i1+1 → %#v" , var1 )
if var1 . Name != name {
t . Fatalf ( "Wrong variable name %q, expected %q" , var1 . Name , name )
}
} )
}
2016-05-18 07:07:19 +00:00
func TestClientServer_Issue528 ( t * testing . T ) {
// FindLocation with Receiver.MethodName syntax does not work
// on remote package names due to a bug in debug/gosym that
// Was fixed in go 1.7 // Commit that fixes the issue in go:
// f744717d1924340b8f5e5a385e99078693ad9097
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
2020-03-18 16:25:32 +00:00
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { Major : 1 , Minor : 7 , Rev : - 1 } ) {
2016-05-18 07:07:19 +00:00
t . Log ( "Test skipped" )
return
}
withTestClient2 ( "issue528" , t , func ( c service . Client ) {
findLocationHelper ( t , c , "State.Close" , false , 1 , 0 )
} )
}
2016-11-15 16:16:33 +00:00
func TestClientServer_FpRegisters ( t * testing . T ) {
2019-11-16 13:43:44 +00:00
if runtime . GOARCH != "amd64" {
t . Skip ( "test is valid only on AMD64" )
2019-11-08 14:17:57 +00:00
}
2016-11-15 16:16:33 +00:00
regtests := [ ] struct { name , value string } {
2020-08-31 17:55:43 +00:00
// x87
2016-11-15 16:16:33 +00:00
{ "ST(0)" , "0x3fffe666660000000000" } ,
{ "ST(1)" , "0x3fffd9999a0000000000" } ,
{ "ST(2)" , "0x3fffcccccd0000000000" } ,
{ "ST(3)" , "0x3fffc000000000000000" } ,
{ "ST(4)" , "0x3fffb333333333333000" } ,
{ "ST(5)" , "0x3fffa666666666666800" } ,
{ "ST(6)" , "0x3fff9999999999999800" } ,
{ "ST(7)" , "0x3fff8cccccccccccd000" } ,
2020-08-31 17:55:43 +00:00
// SSE
2016-11-15 16:16:33 +00:00
{ "XMM0" , "0x3ff33333333333333ff199999999999a v2_int={ 3ff199999999999a 3ff3333333333333 } v4_int={ 9999999a 3ff19999 33333333 3ff33333 } v8_int={ 999a 9999 9999 3ff1 3333 3333 3333 3ff3 } v16_int={ 9a 99 99 99 99 99 f1 3f 33 33 33 33 33 33 f3 3f }" } ,
{ "XMM1" , "0x3ff66666666666663ff4cccccccccccd" } ,
{ "XMM2" , "0x3fe666663fd9999a3fcccccd3fc00000" } ,
{ "XMM3" , "0x3ff199999999999a3ff3333333333333" } ,
{ "XMM4" , "0x3ff4cccccccccccd3ff6666666666666" } ,
{ "XMM5" , "0x3fcccccd3fc000003fe666663fd9999a" } ,
{ "XMM6" , "0x4004cccccccccccc4003333333333334" } ,
{ "XMM7" , "0x40026666666666664002666666666666" } ,
{ "XMM8" , "0x4059999a404ccccd4059999a404ccccd" } ,
2020-08-31 17:55:43 +00:00
// AVX 2
{ "XMM11" , "0x3ff66666666666663ff4cccccccccccd" } ,
{ "XMM11" , "…[YMM11h] 0x3ff66666666666663ff4cccccccccccd" } ,
// AVX 512
{ "XMM12" , "0x3ff66666666666663ff4cccccccccccd" } ,
{ "XMM12" , "…[YMM12h] 0x3ff66666666666663ff4cccccccccccd" } ,
{ "XMM12" , "…[ZMM12hl] 0x3ff66666666666663ff4cccccccccccd" } ,
{ "XMM12" , "…[ZMM12hh] 0x3ff66666666666663ff4cccccccccccd" } ,
2016-11-15 16:16:33 +00:00
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-11-15 16:16:33 +00:00
withTestClient2 ( "fputest/" , t , func ( c service . Client ) {
2020-08-31 17:55:43 +00:00
state := <- c . Continue ( )
t . Logf ( "state after continue: %#v" , state )
boolvar := func ( name string ) bool {
scope := api . EvalScope { GoroutineID : - 1 }
if testBackend == "rr" {
scope . Frame = 2
}
v , err := c . EvalVariable ( scope , name , normalLoadConfig )
if err != nil {
t . Fatalf ( "could not read %s variable" , name )
}
t . Logf ( "%s variable: %#v" , name , v )
return v . Value != "false"
}
avx2 := boolvar ( "avx2" )
avx512 := boolvar ( "avx512" )
if runtime . GOOS == "windows" {
// not supported
avx2 = false
avx512 = false
}
2020-02-24 18:47:02 +00:00
regs , err := c . ListThreadRegisters ( 0 , true )
assertNoError ( err , t , "ListThreadRegisters()" )
2016-11-15 16:16:33 +00:00
t . Logf ( "%s" , regs . String ( ) )
for _ , regtest := range regtests {
2020-08-31 17:55:43 +00:00
if regtest . name == "XMM11" && ! avx2 {
continue
}
if regtest . name == "XMM12" && ! avx512 {
continue
}
2016-11-15 16:16:33 +00:00
found := false
for _ , reg := range regs {
if reg . Name == regtest . name {
found = true
2020-08-31 17:55:43 +00:00
if strings . HasPrefix ( regtest . value , "…" ) {
if ! strings . Contains ( reg . Value , regtest . value [ len ( "…" ) : ] ) {
t . Fatalf ( "register %s expected to contain %q got %q" , reg . Name , regtest . value , reg . Value )
}
} else {
if ! strings . HasPrefix ( reg . Value , regtest . value ) {
t . Fatalf ( "register %s expected %q got %q" , reg . Name , regtest . value , reg . Value )
}
2016-11-15 16:16:33 +00:00
}
}
}
if ! found {
t . Fatalf ( "register %s not found: %v" , regtest . name , regs )
}
}
} )
}
2017-01-05 19:13:07 +00:00
func TestClientServer_RestartBreakpointPosition ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2018-05-29 15:01:51 +00:00
if buildMode == "pie" {
t . Skip ( "not meaningful in PIE mode" )
}
2017-01-05 19:13:07 +00:00
withTestClient2 ( "locationsprog2" , t , func ( c service . Client ) {
bpBefore , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.afunction" , Line : - 1 , Tracepoint : true , Name : "this" } )
addrBefore := bpBefore . Addr
t . Logf ( "%x\n" , bpBefore . Addr )
assertNoError ( err , t , "CreateBreakpoint" )
2017-05-05 22:17:52 +00:00
stateCh := c . Continue ( )
for range stateCh {
}
2017-01-05 19:13:07 +00:00
_ , err = c . Halt ( )
assertNoError ( err , t , "Halt" )
2020-06-05 18:03:09 +00:00
_ , err = c . Restart ( false )
2017-01-05 19:13:07 +00:00
assertNoError ( err , t , "Restart" )
bps , err := c . ListBreakpoints ( )
assertNoError ( err , t , "ListBreakpoints" )
for _ , bp := range bps {
if bp . Name == bpBefore . Name {
if bp . Addr != addrBefore {
t . Fatalf ( "Address changed after restart: %x %x" , bp . Addr , addrBefore )
}
t . Logf ( "%x %x\n" , bp . Addr , addrBefore )
}
}
} )
}
2017-02-07 21:44:36 +00:00
func TestClientServer_SelectedGoroutineLoc ( t * testing . T ) {
// CurrentLocation of SelectedGoroutine should reflect what's happening on
// the thread running the goroutine, not the position the goroutine was in
// the last time it was parked.
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-02-07 21:44:36 +00:00
withTestClient2 ( "testprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : - 11 } )
assertNoError ( err , t , "CreateBreakpoint" )
s := <- c . Continue ( )
assertNoError ( s . Err , t , "Continue" )
gloc := s . SelectedGoroutine . CurrentLoc
if gloc . PC != s . CurrentThread . PC {
t . Errorf ( "mismatched PC %#x %#x" , gloc . PC , s . CurrentThread . PC )
}
if gloc . File != s . CurrentThread . File || gloc . Line != s . CurrentThread . Line {
t . Errorf ( "mismatched file:lineno: %s:%d %s:%d" , gloc . File , gloc . Line , s . CurrentThread . File , s . CurrentThread . Line )
}
} )
}
2017-05-05 22:17:52 +00:00
func TestClientServer_ReverseContinue ( t * testing . T ) {
protest . AllowRecording ( t )
if testBackend != "rr" {
t . Skip ( "backend is not rr" )
}
withTestClient2 ( "continuetestprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint(main.main)" )
_ , err = c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.sayhi" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint(main.sayhi)" )
state := <- c . Continue ( )
assertNoError ( state . Err , t , "first continue" )
mainPC := state . CurrentThread . PC
t . Logf ( "after first continue %#x" , mainPC )
state = <- c . Continue ( )
assertNoError ( state . Err , t , "second continue" )
sayhiPC := state . CurrentThread . PC
t . Logf ( "after second continue %#x" , sayhiPC )
if mainPC == sayhiPC {
t . Fatalf ( "expected different PC after second PC (%#x)" , mainPC )
}
state = <- c . Rewind ( )
assertNoError ( state . Err , t , "rewind" )
if mainPC != state . CurrentThread . PC {
t . Fatalf ( "Expected rewind to go back to the first breakpoint: %#x" , state . CurrentThread . PC )
}
} )
2017-06-06 12:59:52 +00:00
}
func TestClientServer_collectBreakpointInfoOnNext ( t * testing . T ) {
protest . AllowRecording ( t )
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint {
Addr : findLocationHelper ( t , c , "testnextprog.go:23" , false , 1 , 0 ) [ 0 ] ,
Variables : [ ] string { "j" } ,
LoadLocals : & normalLoadConfig } )
assertNoError ( err , t , "CreateBreakpoint()" )
_ , err = c . CreateBreakpoint ( & api . Breakpoint {
Addr : findLocationHelper ( t , c , "testnextprog.go:24" , false , 1 , 0 ) [ 0 ] ,
Variables : [ ] string { "j" } ,
LoadLocals : & normalLoadConfig } )
assertNoError ( err , t , "CreateBreakpoint()" )
stateBefore := <- c . Continue ( )
assertNoError ( stateBefore . Err , t , "Continue()" )
if stateBefore . CurrentThread . Line != 23 {
t . Fatalf ( "wrong line number %s:%d, expected %d" , stateBefore . CurrentThread . File , stateBefore . CurrentThread . Line , 23 )
}
if bi := stateBefore . CurrentThread . BreakpointInfo ; bi == nil || len ( bi . Variables ) != 1 {
t . Fatalf ( "bad breakpoint info %v" , bi )
}
2017-05-05 22:17:52 +00:00
2017-06-06 12:59:52 +00:00
stateAfter , err := c . Next ( )
assertNoError ( err , t , "Next()" )
if stateAfter . CurrentThread . Line != 24 {
t . Fatalf ( "wrong line number %s:%d, expected %d" , stateAfter . CurrentThread . File , stateAfter . CurrentThread . Line , 24 )
}
if bi := stateAfter . CurrentThread . BreakpointInfo ; bi == nil || len ( bi . Variables ) != 1 {
t . Fatalf ( "bad breakpoint info %v" , bi )
}
} )
2017-05-05 22:17:52 +00:00
}
2017-06-23 12:11:51 +00:00
func TestClientServer_collectBreakpointInfoError ( t * testing . T ) {
protest . AllowRecording ( t )
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint {
Addr : findLocationHelper ( t , c , "testnextprog.go:23" , false , 1 , 0 ) [ 0 ] ,
Variables : [ ] string { "nonexistentvariable" , "j" } ,
LoadLocals : & normalLoadConfig } )
assertNoError ( err , t , "CreateBreakpoint()" )
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
} )
}
2017-12-19 01:24:56 +00:00
func TestClientServerConsistentExit ( t * testing . T ) {
// This test is useful because it ensures that Next and Continue operations both
// exit with the same exit status and details when the target application terminates.
// Other program execution API calls should also behave in the same way.
2018-03-20 10:05:35 +00:00
// An error should be present in state.Err.
2017-12-19 01:24:56 +00:00
withTestClient2 ( "pr1055" , t , func ( c service . Client ) {
fp := testProgPath ( t , "pr1055" )
_ , err := c . CreateBreakpoint ( & api . Breakpoint { File : fp , Line : 12 } )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
}
state , err = c . Next ( )
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
if ! state . Exited {
t . Fatal ( "Process state is not exited" )
}
if state . ExitStatus != 2 {
t . Fatalf ( "Process exit status is not 2, got: %v" , state . ExitStatus )
}
} )
}
2018-05-11 12:51:15 +00:00
func TestClientServer_StepOutReturn ( t * testing . T ) {
ver , _ := goversion . Parse ( runtime . Version ( ) )
2020-03-18 16:25:32 +00:00
if ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { Major : 1 , Minor : 10 , Rev : - 1 } ) {
2018-05-11 12:51:15 +00:00
t . Skip ( "return variables aren't marked on 1.9 or earlier" )
}
withTestClient2 ( "stepoutret" , t , func ( c service . Client ) {
c . SetReturnValuesLoadConfig ( & normalLoadConfig )
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.stepout" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoint()" )
stateBefore := <- c . Continue ( )
assertNoError ( stateBefore . Err , t , "Continue()" )
stateAfter , err := c . StepOut ( )
assertNoError ( err , t , "StepOut" )
ret := stateAfter . CurrentThread . ReturnValues
if len ( ret ) != 2 {
t . Fatalf ( "wrong number of return values %v" , ret )
}
2018-11-10 17:35:37 +00:00
stridx := 0
numidx := 1
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 12 ) {
// in 1.11 and earlier the order of return values in DWARF is
// unspecified, in 1.11 and later it follows the order of definition
// specified by the user
for i := range ret {
if ret [ i ] . Name == "str" {
stridx = i
numidx = 1 - i
break
}
}
}
if ret [ stridx ] . Name != "str" {
t . Fatalf ( "(str) bad return value name %s" , ret [ stridx ] . Name )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ stridx ] . Kind != reflect . String {
t . Fatalf ( "(str) bad return value kind %v" , ret [ stridx ] . Kind )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ stridx ] . Value != "return 47" {
t . Fatalf ( "(str) bad return value %q" , ret [ stridx ] . Value )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ numidx ] . Name != "num" {
t . Fatalf ( "(num) bad return value name %s" , ret [ numidx ] . Name )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ numidx ] . Kind != reflect . Int {
t . Fatalf ( "(num) bad return value kind %v" , ret [ numidx ] . Kind )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ numidx ] . Value != "48" {
t . Fatalf ( "(num) bad return value %s" , ret [ numidx ] . Value )
2018-05-11 12:51:15 +00:00
}
} )
}
2018-06-21 11:07:37 +00:00
func TestAcceptMulticlient ( t * testing . T ) {
if testBackend == "rr" {
t . Skip ( "recording not allowed for TestAcceptMulticlient" )
}
2019-07-30 15:38:25 +00:00
listener , err := net . Listen ( "tcp" , "127.0.0.1:0" )
2018-06-21 11:07:37 +00:00
if err != nil {
t . Fatalf ( "couldn't start listener: %s\n" , err )
}
serverDone := make ( chan struct { } )
go func ( ) {
defer close ( serverDone )
defer listener . Close ( )
disconnectChan := make ( chan struct { } )
server := rpccommon . NewServer ( & service . Config {
Listener : listener ,
ProcessArgs : [ ] string { protest . BuildFixture ( "testvariables2" , 0 ) . Path } ,
AcceptMulti : true ,
DisconnectChan : disconnectChan ,
2020-04-13 18:07:15 +00:00
Debugger : debugger . Config {
2020-06-05 18:03:09 +00:00
Backend : testBackend ,
ExecuteKind : debugger . ExecutingGeneratedTest ,
2020-04-13 18:07:15 +00:00
} ,
2018-06-21 11:07:37 +00:00
} )
if err := server . Run ( ) ; err != nil {
t . Fatal ( err )
}
<- disconnectChan
server . Stop ( )
} ( )
client1 := rpc2 . NewClient ( listener . Addr ( ) . String ( ) )
client1 . Disconnect ( false )
client2 := rpc2 . NewClient ( listener . Addr ( ) . String ( ) )
state := <- client2 . Continue ( )
2018-07-06 07:37:31 +00:00
if state . CurrentThread . Function . Name ( ) != "main.main" {
2018-06-21 11:07:37 +00:00
t . Fatalf ( "bad state after continue: %v\n" , state )
}
client2 . Detach ( true )
<- serverDone
}
2018-05-04 17:31:45 +00:00
func mustHaveDebugCalls ( t * testing . T , c service . Client ) {
2020-10-26 12:36:52 +00:00
locs , err := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "runtime.debugCallV1" , false , nil )
2018-05-04 17:31:45 +00:00
if len ( locs ) == 0 || err != nil {
t . Skip ( "function calls not supported on this version of go" )
}
}
func TestClientServerFunctionCall ( t * testing . T ) {
2018-07-28 19:12:07 +00:00
protest . MustSupportFunctionCalls ( t , testBackend )
2018-05-04 17:31:45 +00:00
withTestClient2 ( "fncall" , t , func ( c service . Client ) {
mustHaveDebugCalls ( t , c )
c . SetReturnValuesLoadConfig ( & normalLoadConfig )
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
2018-07-06 07:37:31 +00:00
beforeCallFn := state . CurrentThread . Function . Name ( )
2019-06-30 17:44:30 +00:00
state , err := c . Call ( - 1 , "call1(one, two)" , false )
2018-05-04 17:31:45 +00:00
assertNoError ( err , t , "Call()" )
2018-07-06 07:37:31 +00:00
t . Logf ( "returned to %q" , state . CurrentThread . Function . Name ( ) )
if state . CurrentThread . Function . Name ( ) != beforeCallFn {
t . Fatalf ( "did not return to the calling function %q %q" , beforeCallFn , state . CurrentThread . Function . Name ( ) )
2018-05-04 17:31:45 +00:00
}
if state . CurrentThread . ReturnValues == nil {
t . Fatal ( "no return values on return from call" )
}
t . Logf ( "Return values %v" , state . CurrentThread . ReturnValues )
if len ( state . CurrentThread . ReturnValues ) != 1 {
t . Fatal ( "not enough return values" )
}
if state . CurrentThread . ReturnValues [ 0 ] . Value != "3" {
t . Fatalf ( "wrong return value %s" , state . CurrentThread . ReturnValues [ 0 ] . Value )
}
state = <- c . Continue ( )
if ! state . Exited {
t . Fatalf ( "expected process to exit after call %v" , state . CurrentThread )
}
} )
}
func TestClientServerFunctionCallBadPos ( t * testing . T ) {
2018-07-28 19:12:07 +00:00
protest . MustSupportFunctionCalls ( t , testBackend )
2018-11-10 17:35:51 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 12 ) {
t . Skip ( "this is a safe point for Go 1.12" )
}
2018-05-04 17:31:45 +00:00
withTestClient2 ( "fncall" , t , func ( c service . Client ) {
mustHaveDebugCalls ( t , c )
2020-10-26 12:36:52 +00:00
loc , err := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "fmt/print.go:649" , false , nil )
2018-05-04 17:31:45 +00:00
assertNoError ( err , t , "could not find location" )
_ , err = c . CreateBreakpoint ( & api . Breakpoint { File : loc [ 0 ] . File , Line : loc [ 0 ] . Line } )
assertNoError ( err , t , "CreateBreakpoin" )
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
state = <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
2019-05-09 15:29:58 +00:00
c . SetReturnValuesLoadConfig ( & normalLoadConfig )
2019-06-30 17:44:30 +00:00
state , err = c . Call ( - 1 , "main.call1(main.zero, main.zero)" , false )
2018-05-04 17:31:45 +00:00
if err == nil || err . Error ( ) != "call not at safe point" {
t . Fatalf ( "wrong error or no error: %v" , err )
}
} )
}
func TestClientServerFunctionCallPanic ( t * testing . T ) {
2018-07-28 19:12:07 +00:00
protest . MustSupportFunctionCalls ( t , testBackend )
2018-05-04 17:31:45 +00:00
withTestClient2 ( "fncall" , t , func ( c service . Client ) {
mustHaveDebugCalls ( t , c )
c . SetReturnValuesLoadConfig ( & normalLoadConfig )
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
2019-06-30 17:44:30 +00:00
state , err := c . Call ( - 1 , "callpanic()" , false )
2018-05-04 17:31:45 +00:00
assertNoError ( err , t , "Call()" )
t . Logf ( "at: %s:%d" , state . CurrentThread . File , state . CurrentThread . Line )
if state . CurrentThread . ReturnValues == nil {
t . Fatal ( "no return values on return from call" )
}
t . Logf ( "Return values %v" , state . CurrentThread . ReturnValues )
if len ( state . CurrentThread . ReturnValues ) != 1 {
t . Fatal ( "not enough return values" )
}
if state . CurrentThread . ReturnValues [ 0 ] . Name != "~panic" {
t . Fatal ( "not a panic" )
}
if state . CurrentThread . ReturnValues [ 0 ] . Children [ 0 ] . Value != "callpanic panicked" {
t . Fatalf ( "wrong panic value %s" , state . CurrentThread . ReturnValues [ 0 ] . Children [ 0 ] . Value )
}
} )
}
func TestClientServerFunctionCallStacktrace ( t * testing . T ) {
2020-07-28 16:19:51 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 15 ) {
t . Skip ( "Go 1.15 executes function calls in a different goroutine so the stack trace will not contain main.main or runtime.main" )
}
2018-07-28 19:12:07 +00:00
protest . MustSupportFunctionCalls ( t , testBackend )
2018-05-04 17:31:45 +00:00
withTestClient2 ( "fncall" , t , func ( c service . Client ) {
mustHaveDebugCalls ( t , c )
2020-03-18 16:25:32 +00:00
c . SetReturnValuesLoadConfig ( & api . LoadConfig { FollowPointers : false , MaxStringLen : 2048 } )
2018-05-04 17:31:45 +00:00
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
2019-06-30 17:44:30 +00:00
state , err := c . Call ( - 1 , "callstacktrace()" , false )
2018-05-04 17:31:45 +00:00
assertNoError ( err , t , "Call()" )
t . Logf ( "at: %s:%d" , state . CurrentThread . File , state . CurrentThread . Line )
if state . CurrentThread . ReturnValues == nil {
t . Fatal ( "no return values on return from call" )
}
if len ( state . CurrentThread . ReturnValues ) != 1 || state . CurrentThread . ReturnValues [ 0 ] . Kind != reflect . String {
t . Fatal ( "not enough return values" )
}
st := state . CurrentThread . ReturnValues [ 0 ] . Value
t . Logf ( "Returned stacktrace:\n%s" , st )
if ! strings . Contains ( st , "main.callstacktrace" ) || ! strings . Contains ( st , "main.main" ) || ! strings . Contains ( st , "runtime.main" ) {
t . Fatal ( "bad stacktrace returned" )
}
} )
}
2019-03-16 13:50:18 +00:00
func TestAncestors ( t * testing . T ) {
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
t . Skip ( "not supported on Go <= 1.10" )
}
savedGodebug := os . Getenv ( "GODEBUG" )
os . Setenv ( "GODEBUG" , "tracebackancestors=100" )
defer os . Setenv ( "GODEBUG" , savedGodebug )
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.testgoroutine" , Line : - 1 } )
assertNoError ( err , t , "CreateBreakpoin" )
state := <- c . Continue ( )
assertNoError ( state . Err , t , "Continue()" )
ancestors , err := c . Ancestors ( - 1 , 1000 , 1000 )
assertNoError ( err , t , "Ancestors" )
t . Logf ( "ancestors: %#v\n" , ancestors )
if len ( ancestors ) != 1 {
t . Fatalf ( "expected only one ancestor got %d" , len ( ancestors ) )
}
mainFound := false
for _ , ancestor := range ancestors {
for _ , frame := range ancestor . Stack {
if frame . Function . Name ( ) == "main.main" {
mainFound = true
}
}
}
if ! mainFound {
t . Fatal ( "function main.main not found in any ancestor" )
}
} )
}
2019-06-04 21:33:19 +00:00
type brokenRPCClient struct {
client * rpc . Client
}
func ( c * brokenRPCClient ) Detach ( kill bool ) error {
defer c . client . Close ( )
out := new ( rpc2 . DetachOut )
2020-03-18 16:25:32 +00:00
return c . call ( "Detach" , rpc2 . DetachIn { Kill : kill } , out )
2019-06-04 21:33:19 +00:00
}
func ( c * brokenRPCClient ) call ( method string , args , reply interface { } ) error {
return c . client . Call ( "RPCServer." + method , args , reply )
}
func TestUnknownMethodCall ( t * testing . T ) {
2020-08-21 14:14:02 +00:00
clientConn , _ := startServer ( "continuetestprog" , 0 , t , [ 3 ] string { } )
2019-06-04 21:33:19 +00:00
client := & brokenRPCClient { jsonrpc . NewClient ( clientConn ) }
2020-03-18 16:25:32 +00:00
client . call ( "SetApiVersion" , api . SetAPIVersionIn { APIVersion : 2 } , & api . SetAPIVersionOut { } )
2019-06-04 21:33:19 +00:00
defer client . Detach ( true )
var out int
err := client . call ( "NonexistentRPCCall" , nil , & out )
assertError ( err , t , "call()" )
if ! strings . HasPrefix ( err . Error ( ) , "unknown method: " ) {
t . Errorf ( "wrong error message: %v" , err )
}
}
2019-10-07 16:32:38 +00:00
func TestIssue1703 ( t * testing . T ) {
// Calling Disassemble when there is no current goroutine should work.
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
2020-10-26 12:36:52 +00:00
locs , err := c . FindLocation ( api . EvalScope { GoroutineID : - 1 } , "main.main" , true , nil )
2019-10-07 16:32:38 +00:00
assertNoError ( err , t , "FindLocation" )
t . Logf ( "FindLocation: %#v" , locs )
text , err := c . DisassemblePC ( api . EvalScope { GoroutineID : - 1 } , locs [ 0 ] . PC , api . IntelFlavour )
assertNoError ( err , t , "DisassemblePC" )
t . Logf ( "text: %#v\n" , text )
} )
}
2019-10-21 18:48:04 +00:00
func TestRerecord ( t * testing . T ) {
protest . AllowRecording ( t )
if testBackend != "rr" {
t . Skip ( "only valid for recorded targets" )
}
withTestClient2 ( "testrerecord" , t , func ( c service . Client ) {
fp := testProgPath ( t , "testrerecord" )
_ , err := c . CreateBreakpoint ( & api . Breakpoint { File : fp , Line : 10 } )
assertNoError ( err , t , "CreateBreakpoin" )
gett := func ( ) int {
state := <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , state . Err , state )
}
2020-03-18 16:25:32 +00:00
vart , err := c . EvalVariable ( api . EvalScope { GoroutineID : - 1 } , "t" , normalLoadConfig )
2019-10-21 18:48:04 +00:00
assertNoError ( err , t , "EvalVariable" )
if vart . Unreadable != "" {
t . Fatalf ( "Could not read variable 't': %s\n" , vart . Unreadable )
}
t . Logf ( "Value of t is %s\n" , vart . Value )
vartval , err := strconv . Atoi ( vart . Value )
assertNoError ( err , t , "Parsing value of variable t" )
return vartval
}
t0 := gett ( )
2020-08-21 14:14:02 +00:00
_ , err = c . RestartFrom ( false , "" , false , nil , [ 3 ] string { } , false )
2019-10-21 18:48:04 +00:00
assertNoError ( err , t , "First restart" )
t1 := gett ( )
if t0 != t1 {
t . Fatalf ( "Expected same value for t after restarting (without rerecording) %d %d" , t0 , t1 )
}
time . Sleep ( 2 * time . Second ) // make sure that we're not running inside the same second
2020-08-21 14:14:02 +00:00
_ , err = c . RestartFrom ( true , "" , false , nil , [ 3 ] string { } , false )
2019-10-21 18:48:04 +00:00
assertNoError ( err , t , "Second restart" )
t2 := gett ( )
if t0 == t2 {
t . Fatalf ( "Expected new value for t after restarting (with rerecording) %d %d" , t0 , t2 )
}
} )
}
2019-12-06 09:55:22 +00:00
func TestIssue1787 ( t * testing . T ) {
// Calling FunctionReturnLocations without a selected goroutine should
// work.
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
if c , _ := c . ( * rpc2 . RPCClient ) ; c != nil {
c . FunctionReturnLocations ( "main.main" )
}
} )
}
2020-02-25 20:29:20 +00:00
func TestDoubleCreateBreakpoint ( t * testing . T ) {
withTestClient2 ( "testnextprog" , t , func ( c service . Client ) {
_ , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 1 , Name : "firstbreakpoint" , Tracepoint : true } )
assertNoError ( err , t , "CreateBreakpoint 1" )
bps , err := c . ListBreakpoints ( )
assertNoError ( err , t , "ListBreakpoints 1" )
t . Logf ( "breakpoints before second call:" )
for _ , bp := range bps {
t . Logf ( "\t%v" , bp )
}
numBreakpoints := len ( bps )
_ , err = c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" , Line : 1 , Name : "secondbreakpoint" , Tracepoint : true } )
assertError ( err , t , "CreateBreakpoint 2" ) // breakpoint exists
bps , err = c . ListBreakpoints ( )
assertNoError ( err , t , "ListBreakpoints 2" )
t . Logf ( "breakpoints after second call:" )
for _ , bp := range bps {
t . Logf ( "\t%v" , bp )
}
if len ( bps ) != numBreakpoints {
t . Errorf ( "wrong number of breakpoints, got %d expected %d" , len ( bps ) , numBreakpoints )
}
} )
}
2020-03-24 16:09:28 +00:00
func TestStopRecording ( t * testing . T ) {
protest . AllowRecording ( t )
if testBackend != "rr" {
t . Skip ( "only for rr backend" )
}
withTestClient2 ( "sleep" , t , func ( c service . Client ) {
time . Sleep ( time . Second )
c . StopRecording ( )
_ , err := c . GetState ( )
assertNoError ( err , t , "GetState()" )
// try rerecording
go func ( ) {
2020-08-21 14:14:02 +00:00
c . RestartFrom ( true , "" , false , nil , [ 3 ] string { } , false )
2020-03-24 16:09:28 +00:00
} ( )
time . Sleep ( time . Second ) // hopefully the re-recording started...
c . StopRecording ( )
_ , err = c . GetState ( )
assertNoError ( err , t , "GetState()" )
} )
}
2020-03-24 09:47:36 +00:00
func TestClearLogicalBreakpoint ( t * testing . T ) {
// Clearing a logical breakpoint should clear all associated physical
// breakpoints.
// Issue #1955.
2020-08-21 14:14:02 +00:00
withTestClient2Extended ( "testinline" , t , protest . EnableInlining , [ 3 ] string { } , func ( c service . Client , fixture protest . Fixture ) {
2020-03-24 09:47:36 +00:00
bp , err := c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.inlineThis" } )
assertNoError ( err , t , "CreateBreakpoint()" )
t . Logf ( "breakpoint set at %#v" , bp . Addrs )
if len ( bp . Addrs ) < 2 {
t . Fatal ( "Wrong number of addresses for main.inlineThis breakpoint" )
}
_ , err = c . ClearBreakpoint ( bp . ID )
assertNoError ( err , t , "ClearBreakpoint()" )
bps , err := c . ListBreakpoints ( )
assertNoError ( err , t , "ListBreakpoints()" )
for _ , curbp := range bps {
if curbp . ID == bp . ID {
t . Errorf ( "logical breakpoint still exists: %#v" , curbp )
break
}
}
} )
}
2020-08-21 14:14:02 +00:00
func TestRedirects ( t * testing . T ) {
const (
infile = "redirect-input.txt"
outfile = "redirect-output.txt"
)
protest . AllowRecording ( t )
withTestClient2Extended ( "redirect" , t , 0 , [ 3 ] string { infile , outfile , "" } , func ( c service . Client , fixture protest . Fixture ) {
outpath := filepath . Join ( fixture . BuildDir , outfile )
<- c . Continue ( )
buf , err := ioutil . ReadFile ( outpath )
assertNoError ( err , t , "Reading output file" )
t . Logf ( "output %q" , buf )
if ! strings . HasPrefix ( string ( buf ) , "Redirect test" ) {
t . Fatalf ( "Wrong output %q" , string ( buf ) )
}
os . Remove ( outpath )
if testBackend != "rr" {
_ , err = c . Restart ( false )
assertNoError ( err , t , "Restart" )
<- c . Continue ( )
buf2 , err := ioutil . ReadFile ( outpath )
t . Logf ( "output %q" , buf2 )
assertNoError ( err , t , "Reading output file (second time)" )
if ! strings . HasPrefix ( string ( buf2 ) , "Redirect test" ) {
t . Fatalf ( "Wrong output %q" , string ( buf2 ) )
}
if string ( buf2 ) == string ( buf ) {
t . Fatalf ( "Expected output change got %q and %q" , string ( buf ) , string ( buf2 ) )
}
os . Remove ( outpath )
}
} )
}
2020-09-08 22:18:49 +00:00
func TestIssue2162 ( t * testing . T ) {
if buildMode == "pie" || runtime . GOOS == "windows" {
t . Skip ( "skip it for stepping into one place where no source for pc when on pie mode or windows" )
}
withTestClient2 ( "issue2162" , t , func ( c service . Client ) {
2020-11-09 19:28:40 +00:00
state , err := c . GetState ( )
assertNoError ( err , t , "GetState()" )
if state . CurrentThread . Function == nil {
// Can't call Step if we don't have the source code of the current function
return
}
_ , err = c . CreateBreakpoint ( & api . Breakpoint { FunctionName : "main.main" } )
2020-09-08 22:18:49 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v" , err )
}
_ , err = c . Step ( )
2020-11-09 19:28:40 +00:00
assertNoError ( err , t , "Step()" )
2020-09-08 22:18:49 +00:00
} )
}