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"
2016-02-15 14:39:29 +00:00
"math/rand"
2015-03-20 21:11:11 +00:00
"net"
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
2017-02-08 16:00:44 +00:00
protest "github.com/derekparker/delve/pkg/proc/test"
2015-06-21 03:47:44 +00:00
2017-05-04 10:22:08 +00:00
"github.com/derekparker/delve/pkg/goversion"
2015-03-20 21:11:11 +00:00
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/api"
2016-04-18 19:20:20 +00:00
"github.com/derekparker/delve/service/rpc2"
2016-06-19 06:43:29 +00:00
"github.com/derekparker/delve/service/rpccommon"
2015-03-20 21:11:11 +00:00
)
2016-04-24 17:15:39 +00:00
var normalLoadConfig = api . LoadConfig { true , 1 , 64 , 64 , - 1 }
2017-02-10 14:11:40 +00:00
var testBackend 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" )
flag . Parse ( )
if testBackend == "" {
testBackend = os . Getenv ( "PROCTEST" )
if testBackend == "" {
testBackend = "native"
}
}
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 ) ) {
2017-05-05 22:17:52 +00:00
if testBackend == "rr" {
protest . MustHaveRecordingAllowed ( t )
}
2015-03-20 21:11:11 +00:00
listener , err := net . Listen ( "tcp" , "localhost:0" )
if err != nil {
t . Fatalf ( "couldn't start listener: %s\n" , err )
}
2015-06-21 03:47:44 +00:00
defer listener . Close ( )
2016-06-19 06:43:29 +00:00
server := rpccommon . NewServer ( & service . Config {
2015-06-24 13:08:48 +00:00
Listener : listener ,
2017-08-15 06:21:24 +00:00
ProcessArgs : [ ] string { protest . BuildFixture ( name , 0 ) . Path } ,
2017-02-10 14:11:40 +00:00
Backend : testBackend ,
2015-06-24 13:08:48 +00:00
} , false )
2015-06-27 04:05:15 +00:00
if err := server . Run ( ) ; err != nil {
t . Fatal ( err )
}
2016-04-18 19:20:20 +00:00
client := rpc2 . NewClient ( listener . Addr ( ) . String ( ) )
2015-06-24 14:44:24 +00:00
defer func ( ) {
2017-08-01 17:14:11 +00:00
dir , _ := client . TraceDirectory ( )
2015-06-24 14:44:24 +00:00
client . Detach ( true )
2017-08-01 17:14:11 +00:00
if dir != "" {
2017-05-05 22:17:52 +00:00
protest . SafeRemoveAll ( dir )
}
2015-06-24 14:44:24 +00:00
} ( )
2015-06-24 13:08:48 +00:00
fn ( client )
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
}
2015-06-27 04:05:15 +00:00
listener , err := net . Listen ( "tcp" , "localhost:0" )
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 ,
2017-02-10 14:11:40 +00:00
Backend : testBackend ,
2015-06-27 04:05:15 +00:00
} , false )
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" )
}
2016-12-22 16:53:34 +00:00
if _ , err := c . Restart ( ) ; 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" )
c . Restart ( )
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" )
}
2016-12-22 16:53:34 +00:00
if _ , err := c . Restart ( ) ; 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
}
} )
}
func TestRestart_attachPid ( t * testing . T ) {
// Assert it does not work and returns error.
// We cannot restart a process we did not spawn.
2016-06-19 06:43:29 +00:00
server := rpccommon . NewServer ( & service . Config {
Listener : nil ,
AttachPid : 999 ,
APIVersion : 2 ,
2017-02-10 14:11:40 +00:00
Backend : testBackend ,
2015-07-03 20:35:22 +00:00
} , false )
2016-01-29 11:37:58 +00:00
if err := server . Restart ( ) ; err == nil {
2015-07-03 20:35:22 +00:00
t . Fatal ( "expected error on restart after attaching to pid but got none" )
}
}
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 {
t . Errorf ( "Expected %#v to be greater than %#v" , before , after )
}
} )
}
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
2017-05-04 10:22:08 +00:00
if ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVersion { 1 , 7 , - 1 , 0 , 0 , "" } ) {
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 ( )
2015-03-20 21:11:11 +00:00
if err != nil {
t . Fatalf ( "Unexpected error: %v, state: %#v" , err , state )
}
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" )
_ , err := c . CreateBreakpoint ( & api . Breakpoint { File : fp , Line : 23 } )
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
}
2016-04-24 17:15:39 +00:00
locals , err := c . ListLocalVariables ( api . EvalScope { - 1 , 0 } , 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
}
2016-11-15 16:16:33 +00:00
regs , err := c . ListRegisters ( 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" )
}
2016-04-24 17:15:39 +00:00
locals , err := c . ListFunctionArgs ( api . EvalScope { - 1 , 0 } , 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 ] )
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
2016-04-18 19:20:20 +00:00
withTestClient2 ( "locationsUpperCase" , t , func ( c service . Client ) {
2016-01-30 21:27:08 +00:00
// Upper case
findLocationHelper ( t , c , "locationsUpperCase.go:6" , false , 1 , 0 )
// Fully qualified path
2018-04-19 07:02:12 +00:00
path := protest . Fixtures [ protest . FixtureKey { "locationsUpperCase" , 0 } ] . Source
2016-01-30 21:27:08 +00:00
findLocationHelper ( t , c , path + ":6" , false , 1 , 0 )
bp , err := c . CreateBreakpoint ( & api . Breakpoint { File : path , Line : 6 } )
if err != nil {
t . Fatalf ( "Could not set breakpoint in %s: %v\n" , path , err )
}
c . ClearBreakpoint ( bp . ID )
// Allow `/` or `\` on Windows
if runtime . GOOS == "windows" {
findLocationHelper ( t , c , filepath . FromSlash ( path ) + ":6" , false , 1 , 0 )
bp , err = c . CreateBreakpoint ( & api . Breakpoint { File : filepath . FromSlash ( path ) , Line : 6 } )
if err != nil {
t . Fatalf ( "Could not set breakpoint in %s: %v\n" , filepath . FromSlash ( path ) , err )
}
c . ClearBreakpoint ( bp . ID )
}
// Case-insensitive on Windows, case-sensitive otherwise
shouldWrongCaseBeError := true
numExpectedMatches := 0
if runtime . GOOS == "windows" {
shouldWrongCaseBeError = false
numExpectedMatches = 1
}
findLocationHelper ( t , c , strings . ToLower ( path ) + ":6" , shouldWrongCaseBeError , numExpectedMatches , 0 )
bp , err = c . CreateBreakpoint ( & api . Breakpoint { File : strings . ToLower ( path ) , Line : 6 } )
if ( err == nil ) == shouldWrongCaseBeError {
t . Fatalf ( "Could not set breakpoint in %s: %v\n" , strings . ToLower ( path ) , err )
}
c . ClearBreakpoint ( bp . ID )
} )
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 )
}
2016-04-24 17:15:39 +00:00
var1 , err := c . EvalVariable ( api . EvalScope { - 1 , 0 } , "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 )
}
2015-10-04 17:58:32 +00:00
assertNoError ( c . SetVariable ( api . EvalScope { - 1 , 0 } , "a2" , "8" ) , t , "SetVariable()" )
2015-09-28 10:01:18 +00:00
2016-04-24 17:15:39 +00:00
a2 , err := c . EvalVariable ( api . EvalScope { - 1 , 0 } , "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 )
}
gs , err := c . ListGoroutines ( )
assertNoError ( err , t , "GoroutinesInfo()" )
found := make ( [ ] bool , 10 )
for _ , g := range gs {
2016-04-24 17:15:39 +00:00
frames , err := c . Stacktrace ( g . ID , 10 , & 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
}
if frame . Function . Name != "main.agoroutine" {
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 ] {
t . Fatalf ( "Goroutine %d not found" , i )
}
}
state = <- c . Continue ( )
if state . Err != nil {
t . Fatalf ( "Continue(): %v\n" , state . Err )
}
2016-04-24 17:15:39 +00:00
frames , err := c . Stacktrace ( - 1 , 10 , & 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()" )
assertError ( c . SetVariable ( api . EvalScope { gid , 0 } , "a" , "10" ) , t , "SetVariable()" )
2016-04-24 17:15:39 +00:00
_ , err = c . ListLocalVariables ( api . EvalScope { gid , 0 } , normalLoadConfig )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "ListLocalVariables()" )
2016-04-24 17:15:39 +00:00
_ , err = c . ListFunctionArgs ( api . EvalScope { gid , 0 } , normalLoadConfig )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "ListFunctionArgs()" )
2016-11-15 16:16:33 +00:00
_ , err = c . ListRegisters ( 0 , false )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "ListRegisters()" )
_ , err = c . ListGoroutines ( )
assertError ( err , t , "ListGoroutines()" )
2016-04-24 17:15:39 +00:00
_ , err = c . Stacktrace ( gid , 10 , & normalLoadConfig )
2016-01-30 06:23:14 +00:00
assertError ( err , t , "Stacktrace()" )
_ , err = c . FindLocation ( api . EvalScope { gid , 0 } , "+1" )
assertError ( err , t , "FindLocation()" )
2016-02-06 06:00:48 +00:00
_ , err = c . DisassemblePC ( api . EvalScope { - 1 , 0 } , 0x40100 , api . IntelFlavour )
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()" )
locs , err := c . FindLocation ( api . EvalScope { - 1 , 0 } , "main.main" )
assertNoError ( err , t , "FindLocation()" )
if len ( locs ) != 1 {
t . Fatalf ( "wrong number of locations for main.main: %d" , len ( locs ) )
}
d1 , err := c . DisassemblePC ( api . EvalScope { - 1 , 0 } , locs [ 0 ] . PC , api . IntelFlavour )
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 ) )
d2 , err := c . DisassembleRange ( api . EvalScope { - 1 , 0 } , pcstart , pcend , api . IntelFlavour )
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" )
}
d3 , err := c . DisassemblePC ( api . EvalScope { - 1 , 0 } , state . CurrentThread . PC , api . IntelFlavour )
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 {
if d3 [ i ] . Loc . Line == 29 && strings . HasPrefix ( d3 [ i ] . Text , "call" ) && d3 [ i ] . DestLoc != nil && d3 [ i ] . DestLoc . Function != nil && d3 [ i ] . DestLoc . Function . Name == "main.afunction" {
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" )
}
startinstr := getCurinstr ( d3 )
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 ) )
d3 , err = c . DisassemblePC ( api . EvalScope { - 1 , 0 } , state . CurrentThread . PC , api . IntelFlavour )
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" )
}
if strings . HasPrefix ( curinstr . Text , "call" ) {
t . Logf ( "call: %v" , curinstr )
if curinstr . DestLoc == nil || curinstr . DestLoc . Function == nil {
t . Fatalf ( "Call instruction does not have destination: %v" , curinstr )
}
if curinstr . DestLoc . Function . Name != "main.afunction" {
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()" )
2016-04-24 17:15:39 +00:00
_ , err = c . Stacktrace ( - 1 , - 2 , & 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 ) {
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()" )
2016-04-24 17:15:39 +00:00
nvar , err := c . EvalVariable ( api . EvalScope { - 1 , 0 } , "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
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 )
afunction0 := findLocationHelper ( t , c , "main.afunction:0" , false , 1 , 0 ) [ 0 ]
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 ]
callmeZ := findLocationHelper ( t , c , "main.callme:0" , false , 1 , 0 ) [ 0 ]
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 ]
callme2Z := findLocationHelper ( t , c , "main.callme2:0" , false , 1 , 0 ) [ 0 ]
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 ]
callme3Z := findLocationHelper ( t , c , "main.callme3:0" , false , 1 , 0 ) [ 0 ]
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVer18Beta ) {
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
// This is only true before 1.8 where frame pointer chaining introduced a
// bit of prologue even for functions without local variables
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
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 ( ) {
rand . Seed ( time . Now ( ) . Unix ( ) )
d := time . Duration ( rand . Intn ( 4 ) + 1 )
time . Sleep ( d * time . Second )
_ , err := c . Halt ( )
assertNoError ( err , t , "RequestManualStop()" )
} ( )
statech := c . Continue ( )
state := <- statech
assertNoError ( state . Err , t , "Continue()" )
} )
}
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 ) {
locs , err := c . FindLocation ( api . EvalScope { - 1 , 0 } , "issue406.go:146" )
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()" )
2016-04-24 17:15:39 +00:00
v , err := c . EvalVariable ( api . EvalScope { - 1 , 0 } , "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()" )
var1 , err := c . EvalVariable ( api . EvalScope { - 1 , 0 } , "i1+1" , normalLoadConfig )
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 ( ) )
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 7 , - 1 , 0 , 0 , "" } ) {
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 ) {
regtests := [ ] struct { name , value string } {
{ "ST(0)" , "0x3fffe666660000000000" } ,
{ "ST(1)" , "0x3fffd9999a0000000000" } ,
{ "ST(2)" , "0x3fffcccccd0000000000" } ,
{ "ST(3)" , "0x3fffc000000000000000" } ,
{ "ST(4)" , "0x3fffb333333333333000" } ,
{ "ST(5)" , "0x3fffa666666666666800" } ,
{ "ST(6)" , "0x3fff9999999999999800" } ,
{ "ST(7)" , "0x3fff8cccccccccccd000" } ,
{ "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" } ,
}
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 ) {
<- c . Continue ( )
regs , err := c . ListRegisters ( 0 , true )
assertNoError ( err , t , "ListRegisters()" )
t . Logf ( "%s" , regs . String ( ) )
for _ , regtest := range regtests {
found := false
for _ , reg := range regs {
if reg . Name == regtest . name {
found = true
if ! strings . HasPrefix ( reg . Value , regtest . value ) {
t . Fatalf ( "register %s expected %q got %q" , reg . Name , regtest . value , reg . Value )
}
}
}
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 )
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" )
_ , err = c . Restart ( )
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 ( ) )
if ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
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 )
}
if ret [ 0 ] . Name != "str" {
t . Fatalf ( "(str) bad return value name %s" , ret [ 0 ] . Name )
}
if ret [ 0 ] . Kind != reflect . String {
t . Fatalf ( "(str) bad return value kind %v" , ret [ 0 ] . Kind )
}
if ret [ 0 ] . Value != "return 47" {
t . Fatalf ( "(str) bad return value %q" , ret [ 0 ] . Value )
}
if ret [ 1 ] . Name != "num" {
t . Fatalf ( "(num) bad return value name %s" , ret [ 1 ] . Name )
}
if ret [ 1 ] . Kind != reflect . Int {
t . Fatalf ( "(num) bad return value kind %v" , ret [ 1 ] . Kind )
}
if ret [ 1 ] . Value != "48" {
t . Fatalf ( "(num) bad return value %s" , ret [ 1 ] . Value )
}
} )
}