2015-06-12 19:49:23 +00:00
package proc
2014-05-20 18:23:35 +00:00
import (
2014-05-27 18:33:49 +00:00
"bytes"
2015-07-11 06:43:47 +00:00
"fmt"
2016-01-12 08:01:42 +00:00
"go/ast"
2015-10-21 07:06:36 +00:00
"go/constant"
2016-01-12 08:01:42 +00:00
"go/token"
2015-07-16 00:57:54 +00:00
"net"
"net/http"
2015-06-21 03:47:44 +00:00
"os"
2016-04-21 10:19:21 +00:00
"os/exec"
2014-06-29 16:52:21 +00:00
"path/filepath"
2015-10-18 17:37:13 +00:00
"reflect"
2014-12-09 16:51:17 +00:00
"runtime"
2015-07-28 05:33:07 +00:00
"strings"
2014-05-20 18:23:35 +00:00
"testing"
2015-07-16 00:57:54 +00:00
"time"
2015-05-04 13:31:50 +00:00
2015-06-12 19:49:23 +00:00
protest "github.com/derekparker/delve/proc/test"
2014-06-25 19:06:04 +00:00
)
2014-05-30 15:12:18 +00:00
2016-04-24 17:15:39 +00:00
var normalLoadConfig = LoadConfig { true , 1 , 64 , 64 , - 1 }
2015-06-13 04:47:30 +00:00
func init ( ) {
2015-06-26 12:46:46 +00:00
runtime . GOMAXPROCS ( 4 )
os . Setenv ( "GOMAXPROCS" , "4" )
2015-06-13 04:47:30 +00:00
}
2015-05-04 13:31:50 +00:00
func TestMain ( m * testing . M ) {
2015-06-21 03:47:44 +00:00
os . Exit ( protest . RunTestsWithFixtures ( m ) )
2015-05-04 13:31:50 +00:00
}
2016-01-10 12:49:03 +00:00
func withTestProcess ( name string , t testing . TB , fn func ( p * Process , fixture protest . Fixture ) ) {
2015-05-15 14:23:58 +00:00
fixture := protest . BuildFixture ( name )
2015-05-04 13:31:50 +00:00
p , err := Launch ( [ ] string { fixture . Path } )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( "Launch():" , err )
}
2015-06-24 14:44:24 +00:00
defer func ( ) {
p . Halt ( )
2015-07-11 06:43:47 +00:00
p . Kill ( )
2015-06-24 14:44:24 +00:00
} ( )
2014-12-09 16:51:17 +00:00
2015-05-04 13:31:50 +00:00
fn ( p , fixture )
2014-12-09 16:51:17 +00:00
}
2016-04-21 21:20:38 +00:00
func withTestProcessArgs ( name string , t testing . TB , fn func ( p * Process , fixture protest . Fixture ) , args [ ] string ) {
fixture := protest . BuildFixture ( name )
p , err := Launch ( append ( [ ] string { fixture . Path } , args ... ) )
if err != nil {
t . Fatal ( "Launch():" , err )
}
defer func ( ) {
p . Halt ( )
p . Kill ( )
} ( )
fn ( p , fixture )
}
2015-06-20 22:54:52 +00:00
func getRegisters ( p * Process , t * testing . T ) Registers {
2014-12-09 16:35:55 +00:00
regs , err := p . Registers ( )
if err != nil {
t . Fatal ( "Registers():" , err )
}
return regs
}
2015-06-12 19:51:23 +00:00
func dataAtAddr ( thread * Thread , addr uint64 ) ( [ ] byte , error ) {
2015-08-02 02:43:03 +00:00
return thread . readMemory ( uintptr ( addr ) , 1 )
2014-05-27 18:33:49 +00:00
}
2016-01-10 12:49:03 +00:00
func assertNoError ( err error , t testing . TB , s string ) {
2014-06-29 16:52:21 +00:00
if err != nil {
2015-01-21 21:26:01 +00:00
_ , file , line , _ := runtime . Caller ( 1 )
fname := filepath . Base ( file )
2015-06-26 12:46:46 +00:00
t . Fatalf ( "failed assertion at %s:%d: %s - %s\n" , fname , line , s , err )
2014-06-29 16:52:21 +00:00
}
}
2015-06-20 22:54:52 +00:00
func currentPC ( p * Process , t * testing . T ) uint64 {
2015-04-23 15:40:33 +00:00
pc , err := p . PC ( )
2014-06-29 16:52:21 +00:00
if err != nil {
t . Fatal ( err )
}
return pc
}
2015-06-20 22:54:52 +00:00
func currentLineNumber ( p * Process , t * testing . T ) ( string , int ) {
2014-06-29 16:52:21 +00:00
pc := currentPC ( p , t )
2015-04-03 16:10:35 +00:00
f , l , _ := p . goSymTable . PCToLine ( pc )
2014-06-29 16:52:21 +00:00
2014-10-07 17:25:33 +00:00
return f , l
2014-06-29 16:52:21 +00:00
}
2015-03-05 22:59:51 +00:00
func TestExit ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "continuetestprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-03-05 22:59:51 +00:00
err := p . Continue ( )
pe , ok := err . ( ProcessExitedError )
if ! ok {
2015-04-25 14:46:16 +00:00
t . Fatalf ( "Continue() returned unexpected error type %s" , err )
2015-03-05 22:59:51 +00:00
}
if pe . Status != 0 {
t . Errorf ( "Unexpected error status: %d" , pe . Status )
}
if pe . Pid != p . Pid {
t . Errorf ( "Unexpected process id: %d" , pe . Pid )
}
} )
}
2015-10-02 16:17:07 +00:00
func TestExitAfterContinue ( t * testing . T ) {
withTestProcess ( "continuetestprog" , t , func ( p * Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "First Continue()" )
err = p . Continue ( )
pe , ok := err . ( ProcessExitedError )
if ! ok {
2016-01-15 05:26:54 +00:00
t . Fatalf ( "Continue() returned unexpected error type %s" , pe )
2015-10-02 16:17:07 +00:00
}
if pe . Status != 0 {
t . Errorf ( "Unexpected error status: %d" , pe . Status )
}
if pe . Pid != p . Pid {
t . Errorf ( "Unexpected process id: %d" , pe . Pid )
}
} )
}
2015-08-07 16:50:14 +00:00
func setFunctionBreakpoint ( p * Process , fname string ) ( * Breakpoint , error ) {
addr , err := p . FindFunctionLocation ( fname , true , 0 )
if err != nil {
return nil , err
}
return p . SetBreakpoint ( addr )
}
2015-04-13 22:17:06 +00:00
func TestHalt ( t * testing . T ) {
2015-06-26 12:46:46 +00:00
stopChan := make ( chan interface { } )
withTestProcess ( "loopprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-08-07 16:50:14 +00:00
_ , err := setFunctionBreakpoint ( p , "main.loop" )
2015-06-26 12:46:46 +00:00
assertNoError ( err , t , "SetBreakpoint" )
assertNoError ( p . Continue ( ) , t , "Continue" )
for _ , th := range p . Threads {
if th . running != false {
2016-01-10 08:57:52 +00:00
t . Fatal ( "expected running = false for thread" , th . ID )
2015-06-26 12:46:46 +00:00
}
_ , err := th . Registers ( )
assertNoError ( err , t , "Registers" )
}
2015-04-13 22:17:06 +00:00
go func ( ) {
2015-04-26 11:31:28 +00:00
for {
if p . Running ( ) {
2015-06-26 12:46:46 +00:00
if err := p . RequestManualStop ( ) ; err != nil {
2015-04-26 11:31:28 +00:00
t . Fatal ( err )
}
2015-06-26 12:46:46 +00:00
stopChan <- nil
2015-04-26 11:31:28 +00:00
return
}
2015-04-13 22:17:06 +00:00
}
} ( )
2015-06-26 12:46:46 +00:00
assertNoError ( p . Continue ( ) , t , "Continue" )
<- stopChan
2015-04-13 22:17:06 +00:00
// Loop through threads and make sure they are all
// actually stopped, err will not be nil if the process
// is still running.
for _ , th := range p . Threads {
2015-08-20 14:28:11 +00:00
if ! th . Stopped ( ) {
t . Fatal ( "expected thread to be stopped, but was not" )
}
2015-06-26 12:46:46 +00:00
if th . running != false {
2016-01-10 08:57:52 +00:00
t . Fatal ( "expected running = false for thread" , th . ID )
2015-04-13 22:17:06 +00:00
}
2015-06-26 12:46:46 +00:00
_ , err := th . Registers ( )
assertNoError ( err , t , "Registers" )
2015-04-13 22:17:06 +00:00
}
} )
}
2014-05-30 15:12:18 +00:00
func TestStep ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-04-03 16:10:35 +00:00
helloworldfunc := p . goSymTable . LookupFunc ( "main.helloworld" )
2014-11-27 02:35:53 +00:00
helloworldaddr := helloworldfunc . Entry
2015-06-20 23:01:06 +00:00
_ , err := p . SetBreakpoint ( helloworldaddr )
assertNoError ( err , t , "SetBreakpoint()" )
2014-11-27 02:35:53 +00:00
assertNoError ( p . Continue ( ) , t , "Continue()" )
2014-12-09 16:35:55 +00:00
regs := getRegisters ( p , t )
2014-05-30 15:12:18 +00:00
rip := regs . PC ( )
2014-05-20 18:23:36 +00:00
2016-01-24 05:25:46 +00:00
err = p . CurrentThread . StepInstruction ( )
2014-09-06 23:53:22 +00:00
assertNoError ( err , t , "Step()" )
2014-05-20 18:23:36 +00:00
2014-12-09 16:35:55 +00:00
regs = getRegisters ( p , t )
2014-05-30 15:12:18 +00:00
if rip >= regs . PC ( ) {
t . Errorf ( "Expected %#v to be greater than %#v" , regs . PC ( ) , rip )
}
} )
}
2014-05-20 18:23:36 +00:00
2015-06-12 19:32:32 +00:00
func TestBreakpoint ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-04-03 16:10:35 +00:00
helloworldfunc := p . goSymTable . LookupFunc ( "main.helloworld" )
2015-01-14 02:37:10 +00:00
helloworldaddr := helloworldfunc . Entry
2014-05-30 15:12:18 +00:00
2015-06-20 23:01:06 +00:00
bp , err := p . SetBreakpoint ( helloworldaddr )
assertNoError ( err , t , "SetBreakpoint()" )
2015-01-14 02:37:10 +00:00
assertNoError ( p . Continue ( ) , t , "Continue()" )
2014-05-30 15:12:18 +00:00
2015-04-23 15:40:33 +00:00
pc , err := p . PC ( )
2014-10-18 01:34:58 +00:00
if err != nil {
t . Fatal ( err )
}
2014-05-30 15:12:18 +00:00
2015-09-26 00:04:39 +00:00
if bp . TotalHitCount != 1 {
t . Fatalf ( "Breakpoint should be hit once, got %d\n" , bp . TotalHitCount )
}
2015-02-28 03:35:26 +00:00
if pc - 1 != bp . Addr && pc != bp . Addr {
2015-04-03 16:10:35 +00:00
f , l , _ := p . goSymTable . PCToLine ( pc )
2015-01-14 02:37:10 +00:00
t . Fatalf ( "Break not respected:\nPC:%#v %s:%d\nFN:%#v \n" , pc , f , l , bp . Addr )
2014-05-30 15:12:18 +00:00
}
} )
2014-05-24 16:22:06 +00:00
}
2014-05-24 16:36:18 +00:00
2015-06-12 19:32:32 +00:00
func TestBreakpointInSeperateGoRoutine ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testthreads" , t , func ( p * Process , fixture protest . Fixture ) {
2015-04-03 16:10:35 +00:00
fn := p . goSymTable . LookupFunc ( "main.anotherthread" )
2014-10-18 01:34:58 +00:00
if fn == nil {
t . Fatal ( "No fn exists" )
}
2015-06-20 23:01:06 +00:00
_ , err := p . SetBreakpoint ( fn . Entry )
2014-10-18 01:34:58 +00:00
if err != nil {
t . Fatal ( err )
}
err = p . Continue ( )
if err != nil {
t . Fatal ( err )
}
2015-04-23 15:40:33 +00:00
pc , err := p . PC ( )
2014-10-18 01:34:58 +00:00
if err != nil {
t . Fatal ( err )
}
2015-04-03 16:10:35 +00:00
f , l , _ := p . goSymTable . PCToLine ( pc )
2014-10-18 01:34:58 +00:00
if f != "testthreads.go" && l != 8 {
t . Fatal ( "Program did not hit breakpoint" )
}
} )
}
2015-06-12 19:32:32 +00:00
func TestBreakpointWithNonExistantFunction ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-06-20 23:01:06 +00:00
_ , err := p . SetBreakpoint ( 0 )
2014-05-30 15:12:18 +00:00
if err == nil {
t . Fatal ( "Should not be able to break at non existant function" )
}
} )
2014-05-24 16:36:18 +00:00
}
2014-05-27 18:33:49 +00:00
2015-06-20 23:01:06 +00:00
func TestClearBreakpointBreakpoint ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-04-03 16:10:35 +00:00
fn := p . goSymTable . LookupFunc ( "main.sleepytime" )
2015-06-20 23:01:06 +00:00
bp , err := p . SetBreakpoint ( fn . Entry )
assertNoError ( err , t , "SetBreakpoint()" )
2014-05-30 15:12:18 +00:00
2015-06-20 23:01:06 +00:00
bp , err = p . ClearBreakpoint ( fn . Entry )
assertNoError ( err , t , "ClearBreakpoint()" )
2014-05-30 15:12:18 +00:00
2015-01-14 02:37:10 +00:00
data , err := dataAtAddr ( p . CurrentThread , bp . Addr )
2014-05-30 15:12:18 +00:00
if err != nil {
t . Fatal ( err )
}
2015-01-10 23:33:06 +00:00
int3 := [ ] byte { 0xcc }
2014-05-30 15:12:18 +00:00
if bytes . Equal ( data , int3 ) {
t . Fatalf ( "Breakpoint was not cleared data: %#v, int3: %#v" , data , int3 )
}
2016-03-06 17:54:43 +00:00
if countBreakpoints ( p ) != 0 {
2014-05-30 15:12:18 +00:00
t . Fatal ( "Breakpoint not removed internally" )
}
} )
2014-05-27 18:33:49 +00:00
}
2014-06-29 16:52:21 +00:00
2015-04-19 22:11:33 +00:00
type nextTest struct {
begin , end int
}
2014-06-29 16:52:21 +00:00
2016-03-06 17:54:43 +00:00
func countBreakpoints ( p * Process ) int {
bpcount := 0
for _ , bp := range p . Breakpoints {
if bp . ID >= 0 {
bpcount ++
}
}
return bpcount
}
2015-05-09 17:44:38 +00:00
func testnext ( program string , testcases [ ] nextTest , initialLocation string , t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( program , t , func ( p * Process , fixture protest . Fixture ) {
2015-08-07 16:50:14 +00:00
bp , err := setFunctionBreakpoint ( p , initialLocation )
2015-06-20 23:01:06 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2014-09-06 23:56:25 +00:00
assertNoError ( p . Continue ( ) , t , "Continue()" )
2015-06-20 23:01:06 +00:00
p . ClearBreakpoint ( bp . Addr )
2015-04-19 22:11:33 +00:00
p . CurrentThread . SetPC ( bp . Addr )
2014-06-29 16:52:21 +00:00
2014-12-29 02:48:58 +00:00
f , ln := currentLineNumber ( p , t )
2014-06-29 16:52:21 +00:00
for _ , tc := range testcases {
if ln != tc . begin {
2015-03-28 01:12:07 +00:00
t . Fatalf ( "Program not stopped at correct spot expected %d was %s:%d" , tc . begin , filepath . Base ( f ) , ln )
2014-06-29 16:52:21 +00:00
}
assertNoError ( p . Next ( ) , t , "Next() returned an error" )
2014-10-07 17:25:33 +00:00
f , ln = currentLineNumber ( p , t )
2014-06-29 16:52:21 +00:00
if ln != tc . end {
2015-03-28 01:12:07 +00:00
t . Fatalf ( "Program did not continue to correct next location expected %d was %s:%d" , tc . end , filepath . Base ( f ) , ln )
2014-06-29 16:52:21 +00:00
}
}
2014-09-17 03:37:48 +00:00
2016-03-06 17:54:43 +00:00
if countBreakpoints ( p ) != 0 {
2015-06-12 19:32:32 +00:00
t . Fatal ( "Not all breakpoints were cleaned up" , len ( p . Breakpoints ) )
2015-01-10 23:33:06 +00:00
}
2014-06-29 16:52:21 +00:00
} )
}
2014-12-09 16:51:17 +00:00
2015-04-19 22:11:33 +00:00
func TestNextGeneral ( t * testing . T ) {
2016-02-25 09:48:42 +00:00
var testcases [ ] nextTest
ver , _ := ParseVersionString ( runtime . Version ( ) )
2016-06-22 10:38:23 +00:00
if ver . Major < 0 || ver . AfterOrEqual ( 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-04-19 22:11:33 +00:00
}
2016-02-25 09:48:42 +00:00
2015-05-09 17:44:38 +00:00
testnext ( "testnextprog" , testcases , "main.testnext" , t )
2015-04-19 22:11:33 +00:00
}
2015-08-20 14:28:11 +00:00
func TestNextConcurrent ( t * testing . T ) {
testcases := [ ] nextTest {
2016-02-11 07:18:39 +00:00
{ 8 , 9 } ,
2015-08-20 14:28:11 +00:00
{ 9 , 10 } ,
{ 10 , 11 } ,
}
withTestProcess ( "parallel_next" , t , func ( p * Process , fixture protest . Fixture ) {
2015-11-18 09:07:08 +00:00
bp , err := setFunctionBreakpoint ( p , "main.sayhi" )
2015-08-20 14:28:11 +00:00
assertNoError ( err , t , "SetBreakpoint" )
assertNoError ( p . Continue ( ) , t , "Continue" )
f , ln := currentLineNumber ( p , t )
2015-08-28 20:06:29 +00:00
initV , err := evalVariable ( p , "n" )
2015-10-21 07:06:36 +00:00
initVval , _ := constant . Int64Val ( initV . Value )
2015-08-20 14:28:11 +00:00
assertNoError ( err , t , "EvalVariable" )
2015-11-18 09:07:08 +00:00
_ , err = p . ClearBreakpoint ( bp . Addr )
assertNoError ( err , t , "ClearBreakpoint()" )
2015-08-20 14:28:11 +00:00
for _ , tc := range testcases {
2015-08-28 20:06:29 +00:00
g , err := p . CurrentThread . GetG ( )
assertNoError ( err , t , "GetG()" )
2016-01-10 08:57:52 +00:00
if p . SelectedGoroutine . ID != g . ID {
t . Fatalf ( "SelectedGoroutine not CurrentThread's goroutine: %d %d" , g . ID , p . SelectedGoroutine . ID )
2015-08-28 20:06:29 +00:00
}
2015-08-20 14:28:11 +00:00
if ln != tc . begin {
t . Fatalf ( "Program not stopped at correct spot expected %d was %s:%d" , tc . begin , filepath . Base ( f ) , ln )
}
assertNoError ( p . Next ( ) , t , "Next() returned an error" )
f , ln = currentLineNumber ( p , t )
if ln != tc . end {
t . Fatalf ( "Program did not continue to correct next location expected %d was %s:%d" , tc . end , filepath . Base ( f ) , ln )
}
2015-08-28 20:06:29 +00:00
v , err := evalVariable ( p , "n" )
2015-08-20 14:28:11 +00:00
assertNoError ( err , t , "EvalVariable" )
2015-10-21 07:06:36 +00:00
vval , _ := constant . Int64Val ( v . Value )
if vval != initVval {
2015-08-20 14:28:11 +00:00
t . Fatal ( "Did not end up on same goroutine" )
}
}
} )
}
2015-11-18 09:07:08 +00:00
func TestNextConcurrentVariant2 ( t * testing . T ) {
// Just like TestNextConcurrent but instead of removing the initial breakpoint we check that when it happens is for other goroutines
testcases := [ ] nextTest {
2016-02-11 07:18:39 +00:00
{ 8 , 9 } ,
2015-11-18 09:07:08 +00:00
{ 9 , 10 } ,
{ 10 , 11 } ,
}
withTestProcess ( "parallel_next" , t , func ( p * Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "SetBreakpoint" )
assertNoError ( p . Continue ( ) , t , "Continue" )
f , ln := currentLineNumber ( p , t )
initV , err := evalVariable ( p , "n" )
initVval , _ := constant . Int64Val ( initV . Value )
assertNoError ( err , t , "EvalVariable" )
for _ , tc := range testcases {
g , err := p . CurrentThread . GetG ( )
assertNoError ( err , t , "GetG()" )
2016-01-10 08:57:52 +00:00
if p . SelectedGoroutine . ID != g . ID {
t . Fatalf ( "SelectedGoroutine not CurrentThread's goroutine: %d %d" , g . ID , p . SelectedGoroutine . ID )
2015-11-18 09:07:08 +00:00
}
if ln != tc . begin {
t . Fatalf ( "Program not stopped at correct spot expected %d was %s:%d" , tc . begin , filepath . Base ( f ) , ln )
}
assertNoError ( p . Next ( ) , t , "Next() returned an error" )
var vval int64
for {
v , err := evalVariable ( p , "n" )
assertNoError ( err , t , "EvalVariable" )
vval , _ = constant . Int64Val ( v . Value )
if p . CurrentThread . CurrentBreakpoint == nil {
if vval != initVval {
t . Fatal ( "Did not end up on same goroutine" )
}
break
} else {
if vval == initVval {
t . Fatal ( "Initial breakpoint triggered twice for the same goroutine" )
}
assertNoError ( p . Continue ( ) , t , "Continue 2" )
}
}
f , ln = currentLineNumber ( p , t )
if ln != tc . end {
t . Fatalf ( "Program did not continue to correct next location expected %d was %s:%d" , tc . end , filepath . Base ( f ) , ln )
}
}
} )
}
2015-04-19 22:11:33 +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-04-19 22:11:33 +00:00
}
2015-05-09 17:44:38 +00:00
testnext ( "testnextprog" , testcases , "main.helloworld" , t )
}
func TestNextFunctionReturnDefer ( t * testing . T ) {
testcases := [ ] nextTest {
2016-02-11 07:18:39 +00:00
{ 5 , 8 } ,
2015-10-22 17:07:24 +00:00
{ 8 , 9 } ,
{ 9 , 10 } ,
2016-04-11 11:50:01 +00:00
{ 10 , 6 } ,
{ 6 , 7 } ,
2015-10-22 17:07:24 +00:00
{ 7 , 8 } ,
2015-05-09 17:44:38 +00:00
}
testnext ( "testnextdefer" , testcases , "main.main" , t )
2015-04-19 22:11:33 +00:00
}
2015-07-16 00:57:54 +00:00
func TestNextNetHTTP ( t * testing . T ) {
testcases := [ ] nextTest {
{ 11 , 12 } ,
{ 12 , 13 } ,
}
withTestProcess ( "testnextnethttp" , t , func ( p * Process , fixture protest . Fixture ) {
go func ( ) {
for ! p . Running ( ) {
time . Sleep ( 50 * time . Millisecond )
}
// Wait for program to start listening.
for {
2016-01-24 04:23:36 +00:00
conn , err := net . Dial ( "tcp" , "localhost:9191" )
2015-07-16 00:57:54 +00:00
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
}
2015-08-08 19:30:23 +00:00
http . Get ( "http://localhost:9191" )
2015-07-16 00:57:54 +00:00
} ( )
if err := p . Continue ( ) ; err != nil {
t . Fatal ( err )
}
f , ln := currentLineNumber ( p , t )
for _ , tc := range testcases {
if ln != tc . begin {
t . Fatalf ( "Program not stopped at correct spot expected %d was %s:%d" , tc . begin , filepath . Base ( f ) , ln )
}
assertNoError ( p . Next ( ) , t , "Next() returned an error" )
f , ln = currentLineNumber ( p , t )
if ln != tc . end {
t . Fatalf ( "Program did not continue to correct next location expected %d was %s:%d" , tc . end , filepath . Base ( f ) , ln )
}
}
} )
}
2015-04-25 19:13:35 +00:00
func TestRuntimeBreakpoint ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testruntimebreakpoint" , t , func ( p * Process , fixture protest . Fixture ) {
2015-04-25 19:13:35 +00:00
err := p . Continue ( )
if err != nil {
t . Fatal ( err )
}
pc , err := p . PC ( )
if err != nil {
t . Fatal ( err )
}
_ , l , _ := p . PCToLine ( pc )
if l != 10 {
t . Fatal ( "did not respect breakpoint" )
}
} )
}
2014-12-09 16:51:17 +00:00
func TestFindReturnAddress ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testnextprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-08-10 13:55:57 +00:00
start , _ , err := p . goSymTable . LineToPC ( fixture . Source , 24 )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
2015-06-20 23:01:06 +00:00
_ , err = p . SetBreakpoint ( start )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
err = p . Continue ( )
if err != nil {
t . Fatal ( err )
}
2015-08-10 13:55:57 +00:00
addr , err := p . CurrentThread . ReturnAddress ( )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
2015-08-10 13:55:57 +00:00
_ , l , _ := p . goSymTable . PCToLine ( addr )
if l != 40 {
t . Fatalf ( "return address not found correctly, expected line 40" )
2014-12-09 16:51:17 +00:00
}
2015-08-10 13:55:57 +00:00
} )
}
2014-12-09 16:51:17 +00:00
2015-08-10 13:55:57 +00:00
func TestFindReturnAddressTopOfStackFn ( t * testing . T ) {
withTestProcess ( "testreturnaddress" , t , func ( p * Process , fixture protest . Fixture ) {
fnName := "runtime.rt0_go"
fn := p . goSymTable . LookupFunc ( fnName )
if fn == nil {
t . Fatalf ( "could not find function %s" , fnName )
}
if _ , err := p . SetBreakpoint ( fn . Entry ) ; err != nil {
2014-12-09 16:51:17 +00:00
t . Fatal ( err )
}
2015-08-10 13:55:57 +00:00
if err := p . Continue ( ) ; err != nil {
2015-08-02 02:43:03 +00:00
t . Fatal ( err )
}
2015-08-10 13:55:57 +00:00
if _ , err := p . CurrentThread . ReturnAddress ( ) ; err == nil {
t . Fatal ( "expected error to be returned" )
2014-12-09 16:51:17 +00:00
}
} )
}
2015-03-26 18:15:35 +00:00
func TestSwitchThread ( t * testing . T ) {
2015-06-20 22:54:52 +00:00
withTestProcess ( "testnextprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-03-26 18:15:35 +00:00
// With invalid thread id
err := p . SwitchThread ( - 1 )
if err == nil {
t . Fatal ( "Expected error for invalid thread id" )
}
2015-08-07 16:50:14 +00:00
pc , err := p . FindFunctionLocation ( "main.main" , true , 0 )
2015-03-26 18:15:35 +00:00
if err != nil {
t . Fatal ( err )
}
2015-06-20 23:01:06 +00:00
_ , err = p . SetBreakpoint ( pc )
2015-03-26 18:15:35 +00:00
if err != nil {
t . Fatal ( err )
}
err = p . Continue ( )
if err != nil {
t . Fatal ( err )
}
var nt int
2016-01-10 08:57:52 +00:00
ct := p . CurrentThread . ID
2015-04-26 11:31:28 +00:00
for tid := range p . Threads {
2015-03-26 18:15:35 +00:00
if tid != ct {
nt = tid
break
}
}
if nt == 0 {
t . Fatal ( "could not find thread to switch to" )
}
// With valid thread id
err = p . SwitchThread ( nt )
if err != nil {
t . Fatal ( err )
}
2016-01-10 08:57:52 +00:00
if p . CurrentThread . ID != nt {
2015-03-26 18:15:35 +00:00
t . Fatal ( "Did not switch threads" )
}
} )
}
2015-06-17 17:11:57 +00:00
2015-08-28 08:06:22 +00:00
func TestCGONext ( t * testing . T ) {
// Test if one can do 'next' in a cgo binary
// On OSX with Go < 1.5 CGO is not supported due to: https://github.com/golang/go/issues/8973
if runtime . GOOS == "darwin" && strings . Contains ( runtime . Version ( ) , "1.4" ) {
return
}
withTestProcess ( "cgotest" , t , func ( p * Process , fixture protest . Fixture ) {
pc , err := p . FindFunctionLocation ( "main.main" , true , 0 )
if err != nil {
t . Fatal ( err )
}
_ , err = p . SetBreakpoint ( pc )
if err != nil {
t . Fatal ( err )
}
err = p . Continue ( )
if err != nil {
t . Fatal ( err )
}
err = p . Next ( )
if err != nil {
t . Fatal ( err )
}
} )
}
2015-06-17 17:11:57 +00:00
type loc struct {
line int
fn string
}
2015-08-28 20:06:29 +00:00
func ( l1 * loc ) match ( l2 Stackframe ) bool {
2015-06-17 17:11:57 +00:00
if l1 . line >= 0 {
2015-09-17 08:42:34 +00:00
if l1 . line != l2 . Call . Line {
2015-06-17 17:11:57 +00:00
return false
}
}
2015-09-17 08:42:34 +00:00
return l1 . fn == l2 . Call . Fn . Name
2015-06-17 17:11:57 +00:00
}
func TestStacktrace ( t * testing . T ) {
stacks := [ ] [ ] loc {
2015-10-22 17:07:24 +00:00
{ { 4 , "main.stacktraceme" } , { 8 , "main.func1" } , { 16 , "main.main" } } ,
{ { 4 , "main.stacktraceme" } , { 8 , "main.func1" } , { 12 , "main.func2" } , { 17 , "main.main" } } ,
2015-06-17 17:11:57 +00:00
}
2015-06-20 22:54:52 +00:00
withTestProcess ( "stacktraceprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-08-07 16:50:14 +00:00
bp , err := setFunctionBreakpoint ( p , "main.stacktraceme" )
2015-06-17 17:11:57 +00:00
assertNoError ( err , t , "BreakByLocation()" )
for i := range stacks {
assertNoError ( p . Continue ( ) , t , "Continue()" )
2015-07-01 03:16:52 +00:00
locations , err := p . CurrentThread . Stacktrace ( 40 )
2015-06-17 17:11:57 +00:00
assertNoError ( err , t , "Stacktrace()" )
if len ( locations ) != len ( stacks [ i ] ) + 2 {
t . Fatalf ( "Wrong stack trace size %d %d\n" , len ( locations ) , len ( stacks [ i ] ) + 2 )
}
2015-09-17 08:42:34 +00:00
t . Logf ( "Stacktrace %d:\n" , i )
for i := range locations {
t . Logf ( "\t%s:%d\n" , locations [ i ] . Call . File , locations [ i ] . Call . Line )
}
2015-08-28 20:06:29 +00:00
2015-06-17 17:11:57 +00:00
for j := range stacks [ i ] {
if ! stacks [ i ] [ j ] . match ( locations [ j ] ) {
t . Fatalf ( "Wrong stack trace pos %d\n" , j )
}
}
}
2015-06-20 23:01:06 +00:00
p . ClearBreakpoint ( bp . Addr )
2015-06-17 17:11:57 +00:00
p . Continue ( )
} )
}
2015-09-17 08:42:34 +00:00
func TestStacktrace2 ( t * testing . T ) {
withTestProcess ( "retstack" , t , func ( p * Process , fixture protest . Fixture ) {
assertNoError ( p . Continue ( ) , t , "Continue()" )
locations , err := p . CurrentThread . Stacktrace ( 40 )
assertNoError ( err , t , "Stacktrace()" )
2015-11-06 18:49:29 +00:00
if ! stackMatch ( [ ] loc { { - 1 , "main.f" } , { 16 , "main.main" } } , locations , false ) {
2015-09-17 08:42:34 +00:00
for i := range locations {
t . Logf ( "\t%s:%d [%s]\n" , locations [ i ] . Call . File , locations [ i ] . Call . Line , locations [ i ] . Call . Fn . Name )
}
2016-01-10 08:57:52 +00:00
t . Fatalf ( "Stack error at main.f()\n%v\n" , locations )
2015-09-17 08:42:34 +00:00
}
assertNoError ( p . Continue ( ) , t , "Continue()" )
locations , err = p . CurrentThread . Stacktrace ( 40 )
assertNoError ( err , t , "Stacktrace()" )
2015-11-06 18:49:29 +00:00
if ! stackMatch ( [ ] loc { { - 1 , "main.g" } , { 17 , "main.main" } } , locations , false ) {
2015-09-17 08:42:34 +00:00
for i := range locations {
t . Logf ( "\t%s:%d [%s]\n" , locations [ i ] . Call . File , locations [ i ] . Call . Line , locations [ i ] . Call . Fn . Name )
}
2016-01-10 08:57:52 +00:00
t . Fatalf ( "Stack error at main.g()\n%v\n" , locations )
2015-09-17 08:42:34 +00:00
}
} )
}
2015-11-06 18:49:29 +00:00
func stackMatch ( stack [ ] loc , locations [ ] Stackframe , skipRuntime bool ) bool {
2015-06-17 17:11:57 +00:00
if len ( stack ) > len ( locations ) {
return false
}
2015-11-06 18:49:29 +00:00
i := 0
for j := range locations {
if i >= len ( stack ) {
break
}
if skipRuntime {
if locations [ j ] . Call . Fn == nil || strings . HasPrefix ( locations [ j ] . Call . Fn . Name , "runtime." ) {
continue
}
}
if ! stack [ i ] . match ( locations [ j ] ) {
2015-06-17 17:11:57 +00:00
return false
}
2015-11-06 18:49:29 +00:00
i ++
2015-06-17 17:11:57 +00:00
}
2015-11-06 18:49:29 +00:00
return i >= len ( stack )
2015-06-17 17:11:57 +00:00
}
func TestStacktraceGoroutine ( t * testing . T ) {
2015-11-06 18:49:29 +00:00
mainStack := [ ] loc { { 13 , "main.stacktraceme" } , { 26 , "main.main" } }
agoroutineStackA := [ ] loc { { 9 , "main.agoroutine" } }
agoroutineStackB := [ ] loc { { 10 , "main.agoroutine" } }
2015-06-17 17:11:57 +00:00
2015-06-20 22:54:52 +00:00
withTestProcess ( "goroutinestackprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-08-07 16:50:14 +00:00
bp , err := setFunctionBreakpoint ( p , "main.stacktraceme" )
2015-06-17 17:11:57 +00:00
assertNoError ( err , t , "BreakByLocation()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
gs , err := p . GoroutinesInfo ( )
assertNoError ( err , t , "GoroutinesInfo" )
agoroutineCount := 0
mainCount := 0
2015-07-01 03:16:52 +00:00
for i , g := range gs {
2016-03-18 08:51:48 +00:00
locations , err := g . Stacktrace ( 40 )
2015-06-17 17:11:57 +00:00
assertNoError ( err , t , "GoroutineStacktrace()" )
2015-11-06 18:49:29 +00:00
if stackMatch ( mainStack , locations , false ) {
2015-06-17 17:11:57 +00:00
mainCount ++
}
2015-11-06 18:49:29 +00:00
if stackMatch ( agoroutineStackA , locations , true ) {
agoroutineCount ++
} else if stackMatch ( agoroutineStackB , locations , true ) {
2015-06-17 17:11:57 +00:00
agoroutineCount ++
} else {
2015-07-01 03:16:52 +00:00
t . Logf ( "Non-goroutine stack: %d (%d)" , i , len ( locations ) )
2015-06-17 17:11:57 +00:00
for i := range locations {
name := ""
2015-09-17 08:42:34 +00:00
if locations [ i ] . Call . Fn != nil {
name = locations [ i ] . Call . Fn . Name
2015-06-17 17:11:57 +00:00
}
2015-09-17 08:42:34 +00:00
t . Logf ( "\t%s:%d %s\n" , locations [ i ] . Call . File , locations [ i ] . Call . Line , name )
2015-06-17 17:11:57 +00:00
}
}
}
if mainCount != 1 {
2015-11-06 18:49:29 +00:00
t . Fatalf ( "Main goroutine stack not found %d" , mainCount )
2015-06-17 17:11:57 +00:00
}
if agoroutineCount != 10 {
t . Fatalf ( "Goroutine stacks not found (%d)" , agoroutineCount )
}
2015-06-20 23:01:06 +00:00
p . ClearBreakpoint ( bp . Addr )
2015-06-17 17:11:57 +00:00
p . Continue ( )
} )
}
2015-07-11 06:43:47 +00:00
func TestKill ( t * testing . T ) {
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
if err := p . Kill ( ) ; err != nil {
t . Fatal ( err )
}
if p . Exited ( ) != true {
t . Fatal ( "expected process to have exited" )
}
if runtime . GOOS == "linux" {
_ , err := os . Open ( fmt . Sprintf ( "/proc/%d/" , p . Pid ) )
if err == nil {
t . Fatal ( "process has not exited" , p . Pid )
}
}
} )
}
2015-07-23 17:08:28 +00:00
func testGSupportFunc ( name string , t * testing . T , p * Process , fixture protest . Fixture ) {
2015-08-07 16:50:14 +00:00
bp , err := setFunctionBreakpoint ( p , "main.main" )
2015-07-23 17:08:28 +00:00
assertNoError ( err , t , name + ": BreakByLocation()" )
assertNoError ( p . Continue ( ) , t , name + ": Continue()" )
g , err := p . CurrentThread . GetG ( )
assertNoError ( err , t , name + ": GetG()" )
if g == nil {
t . Fatal ( name + ": g was nil" )
}
t . Logf ( name + ": g is: %v" , g )
p . ClearBreakpoint ( bp . Addr )
}
func TestGetG ( t * testing . T ) {
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
testGSupportFunc ( "nocgo" , t , p , fixture )
} )
2015-07-28 05:33:07 +00:00
// On OSX with Go < 1.5 CGO is not supported due to: https://github.com/golang/go/issues/8973
if runtime . GOOS == "darwin" && strings . Contains ( runtime . Version ( ) , "1.4" ) {
return
}
2015-07-23 17:08:28 +00:00
withTestProcess ( "cgotest" , t , func ( p * Process , fixture protest . Fixture ) {
testGSupportFunc ( "cgo" , t , p , fixture )
} )
}
2015-07-09 16:41:03 +00:00
func TestContinueMulti ( t * testing . T ) {
withTestProcess ( "integrationprog" , t , func ( p * Process , fixture protest . Fixture ) {
2015-08-07 16:50:14 +00:00
bp1 , err := setFunctionBreakpoint ( p , "main.main" )
2015-07-09 16:41:03 +00:00
assertNoError ( err , t , "BreakByLocation()" )
2015-08-07 16:50:14 +00:00
bp2 , err := setFunctionBreakpoint ( p , "main.sayhi" )
2015-07-09 16:41:03 +00:00
assertNoError ( err , t , "BreakByLocation()" )
mainCount := 0
sayhiCount := 0
for {
err := p . Continue ( )
2015-09-26 20:56:24 +00:00
if p . Exited ( ) {
2015-07-09 16:41:03 +00:00
break
}
assertNoError ( err , t , "Continue()" )
if p . CurrentBreakpoint ( ) . ID == bp1 . ID {
mainCount ++
}
if p . CurrentBreakpoint ( ) . ID == bp2 . ID {
sayhiCount ++
}
}
if mainCount != 1 {
t . Fatalf ( "Main breakpoint hit wrong number of times: %d\n" , mainCount )
}
if sayhiCount != 3 {
t . Fatalf ( "Sayhi breakpoint hit wrong number of times: %d\n" , sayhiCount )
}
} )
}
2015-07-28 19:54:40 +00:00
2015-07-28 20:42:56 +00:00
func versionAfterOrEqual ( t * testing . T , verStr string , ver GoVersion ) {
2016-02-25 09:48:42 +00:00
pver , ok := ParseVersionString ( verStr )
2015-07-28 19:54:40 +00:00
if ! ok {
t . Fatalf ( "Could not parse version string <%s>" , verStr )
}
2015-07-28 20:42:56 +00:00
if ! pver . AfterOrEqual ( ver ) {
2015-07-28 19:54:40 +00:00
t . Fatalf ( "Version <%s> parsed as %v not after %v" , verStr , pver , ver )
}
t . Logf ( "version string <%s> → %v" , verStr , ver )
}
func TestParseVersionString ( t * testing . T ) {
2015-08-12 11:37:36 +00:00
versionAfterOrEqual ( t , "go1.4" , GoVersion { 1 , 4 , 0 , 0 , 0 } )
2015-08-11 13:37:27 +00:00
versionAfterOrEqual ( t , "go1.5.0" , GoVersion { 1 , 5 , 0 , 0 , 0 } )
versionAfterOrEqual ( t , "go1.4.2" , GoVersion { 1 , 4 , 2 , 0 , 0 } )
versionAfterOrEqual ( t , "go1.5beta2" , GoVersion { 1 , 5 , - 1 , 2 , 0 } )
versionAfterOrEqual ( t , "go1.5rc2" , GoVersion { 1 , 5 , - 1 , 0 , 2 } )
2016-06-07 18:07:37 +00:00
versionAfterOrEqual ( t , "go1.6.1 (appengine-1.9.37)" , GoVersion { 1 , 6 , 1 , 0 , 0 } )
2016-02-25 09:48:42 +00:00
ver , ok := ParseVersionString ( "devel +17efbfc Tue Jul 28 17:39:19 2015 +0000 linux/amd64" )
2015-07-28 19:54:40 +00:00
if ! ok {
t . Fatalf ( "Could not parse devel version string" )
}
if ! ver . IsDevel ( ) {
t . Fatalf ( "Devel version string not correctly recognized" )
}
}
2015-08-07 16:50:14 +00:00
func TestBreakpointOnFunctionEntry ( t * testing . T ) {
withTestProcess ( "testprog" , t , func ( p * Process , fixture protest . Fixture ) {
addr , err := p . FindFunctionLocation ( "main.main" , false , 0 )
assertNoError ( err , t , "FindFunctionLocation()" )
_ , err = p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
_ , ln := currentLineNumber ( p , t )
if ln != 17 {
t . Fatalf ( "Wrong line number: %d (expected: 17)\n" , ln )
}
} )
}
2015-08-12 00:12:37 +00:00
func TestProcessReceivesSIGCHLD ( t * testing . T ) {
withTestProcess ( "sigchldprog" , t , func ( p * Process , fixture protest . Fixture ) {
err := p . Continue ( )
_ , ok := err . ( ProcessExitedError )
if ! ok {
t . Fatalf ( "Continue() returned unexpected error type %s" , err )
}
} )
}
2015-10-04 18:16:39 +00:00
func TestIssue239 ( t * testing . T ) {
withTestProcess ( "is sue239" , t , func ( p * Process , fixture protest . Fixture ) {
pos , _ , err := p . goSymTable . LineToPC ( fixture . Source , 17 )
assertNoError ( err , t , "LineToPC()" )
_ , err = p . SetBreakpoint ( pos )
assertNoError ( err , t , fmt . Sprintf ( "SetBreakpoint(%d)" , pos ) )
assertNoError ( p . Continue ( ) , t , fmt . Sprintf ( "Continue()" ) )
} )
}
2015-10-18 17:37:13 +00:00
func evalVariable ( p * Process , symbol string ) ( * Variable , error ) {
scope , err := p . CurrentThread . Scope ( )
if err != nil {
return nil , err
}
2016-04-24 17:15:39 +00:00
return scope . EvalVariable ( symbol , normalLoadConfig )
2015-10-18 17:37:13 +00:00
}
func setVariable ( p * Process , symbol , value string ) error {
scope , err := p . CurrentThread . Scope ( )
if err != nil {
return err
}
return scope . SetVariable ( symbol , value )
}
func TestVariableEvaluation ( t * testing . T ) {
testcases := [ ] struct {
name string
st reflect . Kind
value interface { }
length , cap int64
childrenlen int
} {
{ "a1" , reflect . String , "foofoofoofoofoofoo" , 18 , 0 , 0 } ,
{ "a11" , reflect . Array , nil , 3 , - 1 , 3 } ,
{ "a12" , reflect . Slice , nil , 2 , 2 , 2 } ,
{ "a13" , reflect . Slice , nil , 3 , 3 , 3 } ,
{ "a2" , reflect . Int , int64 ( 6 ) , 0 , 0 , 0 } ,
{ "a3" , reflect . Float64 , float64 ( 7.23 ) , 0 , 0 , 0 } ,
{ "a4" , reflect . Array , nil , 2 , - 1 , 2 } ,
{ "a5" , reflect . Slice , nil , 5 , 5 , 5 } ,
{ "a6" , reflect . Struct , nil , 2 , 0 , 2 } ,
{ "a7" , reflect . Ptr , nil , 1 , 0 , 1 } ,
{ "a8" , reflect . Struct , nil , 2 , 0 , 2 } ,
{ "a9" , reflect . Ptr , nil , 1 , 0 , 1 } ,
{ "baz" , reflect . String , "bazburzum" , 9 , 0 , 0 } ,
{ "neg" , reflect . Int , int64 ( - 1 ) , 0 , 0 , 0 } ,
{ "f32" , reflect . Float32 , float64 ( float32 ( 1.2 ) ) , 0 , 0 , 0 } ,
2015-10-30 11:39:32 +00:00
{ "c64" , reflect . Complex64 , complex128 ( complex64 ( 1 + 2i ) ) , 0 , 0 , 0 } ,
{ "c128" , reflect . Complex128 , complex128 ( 2 + 3i ) , 0 , 0 , 0 } ,
2015-10-18 17:37:13 +00:00
{ "a6.Baz" , reflect . Int , int64 ( 8 ) , 0 , 0 , 0 } ,
{ "a7.Baz" , reflect . Int , int64 ( 5 ) , 0 , 0 , 0 } ,
{ "a8.Baz" , reflect . String , "feh" , 3 , 0 , 0 } ,
{ "a8" , reflect . Struct , nil , 2 , 0 , 2 } ,
{ "i32" , reflect . Array , nil , 2 , - 1 , 2 } ,
{ "b1" , reflect . Bool , true , 0 , 0 , 0 } ,
{ "b2" , reflect . Bool , false , 0 , 0 , 0 } ,
{ "f" , reflect . Func , "main.barfoo" , 0 , 0 , 0 } ,
{ "ba" , reflect . Slice , nil , 200 , 200 , 64 } ,
}
withTestProcess ( "testvariables" , t , func ( p * Process , fixture protest . Fixture ) {
assertNoError ( p . Continue ( ) , t , "Continue() returned an error" )
for _ , tc := range testcases {
v , err := evalVariable ( p , tc . name )
assertNoError ( err , t , fmt . Sprintf ( "EvalVariable(%s)" , tc . name ) )
if v . Kind != tc . st {
t . Fatalf ( "%s simple type: expected: %s got: %s" , tc . name , tc . st , v . Kind . String ( ) )
}
if v . Value == nil && tc . value != nil {
t . Fatalf ( "%s value: expected: %v got: %v" , tc . name , tc . value , v . Value )
} else {
2015-10-21 07:06:36 +00:00
switch v . Kind {
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
x , _ := constant . Int64Val ( v . Value )
2015-10-18 17:37:13 +00:00
if y , ok := tc . value . ( int64 ) ; ! ok || x != y {
t . Fatalf ( "%s value: expected: %v got: %v" , tc . name , tc . value , v . Value )
}
2015-10-21 07:06:36 +00:00
case reflect . Float32 , reflect . Float64 :
x , _ := constant . Float64Val ( v . Value )
2015-10-18 17:37:13 +00:00
if y , ok := tc . value . ( float64 ) ; ! ok || x != y {
t . Fatalf ( "%s value: expected: %v got: %v" , tc . name , tc . value , v . Value )
}
2015-10-30 11:39:32 +00:00
case reflect . Complex64 , reflect . Complex128 :
xr , _ := constant . Float64Val ( constant . Real ( v . Value ) )
xi , _ := constant . Float64Val ( constant . Imag ( v . Value ) )
if y , ok := tc . value . ( complex128 ) ; ! ok || complex ( xr , xi ) != y {
t . Fatalf ( "%s value: expected: %v got: %v" , tc . name , tc . value , v . Value )
}
2015-10-21 07:06:36 +00:00
case reflect . String :
if y , ok := tc . value . ( string ) ; ! ok || constant . StringVal ( v . Value ) != y {
2015-10-18 17:37:13 +00:00
t . Fatalf ( "%s value: expected: %v got: %v" , tc . name , tc . value , v . Value )
}
}
}
if v . Len != tc . length {
t . Fatalf ( "%s len: expected: %d got: %d" , tc . name , tc . length , v . Len )
}
if v . Cap != tc . cap {
t . Fatalf ( "%s cap: expected: %d got: %d" , tc . name , tc . cap , v . Cap )
}
if len ( v . Children ) != tc . childrenlen {
t . Fatalf ( "%s children len: expected %d got: %d" , tc . name , tc . childrenlen , len ( v . Children ) )
}
}
} )
}
func TestFrameEvaluation ( t * testing . T ) {
withTestProcess ( "goroutinestackprog" , t , func ( p * Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.stacktraceme" )
assertNoError ( err , t , "setFunctionBreakpoint" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
2015-11-10 09:49:47 +00:00
// Testing evaluation on goroutines
2015-10-18 17:37:13 +00:00
gs , err := p . GoroutinesInfo ( )
assertNoError ( err , t , "GoroutinesInfo" )
found := make ( [ ] bool , 10 )
for _ , g := range gs {
frame := - 1
2016-03-18 08:51:48 +00:00
frames , err := g . Stacktrace ( 10 )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , "GoroutineStacktrace()" )
for i := range frames {
if frames [ i ] . Call . Fn != nil && frames [ i ] . Call . Fn . Name == "main.agoroutine" {
frame = i
break
}
}
if frame < 0 {
2016-01-10 08:57:52 +00:00
t . Logf ( "Goroutine %d: could not find correct frame" , g . ID )
2015-10-18 17:37:13 +00:00
continue
}
2016-01-10 08:57:52 +00:00
scope , err := p . ConvertEvalScope ( g . ID , frame )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , "ConvertEvalScope()" )
t . Logf ( "scope = %v" , scope )
2016-04-24 17:15:39 +00:00
v , err := scope . EvalVariable ( "i" , normalLoadConfig )
2015-10-18 17:37:13 +00:00
t . Logf ( "v = %v" , v )
if err != nil {
2016-01-10 08:57:52 +00:00
t . Logf ( "Goroutine %d: %v\n" , g . ID , err )
2015-10-18 17:37:13 +00:00
continue
}
2015-10-21 07:06:36 +00:00
vval , _ := constant . Int64Val ( v . Value )
found [ vval ] = true
2015-10-18 17:37:13 +00:00
}
for i := range found {
if ! found [ i ] {
t . Fatalf ( "Goroutine %d not found\n" , i )
}
}
2015-11-10 09:49:47 +00:00
// Testing evaluation on frames
2015-10-18 17:37:13 +00:00
assertNoError ( p . Continue ( ) , t , "Continue() 2" )
g , err := p . CurrentThread . GetG ( )
assertNoError ( err , t , "GetG()" )
for i := 0 ; i <= 3 ; i ++ {
2016-01-10 08:57:52 +00:00
scope , err := p . ConvertEvalScope ( g . ID , i + 1 )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , fmt . Sprintf ( "ConvertEvalScope() on frame %d" , i + 1 ) )
2016-04-24 17:15:39 +00:00
v , err := scope . EvalVariable ( "n" , normalLoadConfig )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , fmt . Sprintf ( "EvalVariable() on frame %d" , i + 1 ) )
2015-10-21 07:06:36 +00:00
n , _ := constant . Int64Val ( v . Value )
2015-10-18 17:37:13 +00:00
t . Logf ( "frame %d n %d\n" , i + 1 , n )
if n != int64 ( 3 - i ) {
t . Fatalf ( "On frame %d value of n is %d (not %d)" , i + 1 , n , 3 - i )
}
}
} )
}
func TestPointerSetting ( t * testing . T ) {
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
2015-10-18 17:37:13 +00:00
assertNoError ( p . Continue ( ) , t , "Continue() returned an error" )
pval := func ( n int64 ) {
variable , err := evalVariable ( p , "p1" )
assertNoError ( err , t , "EvalVariable()" )
2015-10-21 07:06:36 +00:00
c0val , _ := constant . Int64Val ( variable . Children [ 0 ] . Value )
if c0val != n {
t . Fatalf ( "Wrong value of p1, *%d expected *%d" , c0val , n )
2015-10-18 17:37:13 +00:00
}
}
pval ( 1 )
// change p1 to point to i2
scope , err := p . CurrentThread . Scope ( )
assertNoError ( err , t , "Scope()" )
2016-04-24 17:15:39 +00:00
i2addr , err := scope . EvalExpression ( "i2" , normalLoadConfig )
2015-10-30 11:39:32 +00:00
assertNoError ( err , t , "EvalExpression()" )
assertNoError ( setVariable ( p , "p1" , fmt . Sprintf ( "(*int)(0x%x)" , i2addr . Addr ) ) , t , "SetVariable()" )
2015-10-18 17:37:13 +00:00
pval ( 2 )
// change the value of i2 check that p1 also changes
assertNoError ( setVariable ( p , "i2" , "5" ) , t , "SetVariable()" )
pval ( 5 )
} )
}
func TestVariableFunctionScoping ( t * testing . T ) {
withTestProcess ( "testvariables" , t , func ( p * Process , fixture protest . Fixture ) {
err := p . Continue ( )
assertNoError ( err , t , "Continue() returned an error" )
_ , err = evalVariable ( p , "a1" )
assertNoError ( err , t , "Unable to find variable a1" )
_ , err = evalVariable ( p , "a2" )
assertNoError ( err , t , "Unable to find variable a1" )
// Move scopes, a1 exists here by a2 does not
err = p . Continue ( )
assertNoError ( err , t , "Continue() returned an error" )
_ , err = evalVariable ( p , "a1" )
assertNoError ( err , t , "Unable to find variable a1" )
_ , err = evalVariable ( p , "a2" )
if err == nil {
t . Fatalf ( "Can eval out of scope variable a2" )
}
} )
}
func TestRecursiveStructure ( t * testing . T ) {
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
assertNoError ( p . Continue ( ) , t , "Continue()" )
v , err := evalVariable ( p , "aas" )
assertNoError ( err , t , "EvalVariable()" )
t . Logf ( "v: %v\n" , v )
} )
}
2015-12-19 13:57:48 +00:00
func TestIssue316 ( t * testing . T ) {
// A pointer loop that includes one interface should not send dlv into an infinite loop
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
2015-12-19 13:57:48 +00:00
assertNoError ( p . Continue ( ) , t , "Continue()" )
_ , err := evalVariable ( p , "iface5" )
assertNoError ( err , t , "EvalVariable()" )
} )
}
2015-12-20 07:43:17 +00:00
func TestIssue325 ( t * testing . T ) {
// nil pointer dereference when evaluating interfaces to function pointers
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
2015-12-20 07:43:17 +00:00
assertNoError ( p . Continue ( ) , t , "Continue()" )
iface2fn1v , err := evalVariable ( p , "iface2fn1" )
assertNoError ( err , t , "EvalVariable()" )
t . Logf ( "iface2fn1: %v\n" , iface2fn1v )
iface2fn2v , err := evalVariable ( p , "iface2fn2" )
assertNoError ( err , t , "EvalVariable()" )
t . Logf ( "iface2fn2: %v\n" , iface2fn2v )
} )
}
2015-10-02 16:17:07 +00:00
func TestBreakpointCounts ( t * testing . T ) {
withTestProcess ( "bpcountstest" , t , func ( p * Process , fixture protest . Fixture ) {
addr , _ , err := p . goSymTable . LineToPC ( fixture . Source , 12 )
assertNoError ( err , t , "LineToPC" )
bp , err := p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
for {
if err := p . Continue ( ) ; err != nil {
if _ , exited := err . ( ProcessExitedError ) ; exited {
break
}
assertNoError ( err , t , "Continue()" )
}
}
t . Logf ( "TotalHitCount: %d" , bp . TotalHitCount )
2015-11-19 15:19:42 +00:00
if bp . TotalHitCount != 200 {
t . Fatalf ( "Wrong TotalHitCount for the breakpoint (%d)" , bp . TotalHitCount )
}
if len ( bp . HitCount ) != 2 {
t . Fatalf ( "Wrong number of goroutines for breakpoint (%d)" , len ( bp . HitCount ) )
}
for _ , v := range bp . HitCount {
if v != 100 {
t . Fatalf ( "Wrong HitCount for breakpoint (%v)" , bp . HitCount )
}
}
} )
}
2016-01-10 12:49:03 +00:00
func BenchmarkArray ( b * testing . B ) {
// each bencharr struct is 128 bytes, bencharr is 64 elements long
b . SetBytes ( int64 ( 64 * 128 ) )
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , b , func ( p * Process , fixture protest . Fixture ) {
2016-01-10 12:49:03 +00:00
assertNoError ( p . Continue ( ) , b , "Continue()" )
for i := 0 ; i < b . N ; i ++ {
_ , err := evalVariable ( p , "bencharr" )
assertNoError ( err , b , "EvalVariable()" )
}
} )
}
2015-11-19 15:19:42 +00:00
const doTestBreakpointCountsWithDetection = false
func TestBreakpointCountsWithDetection ( t * testing . T ) {
if ! doTestBreakpointCountsWithDetection {
return
}
m := map [ int64 ] int64 { }
withTestProcess ( "bpcountstest" , t , func ( p * Process , fixture protest . Fixture ) {
addr , _ , err := p . goSymTable . LineToPC ( fixture . Source , 12 )
assertNoError ( err , t , "LineToPC" )
bp , err := p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
for {
if err := p . Continue ( ) ; err != nil {
if _ , exited := err . ( ProcessExitedError ) ; exited {
break
}
assertNoError ( err , t , "Continue()" )
}
fmt . Printf ( "Continue returned %d\n" , bp . TotalHitCount )
for _ , th := range p . Threads {
if th . CurrentBreakpoint == nil {
continue
}
scope , err := th . Scope ( )
assertNoError ( err , t , "Scope()" )
2016-04-24 17:15:39 +00:00
v , err := scope . EvalVariable ( "i" , normalLoadConfig )
2015-11-19 15:19:42 +00:00
assertNoError ( err , t , "evalVariable" )
i , _ := constant . Int64Val ( v . Value )
2016-04-24 17:15:39 +00:00
v , err = scope . EvalVariable ( "id" , normalLoadConfig )
2015-11-19 15:19:42 +00:00
assertNoError ( err , t , "evalVariable" )
id , _ := constant . Int64Val ( v . Value )
m [ id ] = i
2016-01-10 08:57:52 +00:00
fmt . Printf ( "\tgoroutine (%d) %d: %d\n" , th . ID , id , i )
2015-11-19 15:19:42 +00:00
}
total := int64 ( 0 )
for i := range m {
total += m [ i ] + 1
}
if uint64 ( total ) != bp . TotalHitCount {
t . Fatalf ( "Mismatched total count %d %d\n" , total , bp . TotalHitCount )
}
}
t . Logf ( "TotalHitCount: %d" , bp . TotalHitCount )
2015-10-02 16:17:07 +00:00
if bp . TotalHitCount != 200 {
t . Fatalf ( "Wrong TotalHitCount for the breakpoint (%d)" , bp . TotalHitCount )
}
if len ( bp . HitCount ) != 2 {
t . Fatalf ( "Wrong number of goroutines for breakpoint (%d)" , len ( bp . HitCount ) )
}
for _ , v := range bp . HitCount {
if v != 100 {
t . Fatalf ( "Wrong HitCount for breakpoint (%v)" , bp . HitCount )
}
}
} )
}
2015-11-10 09:49:47 +00:00
2016-01-10 12:49:03 +00:00
func BenchmarkArrayPointer ( b * testing . B ) {
// each bencharr struct is 128 bytes, benchparr is an array of 64 pointers to bencharr
// each read will read 64 bencharr structs plus the 64 pointers of benchparr
b . SetBytes ( int64 ( 64 * 128 + 64 * 8 ) )
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , b , func ( p * Process , fixture protest . Fixture ) {
2016-01-10 12:49:03 +00:00
assertNoError ( p . Continue ( ) , b , "Continue()" )
for i := 0 ; i < b . N ; i ++ {
_ , err := evalVariable ( p , "bencharr" )
assertNoError ( err , b , "EvalVariable()" )
}
} )
}
func BenchmarkMap ( b * testing . B ) {
// m1 contains 41 entries, each one has a value that's 2 int values (2* 8 bytes) and a string key
// each string key has an average of 9 character
// reading strings and the map structure imposes a overhead that we ignore here
b . SetBytes ( int64 ( 41 * ( 2 * 8 + 9 ) ) )
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , b , func ( p * Process , fixture protest . Fixture ) {
2016-01-10 12:49:03 +00:00
assertNoError ( p . Continue ( ) , b , "Continue()" )
for i := 0 ; i < b . N ; i ++ {
_ , err := evalVariable ( p , "m1" )
assertNoError ( err , b , "EvalVariable()" )
}
} )
}
func BenchmarkGoroutinesInfo ( b * testing . B ) {
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , b , func ( p * Process , fixture protest . Fixture ) {
2016-01-10 12:49:03 +00:00
assertNoError ( p . Continue ( ) , b , "Continue()" )
for i := 0 ; i < b . N ; i ++ {
p . allGCache = nil
_ , err := p . GoroutinesInfo ( )
assertNoError ( err , b , "GoroutinesInfo" )
}
} )
}
2015-11-10 09:49:47 +00:00
func TestIssue262 ( t * testing . T ) {
// Continue does not work when the current breakpoint is set on a NOP instruction
withTestProcess ( "issue262" , t , func ( p * Process , fixture protest . Fixture ) {
addr , _ , err := p . goSymTable . LineToPC ( fixture . Source , 11 )
assertNoError ( err , t , "LineToPC" )
_ , err = p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
err = p . Continue ( )
if err == nil {
t . Fatalf ( "No error on second continue" )
}
_ , exited := err . ( ProcessExitedError )
if ! exited {
t . Fatalf ( "Process did not exit after second continue: %v" , err )
}
} )
}
2016-01-10 09:04:14 +00:00
2016-01-16 08:13:15 +00:00
func TestIssue305 ( t * testing . T ) {
// If 'next' hits a breakpoint on the goroutine it's stepping through the temp breakpoints aren't cleared
// preventing further use of 'next' command
withTestProcess ( "issue305" , t , func ( p * Process , fixture protest . Fixture ) {
addr , _ , err := p . goSymTable . LineToPC ( fixture . Source , 5 )
assertNoError ( err , t , "LineToPC()" )
_ , err = p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
assertNoError ( p . Next ( ) , t , "Next() 1" )
assertNoError ( p . Next ( ) , t , "Next() 2" )
assertNoError ( p . Next ( ) , t , "Next() 3" )
assertNoError ( p . Next ( ) , t , "Next() 4" )
assertNoError ( p . Next ( ) , t , "Next() 5" )
} )
}
2016-01-10 09:04:14 +00:00
func TestIssue341 ( t * testing . T ) {
// pointer loop through map entries
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
2016-01-10 09:04:14 +00:00
assertNoError ( p . Continue ( ) , t , "Continue()" )
t . Logf ( "requesting mapinf" )
mapinf , err := evalVariable ( p , "mapinf" )
assertNoError ( err , t , "EvalVariable()" )
t . Logf ( "mapinf: %v\n" , mapinf )
} )
}
2016-01-10 12:49:03 +00:00
func BenchmarkLocalVariables ( b * testing . B ) {
withTestProcess ( "testvariables" , b , func ( p * Process , fixture protest . Fixture ) {
assertNoError ( p . Continue ( ) , b , "Continue() returned an error" )
scope , err := p . CurrentThread . Scope ( )
assertNoError ( err , b , "Scope()" )
for i := 0 ; i < b . N ; i ++ {
2016-04-24 17:15:39 +00:00
_ , err := scope . LocalVariables ( normalLoadConfig )
2016-01-10 12:49:03 +00:00
assertNoError ( err , b , "LocalVariables()" )
}
} )
}
2016-01-12 08:01:42 +00:00
func TestCondBreakpoint ( t * testing . T ) {
withTestProcess ( "parallel_next" , t , func ( p * Process , fixture protest . Fixture ) {
addr , _ , err := p . goSymTable . LineToPC ( fixture . Source , 9 )
assertNoError ( err , t , "LineToPC" )
bp , err := p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "n" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "7" } ,
}
assertNoError ( p . Continue ( ) , t , "Continue()" )
nvar , err := evalVariable ( p , "n" )
assertNoError ( err , t , "EvalVariable()" )
n , _ := constant . Int64Val ( nvar . Value )
if n != 7 {
t . Fatalf ( "Stoppend on wrong goroutine %d\n" , n )
}
} )
}
func TestCondBreakpointError ( t * testing . T ) {
withTestProcess ( "parallel_next" , t , func ( p * Process , fixture protest . Fixture ) {
addr , _ , err := p . goSymTable . LineToPC ( fixture . Source , 9 )
assertNoError ( err , t , "LineToPC" )
bp , err := p . SetBreakpoint ( addr )
assertNoError ( err , t , "SetBreakpoint()" )
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "nonexistentvariable" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "7" } ,
}
err = p . Continue ( )
if err == nil {
t . Fatalf ( "No error on first Continue()" )
}
if err . Error ( ) != "error evaluating expression: could not find symbol value for nonexistentvariable" && err . Error ( ) != "multiple errors evaluating conditions" {
t . Fatalf ( "Unexpected error on first Continue(): %v" , err )
}
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "n" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "7" } ,
}
err = p . Continue ( )
if err != nil {
if _ , exited := err . ( ProcessExitedError ) ; ! exited {
t . Fatalf ( "Unexpected error on second Continue(): %v" , err )
}
} else {
nvar , err := evalVariable ( p , "n" )
assertNoError ( err , t , "EvalVariable()" )
n , _ := constant . Int64Val ( nvar . Value )
if n != 7 {
t . Fatalf ( "Stoppend on wrong goroutine %d\n" , n )
}
}
} )
}
2016-01-24 09:25:54 +00:00
func TestIssue356 ( t * testing . T ) {
// slice with a typedef does not get printed correctly
2016-02-18 17:50:29 +00:00
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
2016-01-24 09:25:54 +00:00
assertNoError ( p . Continue ( ) , t , "Continue() returned an error" )
mmvar , err := evalVariable ( p , "mainMenu" )
assertNoError ( err , t , "EvalVariable()" )
if mmvar . Kind != reflect . Slice {
t . Fatalf ( "Wrong kind for mainMenu: %v\n" , mmvar . Kind )
}
} )
}
2016-01-24 05:25:46 +00:00
func TestStepIntoFunction ( t * testing . T ) {
withTestProcess ( "teststep" , t , func ( p * Process , fixture protest . Fixture ) {
// Continue until breakpoint
assertNoError ( p . Continue ( ) , t , "Continue() returned an error" )
// Step into function
assertNoError ( p . Step ( ) , t , "Step() returned an error" )
// We should now be inside the function.
loc , err := p . CurrentLocation ( )
if err != nil {
t . Fatal ( err )
}
if loc . Fn . Name != "main.callme" {
t . Fatalf ( "expected to be within the 'callme' function, was in %s instead" , loc . Fn . Name )
}
if ! strings . Contains ( loc . File , "teststep" ) {
t . Fatalf ( "debugger stopped at incorrect location: %s:%d" , loc . File , loc . Line )
}
if loc . Line != 8 {
t . Fatalf ( "debugger stopped at incorrect line: %d" , loc . Line )
}
} )
}
2016-01-30 21:21:07 +00:00
func TestIssue384 ( t * testing . T ) {
// Crash related to reading uninitialized memory, introduced by the memory prefetching optimization
withTestProcess ( "issue384" , t , func ( p * Process , fixture protest . Fixture ) {
start , _ , err := p . goSymTable . LineToPC ( fixture . Source , 13 )
assertNoError ( err , t , "LineToPC()" )
_ , err = p . SetBreakpoint ( start )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
_ , err = evalVariable ( p , "st" )
assertNoError ( err , t , "EvalVariable()" )
} )
}
2016-01-31 12:03:53 +00:00
func TestIssue332_Part1 ( t * testing . T ) {
// Next shouldn't step inside a function call
withTestProcess ( "issue332" , t , func ( p * Process , fixture protest . Fixture ) {
start , _ , err := p . goSymTable . LineToPC ( fixture . Source , 8 )
assertNoError ( err , t , "LineToPC()" )
_ , err = p . SetBreakpoint ( start )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
assertNoError ( p . Next ( ) , t , "first Next()" )
locations , err := p . CurrentThread . Stacktrace ( 2 )
assertNoError ( err , t , "Stacktrace()" )
if locations [ 0 ] . Call . Fn == nil {
t . Fatalf ( "Not on a function" )
}
if locations [ 0 ] . Call . Fn . Name != "main.main" {
t . Fatalf ( "Not on main.main after Next: %s (%s:%d)" , locations [ 0 ] . Call . Fn . Name , locations [ 0 ] . Call . File , locations [ 0 ] . Call . Line )
}
if locations [ 0 ] . Call . Line != 9 {
t . Fatalf ( "Not on line 9 after Next: %s (%s:%d)" , locations [ 0 ] . Call . Fn . Name , locations [ 0 ] . Call . File , locations [ 0 ] . Call . Line )
}
} )
}
func TestIssue332_Part2 ( t * testing . T ) {
// Step should skip a function's prologue
// In some parts of the prologue, for some functions, the FDE data is incorrect
// which leads to 'next' and 'stack' failing with error "could not find FDE for PC: <garbage>"
// because the incorrect FDE data leads to reading the wrong stack address as the return address
withTestProcess ( "issue332" , t , func ( p * Process , fixture protest . Fixture ) {
start , _ , err := p . goSymTable . LineToPC ( fixture . Source , 8 )
assertNoError ( err , t , "LineToPC()" )
_ , err = p . SetBreakpoint ( start )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
// step until we enter changeMe
for {
assertNoError ( p . Step ( ) , t , "Step()" )
locations , err := p . CurrentThread . Stacktrace ( 2 )
assertNoError ( err , t , "Stacktrace()" )
if locations [ 0 ] . Call . Fn == nil {
t . Fatalf ( "Not on a function" )
}
if locations [ 0 ] . Call . Fn . Name == "main.changeMe" {
break
}
}
2016-02-12 07:43:22 +00:00
pc , err := p . CurrentThread . PC ( )
assertNoError ( err , t , "PC()" )
pcAfterPrologue , err := p . FindFunctionLocation ( "main.changeMe" , true , - 1 )
assertNoError ( err , t , "FindFunctionLocation()" )
pcEntry , err := p . FindFunctionLocation ( "main.changeMe" , false , 0 )
if pcAfterPrologue == pcEntry {
t . Fatalf ( "main.changeMe and main.changeMe:0 are the same (%x)" , pcAfterPrologue )
}
if pc != pcAfterPrologue {
t . Fatalf ( "Step did not skip the prologue: current pc: %x, first instruction after prologue: %x" , pc , pcAfterPrologue )
}
2016-01-31 12:03:53 +00:00
assertNoError ( p . Next ( ) , t , "first Next()" )
assertNoError ( p . Next ( ) , t , "second Next()" )
assertNoError ( p . Next ( ) , t , "third Next()" )
err = p . Continue ( )
if _ , exited := err . ( ProcessExitedError ) ; ! exited {
assertNoError ( err , t , "final Continue()" )
}
} )
}
2016-02-11 07:18:39 +00:00
func TestIssue396 ( t * testing . T ) {
withTestProcess ( "callme" , t , func ( p * Process , fixture protest . Fixture ) {
_ , err := p . FindFunctionLocation ( "main.init" , true , - 1 )
assertNoError ( err , t , "FindFunctionLocation()" )
} )
}
2016-02-14 21:26:06 +00:00
func TestIssue414 ( t * testing . T ) {
// Stepping until the program exits
withTestProcess ( "math" , t , func ( p * Process , fixture protest . Fixture ) {
start , _ , err := p . goSymTable . LineToPC ( fixture . Source , 9 )
assertNoError ( err , t , "LineToPC()" )
_ , err = p . SetBreakpoint ( start )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
for {
err := p . Step ( )
if err != nil {
if _ , exited := err . ( ProcessExitedError ) ; exited {
break
}
}
assertNoError ( err , t , "Step()" )
}
} )
}
2016-02-21 16:26:13 +00:00
func TestPackageVariables ( t * testing . T ) {
withTestProcess ( "testvariables" , t , func ( p * Process , fixture protest . Fixture ) {
err := p . Continue ( )
assertNoError ( err , t , "Continue()" )
scope , err := p . CurrentThread . Scope ( )
assertNoError ( err , t , "Scope()" )
2016-04-24 17:15:39 +00:00
vars , err := scope . PackageVariables ( normalLoadConfig )
2016-02-21 16:26:13 +00:00
assertNoError ( err , t , "PackageVariables()" )
failed := false
for _ , v := range vars {
if v . Unreadable != nil {
failed = true
t . Logf ( "Unreadable variable %s: %v" , v . Name , v . Unreadable )
}
}
if failed {
t . Fatalf ( "previous errors" )
}
} )
}
2016-02-25 09:48:42 +00:00
func TestIssue149 ( t * testing . T ) {
ver , _ := ParseVersionString ( runtime . Version ( ) )
2016-06-22 10:38:23 +00:00
if ver . Major > 0 && ! ver . AfterOrEqual ( GoVersion { 1 , 7 , - 1 , 0 , 0 } ) {
2016-02-25 09:48:42 +00:00
return
}
// setting breakpoint on break statement
withTestProcess ( "break" , t , func ( p * Process , fixture protest . Fixture ) {
_ , err := p . FindFileLocation ( fixture . Source , 8 )
assertNoError ( err , t , "FindFileLocation()" )
} )
}
2016-03-06 17:54:43 +00:00
func TestPanicBreakpoint ( t * testing . T ) {
withTestProcess ( "panic" , t , func ( p * Process , fixture protest . Fixture ) {
assertNoError ( p . Continue ( ) , t , "Continue()" )
bp := p . CurrentBreakpoint ( )
if bp == nil || bp . Name != "unrecovered-panic" {
t . Fatalf ( "not on unrecovered-panic breakpoint: %v" , p . CurrentBreakpoint )
}
} )
}
2016-03-18 08:32:17 +00:00
2016-04-21 21:20:38 +00:00
func TestCmdLineArgs ( t * testing . T ) {
expectSuccess := func ( p * Process , fixture protest . Fixture ) {
err := p . Continue ( )
bp := p . CurrentBreakpoint ( )
if bp != nil && bp . Name == "unrecovered-panic" {
t . Fatalf ( "testing args failed on unrecovered-panic breakpoint: %v" , p . CurrentBreakpoint )
}
exit , exited := err . ( ProcessExitedError )
if ! exited {
2016-07-20 22:36:31 +00:00
t . Fatalf ( "Process did not exit: %v" , err )
2016-04-21 21:20:38 +00:00
} else {
if exit . Status != 0 {
t . Fatalf ( "process exited with invalid status" , exit . Status )
}
}
}
expectPanic := func ( p * Process , fixture protest . Fixture ) {
p . Continue ( )
bp := p . CurrentBreakpoint ( )
if bp == nil || bp . Name != "unrecovered-panic" {
t . Fatalf ( "not on unrecovered-panic breakpoint: %v" , p . CurrentBreakpoint )
}
}
// make sure multiple arguments (including one with spaces) are passed to the binary correctly
withTestProcessArgs ( "testargs" , t , expectSuccess , [ ] string { "test" } )
withTestProcessArgs ( "testargs" , t , expectSuccess , [ ] string { "test" , "pass flag" } )
// check that arguments with spaces are *only* passed correctly when correctly called
withTestProcessArgs ( "testargs" , t , expectPanic , [ ] string { "test pass" , "flag" } )
withTestProcessArgs ( "testargs" , t , expectPanic , [ ] string { "test" , "pass" , "flag" } )
withTestProcessArgs ( "testargs" , t , expectPanic , [ ] string { "test pass flag" } )
// and that invalid cases (wrong arguments or no arguments) panic
withTestProcess ( "testargs" , t , expectPanic )
withTestProcessArgs ( "testargs" , t , expectPanic , [ ] string { "invalid" } )
withTestProcessArgs ( "testargs" , t , expectPanic , [ ] string { "test" , "invalid" } )
withTestProcessArgs ( "testargs" , t , expectPanic , [ ] string { "invalid" , "pass flag" } )
}
2016-03-18 08:32:17 +00:00
func TestIssue462 ( t * testing . T ) {
// Stacktrace of Goroutine 0 fails with an error
if runtime . GOOS == "windows" {
return
}
withTestProcess ( "testnextnethttp" , t , func ( p * Process , fixture protest . Fixture ) {
go func ( ) {
for ! p . Running ( ) {
time . Sleep ( 50 * time . Millisecond )
}
// Wait for program to start listening.
for {
conn , err := net . Dial ( "tcp" , "localhost:9191" )
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
}
p . RequestManualStop ( )
} ( )
assertNoError ( p . Continue ( ) , t , "Continue()" )
_ , err := p . CurrentThread . Stacktrace ( 40 )
assertNoError ( err , t , "Stacktrace()" )
} )
}
2016-05-29 19:11:00 +00:00
func TestIssue554 ( t * testing . T ) {
// unsigned integer overflow in proc.(*memCache).contains was
// causing it to always return true for address 0xffffffffffffffff
mem := memCache { 0x20 , make ( [ ] byte , 100 ) , nil }
if mem . contains ( 0xffffffffffffffff , 40 ) {
t . Fatalf ( "should be false" )
}
}
2016-04-11 11:50:01 +00:00
func TestNextParked ( t * testing . T ) {
withTestProcess ( "parallel_next" , t , func ( p * Process , fixture protest . Fixture ) {
bp , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "SetBreakpoint()" )
// continue until a parked goroutine exists
var parkedg * G
LookForParkedG :
for {
err := p . Continue ( )
if _ , exited := err . ( ProcessExitedError ) ; exited {
t . Log ( "could not find parked goroutine" )
return
}
assertNoError ( err , t , "Continue()" )
gs , err := p . GoroutinesInfo ( )
assertNoError ( err , t , "GoroutinesInfo()" )
for _ , g := range gs {
if g . thread == nil {
parkedg = g
break LookForParkedG
}
}
}
assertNoError ( p . SwitchGoroutine ( parkedg . ID ) , t , "SwitchGoroutine()" )
p . ClearBreakpoint ( bp . Addr )
assertNoError ( p . Next ( ) , t , "Next()" )
if p . SelectedGoroutine . ID != parkedg . ID {
t . Fatalf ( "Next did not continue on the selected goroutine, expected %d got %d" , parkedg . ID , p . SelectedGoroutine . ID )
}
} )
}
2016-04-13 13:25:23 +00:00
func TestStepParked ( t * testing . T ) {
withTestProcess ( "parallel_next" , t , func ( p * Process , fixture protest . Fixture ) {
bp , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "SetBreakpoint()" )
// continue until a parked goroutine exists
var parkedg * G
LookForParkedG :
for {
err := p . Continue ( )
if _ , exited := err . ( ProcessExitedError ) ; exited {
t . Log ( "could not find parked goroutine" )
return
}
assertNoError ( err , t , "Continue()" )
gs , err := p . GoroutinesInfo ( )
assertNoError ( err , t , "GoroutinesInfo()" )
for _ , g := range gs {
if g . thread == nil {
parkedg = g
break LookForParkedG
}
}
}
assertNoError ( p . SwitchGoroutine ( parkedg . ID ) , t , "SwitchGoroutine()" )
p . ClearBreakpoint ( bp . Addr )
assertNoError ( p . Step ( ) , t , "Step()" )
if p . SelectedGoroutine . ID != parkedg . ID {
t . Fatalf ( "Step did not continue on the selected goroutine, expected %d got %d" , parkedg . ID , p . SelectedGoroutine . ID )
}
} )
}
2016-04-21 10:19:21 +00:00
func TestIssue509 ( t * testing . T ) {
fixturesDir := protest . FindFixturesDir ( )
nomaindir := filepath . Join ( fixturesDir , "nomaindir" )
cmd := exec . Command ( "go" , "build" , "-gcflags=-N -l" , "-o" , "debug" )
cmd . Dir = nomaindir
assertNoError ( cmd . Run ( ) , t , "go build" )
exepath := filepath . Join ( nomaindir , "debug" )
_ , err := Launch ( [ ] string { exepath } )
if err == nil {
t . Fatalf ( "expected error but none was generated" )
}
if err != NotExecutableErr {
t . Fatalf ( "expected error \"%v\" got \"%v\"" , NotExecutableErr , err )
}
os . Remove ( exepath )
}
func TestUnsupportedArch ( t * testing . T ) {
ver , _ := ParseVersionString ( runtime . Version ( ) )
2016-07-03 07:02:21 +00:00
if ver . Major < 0 || ! ver . AfterOrEqual ( GoVersion { 1 , 6 , - 1 , 0 , 0 } ) || ver . AfterOrEqual ( GoVersion { 1 , 7 , - 1 , 0 , 0 } ) {
2016-04-21 10:19:21 +00:00
// cross compile (with -N?) works only on select versions of go
return
}
2016-07-03 07:02:21 +00:00
2016-04-21 10:19:21 +00:00
fixturesDir := protest . FindFixturesDir ( )
infile := filepath . Join ( fixturesDir , "math.go" )
outfile := filepath . Join ( fixturesDir , "_math_debug_386" )
2016-07-03 07:02:21 +00:00
2016-04-21 10:19:21 +00:00
cmd := exec . Command ( "go" , "build" , "-gcflags=-N -l" , "-o" , outfile , infile )
for _ , v := range os . Environ ( ) {
if ! strings . HasPrefix ( v , "GOARCH=" ) {
cmd . Env = append ( cmd . Env , v )
}
}
cmd . Env = append ( cmd . Env , "GOARCH=386" )
out , err := cmd . CombinedOutput ( )
if err != nil {
t . Fatalf ( "go build failed: %v: %v" , err , string ( out ) )
}
defer os . Remove ( outfile )
2016-07-03 07:02:21 +00:00
2016-04-21 10:19:21 +00:00
p , err := Launch ( [ ] string { outfile } )
switch err {
case UnsupportedArchErr :
// all good
case nil :
p . Halt ( )
p . Kill ( )
t . Fatal ( "Launch is expected to fail, but succeeded" )
default :
t . Fatal ( err )
}
}
2016-07-21 20:10:35 +00:00
2016-09-06 17:27:07 +00:00
func TestIssue573 ( t * testing . T ) {
2016-07-21 20:10:35 +00:00
// calls to runtime.duffzero and runtime.duffcopy jump directly into the middle
// of the function and the temp breakpoint set by StepInto may be missed.
withTestProcess ( "issue573" , t , func ( p * Process , fixture protest . Fixture ) {
f := p . goSymTable . LookupFunc ( "main.foo" )
_ , err := p . SetBreakpoint ( f . Entry )
assertNoError ( err , t , "SetBreakpoint()" )
assertNoError ( p . Continue ( ) , t , "Continue()" )
assertNoError ( p . Step ( ) , t , "Step() #1" )
assertNoError ( p . Step ( ) , t , "Step() #2" ) // Bug exits here.
assertNoError ( p . Step ( ) , t , "Step() #3" ) // Third step ought to be possible; program ought not have exited.
} )
}
2016-09-06 17:27:07 +00:00
func TestTestvariables2Prologue ( t * testing . T ) {
withTestProcess ( "testvariables2" , t , func ( p * Process , fixture protest . Fixture ) {
addrEntry , err := p . FindFunctionLocation ( "main.main" , false , 0 )
assertNoError ( err , t , "FindFunctionLocation - entrypoint" )
addrPrologue , err := p . FindFunctionLocation ( "main.main" , true , 0 )
assertNoError ( err , t , "FindFunctionLocation - postprologue" )
if addrEntry == addrPrologue {
t . Fatalf ( "Prologue detection failed on testvariables2.go/main.main" )
}
} )
}
2016-07-14 07:43:39 +00:00
func TestNextDeferReturnAndDirectCall ( t * testing . T ) {
// Next should not step into a deferred function if it is called
// directly, only if it is called through a panic or a deferreturn.
// Here we test the case where the function is called by a deferreturn
testnext ( "defercall" , [ ] nextTest {
{ 9 , 10 } ,
{ 10 , 11 } ,
{ 11 , 12 } ,
{ 12 , 13 } ,
{ 13 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 13 } ,
{ 13 , 28 } } , "main.callAndDeferReturn" , t )
}
func TestNextPanicAndDirectCall ( t * testing . T ) {
// Next should not step into a deferred function if it is called
// directly, only if it is called through a panic or a deferreturn.
// Here we test the case where the function is called by a panic
testnext ( "defercall" , [ ] nextTest {
{ 15 , 16 } ,
{ 16 , 17 } ,
{ 17 , 18 } ,
{ 18 , 5 } } , "main.callAndPanic2" , t )
}