2017-04-21 06:55:53 +00:00
package proc_test
2014-05-20 18:23:35 +00:00
import (
2014-05-27 18:33:49 +00:00
"bytes"
2017-02-10 14:11:40 +00:00
"flag"
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"
2017-03-28 16:30:27 +00:00
"io/ioutil"
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
2017-06-23 12:22:26 +00:00
"github.com/derekparker/delve/pkg/dwarf/frame"
2017-05-04 10:22:08 +00:00
"github.com/derekparker/delve/pkg/goversion"
2017-04-21 06:55:53 +00:00
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/gdbserial"
"github.com/derekparker/delve/pkg/proc/native"
2017-02-08 16:00:44 +00:00
protest "github.com/derekparker/delve/pkg/proc/test"
2014-06-25 19:06:04 +00:00
)
2014-05-30 15:12:18 +00:00
2017-04-21 06:55:53 +00:00
var normalLoadConfig = proc . 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
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 ) {
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"
}
}
2015-06-21 03:47:44 +00:00
os . Exit ( protest . RunTestsWithFixtures ( m ) )
2015-05-04 13:31:50 +00:00
}
2017-04-21 07:50:38 +00:00
func withTestProcess ( name string , t testing . TB , fn func ( p proc . Process , fixture protest . Fixture ) ) {
2018-01-20 15:18:39 +00:00
withTestProcessArgs ( name , t , "." , [ ] string { } , 0 , fn )
2014-12-09 16:51:17 +00:00
}
2018-01-20 15:18:39 +00:00
func withTestProcessArgs ( name string , t testing . TB , wd string , args [ ] string , buildFlags protest . BuildFlags , fn func ( p proc . Process , fixture protest . Fixture ) ) {
fixture := protest . BuildFixture ( name , buildFlags )
2017-04-21 07:50:38 +00:00
var p proc . Process
2017-02-10 14:11:40 +00:00
var err error
2017-05-05 22:17:52 +00:00
var tracedir string
2017-02-10 14:11:40 +00:00
switch testBackend {
case "native" :
2017-04-21 06:55:53 +00:00
p , err = native . Launch ( append ( [ ] string { fixture . Path } , args ... ) , wd )
2017-02-10 14:11:40 +00:00
case "lldb" :
2017-04-21 06:55:53 +00:00
p , err = gdbserial . LLDBLaunch ( append ( [ ] string { fixture . Path } , args ... ) , wd )
2017-05-05 22:17:52 +00:00
case "rr" :
protest . MustHaveRecordingAllowed ( t )
t . Log ( "recording" )
2017-07-31 12:51:35 +00:00
p , tracedir , err = gdbserial . RecordAndReplay ( append ( [ ] string { fixture . Path } , args ... ) , wd , true )
2017-05-05 22:17:52 +00:00
t . Logf ( "replaying %q" , tracedir )
2017-02-10 14:11:40 +00:00
default :
t . Fatal ( "unknown backend" )
}
2016-04-21 21:20:38 +00:00
if err != nil {
t . Fatal ( "Launch():" , err )
}
defer func ( ) {
2017-02-10 14:11:40 +00:00
p . Detach ( true )
2017-05-05 22:17:52 +00:00
if tracedir != "" {
protest . SafeRemoveAll ( tracedir )
}
2016-04-21 21:20:38 +00:00
} ( )
fn ( p , fixture )
}
2017-04-21 07:50:38 +00:00
func getRegisters ( p proc . Process , t * testing . T ) proc . Registers {
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
2014-12-09 16:35:55 +00:00
if err != nil {
t . Fatal ( "Registers():" , err )
}
return regs
}
2017-04-21 06:55:53 +00:00
func dataAtAddr ( thread proc . MemoryReadWriter , addr uint64 ) ( [ ] byte , error ) {
2017-04-18 14:24:45 +00:00
data := make ( [ ] byte , 1 )
_ , err := thread . ReadMemory ( data , uintptr ( addr ) )
return data , err
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
}
}
2017-04-21 07:50:38 +00:00
func currentPC ( p proc . Process , t * testing . T ) uint64 {
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
2014-06-29 16:52:21 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-10 14:11:40 +00:00
return regs . PC ( )
2014-06-29 16:52:21 +00:00
}
2017-04-21 07:50:38 +00:00
func currentLineNumber ( p proc . Process , t * testing . T ) ( string , int ) {
2014-06-29 16:52:21 +00:00
pc := currentPC ( p , t )
2017-04-21 06:55:53 +00:00
f , l , _ := p . BinInfo ( ) . PCToLine ( pc )
2014-10-07 17:25:33 +00:00
return f , l
2014-06-29 16:52:21 +00:00
}
2018-01-20 15:18:39 +00:00
func assertLineNumber ( p proc . Process , t * testing . T , lineno int , descr string ) ( string , int ) {
f , l := currentLineNumber ( p , t )
if l != lineno {
_ , callerFile , callerLine , _ := runtime . Caller ( 1 )
t . Fatalf ( "%s expected line :%d got %s:%d\n\tat %s:%d" , descr , lineno , f , l , callerFile , callerLine )
}
return f , l
}
2015-03-05 22:59:51 +00:00
func TestExit ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "continuetestprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
pe , ok := err . ( proc . ProcessExitedError )
2015-03-05 22:59:51 +00:00
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 )
}
2017-02-10 14:11:40 +00:00
if pe . Pid != p . Pid ( ) {
2015-03-05 22:59:51 +00:00
t . Errorf ( "Unexpected process id: %d" , pe . Pid )
}
} )
}
2015-10-02 16:17:07 +00:00
func TestExitAfterContinue ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "continuetestprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-10-02 16:17:07 +00:00
_ , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "First Continue()" )
err = proc . Continue ( p )
pe , ok := err . ( proc . ProcessExitedError )
2015-10-02 16:17:07 +00:00
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 )
}
2017-02-10 14:11:40 +00:00
if pe . Pid != p . Pid ( ) {
2015-10-02 16:17:07 +00:00
t . Errorf ( "Unexpected process id: %d" , pe . Pid )
}
} )
}
2017-04-21 07:50:38 +00:00
func setFunctionBreakpoint ( p proc . Process , fname string ) ( * proc . Breakpoint , error ) {
2017-04-28 17:15:39 +00:00
addr , err := proc . FindFunctionLocation ( p , fname , true , 0 )
2015-08-07 16:50:14 +00:00
if err != nil {
return nil , err
}
2017-04-21 06:55:53 +00:00
return p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2015-08-07 16:50:14 +00:00
}
2017-04-21 07:50:38 +00:00
func setFileBreakpoint ( p proc . Process , t * testing . T , fixture protest . Fixture , lineno int ) * proc . Breakpoint {
2017-04-28 17:15:39 +00:00
addr , err := proc . FindFileLocation ( p , fixture . Source , lineno )
2016-04-14 09:58:59 +00:00
if err != nil {
t . Fatalf ( "FindFileLocation: %v" , err )
}
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2016-04-14 09:58:59 +00:00
if err != nil {
t . Fatalf ( "SetBreakpoint: %v" , err )
}
return bp
}
2015-04-13 22:17:06 +00:00
func TestHalt ( t * testing . T ) {
2017-06-29 18:15:59 +00:00
stopChan := make ( chan interface { } , 1 )
2017-04-21 07:50:38 +00:00
withTestProcess ( "loopprog" , t , func ( p proc . 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" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
if p , ok := p . ( * native . Process ) ; ok {
for _ , th := range p . ThreadList ( ) {
2017-02-10 14:11:40 +00:00
_ , err := th . Registers ( false )
assertNoError ( err , t , "Registers" )
2015-06-26 12:46:46 +00:00
}
}
2017-06-29 18:15:59 +00:00
resumeChan := make ( chan struct { } , 1 )
2015-04-13 22:17:06 +00:00
go func ( ) {
2017-06-06 15:26:16 +00:00
<- resumeChan
time . Sleep ( 100 * time . Millisecond )
2017-06-29 18:15:59 +00:00
stopChan <- p . RequestManualStop ( )
2015-04-13 22:17:06 +00:00
} ( )
2017-06-06 15:26:16 +00:00
p . ResumeNotify ( resumeChan )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2017-06-29 18:15:59 +00:00
retVal := <- stopChan
if err , ok := retVal . ( error ) ; ok && err != nil {
t . Fatal ( )
}
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.
2017-04-21 06:55:53 +00:00
if p , ok := p . ( * native . Process ) ; ok {
for _ , th := range p . ThreadList ( ) {
if th , ok := th . ( * native . Thread ) ; ok {
if ! th . Stopped ( ) {
t . Fatal ( "expected thread to be stopped, but was not" )
}
2017-02-10 14:11:40 +00:00
}
_ , err := th . Registers ( false )
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 ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
helloworldaddr , err := proc . FindFunctionLocation ( p , "main.helloworld" , false , 0 )
2017-02-10 14:11:40 +00:00
assertNoError ( err , t , "FindFunctionLocation" )
2014-11-27 02:35:53 +00:00
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( helloworldaddr , proc . UserBreakpoint , nil )
2015-06-20 23:01:06 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2014-11-27 02:35:53 +00:00
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
2017-02-10 14:11:40 +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 ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
helloworldaddr , err := proc . FindFunctionLocation ( p , "main.helloworld" , false , 0 )
2017-02-10 14:11:40 +00:00
assertNoError ( err , t , "FindFunctionLocation" )
2014-05-30 15:12:18 +00:00
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( helloworldaddr , proc . UserBreakpoint , nil )
2015-06-20 23:01:06 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2014-05-30 15:12:18 +00:00
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
assertNoError ( err , t , "Registers" )
pc := regs . PC ( )
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 {
2017-04-21 06:55:53 +00:00
f , l , _ := p . BinInfo ( ) . 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
2018-03-20 10:05:35 +00:00
func TestBreakpointInSeparateGoRoutine ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testthreads" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
fnentry , err := proc . FindFunctionLocation ( p , "main.anotherthread" , false , 0 )
2017-02-10 14:11:40 +00:00
assertNoError ( err , t , "FindFunctionLocation" )
2014-10-18 01:34:58 +00:00
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( fnentry , proc . UserBreakpoint , nil )
2017-02-10 14:11:40 +00:00
assertNoError ( err , t , "SetBreakpoint" )
2014-10-18 01:34:58 +00:00
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2014-10-18 01:34:58 +00:00
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
assertNoError ( err , t , "Registers" )
pc := regs . PC ( )
2014-10-18 01:34:58 +00:00
2017-04-21 06:55:53 +00:00
f , l , _ := p . BinInfo ( ) . 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 ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
_ , err := p . SetBreakpoint ( 0 , proc . UserBreakpoint , nil )
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 ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
fnentry , err := proc . FindFunctionLocation ( p , "main.sleepytime" , false , 0 )
2017-02-10 14:11:40 +00:00
assertNoError ( err , t , "FindFunctionLocation" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( fnentry , proc . UserBreakpoint , nil )
2015-06-20 23:01:06 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2014-05-30 15:12:18 +00:00
2017-02-10 14:11:40 +00:00
bp , err = p . ClearBreakpoint ( fnentry )
2015-06-20 23:01:06 +00:00
assertNoError ( err , t , "ClearBreakpoint()" )
2014-05-30 15:12:18 +00:00
2017-02-10 14:11:40 +00:00
data , err := dataAtAddr ( p . CurrentThread ( ) , bp . Addr )
assertNoError ( err , t , "dataAtAddr" )
2014-05-30 15:12:18 +00:00
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
2017-04-21 07:50:38 +00:00
func countBreakpoints ( p proc . Process ) int {
2016-03-06 17:54:43 +00:00
bpcount := 0
2017-09-24 13:00:55 +00:00
for _ , bp := range p . Breakpoints ( ) . M {
2016-03-06 17:54:43 +00:00
if bp . ID >= 0 {
bpcount ++
}
}
return bpcount
}
2016-07-26 13:34:31 +00:00
type contFunc int
const (
2018-01-20 15:18:39 +00:00
contContinue contFunc = iota
contNext
2016-07-26 13:34:31 +00:00
contStep
2018-01-20 15:18:39 +00:00
contStepout
2016-07-26 13:34:31 +00:00
)
2018-01-20 15:18:39 +00:00
type seqTest struct {
cf contFunc
pos int
}
2016-07-26 13:34:31 +00:00
func testseq ( program string , contFunc contFunc , testcases [ ] nextTest , initialLocation string , t * testing . T ) {
2018-01-20 15:18:39 +00:00
seqTestcases := make ( [ ] seqTest , len ( testcases ) + 1 )
seqTestcases [ 0 ] = seqTest { contContinue , testcases [ 0 ] . begin }
for i := range testcases {
if i > 0 {
if testcases [ i - 1 ] . end != testcases [ i ] . begin {
panic ( fmt . Errorf ( "begin/end mismatch at index %d" , i ) )
}
}
seqTestcases [ i + 1 ] = seqTest { contFunc , testcases [ i ] . end }
}
testseq2 ( t , program , initialLocation , seqTestcases )
}
const traceTestseq2 = false
func testseq2 ( t * testing . T , program string , initialLocation string , testcases [ ] seqTest ) {
testseq2Args ( "." , [ ] string { } , 0 , t , program , initialLocation , testcases )
}
func testseq2Args ( wd string , args [ ] string , buildFlags protest . BuildFlags , t * testing . T , program string , initialLocation string , testcases [ ] seqTest ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2018-01-20 15:18:39 +00:00
withTestProcessArgs ( program , t , wd , args , buildFlags , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
var bp * proc . Breakpoint
2016-07-26 13:34:31 +00:00
var err error
if initialLocation != "" {
bp , err = setFunctionBreakpoint ( p , initialLocation )
2018-01-20 15:18:39 +00:00
} else if testcases [ 0 ] . cf == contContinue {
2016-07-26 13:34:31 +00:00
var pc uint64
2018-01-20 15:18:39 +00:00
pc , err = proc . FindFileLocation ( p , fixture . Source , testcases [ 0 ] . pos )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "FindFileLocation()" )
2017-04-21 06:55:53 +00:00
bp , err = p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2018-01-20 15:18:39 +00:00
} else {
panic ( "testseq2 can not set initial breakpoint" )
}
if traceTestseq2 {
t . Logf ( "initial breakpoint %v" , bp )
2016-07-26 13:34:31 +00:00
}
2015-06-20 23:01:06 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
assertNoError ( err , t , "Registers" )
2014-06-29 16:52:21 +00:00
2014-12-29 02:48:58 +00:00
f , ln := currentLineNumber ( p , t )
2018-01-20 15:18:39 +00:00
for i , tc := range testcases {
switch tc . cf {
2016-07-26 13:34:31 +00:00
case contNext :
2018-01-20 15:18:39 +00:00
if traceTestseq2 {
t . Log ( "next" )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "Next() returned an error" )
2016-07-26 13:34:31 +00:00
case contStep :
2018-01-20 15:18:39 +00:00
if traceTestseq2 {
t . Log ( "step" )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step() returned an error" )
2018-01-20 15:18:39 +00:00
case contStepout :
if traceTestseq2 {
t . Log ( "stepout" )
}
assertNoError ( proc . StepOut ( p ) , t , "StepOut() returned an error" )
case contContinue :
if traceTestseq2 {
t . Log ( "continue" )
}
assertNoError ( proc . Continue ( p ) , t , "Continue() returned an error" )
if i == 0 {
if traceTestseq2 {
t . Log ( "clearing initial breakpoint" )
}
_ , err := p . ClearBreakpoint ( bp . Addr )
assertNoError ( err , t , "ClearBreakpoint() returned an error" )
}
2016-07-26 13:34:31 +00:00
}
2014-06-29 16:52:21 +00:00
2014-10-07 17:25:33 +00:00
f , ln = currentLineNumber ( p , t )
2017-02-10 14:11:40 +00:00
regs , _ = p . CurrentThread ( ) . Registers ( false )
2018-01-20 15:18:39 +00:00
pc := regs . PC ( )
if traceTestseq2 {
t . Logf ( "at %#x %s:%d" , pc , f , ln )
}
if ln != tc . pos {
t . Fatalf ( "Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)" , tc . pos , filepath . Base ( f ) , ln , pc , i )
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 {
2017-09-24 13:00:55 +00:00
t . Fatal ( "Not all breakpoints were cleaned up" , len ( p . Breakpoints ( ) . M ) )
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
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-04-19 22:11:33 +00:00
}
2016-02-25 09:48:42 +00:00
2016-07-26 13:34:31 +00:00
testseq ( "testnextprog" , contNext , 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 } ,
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "parallel_next" , t , func ( p proc . 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" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2015-08-20 14:28:11 +00:00
f , ln := currentLineNumber ( p , t )
2018-01-20 15:18:39 +00:00
initV := evalVariable ( p , t , "n" )
2015-10-21 07:06:36 +00:00
initVval , _ := constant . Int64Val ( initV . Value )
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 {
2017-04-21 06:55:53 +00:00
g , err := proc . GetG ( p . CurrentThread ( ) )
2015-08-28 20:06:29 +00:00
assertNoError ( err , t , "GetG()" )
2017-02-10 14:11:40 +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 )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "Next() returned an error" )
2018-01-20 15:18:39 +00:00
f , ln = assertLineNumber ( p , t , tc . end , "Program did not continue to the expected location" )
v := evalVariable ( p , t , "n" )
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 } ,
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "parallel_next" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-11-18 09:07:08 +00:00
_ , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "SetBreakpoint" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2015-11-18 09:07:08 +00:00
f , ln := currentLineNumber ( p , t )
2018-01-20 15:18:39 +00:00
initV := evalVariable ( p , t , "n" )
2015-11-18 09:07:08 +00:00
initVval , _ := constant . Int64Val ( initV . Value )
for _ , tc := range testcases {
2017-05-16 18:23:33 +00:00
t . Logf ( "test case %v" , tc )
2017-04-21 06:55:53 +00:00
g , err := proc . GetG ( p . CurrentThread ( ) )
2015-11-18 09:07:08 +00:00
assertNoError ( err , t , "GetG()" )
2017-02-10 14:11:40 +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 )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "Next() returned an error" )
2015-11-18 09:07:08 +00:00
var vval int64
for {
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , "n" )
2017-05-09 07:02:07 +00:00
for _ , thread := range p . ThreadList ( ) {
proc . GetG ( thread )
}
2015-11-18 09:07:08 +00:00
vval , _ = constant . Int64Val ( v . Value )
2017-09-25 06:29:13 +00:00
if bpstate := p . CurrentThread ( ) . Breakpoint ( ) ; bpstate . Breakpoint == nil {
2015-11-18 09:07:08 +00:00
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" )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue 2" )
2015-11-18 09:07:08 +00:00
}
}
2018-01-20 15:18:39 +00:00
f , ln = assertLineNumber ( p , t , tc . end , "Program did not continue to the expected location" )
2015-11-18 09:07:08 +00:00
}
} )
}
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
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-07-26 13:34:31 +00:00
testseq ( "testnextprog" , contNext , testcases , "main.helloworld" , t )
2015-05-09 17:44:38 +00:00
}
func TestNextFunctionReturnDefer ( t * testing . T ) {
2017-05-03 07:10:12 +00:00
var testcases [ ] nextTest
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
2017-05-03 07:10:12 +00:00
2017-05-04 10:22:08 +00:00
if ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) {
2017-05-03 07:10:12 +00:00
testcases = [ ] nextTest {
{ 5 , 6 } ,
{ 6 , 9 } ,
{ 9 , 10 } ,
}
} else {
testcases = [ ] nextTest {
{ 5 , 8 } ,
{ 8 , 9 } ,
{ 9 , 10 } ,
}
2015-05-09 17:44:38 +00:00
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2016-07-26 13:34:31 +00:00
testseq ( "testnextdefer" , contNext , 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 } ,
}
2017-04-21 07:50:38 +00:00
withTestProcess ( "testnextnethttp" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-07-16 00:57:54 +00:00
go func ( ) {
// 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
} ( )
2017-04-21 06:55:53 +00:00
if err := proc . Continue ( p ) ; err != nil {
2015-07-16 00:57:54 +00:00
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 )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "Next() returned an error" )
2015-07-16 00:57:54 +00:00
2018-01-20 15:18:39 +00:00
f , ln = assertLineNumber ( p , t , tc . end , "Program did not continue to correct next location" )
2015-07-16 00:57:54 +00:00
}
} )
}
2015-04-25 19:13:35 +00:00
func TestRuntimeBreakpoint ( t * testing . T ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testruntimebreakpoint" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2015-04-25 19:13:35 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
assertNoError ( err , t , "Registers" )
pc := regs . PC ( )
2017-05-05 22:17:52 +00:00
f , l , _ := p . BinInfo ( ) . PCToLine ( pc )
2015-04-25 19:13:35 +00:00
if l != 10 {
2017-05-05 22:17:52 +00:00
t . Fatalf ( "did not respect breakpoint %s:%d" , f , l )
2015-04-25 19:13:35 +00:00
}
} )
}
2017-04-21 07:50:38 +00:00
func returnAddress ( thread proc . Thread ) ( uint64 , error ) {
2017-04-21 06:55:53 +00:00
locations , err := proc . ThreadStacktrace ( thread , 2 )
2017-02-15 13:41:03 +00:00
if err != nil {
return 0 , err
}
if len ( locations ) < 2 {
2018-01-20 15:29:18 +00:00
return 0 , fmt . Errorf ( "no return address for function: %s" , locations [ 0 ] . Current . Fn . BaseName ( ) )
2017-02-15 13:41:03 +00:00
}
return locations [ 1 ] . Current . PC , nil
}
2014-12-09 16:51:17 +00:00
func TestFindReturnAddress ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testnextprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
start , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 24 )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( start , proc . UserBreakpoint , nil )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
err = proc . Continue ( p )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-10 14:11:40 +00:00
addr , err := returnAddress ( p . CurrentThread ( ) )
2014-12-09 16:51:17 +00:00
if err != nil {
t . Fatal ( err )
}
2017-02-10 14:11:40 +00:00
_ , l , _ := p . BinInfo ( ) . PCToLine ( addr )
2015-08-10 13:55:57 +00:00
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 ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testreturnaddress" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-08-10 13:55:57 +00:00
fnName := "runtime.rt0_go"
2017-04-28 17:15:39 +00:00
fnentry , err := proc . FindFunctionLocation ( p , fnName , false , 0 )
2017-02-10 14:11:40 +00:00
assertNoError ( err , t , "FindFunctionLocation" )
2017-04-21 06:55:53 +00:00
if _ , err := p . SetBreakpoint ( fnentry , proc . UserBreakpoint , nil ) ; err != nil {
2014-12-09 16:51:17 +00:00
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
if err := proc . Continue ( p ) ; err != nil {
2015-08-02 02:43:03 +00:00
t . Fatal ( err )
}
2017-02-10 14:11:40 +00:00
if _ , err := returnAddress ( p . CurrentThread ( ) ) ; err == nil {
2015-08-10 13:55:57 +00:00
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 ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testnextprog" , t , func ( p proc . 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" )
}
2017-04-28 17:15:39 +00:00
pc , err := proc . FindFunctionLocation ( p , "main.main" , true , 0 )
2015-03-26 18:15:35 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2015-03-26 18:15:35 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
err = proc . Continue ( p )
2015-03-26 18:15:35 +00:00
if err != nil {
t . Fatal ( err )
}
var nt int
2017-02-10 14:11:40 +00:00
ct := p . CurrentThread ( ) . ThreadID ( )
for _ , thread := range p . ThreadList ( ) {
if thread . ThreadID ( ) != ct {
nt = thread . ThreadID ( )
2015-03-26 18:15:35 +00:00
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 )
}
2017-02-10 14:11:40 +00:00
if p . CurrentThread ( ) . ThreadID ( ) != 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
}
2016-11-15 16:16:33 +00:00
if os . Getenv ( "CGO_ENABLED" ) == "" {
return
}
2015-08-28 08:06:22 +00:00
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "cgotest" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
pc , err := proc . FindFunctionLocation ( p , "main.main" , true , 0 )
2015-08-28 08:06:22 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2015-08-28 08:06:22 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
err = proc . Continue ( p )
2015-08-28 08:06:22 +00:00
if err != nil {
t . Fatal ( err )
}
2017-04-21 06:55:53 +00:00
err = proc . Next ( p )
2015-08-28 08:06:22 +00:00
if err != nil {
t . Fatal ( err )
}
} )
}
2015-06-17 17:11:57 +00:00
type loc struct {
line int
fn string
}
2017-04-21 06:55:53 +00:00
func ( l1 * loc ) match ( l2 proc . 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
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "stacktraceprog" , t , func ( p proc . 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 {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
locations , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 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 )
2017-04-21 06:55:53 +00:00
proc . Continue ( p )
2015-06-17 17:11:57 +00:00
} )
}
2015-09-17 08:42:34 +00:00
func TestStacktrace2 ( t * testing . T ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "retstack" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2015-09-17 08:42:34 +00:00
2017-04-21 06:55:53 +00:00
locations , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 40 )
2015-09-17 08:42:34 +00:00
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
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
locations , err = proc . ThreadStacktrace ( p . CurrentThread ( ) , 40 )
2015-09-17 08:42:34 +00:00
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
}
} )
}
2017-04-21 06:55:53 +00:00
func stackMatch ( stack [ ] loc , locations [ ] proc . 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" } }
2017-06-29 18:15:59 +00:00
agoroutineStacks := [ ] [ ] loc {
{ { 8 , "main.agoroutine" } } ,
{ { 9 , "main.agoroutine" } } ,
{ { 10 , "main.agoroutine" } } ,
}
2015-06-17 17:11:57 +00:00
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "goroutinestackprog" , t , func ( p proc . 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()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2015-06-17 17:11:57 +00:00
2017-04-21 06:55:53 +00:00
gs , err := proc . GoroutinesInfo ( p )
2015-06-17 17:11:57 +00:00
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 )
2016-10-22 04:51:34 +00:00
if err != nil {
// On windows we do not have frame information for goroutines doing system calls.
2017-02-14 14:17:00 +00:00
t . Logf ( "Could not retrieve goroutine stack for goid=%d: %v" , g . ID , err )
2016-10-22 04:51:34 +00:00
continue
}
2015-06-17 17:11:57 +00:00
2015-11-06 18:49:29 +00:00
if stackMatch ( mainStack , locations , false ) {
2015-06-17 17:11:57 +00:00
mainCount ++
}
2017-03-13 17:45:53 +00:00
found := false
for _ , agoroutineStack := range agoroutineStacks {
if stackMatch ( agoroutineStack , locations , true ) {
found = true
}
}
if found {
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
}
2017-10-05 07:26:19 +00:00
t . Logf ( "\t%s:%d %s (%#x)\n" , locations [ i ] . Call . File , locations [ i ] . Call . Line , name , locations [ i ] . Current . PC )
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 )
2017-04-21 06:55:53 +00:00
proc . Continue ( p )
2015-06-17 17:11:57 +00:00
} )
}
2015-07-11 06:43:47 +00:00
func TestKill ( t * testing . T ) {
2017-02-10 14:11:40 +00:00
if testBackend == "lldb" {
// k command presumably works but leaves the process around?
return
}
2017-05-05 17:04:32 +00:00
withTestProcess ( "testprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
if err := p . Detach ( true ) ; err != nil {
t . Fatal ( err )
}
2017-06-29 18:15:59 +00:00
if ! p . Exited ( ) {
2017-05-05 17:04:32 +00:00
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-11 06:43:47 +00:00
}
2015-07-23 17:08:28 +00:00
2017-04-21 07:50:38 +00:00
func testGSupportFunc ( name string , t * testing . T , p proc . 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()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , name + ": Continue()" )
2015-07-23 17:08:28 +00:00
2017-04-21 06:55:53 +00:00
g , err := proc . GetG ( p . CurrentThread ( ) )
2015-07-23 17:08:28 +00:00
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 ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-07-23 17:08:28 +00:00
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
}
2016-11-15 16:16:33 +00:00
if os . Getenv ( "CGO_ENABLED" ) == "" {
return
}
2015-07-28 05:33:07 +00:00
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "cgotest" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-07-23 17:08:28 +00:00
testGSupportFunc ( "cgo" , t , p , fixture )
} )
}
2015-07-09 16:41:03 +00:00
func TestContinueMulti ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "integrationprog" , t , func ( p proc . 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 {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2015-09-26 20:56:24 +00:00
if p . Exited ( ) {
2015-07-09 16:41:03 +00:00
break
}
assertNoError ( err , t , "Continue()" )
2017-09-25 06:29:13 +00:00
if bp := p . CurrentThread ( ) . Breakpoint ( ) ; bp . ID == bp1 . ID {
2015-07-09 16:41:03 +00:00
mainCount ++
}
2017-09-25 06:29:13 +00:00
if bp := p . CurrentThread ( ) . Breakpoint ( ) ; bp . ID == bp2 . ID {
2015-07-09 16:41:03 +00:00
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-08-07 16:50:14 +00:00
func TestBreakpointOnFunctionEntry ( t * testing . T ) {
2018-01-20 15:18:39 +00:00
testseq2 ( t , "testprog" , "main.main" , [ ] seqTest { { contContinue , 17 } } )
2015-08-07 16:50:14 +00:00
}
2015-08-12 00:12:37 +00:00
func TestProcessReceivesSIGCHLD ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "sigchldprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
_ , ok := err . ( proc . ProcessExitedError )
2015-08-12 00:12:37 +00:00
if ! ok {
2017-02-10 14:11:40 +00:00
t . Fatalf ( "Continue() returned unexpected error type %v" , err )
2015-08-12 00:12:37 +00:00
}
} )
}
2015-10-04 18:16:39 +00:00
func TestIssue239 ( t * testing . T ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "is sue239" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
pos , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 17 )
2015-10-04 18:16:39 +00:00
assertNoError ( err , t , "LineToPC()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( pos , proc . UserBreakpoint , nil )
2015-10-04 18:16:39 +00:00
assertNoError ( err , t , fmt . Sprintf ( "SetBreakpoint(%d)" , pos ) )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , fmt . Sprintf ( "Continue()" ) )
2015-10-04 18:16:39 +00:00
} )
}
2015-10-18 17:37:13 +00:00
2017-05-05 22:17:52 +00:00
func findFirstNonRuntimeFrame ( p proc . Process ) ( proc . Stackframe , error ) {
frames , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 10 )
if err != nil {
return proc . Stackframe { } , err
}
for _ , frame := range frames {
if frame . Current . Fn != nil && ! strings . HasPrefix ( frame . Current . Fn . Name , "runtime." ) {
return frame , nil
}
}
return proc . Stackframe { } , fmt . Errorf ( "non-runtime frame not found" )
}
2018-01-20 15:18:39 +00:00
func evalVariableOrError ( p proc . Process , symbol string ) ( * proc . Variable , error ) {
2017-05-05 22:17:52 +00:00
var scope * proc . EvalScope
var err error
if testBackend == "rr" {
var frame proc . Stackframe
frame , err = findFirstNonRuntimeFrame ( p )
if err == nil {
2018-02-13 17:20:45 +00:00
scope = proc . FrameToScope ( p . BinInfo ( ) , p . CurrentThread ( ) , nil , frame )
2017-05-05 22:17:52 +00:00
}
} else {
scope , err = proc . GoroutineScope ( p . CurrentThread ( ) )
}
2017-02-10 14:11:40 +00:00
2015-10-18 17:37:13 +00:00
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
}
2018-01-20 15:18:39 +00:00
func evalVariable ( p proc . Process , t testing . TB , symbol string ) * proc . Variable {
v , err := evalVariableOrError ( p , symbol )
if err != nil {
_ , file , line , _ := runtime . Caller ( 1 )
fname := filepath . Base ( file )
t . Fatalf ( "%s:%d: EvalVariable(%q): %v" , fname , line , symbol , err )
}
return v
}
2017-04-21 07:50:38 +00:00
func setVariable ( p proc . Process , symbol , value string ) error {
2017-04-21 06:55:53 +00:00
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
2015-10-18 17:37:13 +00:00
if err != nil {
return err
}
return scope . SetVariable ( symbol , value )
}
func TestVariableEvaluation ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2015-10-18 17:37:13 +00:00
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 } ,
}
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue() returned an error" )
2015-10-18 17:37:13 +00:00
for _ , tc := range testcases {
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , tc . name )
2015-10-18 17:37:13 +00:00
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 ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "goroutinestackprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2015-10-18 17:37:13 +00:00
_ , err := setFunctionBreakpoint ( p , "main.stacktraceme" )
assertNoError ( err , t , "setFunctionBreakpoint" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2015-10-18 17:37:13 +00:00
2015-11-10 09:49:47 +00:00
// Testing evaluation on goroutines
2017-04-21 06:55:53 +00:00
gs , err := proc . GoroutinesInfo ( p )
2015-10-18 17:37:13 +00:00
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 )
2017-02-16 13:16:00 +00:00
if err != nil {
t . Logf ( "could not stacktrace goroutine %d: %v\n" , g . ID , err )
continue
}
2015-10-18 17:37:13 +00:00
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
}
2017-04-21 06:55:53 +00:00
scope , err := proc . ConvertEvalScope ( p , 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
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue() 2" )
g , err := proc . GetG ( p . CurrentThread ( ) )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , "GetG()" )
for i := 0 ; i <= 3 ; i ++ {
2017-04-21 06:55:53 +00:00
scope , err := proc . ConvertEvalScope ( p , 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 ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue() returned an error" )
2015-10-18 17:37:13 +00:00
pval := func ( n int64 ) {
2018-01-20 15:18:39 +00:00
variable := evalVariable ( p , t , "p1" )
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
2017-04-21 06:55:53 +00:00
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
2015-10-18 17:37:13 +00:00
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 ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , "Continue() returned an error" )
2018-01-20 15:18:39 +00:00
evalVariable ( p , t , "a1" )
evalVariable ( p , t , "a2" )
2015-10-18 17:37:13 +00:00
// Move scopes, a1 exists here by a2 does not
2017-04-21 06:55:53 +00:00
err = proc . Continue ( p )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , "Continue() returned an error" )
2018-01-20 15:18:39 +00:00
evalVariable ( p , t , "a1" )
2015-10-18 17:37:13 +00:00
2018-01-20 15:18:39 +00:00
_ , err = evalVariableOrError ( p , "a2" )
2015-10-18 17:37:13 +00:00
if err == nil {
t . Fatalf ( "Can eval out of scope variable a2" )
}
} )
}
func TestRecursiveStructure ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , "aas" )
2015-10-18 17:37:13 +00:00
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
evalVariable ( p , t , "iface5" )
2015-12-19 13:57:48 +00:00
} )
}
2015-12-20 07:43:17 +00:00
func TestIssue325 ( t * testing . T ) {
// nil pointer dereference when evaluating interfaces to function pointers
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
iface2fn1v := evalVariable ( p , t , "iface2fn1" )
2015-12-20 07:43:17 +00:00
t . Logf ( "iface2fn1: %v\n" , iface2fn1v )
2018-01-20 15:18:39 +00:00
iface2fn2v := evalVariable ( p , t , "iface2fn2" )
2015-12-20 07:43:17 +00:00
t . Logf ( "iface2fn2: %v\n" , iface2fn2v )
} )
}
2015-10-02 16:17:07 +00:00
func TestBreakpointCounts ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "bpcountstest" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 12 )
2015-10-02 16:17:07 +00:00
assertNoError ( err , t , "LineToPC" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2015-10-02 16:17:07 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
for {
2017-04-21 06:55:53 +00:00
if err := proc . Continue ( p ) ; err != nil {
if _ , exited := err . ( proc . ProcessExitedError ) ; exited {
2015-10-02 16:17:07 +00:00
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( b )
2016-01-10 12:49:03 +00:00
b . SetBytes ( int64 ( 64 * 128 ) )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , b , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , b , "Continue()" )
2016-01-10 12:49:03 +00:00
for i := 0 ; i < b . N ; i ++ {
2018-01-20 15:18:39 +00:00
evalVariable ( p , b , "bencharr" )
2016-01-10 12:49:03 +00:00
}
} )
}
2015-11-19 15:19:42 +00:00
const doTestBreakpointCountsWithDetection = false
func TestBreakpointCountsWithDetection ( t * testing . T ) {
if ! doTestBreakpointCountsWithDetection {
return
}
m := map [ int64 ] int64 { }
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "bpcountstest" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 12 )
2015-11-19 15:19:42 +00:00
assertNoError ( err , t , "LineToPC" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2015-11-19 15:19:42 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
for {
2017-04-21 06:55:53 +00:00
if err := proc . Continue ( p ) ; err != nil {
if _ , exited := err . ( proc . ProcessExitedError ) ; exited {
2015-11-19 15:19:42 +00:00
break
}
assertNoError ( err , t , "Continue()" )
}
2017-02-10 14:11:40 +00:00
for _ , th := range p . ThreadList ( ) {
2017-09-25 06:29:13 +00:00
if bp := th . Breakpoint ( ) ; bp . Breakpoint == nil {
2015-11-19 15:19:42 +00:00
continue
}
2017-04-21 06:55:53 +00:00
scope , err := proc . GoroutineScope ( th )
2015-11-19 15:19:42 +00:00
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
}
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( b )
2016-01-10 12:49:03 +00:00
b . SetBytes ( int64 ( 64 * 128 + 64 * 8 ) )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , b , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , b , "Continue()" )
2016-01-10 12:49:03 +00:00
for i := 0 ; i < b . N ; i ++ {
2018-01-20 15:18:39 +00:00
evalVariable ( p , b , "bencharr" )
2016-01-10 12:49:03 +00:00
}
} )
}
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( b )
2016-01-10 12:49:03 +00:00
b . SetBytes ( int64 ( 41 * ( 2 * 8 + 9 ) ) )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , b , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , b , "Continue()" )
2016-01-10 12:49:03 +00:00
for i := 0 ; i < b . N ; i ++ {
2018-01-20 15:18:39 +00:00
evalVariable ( p , b , "m1" )
2016-01-10 12:49:03 +00:00
}
} )
}
func BenchmarkGoroutinesInfo ( b * testing . B ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( b )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , b , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , b , "Continue()" )
2016-01-10 12:49:03 +00:00
for i := 0 ; i < b . N ; i ++ {
2017-04-21 06:55:53 +00:00
if p , ok := p . ( proc . AllGCache ) ; ok {
allgcache := p . AllGCache ( )
* allgcache = nil
}
_ , err := proc . GoroutinesInfo ( p )
2016-01-10 12:49:03 +00:00
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue262" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 11 )
2015-11-10 09:49:47 +00:00
assertNoError ( err , t , "LineToPC" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2015-11-10 09:49:47 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
err = proc . Continue ( p )
2015-11-10 09:49:47 +00:00
if err == nil {
t . Fatalf ( "No error on second continue" )
}
2017-04-21 06:55:53 +00:00
_ , exited := err . ( proc . ProcessExitedError )
2015-11-10 09:49:47 +00:00
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 ) {
2016-09-30 06:35:29 +00:00
// If 'next' hits a breakpoint on the goroutine it's stepping through
// the internal breakpoints aren't cleared preventing further use of
// 'next' command
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue305" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 5 )
2016-01-16 08:13:15 +00:00
assertNoError ( err , t , "LineToPC()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2016-01-16 08:13:15 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-01-16 08:13:15 +00:00
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "Next() 1" )
assertNoError ( proc . Next ( p ) , t , "Next() 2" )
assertNoError ( proc . Next ( p ) , t , "Next() 3" )
assertNoError ( proc . Next ( p ) , t , "Next() 4" )
assertNoError ( proc . Next ( p ) , t , "Next() 5" )
2016-01-16 08:13:15 +00:00
} )
}
2017-02-10 00:26:38 +00:00
func TestPointerLoops ( t * testing . T ) {
// Pointer loops through map entries, pointers and slices
// Regression test for issue #341
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2017-02-10 00:26:38 +00:00
for _ , expr := range [ ] string { "mapinf" , "ptrinf" , "sliceinf" } {
t . Logf ( "requesting %s" , expr )
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , expr )
2017-02-10 00:26:38 +00:00
t . Logf ( "%s: %v\n" , expr , v )
}
2016-01-10 09:04:14 +00:00
} )
}
2016-01-10 12:49:03 +00:00
func BenchmarkLocalVariables ( b * testing . B ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( b )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables" , b , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , b , "Continue() returned an error" )
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
2016-01-10 12:49:03 +00:00
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 ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "parallel_next" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 9 )
2016-01-12 08:01:42 +00:00
assertNoError ( err , t , "LineToPC" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2016-01-12 08:01:42 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "n" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "7" } ,
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-01-12 08:01:42 +00:00
2018-01-20 15:18:39 +00:00
nvar := evalVariable ( p , t , "n" )
2016-01-12 08:01:42 +00:00
n , _ := constant . Int64Val ( nvar . Value )
if n != 7 {
t . Fatalf ( "Stoppend on wrong goroutine %d\n" , n )
}
} )
}
func TestCondBreakpointError ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "parallel_next" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 9 )
2016-01-12 08:01:42 +00:00
assertNoError ( err , t , "LineToPC" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
2016-01-12 08:01:42 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "nonexistentvariable" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "7" } ,
}
2017-04-21 06:55:53 +00:00
err = proc . Continue ( p )
2016-01-12 08:01:42 +00:00
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" } ,
}
2017-04-21 06:55:53 +00:00
err = proc . Continue ( p )
2016-01-12 08:01:42 +00:00
if err != nil {
2017-04-21 06:55:53 +00:00
if _ , exited := err . ( proc . ProcessExitedError ) ; ! exited {
2016-01-12 08:01:42 +00:00
t . Fatalf ( "Unexpected error on second Continue(): %v" , err )
}
} else {
2018-01-20 15:18:39 +00:00
nvar := evalVariable ( p , t , "n" )
2016-01-12 08:01:42 +00:00
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue() returned an error" )
2018-01-20 15:18:39 +00:00
mmvar := evalVariable ( p , t , "mainMenu" )
2016-01-24 09:25:54 +00:00
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 ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "teststep" , t , func ( p proc . Process , fixture protest . Fixture ) {
2016-01-24 05:25:46 +00:00
// Continue until breakpoint
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue() returned an error" )
2016-01-24 05:25:46 +00:00
// Step into function
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step() returned an error" )
2016-01-24 05:25:46 +00:00
// We should now be inside the function.
2017-02-10 14:11:40 +00:00
loc , err := p . CurrentThread ( ) . Location ( )
2016-01-24 05:25:46 +00:00
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
2017-09-08 10:31:03 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// go 1.10 emits DW_AT_decl_line and we won't be able to evaluate 'st'
// which is declared after line 13.
2017-10-13 20:13:43 +00:00
t . Skip ( "can not evaluate not-yet-declared variables with go 1.10" )
2017-09-08 10:31:03 +00:00
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue384" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
start , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 13 )
2016-01-30 21:21:07 +00:00
assertNoError ( err , t , "LineToPC()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( start , proc . UserBreakpoint , nil )
2016-01-30 21:21:07 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
evalVariable ( p , t , "st" )
2016-01-30 21:21:07 +00:00
} )
}
2016-01-31 12:03:53 +00:00
func TestIssue332_Part1 ( t * testing . T ) {
// Next shouldn't step inside a function call
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue332" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
start , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 8 )
2016-01-31 12:03:53 +00:00
assertNoError ( err , t , "LineToPC()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( start , proc . UserBreakpoint , nil )
2016-01-31 12:03:53 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Next ( p ) , t , "first Next()" )
locations , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 2 )
2016-01-31 12:03:53 +00:00
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
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue332" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
start , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 8 )
2016-01-31 12:03:53 +00:00
assertNoError ( err , t , "LineToPC()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( start , proc . UserBreakpoint , nil )
2016-01-31 12:03:53 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-01-31 12:03:53 +00:00
// step until we enter changeMe
for {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step()" )
locations , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 2 )
2016-01-31 12:03:53 +00:00
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
}
}
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
assertNoError ( err , t , "Registers()" )
pc := regs . PC ( )
2017-04-28 17:15:39 +00:00
pcAfterPrologue , err := proc . FindFunctionLocation ( p , "main.changeMe" , true , - 1 )
2016-02-12 07:43:22 +00:00
assertNoError ( err , t , "FindFunctionLocation()" )
2017-04-28 17:15:39 +00:00
pcEntry , err := proc . FindFunctionLocation ( p , "main.changeMe" , false , 0 )
2017-06-29 18:15:59 +00:00
if err != nil {
t . Fatalf ( "got error while finding function location: %v" , err )
}
2016-02-12 07:43:22 +00:00
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 )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "first Next()" )
assertNoError ( proc . Next ( p ) , t , "second Next()" )
assertNoError ( proc . Next ( p ) , t , "third Next()" )
err = proc . Continue ( p )
if _ , exited := err . ( proc . ProcessExitedError ) ; ! exited {
2016-01-31 12:03:53 +00:00
assertNoError ( err , t , "final Continue()" )
}
} )
}
2016-02-11 07:18:39 +00:00
func TestIssue396 ( t * testing . T ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "callme" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
_ , err := proc . FindFunctionLocation ( p , "main.init" , true , - 1 )
2016-02-11 07:18:39 +00:00
assertNoError ( err , t , "FindFunctionLocation()" )
} )
}
2016-02-14 21:26:06 +00:00
func TestIssue414 ( t * testing . T ) {
// Stepping until the program exits
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "math" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
start , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 9 )
2016-02-14 21:26:06 +00:00
assertNoError ( err , t , "LineToPC()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( start , proc . UserBreakpoint , nil )
2016-02-14 21:26:06 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-02-14 21:26:06 +00:00
for {
2017-04-21 06:55:53 +00:00
err := proc . Step ( p )
2016-02-14 21:26:06 +00:00
if err != nil {
2017-04-21 06:55:53 +00:00
if _ , exited := err . ( proc . ProcessExitedError ) ; exited {
2016-02-14 21:26:06 +00:00
break
}
}
assertNoError ( err , t , "Step()" )
}
} )
}
2016-02-21 16:26:13 +00:00
func TestPackageVariables ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2016-02-21 16:26:13 +00:00
assertNoError ( err , t , "Continue()" )
2017-04-21 06:55:53 +00:00
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
2016-02-21 16:26:13 +00:00
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 ) {
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-02-25 09:48:42 +00:00
return
}
// setting breakpoint on break statement
2017-04-21 07:50:38 +00:00
withTestProcess ( "break" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
_ , err := proc . FindFileLocation ( p , fixture . Source , 8 )
2016-02-25 09:48:42 +00:00
assertNoError ( err , t , "FindFileLocation()" )
} )
}
2016-03-06 17:54:43 +00:00
func TestPanicBreakpoint ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "panic" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2017-09-25 06:29:13 +00:00
bp := p . CurrentThread ( ) . Breakpoint ( )
if bp . Breakpoint == nil || bp . Name != proc . UnrecoveredPanic {
2017-02-10 14:11:40 +00:00
t . Fatalf ( "not on unrecovered-panic breakpoint: %v" , bp )
2016-03-06 17:54:43 +00:00
}
} )
}
2016-03-18 08:32:17 +00:00
2016-04-21 21:20:38 +00:00
func TestCmdLineArgs ( t * testing . T ) {
2017-04-21 07:50:38 +00:00
expectSuccess := func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2017-09-25 06:29:13 +00:00
bp := p . CurrentThread ( ) . Breakpoint ( )
if bp . Breakpoint != nil && bp . Name == proc . UnrecoveredPanic {
2017-02-10 14:11:40 +00:00
t . Fatalf ( "testing args failed on unrecovered-panic breakpoint: %v" , bp )
2016-04-21 21:20:38 +00:00
}
2017-04-21 06:55:53 +00:00
exit , exited := err . ( proc . ProcessExitedError )
2016-04-21 21:20:38 +00:00
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 {
2017-06-29 18:15:59 +00:00
t . Fatalf ( "process exited with invalid status %d" , exit . Status )
2016-04-21 21:20:38 +00:00
}
}
}
2017-04-21 07:50:38 +00:00
expectPanic := func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
proc . Continue ( p )
2017-09-25 06:29:13 +00:00
bp := p . CurrentThread ( ) . Breakpoint ( )
if bp . Breakpoint == nil || bp . Name != proc . UnrecoveredPanic {
2017-02-10 14:11:40 +00:00
t . Fatalf ( "not on unrecovered-panic breakpoint: %v" , bp )
2016-04-21 21:20:38 +00:00
}
}
// make sure multiple arguments (including one with spaces) are passed to the binary correctly
2018-01-20 15:18:39 +00:00
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "test" } , 0 , expectSuccess )
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "-test" } , 0 , expectPanic )
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "test" , "pass flag" } , 0 , expectSuccess )
2016-04-21 21:20:38 +00:00
// check that arguments with spaces are *only* passed correctly when correctly called
2018-01-20 15:18:39 +00:00
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "test pass" , "flag" } , 0 , expectPanic )
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "test" , "pass" , "flag" } , 0 , expectPanic )
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "test pass flag" } , 0 , expectPanic )
2016-04-21 21:20:38 +00:00
// and that invalid cases (wrong arguments or no arguments) panic
withTestProcess ( "testargs" , t , expectPanic )
2018-01-20 15:18:39 +00:00
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "invalid" } , 0 , expectPanic )
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "test" , "invalid" } , 0 , expectPanic )
withTestProcessArgs ( "testargs" , t , "." , [ ] string { "invalid" , "pass flag" } , 0 , expectPanic )
2016-04-21 21:20:38 +00:00
}
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
}
2017-04-21 07:50:38 +00:00
withTestProcess ( "testnextnethttp" , t , func ( p proc . Process , fixture protest . Fixture ) {
2016-03-18 08:32:17 +00:00
go func ( ) {
// 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 ( )
} ( )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
_ , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 40 )
2016-03-18 08:32:17 +00:00
assertNoError ( err , t , "Stacktrace()" )
} )
}
2016-05-29 19:11:00 +00:00
2016-04-11 11:50:01 +00:00
func TestNextParked ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "parallel_next" , t , func ( p proc . Process , fixture protest . Fixture ) {
2016-04-11 11:50:01 +00:00
bp , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "SetBreakpoint()" )
// continue until a parked goroutine exists
2017-04-21 06:55:53 +00:00
var parkedg * proc . G
2017-08-14 05:57:49 +00:00
for parkedg == nil {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
if _ , exited := err . ( proc . ProcessExitedError ) ; exited {
2016-04-11 11:50:01 +00:00
t . Log ( "could not find parked goroutine" )
return
}
assertNoError ( err , t , "Continue()" )
2017-04-21 06:55:53 +00:00
gs , err := proc . GoroutinesInfo ( p )
2016-04-11 11:50:01 +00:00
assertNoError ( err , t , "GoroutinesInfo()" )
2017-08-14 05:57:49 +00:00
// Search for a parked goroutine that we know for sure will have to be
// resumed before the program can exit. This is a parked goroutine that:
// 1. is executing main.sayhi
// 2. hasn't called wg.Done yet
2016-04-11 11:50:01 +00:00
for _ , g := range gs {
2017-08-14 05:57:49 +00:00
if g . Thread != nil {
continue
}
frames , _ := g . Stacktrace ( 5 )
for _ , frame := range frames {
// line 11 is the line where wg.Done is called
if frame . Current . Fn != nil && frame . Current . Fn . Name == "main.sayhi" && frame . Current . Line < 11 {
parkedg = g
break
}
}
if parkedg != nil {
break
2016-04-11 11:50:01 +00:00
}
}
}
assertNoError ( p . SwitchGoroutine ( parkedg . ID ) , t , "SwitchGoroutine()" )
p . ClearBreakpoint ( bp . Addr )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , "Next()" )
2016-04-11 11:50:01 +00:00
2017-02-10 14:11:40 +00:00
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-11 11:50:01 +00:00
}
} )
}
2016-04-13 13:25:23 +00:00
func TestStepParked ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "parallel_next" , t , func ( p proc . Process , fixture protest . Fixture ) {
2016-04-13 13:25:23 +00:00
bp , err := setFunctionBreakpoint ( p , "main.sayhi" )
assertNoError ( err , t , "SetBreakpoint()" )
// continue until a parked goroutine exists
2017-04-21 06:55:53 +00:00
var parkedg * proc . G
2016-04-13 13:25:23 +00:00
LookForParkedG :
for {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
if _ , exited := err . ( proc . ProcessExitedError ) ; exited {
2016-04-13 13:25:23 +00:00
t . Log ( "could not find parked goroutine" )
return
}
assertNoError ( err , t , "Continue()" )
2017-04-21 06:55:53 +00:00
gs , err := proc . GoroutinesInfo ( p )
2016-04-13 13:25:23 +00:00
assertNoError ( err , t , "GoroutinesInfo()" )
for _ , g := range gs {
2017-04-21 06:55:53 +00:00
if g . Thread == nil && g . CurrentLoc . Fn != nil && g . CurrentLoc . Fn . Name == "main.sayhi" {
2016-04-13 13:25:23 +00:00
parkedg = g
break LookForParkedG
}
}
}
2017-02-16 17:22:09 +00:00
t . Logf ( "Parked g is: %v\n" , parkedg )
frames , _ := parkedg . Stacktrace ( 20 )
for _ , frame := range frames {
name := ""
if frame . Call . Fn != nil {
name = frame . Call . Fn . Name
}
t . Logf ( "\t%s:%d in %s (%#x)" , frame . Call . File , frame . Call . Line , name , frame . Current . PC )
}
2016-04-13 13:25:23 +00:00
assertNoError ( p . SwitchGoroutine ( parkedg . ID ) , t , "SwitchGoroutine()" )
p . ClearBreakpoint ( bp . Addr )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step()" )
2016-04-13 13:25:23 +00:00
2017-02-10 14:11:40 +00:00
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-13 13:25:23 +00:00
}
} )
}
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" )
2017-04-21 06:55:53 +00:00
_ , err := native . Launch ( [ ] string { exepath } , "." )
2016-04-21 10:19:21 +00:00
if err == nil {
t . Fatalf ( "expected error but none was generated" )
}
2017-04-21 06:55:53 +00:00
if err != proc . NotExecutableErr {
t . Fatalf ( "expected error \"%v\" got \"%v\"" , proc . NotExecutableErr , err )
2016-04-21 10:19:21 +00:00
}
os . Remove ( exepath )
}
func TestUnsupportedArch ( t * testing . T ) {
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major < 0 || ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 6 , - 1 , 0 , 0 , "" } ) || ver . AfterOrEqual ( goversion . 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
2017-04-21 06:55:53 +00:00
p , err := native . Launch ( [ ] string { outfile } , "." )
2016-04-21 10:19:21 +00:00
switch err {
2017-04-21 06:55:53 +00:00
case proc . UnsupportedLinuxArchErr , proc . UnsupportedWindowsArchErr , proc . UnsupportedDarwinArchErr :
2016-04-21 10:19:21 +00:00
// all good
case nil :
2018-02-13 14:42:14 +00:00
p . Detach ( true )
2016-04-21 10:19:21 +00:00
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
2016-09-30 06:35:29 +00:00
// of the function and the internal breakpoint set by StepInto may be missed.
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue573" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
fentry , _ := proc . FindFunctionLocation ( p , "main.foo" , false , 0 )
2017-04-21 06:55:53 +00:00
_ , err := p . SetBreakpoint ( fentry , proc . UserBreakpoint , nil )
2016-07-21 20:10:35 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Step ( p ) , t , "Step() #1" )
assertNoError ( proc . Step ( p ) , t , "Step() #2" ) // Bug exits here.
assertNoError ( proc . Step ( p ) , t , "Step() #3" ) // Third step ought to be possible; program ought not have exited.
2016-07-21 20:10:35 +00:00
} )
}
2016-09-06 17:27:07 +00:00
func TestTestvariables2Prologue ( t * testing . T ) {
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
addrEntry , err := proc . FindFunctionLocation ( p , "main.main" , false , 0 )
2016-09-06 17:27:07 +00:00
assertNoError ( err , t , "FindFunctionLocation - entrypoint" )
2017-04-28 17:15:39 +00:00
addrPrologue , err := proc . FindFunctionLocation ( p , "main.main" , true , 0 )
2016-09-06 17:27:07 +00:00
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
2016-07-26 13:34:31 +00:00
testseq ( "defercall" , contNext , [ ] nextTest {
2016-07-14 07:43:39 +00:00
{ 9 , 10 } ,
{ 10 , 11 } ,
{ 11 , 12 } ,
{ 12 , 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
2016-07-26 13:34:31 +00:00
testseq ( "defercall" , contNext , [ ] nextTest {
2016-07-14 07:43:39 +00:00
{ 15 , 16 } ,
{ 16 , 17 } ,
{ 17 , 18 } ,
{ 18 , 5 } } , "main.callAndPanic2" , t )
}
2016-07-26 13:34:31 +00:00
func TestStepCall ( t * testing . T ) {
testseq ( "testnextprog" , contStep , [ ] nextTest {
{ 34 , 13 } ,
{ 13 , 14 } } , "" , t )
}
func TestStepCallPtr ( t * testing . T ) {
// Tests that Step works correctly when calling functions with a
// function pointer.
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 9 , 10 } ,
{ 10 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 11 } } , "" , t )
}
func TestStepReturnAndPanic ( t * testing . T ) {
// Tests that Step works correctly when returning from functions
// and when a deferred function is called when panic'ing.
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
2017-10-13 20:13:43 +00:00
if ver . Major > 0 && ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
2017-05-03 07:10:12 +00:00
testseq ( "defercall" , contStep , [ ] nextTest {
{ 17 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 17 } ,
{ 17 , 18 } ,
{ 18 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } } , "" , t )
} else {
testseq ( "defercall" , contStep , [ ] nextTest {
{ 17 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 18 } ,
{ 18 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } } , "" , t )
}
2016-07-26 13:34:31 +00:00
}
func TestStepDeferReturn ( t * testing . T ) {
// Tests that Step works correctly when a deferred function is
// called during a return.
testseq ( "defercall" , contStep , [ ] nextTest {
{ 11 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 12 } ,
{ 12 , 13 } ,
{ 13 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 13 } ,
{ 13 , 28 } } , "" , t )
}
func TestStepIgnorePrivateRuntime ( t * testing . T ) {
// Tests that Step will ignore calls to private runtime functions
// (such as runtime.convT2E in this case)
2017-05-04 10:22:08 +00:00
ver , _ := goversion . Parse ( runtime . Version ( ) )
2016-07-26 13:34:31 +00:00
2017-10-13 20:13:43 +00:00
if ver . Major > 0 && ver . AfterOrEqual ( goversion . GoVersion { 1 , 7 , - 1 , 0 , 0 , "" } ) && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
2016-07-26 13:34:31 +00:00
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 21 , 13 } ,
{ 13 , 14 } ,
{ 14 , 15 } ,
{ 15 , 14 } ,
{ 14 , 17 } ,
{ 17 , 22 } } , "" , t )
2017-10-13 20:13:43 +00:00
} else if ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 21 , 13 } ,
{ 13 , 14 } ,
{ 14 , 15 } ,
{ 15 , 22 } } , "" , t )
2016-07-26 13:34:31 +00:00
} else {
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 21 , 13 } ,
{ 13 , 14 } ,
{ 14 , 15 } ,
{ 15 , 17 } ,
{ 17 , 22 } } , "" , t )
}
}
func TestIssue561 ( t * testing . T ) {
// Step fails to make progress when PC is at a CALL instruction
// where a breakpoint is also set.
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue561" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-07 21:07:18 +00:00
setFileBreakpoint ( p , t , fixture , 10 )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Step ( p ) , t , "Step()" )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 5 , "wrong line number after Step," )
2016-07-26 13:34:31 +00:00
} )
}
2016-04-14 09:58:59 +00:00
func TestStepOut ( t * testing . T ) {
2018-01-20 15:18:39 +00:00
testseq2 ( t , "testnextprog" , "main.helloworld" , [ ] seqTest { { contContinue , 13 } , { contStepout , 35 } } )
2016-04-14 09:58:59 +00:00
}
2016-07-26 13:34:31 +00:00
func TestStepConcurrentDirect ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
pc , err := proc . FindFileLocation ( p , fixture . Source , 37 )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "FindFileLocation()" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-07-26 13:34:31 +00:00
_ , err = p . ClearBreakpoint ( bp . Addr )
assertNoError ( err , t , "ClearBreakpoint()" )
2017-09-24 13:00:55 +00:00
for _ , b := range p . Breakpoints ( ) . M {
2017-09-20 12:19:59 +00:00
if b . Name == proc . UnrecoveredPanic {
2017-02-14 14:17:00 +00:00
_ , err := p . ClearBreakpoint ( b . Addr )
assertNoError ( err , t , "ClearBreakpoint(unrecovered-panic)" )
break
}
}
2017-02-10 14:11:40 +00:00
gid := p . SelectedGoroutine ( ) . ID
2016-07-26 13:34:31 +00:00
seq := [ ] int { 37 , 38 , 13 , 15 , 16 , 38 }
i := 0
count := 0
for {
2017-02-14 14:17:00 +00:00
anyerr := false
2017-02-10 14:11:40 +00:00
if p . SelectedGoroutine ( ) . ID != gid {
t . Errorf ( "Step switched to different goroutine %d %d\n" , gid , p . SelectedGoroutine ( ) . ID )
2017-02-14 14:17:00 +00:00
anyerr = true
}
2016-07-26 13:34:31 +00:00
f , ln := currentLineNumber ( p , t )
if ln != seq [ i ] {
if i == 1 && ln == 40 {
// loop exited
break
}
2017-04-21 06:55:53 +00:00
frames , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 20 )
2017-02-14 14:17:00 +00:00
if err != nil {
2017-02-10 14:11:40 +00:00
t . Errorf ( "Could not get stacktrace of goroutine %d\n" , p . SelectedGoroutine ( ) . ID )
2017-02-14 14:17:00 +00:00
} else {
2017-02-10 14:11:40 +00:00
t . Logf ( "Goroutine %d (thread: %d):" , p . SelectedGoroutine ( ) . ID , p . CurrentThread ( ) . ThreadID ( ) )
2017-02-14 14:17:00 +00:00
for _ , frame := range frames {
t . Logf ( "\t%s:%d (%#x)" , frame . Call . File , frame . Call . Line , frame . Current . PC )
}
}
t . Errorf ( "Program did not continue at expected location (%d) %s:%d [i %d count %d]" , seq [ i ] , f , ln , i , count )
anyerr = true
2016-07-26 13:34:31 +00:00
}
2017-02-14 14:17:00 +00:00
if anyerr {
t . FailNow ( )
2016-07-26 13:34:31 +00:00
}
i = ( i + 1 ) % len ( seq )
if i == 0 {
count ++
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step()" )
2016-07-26 13:34:31 +00:00
}
if count != 100 {
t . Fatalf ( "Program did not loop expected number of times: %d" , count )
}
} )
}
func TestStepConcurrentPtr ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
pc , err := proc . FindFileLocation ( p , fixture . Source , 24 )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "FindFileLocation()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-09-24 13:00:55 +00:00
for _ , b := range p . Breakpoints ( ) . M {
2017-09-20 12:19:59 +00:00
if b . Name == proc . UnrecoveredPanic {
2017-02-14 14:17:00 +00:00
_ , err := p . ClearBreakpoint ( b . Addr )
assertNoError ( err , t , "ClearBreakpoint(unrecovered-panic)" )
break
}
}
2016-07-26 13:34:31 +00:00
kvals := map [ int ] int64 { }
count := 0
for {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
_ , exited := err . ( proc . ProcessExitedError )
2016-07-26 13:34:31 +00:00
if exited {
break
}
assertNoError ( err , t , "Continue()" )
f , ln := currentLineNumber ( p , t )
if ln != 24 {
2017-02-10 14:11:40 +00:00
for _ , th := range p . ThreadList ( ) {
2017-09-25 06:29:13 +00:00
t . Logf ( "thread %d stopped on breakpoint %v" , th . ThreadID ( ) , th . Breakpoint ( ) )
2017-02-14 14:17:00 +00:00
}
2017-09-25 06:29:13 +00:00
curbp := p . CurrentThread ( ) . Breakpoint ( )
2017-02-10 14:11:40 +00:00
t . Fatalf ( "Program did not continue at expected location (24): %s:%d %#x [%v] (gid %d count %d)" , f , ln , currentPC ( p , t ) , curbp , p . SelectedGoroutine ( ) . ID , count )
2016-07-26 13:34:31 +00:00
}
2017-02-10 14:11:40 +00:00
gid := p . SelectedGoroutine ( ) . ID
2016-07-26 13:34:31 +00:00
2018-01-20 15:18:39 +00:00
kvar := evalVariable ( p , t , "k" )
2016-07-26 13:34:31 +00:00
k , _ := constant . Int64Val ( kvar . Value )
if oldk , ok := kvals [ gid ] ; ok {
if oldk >= k {
2017-06-29 18:15:59 +00:00
t . Fatalf ( "Goroutine %d did not make progress?" , gid )
2016-07-26 13:34:31 +00:00
}
}
kvals [ gid ] = k
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step()" )
2017-09-25 06:29:13 +00:00
for p . Breakpoints ( ) . HasInternalBreakpoints ( ) {
2017-02-10 14:11:40 +00:00
if p . SelectedGoroutine ( ) . ID == gid {
t . Fatalf ( "step did not step into function call (but internal breakpoints still active?) (%d %d)" , gid , p . SelectedGoroutine ( ) . ID )
2016-07-26 13:34:31 +00:00
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-07-26 13:34:31 +00:00
}
2017-02-10 14:11:40 +00:00
if p . SelectedGoroutine ( ) . ID != gid {
t . Fatalf ( "Step switched goroutines (wanted: %d got: %d)" , gid , p . SelectedGoroutine ( ) . ID )
2016-07-26 13:34:31 +00:00
}
2018-01-20 15:18:39 +00:00
f , ln = assertLineNumber ( p , t , 13 , "Step did not step into function call" )
2016-07-26 13:34:31 +00:00
count ++
if count > 50 {
// this test could potentially go on for 10000 cycles, since that's
// too slow we cut the execution after 50 cycles
break
}
}
if count == 0 {
t . Fatalf ( "Breakpoint never hit" )
}
} )
}
2016-04-14 09:58:59 +00:00
func TestStepOutDefer ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testnextdefer" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
pc , err := proc . FindFileLocation ( p , fixture . Source , 9 )
2016-04-14 09:58:59 +00:00
assertNoError ( err , t , "FindFileLocation()" )
2017-04-21 06:55:53 +00:00
bp , err := p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2016-04-14 09:58:59 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-04-14 09:58:59 +00:00
p . ClearBreakpoint ( bp . Addr )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 9 , "wrong line number" )
2016-04-14 09:58:59 +00:00
2017-04-21 06:55:53 +00:00
assertNoError ( proc . StepOut ( p ) , t , "StepOut()" )
2016-04-14 09:58:59 +00:00
2017-02-10 14:11:40 +00:00
f , l , _ := p . BinInfo ( ) . PCToLine ( currentPC ( p , t ) )
2016-04-14 09:58:59 +00:00
if f == fixture . Source || l == 6 {
t . Fatalf ( "wrong location %s:%d, expected to end somewhere in runtime" , f , l )
}
} )
}
func TestStepOutDeferReturnAndDirectCall ( t * testing . T ) {
// StepOut should not step into a deferred function if it is called
// directly, only if it is called through a panic.
// Here we test the case where the function is called by a deferreturn
2018-01-20 15:18:39 +00:00
testseq2 ( t , "defercall" , "" , [ ] seqTest {
{ contContinue , 11 } ,
{ contStepout , 28 } } )
2016-04-14 09:58:59 +00:00
}
2017-04-21 06:55:53 +00:00
const maxInstructionLength uint64 = 15
2016-07-26 13:34:31 +00:00
func TestStepOnCallPtrInstr ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "teststepprog" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-28 17:15:39 +00:00
pc , err := proc . FindFileLocation ( p , fixture . Source , 10 )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "FindFileLocation()" )
2017-04-21 06:55:53 +00:00
_ , err = p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "SetBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-07-26 13:34:31 +00:00
found := false
for {
_ , ln := currentLineNumber ( p , t )
if ln != 10 {
break
}
2017-02-10 14:11:40 +00:00
regs , err := p . CurrentThread ( ) . Registers ( false )
2017-04-13 23:19:57 +00:00
assertNoError ( err , t , "Registers()" )
2017-02-10 14:11:40 +00:00
pc := regs . PC ( )
2017-04-21 06:55:53 +00:00
text , err := proc . Disassemble ( p , nil , pc , pc + maxInstructionLength )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "Disassemble()" )
if text [ 0 ] . IsCall ( ) {
found = true
break
}
assertNoError ( p . StepInstruction ( ) , t , "StepInstruction()" )
}
if ! found {
t . Fatal ( "Could not find CALL instruction" )
}
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Step ( p ) , t , "Step()" )
2016-07-26 13:34:31 +00:00
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 5 , "Step continued to wrong line," )
2016-07-26 13:34:31 +00:00
} )
}
2016-10-22 04:51:34 +00:00
func TestIssue594 ( t * testing . T ) {
2017-02-10 14:11:40 +00:00
if runtime . GOOS == "darwin" && testBackend == "lldb" {
// debugserver will receive an EXC_BAD_ACCESS for this, at that point
// there is no way to reconvert this exception into a unix signal and send
// it to the process.
// This is a bug in debugserver/lldb:
// https://bugs.llvm.org//show_bug.cgi?id=22868
return
}
2016-10-22 04:51:34 +00:00
// Exceptions that aren't caused by breakpoints should be propagated
// back to the target.
// In particular the target should be able to cause a nil pointer
// dereference panic and recover from it.
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue594" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2017-05-05 22:17:52 +00:00
var f string
var ln int
if testBackend == "rr" {
frame , err := findFirstNonRuntimeFrame ( p )
assertNoError ( err , t , "findFirstNonRuntimeFrame" )
f , ln = frame . Current . File , frame . Current . Line
} else {
f , ln = currentLineNumber ( p , t )
}
2016-10-22 04:51:34 +00:00
if ln != 21 {
t . Fatalf ( "Program stopped at %s:%d, expected :21" , f , ln )
}
} )
}
2016-04-14 09:58:59 +00:00
func TestStepOutPanicAndDirectCall ( t * testing . T ) {
// StepOut should not step into a deferred function if it is called
// directly, only if it is called through a panic.
// Here we test the case where the function is called by a panic
2018-01-20 15:18:39 +00:00
testseq2 ( t , "defercall" , "" , [ ] seqTest {
{ contContinue , 17 } ,
{ contStepout , 5 } } )
2016-04-14 09:58:59 +00:00
}
2016-11-01 19:58:43 +00:00
func TestWorkDir ( t * testing . T ) {
wd := os . TempDir ( )
// For Darwin `os.TempDir()` returns `/tmp` which is symlink to `/private/tmp`.
if runtime . GOOS == "darwin" {
wd = "/private/tmp"
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2018-01-20 15:18:39 +00:00
withTestProcessArgs ( "workdir" , t , wd , [ ] string { } , 0 , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-10 14:11:40 +00:00
addr , _ , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 14 )
2016-11-01 19:58:43 +00:00
assertNoError ( err , t , "LineToPC" )
2017-04-21 06:55:53 +00:00
p . SetBreakpoint ( addr , proc . UserBreakpoint , nil )
proc . Continue ( p )
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , "pwd" )
2016-11-01 19:58:43 +00:00
str := constant . StringVal ( v . Value )
if wd != str {
t . Fatalf ( "Expected %s got %s\n" , wd , str )
}
2017-07-31 12:51:35 +00:00
} )
2016-11-01 19:58:43 +00:00
}
2016-11-02 21:32:48 +00:00
func TestNegativeIntEvaluation ( t * testing . T ) {
testcases := [ ] struct {
name string
typ string
value interface { }
} {
{ "ni8" , "int8" , int64 ( - 5 ) } ,
{ "ni16" , "int16" , int64 ( - 5 ) } ,
{ "ni32" , "int32" , int64 ( - 5 ) } ,
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2016-11-02 21:32:48 +00:00
for _ , tc := range testcases {
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , tc . name )
2016-11-02 21:32:48 +00:00
if typ := v . RealType . String ( ) ; typ != tc . typ {
t . Fatalf ( "Wrong type for variable %q: %q (expected: %q)" , tc . name , typ , tc . typ )
}
if val , _ := constant . Int64Val ( v . Value ) ; val != tc . value {
t . Fatalf ( "Wrong value for variable %q: %v (expected: %v)" , tc . name , val , tc . value )
}
}
} )
}
2017-01-09 23:21:54 +00:00
func TestIssue683 ( t * testing . T ) {
// Step panics when source file can not be found
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue683" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-01-09 23:21:54 +00:00
_ , err := setFunctionBreakpoint ( p , "main.main" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "First Continue()" )
2017-01-09 23:21:54 +00:00
for i := 0 ; i < 20 ; i ++ {
// eventually an error about the source file not being found will be
// returned, the important thing is that we shouldn't panic
2017-04-21 06:55:53 +00:00
err := proc . Step ( p )
2017-01-09 23:21:54 +00:00
if err != nil {
break
}
}
2017-02-07 21:07:18 +00:00
} )
}
func TestIssue664 ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "issue664" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-07 21:07:18 +00:00
setFileBreakpoint ( p , t , fixture , 4 )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Next ( p ) , t , "Next()" )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 5 , "Did not continue to correct location," )
2017-01-09 23:21:54 +00:00
} )
}
2017-02-07 21:08:11 +00:00
// Benchmarks (*Processs).Continue + (*Scope).FunctionArguments
func BenchmarkTrace ( b * testing . B ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( b )
2017-04-21 07:50:38 +00:00
withTestProcess ( "traceperf" , b , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-07 21:08:11 +00:00
_ , err := setFunctionBreakpoint ( p , "main.PerfCheck" )
assertNoError ( err , b , "setFunctionBreakpoint()" )
b . ResetTimer ( )
for i := 0 ; i < b . N ; i ++ {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , b , "Continue()" )
s , err := proc . GoroutineScope ( p . CurrentThread ( ) )
2017-02-07 21:08:11 +00:00
assertNoError ( err , b , "Scope()" )
2017-04-21 06:55:53 +00:00
_ , err = s . FunctionArguments ( proc . LoadConfig { false , 0 , 64 , 0 , 3 } )
2017-02-07 21:08:11 +00:00
assertNoError ( err , b , "FunctionArguments()" )
}
b . StopTimer ( )
} )
}
2017-02-08 13:14:57 +00:00
func TestNextInDeferReturn ( t * testing . T ) {
// runtime.deferreturn updates the G struct in a way that for one
// instruction leaves the curg._defer field non-nil but with curg._defer.fn
// field being nil.
// We need to deal with this without panicing.
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-21 07:50:38 +00:00
withTestProcess ( "defercall" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-08 13:14:57 +00:00
_ , err := setFunctionBreakpoint ( p , "runtime.deferreturn" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "First Continue()" )
2017-02-08 13:14:57 +00:00
for i := 0 ; i < 20 ; i ++ {
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Next ( p ) , t , fmt . Sprintf ( "Next() %d" , i ) )
2017-02-08 13:14:57 +00:00
}
} )
}
2017-04-21 06:55:53 +00:00
func getg ( goid int , gs [ ] * proc . G ) * proc . G {
2017-02-08 13:14:57 +00:00
for _ , g := range gs {
if g . ID == goid {
return g
}
}
return nil
}
func TestStacktraceWithBarriers ( t * testing . T ) {
// Go's Garbage Collector will insert stack barriers into stacks.
// This stack barrier is inserted by overwriting the return address for the
// stack frame with the address of runtime.stackBarrier.
// The original return address is saved into the stkbar slice inside the G
// struct.
2017-02-21 18:38:13 +00:00
2017-03-13 17:53:16 +00:00
// In Go 1.9 stack barriers have been removed and this test must be disabled.
2017-05-04 10:22:08 +00:00
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major < 0 || ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) {
2017-03-13 17:53:16 +00:00
return
}
2017-02-21 18:38:13 +00:00
// In Go 1.8 stack barriers are not inserted by default, this enables them.
godebugOld := os . Getenv ( "GODEBUG" )
defer os . Setenv ( "GODEBUG" , godebugOld )
os . Setenv ( "GODEBUG" , "gcrescanstacks=1" )
2017-04-21 07:50:38 +00:00
withTestProcess ( "binarytrees" , t , func ( p proc . Process , fixture protest . Fixture ) {
2017-02-08 13:14:57 +00:00
// We want to get a user goroutine with a stack barrier, to get that we execute the program until runtime.gcInstallStackBarrier is executed AND the goroutine it was executed onto contains a call to main.bottomUpTree
_ , err := setFunctionBreakpoint ( p , "runtime.gcInstallStackBarrier" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
stackBarrierGoids := [ ] int { }
for len ( stackBarrierGoids ) == 0 {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
if _ , exited := err . ( proc . ProcessExitedError ) ; exited {
2017-02-21 18:38:13 +00:00
t . Logf ( "Could not run test" )
return
}
assertNoError ( err , t , "Continue()" )
2017-04-21 06:55:53 +00:00
gs , err := proc . GoroutinesInfo ( p )
2017-02-08 13:14:57 +00:00
assertNoError ( err , t , "GoroutinesInfo()" )
2017-02-10 14:11:40 +00:00
for _ , th := range p . ThreadList ( ) {
2017-09-25 06:29:13 +00:00
if bp := th . Breakpoint ( ) ; bp . Breakpoint == nil {
2017-02-08 13:14:57 +00:00
continue
}
2018-01-20 15:18:39 +00:00
goidVar := evalVariable ( p , t , "gp.goid" )
2017-02-08 13:14:57 +00:00
goid , _ := constant . Int64Val ( goidVar . Value )
if g := getg ( int ( goid ) , gs ) ; g != nil {
stack , err := g . Stacktrace ( 50 )
assertNoError ( err , t , fmt . Sprintf ( "Stacktrace(goroutine = %d)" , goid ) )
for _ , frame := range stack {
if frame . Current . Fn != nil && frame . Current . Fn . Name == "main.bottomUpTree" {
stackBarrierGoids = append ( stackBarrierGoids , int ( goid ) )
break
}
}
}
}
}
if len ( stackBarrierGoids ) == 0 {
t . Fatalf ( "Could not find a goroutine with stack barriers" )
}
t . Logf ( "stack barrier goids: %v\n" , stackBarrierGoids )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . StepOut ( p ) , t , "StepOut()" )
2017-02-08 13:14:57 +00:00
2017-04-21 06:55:53 +00:00
gs , err := proc . GoroutinesInfo ( p )
2017-02-08 13:14:57 +00:00
assertNoError ( err , t , "GoroutinesInfo()" )
for _ , goid := range stackBarrierGoids {
g := getg ( goid , gs )
stack , err := g . Stacktrace ( 200 )
assertNoError ( err , t , "Stacktrace()" )
// Check that either main.main or main.main.func1 appear in the
// stacktrace of this goroutine, if we failed at resolving stack barriers
// correctly the stacktrace will be truncated and neither main.main or
// main.main.func1 will appear
found := false
for _ , frame := range stack {
if frame . Current . Fn == nil {
continue
}
if name := frame . Current . Fn . Name ; name == "main.main" || name == "main.main.func1" {
found = true
}
}
t . Logf ( "Stacktrace for %d:\n" , goid )
for _ , frame := range stack {
name := "<>"
if frame . Current . Fn != nil {
name = frame . Current . Fn . Name
}
2017-10-05 07:26:19 +00:00
t . Logf ( "\t%s [CFA: %x Ret: %x] at %s:%d" , name , frame . Regs . CFA , frame . Ret , frame . Current . File , frame . Current . Line )
2017-02-08 13:14:57 +00:00
}
if ! found {
2017-06-29 18:15:59 +00:00
t . Logf ( "Truncated stacktrace for %d\n" , goid )
2017-02-08 13:14:57 +00:00
}
}
} )
}
2017-03-28 16:30:27 +00:00
func TestAttachDetach ( t * testing . T ) {
2017-02-10 14:11:40 +00:00
if testBackend == "lldb" && runtime . GOOS == "linux" {
bs , _ := ioutil . ReadFile ( "/proc/sys/kernel/yama/ptrace_scope" )
if bs == nil || strings . TrimSpace ( string ( bs ) ) != "0" {
t . Logf ( "can not run TestAttachDetach: %v\n" , bs )
return
}
2017-03-28 16:30:27 +00:00
}
2017-05-05 22:17:52 +00:00
if testBackend == "rr" {
return
}
2017-08-15 06:21:24 +00:00
fixture := protest . BuildFixture ( "testnextnethttp" , 0 )
2017-03-28 16:30:27 +00:00
cmd := exec . Command ( fixture . Path )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
assertNoError ( cmd . Start ( ) , t , "starting fixture" )
// wait for testnextnethttp to start listening
t0 := time . Now ( )
for {
conn , err := net . Dial ( "tcp" , "localhost:9191" )
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
if time . Since ( t0 ) > 10 * time . Second {
t . Fatal ( "fixture did not start" )
}
}
2017-04-21 07:50:38 +00:00
var p proc . Process
2017-02-10 14:11:40 +00:00
var err error
switch testBackend {
case "native" :
2017-04-21 06:55:53 +00:00
p , err = native . Attach ( cmd . Process . Pid )
2017-02-10 14:11:40 +00:00
case "lldb" :
path := ""
if runtime . GOOS == "darwin" {
path = fixture . Path
}
2017-04-21 06:55:53 +00:00
p , err = gdbserial . LLDBAttach ( cmd . Process . Pid , path )
2017-02-10 14:11:40 +00:00
default :
err = fmt . Errorf ( "unknown backend %q" , testBackend )
}
2017-03-28 16:30:27 +00:00
assertNoError ( err , t , "Attach" )
go func ( ) {
time . Sleep ( 1 * time . Second )
http . Get ( "http://localhost:9191" )
} ( )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 11 , "Did not continue to correct location," )
2017-03-28 16:30:27 +00:00
assertNoError ( p . Detach ( false ) , t , "Detach" )
resp , err := http . Get ( "http://localhost:9191/nobp" )
assertNoError ( err , t , "Page request after detach" )
bs , err := ioutil . ReadAll ( resp . Body )
assertNoError ( err , t , "Reading /nobp page" )
2017-06-29 18:15:59 +00:00
if out := string ( bs ) ; ! strings . Contains ( out , "hello, world!" ) {
2017-03-28 16:30:27 +00:00
t . Fatalf ( "/nobp page does not contain \"hello, world!\": %q" , out )
}
cmd . Process . Kill ( )
}
2017-04-25 17:42:41 +00:00
func TestVarSum ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-25 17:42:41 +00:00
withTestProcess ( "testvariables2" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
sumvar := evalVariable ( p , t , "s1[0] + s1[1]" )
2017-04-25 17:42:41 +00:00
sumvarstr := constant . StringVal ( sumvar . Value )
if sumvarstr != "onetwo" {
t . Fatalf ( "s1[0] + s1[1] == %q (expected \"onetwo\")" , sumvarstr )
}
if sumvar . Len != int64 ( len ( sumvarstr ) ) {
t . Fatalf ( "sumvar.Len == %d (expected %d)" , sumvar . Len , len ( sumvarstr ) )
}
} )
}
func TestPackageWithPathVar ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-04-25 17:42:41 +00:00
withTestProcess ( "pkgrenames" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
evalVariable ( p , t , "pkg.SomeVar" )
evalVariable ( p , t , "pkg.SomeVar.X" )
2017-04-25 17:42:41 +00:00
} )
}
2017-05-04 17:46:45 +00:00
func TestEnvironment ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2017-05-04 17:46:45 +00:00
os . Setenv ( "SOMEVAR" , "bah" )
withTestProcess ( "testenv" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , "x" )
2017-05-04 17:46:45 +00:00
vv := constant . StringVal ( v . Value )
t . Logf ( "v = %q" , vv )
if vv != "bah" {
t . Fatalf ( "value of v is %q (expected \"bah\")" , vv )
}
} )
}
2017-05-16 18:23:33 +00:00
func getFrameOff ( p proc . Process , t * testing . T ) int64 {
2018-01-20 15:18:39 +00:00
frameoffvar := evalVariable ( p , t , "runtime.frameoff" )
2017-05-16 18:23:33 +00:00
frameoff , _ := constant . Int64Val ( frameoffvar . Value )
return frameoff
}
func TestRecursiveNext ( t * testing . T ) {
protest . AllowRecording ( t )
testcases := [ ] nextTest {
{ 6 , 7 } ,
{ 7 , 10 } ,
{ 10 , 11 } ,
{ 11 , 17 } ,
}
testseq ( "increment" , contNext , testcases , "main.Increment" , t )
withTestProcess ( "increment" , t , func ( p proc . Process , fixture protest . Fixture ) {
bp , err := setFunctionBreakpoint ( p , "main.Increment" )
assertNoError ( err , t , "setFunctionBreakpoint" )
assertNoError ( proc . Continue ( p ) , t , "Continue" )
_ , err = p . ClearBreakpoint ( bp . Addr )
assertNoError ( err , t , "ClearBreakpoint" )
assertNoError ( proc . Next ( p ) , t , "Next 1" )
assertNoError ( proc . Next ( p ) , t , "Next 2" )
assertNoError ( proc . Next ( p ) , t , "Next 3" )
frameoff0 := getFrameOff ( p , t )
assertNoError ( proc . Step ( p ) , t , "Step" )
frameoff1 := getFrameOff ( p , t )
if frameoff0 == frameoff1 {
t . Fatalf ( "did not step into function?" )
}
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 6 , "program did not continue to expected location," )
2017-05-16 18:23:33 +00:00
assertNoError ( proc . Next ( p ) , t , "Next 4" )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 7 , "program did not continue to expected location," )
2017-05-16 18:23:33 +00:00
assertNoError ( proc . StepOut ( p ) , t , "StepOut" )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 11 , "program did not continue to expected location," )
2017-05-16 18:23:33 +00:00
frameoff2 := getFrameOff ( p , t )
if frameoff0 != frameoff2 {
t . Fatalf ( "frame offset mismatch %x != %x" , frameoff0 , frameoff2 )
}
} )
}
2017-07-18 18:57:41 +00:00
// TestIssue877 ensures that the environment variables starting with DYLD_ and LD_
// are passed when executing the binary on OSX via debugserver
func TestIssue877 ( t * testing . T ) {
if runtime . GOOS != "darwin" && testBackend == "lldb" {
return
}
const envval = "/usr/local/lib"
os . Setenv ( "DYLD_LIBRARY_PATH" , envval )
withTestProcess ( "issue877" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2018-01-20 15:18:39 +00:00
v := evalVariable ( p , t , "dyldenv" )
2017-07-18 18:57:41 +00:00
vv := constant . StringVal ( v . Value )
t . Logf ( "v = %q" , vv )
if vv != envval {
t . Fatalf ( "value of v is %q (expected %q)" , vv , envval )
}
} )
}
2017-06-23 12:22:26 +00:00
func TestIssue893 ( t * testing . T ) {
// Test what happens when next is called immediately after launching the
// executable, acceptable behaviors are: (a) no error, (b) no source at PC
2017-10-13 20:13:43 +00:00
// error, (c) program runs to completion
2017-06-23 12:22:26 +00:00
protest . AllowRecording ( t )
withTestProcess ( "increment" , t , func ( p proc . Process , fixture protest . Fixture ) {
err := proc . Next ( p )
if err == nil {
return
}
if _ , ok := err . ( * frame . NoFDEForPCError ) ; ok {
return
}
2017-07-24 08:58:12 +00:00
if _ , ok := err . ( proc . ThreadBlockedError ) ; ok {
return
2017-10-05 07:26:19 +00:00
}
if _ , ok := err . ( * proc . NoSourceForPCError ) ; ok {
return
2017-07-24 08:58:12 +00:00
}
2017-10-13 20:13:43 +00:00
if _ , ok := err . ( proc . ProcessExitedError ) ; ok {
return
}
2017-06-23 12:22:26 +00:00
assertNoError ( err , t , "Next" )
} )
}
func TestStepInstructionNoGoroutine ( t * testing . T ) {
protest . AllowRecording ( t )
withTestProcess ( "increment" , t , func ( p proc . Process , fixture protest . Fixture ) {
// Call StepInstruction immediately after launching the program, it should
// work even though no goroutine is selected.
assertNoError ( p . StepInstruction ( ) , t , "StepInstruction" )
} )
}
2017-06-13 08:38:45 +00:00
func TestIssue871 ( t * testing . T ) {
protest . AllowRecording ( t )
withTestProcess ( "issue871" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue" )
var scope * proc . EvalScope
var err error
if testBackend == "rr" {
var frame proc . Stackframe
frame , err = findFirstNonRuntimeFrame ( p )
if err == nil {
2018-02-13 17:20:45 +00:00
scope = proc . FrameToScope ( p . BinInfo ( ) , p . CurrentThread ( ) , nil , frame )
2017-06-13 08:38:45 +00:00
}
} else {
scope , err = proc . GoroutineScope ( p . CurrentThread ( ) )
}
assertNoError ( err , t , "scope" )
locals , err := scope . LocalVariables ( normalLoadConfig )
assertNoError ( err , t , "LocalVariables" )
foundA , foundB := false , false
for _ , v := range locals {
t . Logf ( "local %v" , v )
switch v . Name {
case "a" :
foundA = true
if v . Flags & proc . VariableEscaped == 0 {
t . Errorf ( "variable a not flagged as escaped" )
}
case "b" :
foundB = true
}
}
if ! foundA {
t . Errorf ( "variable a not found" )
}
if ! foundB {
t . Errorf ( "variable b not found" )
}
} )
}
2017-05-04 14:35:31 +00:00
func TestShadowedFlag ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) {
return
}
withTestProcess ( "testshadow" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue" )
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
assertNoError ( err , t , "GoroutineScope" )
locals , err := scope . LocalVariables ( normalLoadConfig )
assertNoError ( err , t , "LocalVariables" )
foundShadowed := false
foundNonShadowed := false
for _ , v := range locals {
if v . Flags & proc . VariableShadowed != 0 {
if v . Name != "a" {
t . Errorf ( "wrong shadowed variable %s" , v . Name )
}
foundShadowed = true
if n , _ := constant . Int64Val ( v . Value ) ; n != 0 {
t . Errorf ( "wrong value for shadowed variable a: %d" , n )
}
} else {
if v . Name != "a" {
t . Errorf ( "wrong non-shadowed variable %s" , v . Name )
}
foundNonShadowed = true
if n , _ := constant . Int64Val ( v . Value ) ; n != 1 {
t . Errorf ( "wrong value for non-shadowed variable a: %d" , n )
}
}
}
if ! foundShadowed {
t . Error ( "could not find any shadowed variable" )
}
if ! foundNonShadowed {
t . Error ( "could not find any non-shadowed variable" )
}
} )
}
2017-08-15 06:21:24 +00:00
func TestAttachStripped ( t * testing . T ) {
if testBackend == "lldb" && runtime . GOOS == "linux" {
bs , _ := ioutil . ReadFile ( "/proc/sys/kernel/yama/ptrace_scope" )
if bs == nil || strings . TrimSpace ( string ( bs ) ) != "0" {
t . Logf ( "can not run TestAttachStripped: %v\n" , bs )
return
}
}
if testBackend == "rr" {
return
}
if runtime . GOOS == "darwin" {
t . Log ( "-s does not produce stripped executables on macOS" )
return
}
fixture := protest . BuildFixture ( "testnextnethttp" , protest . LinkStrip )
cmd := exec . Command ( fixture . Path )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
assertNoError ( cmd . Start ( ) , t , "starting fixture" )
// wait for testnextnethttp to start listening
t0 := time . Now ( )
for {
conn , err := net . Dial ( "tcp" , "localhost:9191" )
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
if time . Since ( t0 ) > 10 * time . Second {
t . Fatal ( "fixture did not start" )
}
}
var p proc . Process
var err error
switch testBackend {
case "native" :
p , err = native . Attach ( cmd . Process . Pid )
case "lldb" :
path := ""
if runtime . GOOS == "darwin" {
path = fixture . Path
}
p , err = gdbserial . LLDBAttach ( cmd . Process . Pid , path )
default :
t . Fatalf ( "unknown backend %q" , testBackend )
}
t . Logf ( "error is %v" , err )
if err == nil {
p . Detach ( true )
t . Fatalf ( "expected error after attach, got nothing" )
} else {
cmd . Process . Kill ( )
}
os . Remove ( fixture . Path )
}
2017-09-25 06:29:13 +00:00
func TestIssue844 ( t * testing . T ) {
// Conditional breakpoints should not prevent next from working if their
// condition isn't met.
withTestProcess ( "nextcond" , t , func ( p proc . Process , fixture protest . Fixture ) {
setFileBreakpoint ( p , t , fixture , 9 )
condbp := setFileBreakpoint ( p , t , fixture , 10 )
condbp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "n" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "11" } ,
}
assertNoError ( proc . Continue ( p ) , t , "Continue" )
assertNoError ( proc . Next ( p ) , t , "Next" )
2018-01-20 15:18:39 +00:00
assertLineNumber ( p , t , 10 , "continued to wrong location," )
2017-09-25 06:29:13 +00:00
} )
}
2017-09-01 13:34:13 +00:00
func logStacktrace ( t * testing . T , frames [ ] proc . Stackframe ) {
for j := range frames {
name := "?"
if frames [ j ] . Current . Fn != nil {
name = frames [ j ] . Current . Fn . Name
}
t . Logf ( "\t%#x %#x %#x %s at %s:%d\n" , frames [ j ] . Call . PC , frames [ j ] . FrameOffset ( ) , frames [ j ] . FramePointerOffset ( ) , name , filepath . Base ( frames [ j ] . Call . File ) , frames [ j ] . Call . Line )
}
}
// stacktraceCheck checks that all the functions listed in tc appear in
// frames in the same order.
// Checks that all the functions in tc starting with "C." or with "!" are in
// a systemstack frame.
// Returns a slice m where m[i] is the index in frames of the function tc[i]
// or nil if any check fails.
func stacktraceCheck ( t * testing . T , tc [ ] string , frames [ ] proc . Stackframe ) [ ] int {
m := make ( [ ] int , len ( tc ) )
i , j := 0 , 0
for i < len ( tc ) {
tcname := tc [ i ]
tcsystem := strings . HasPrefix ( tcname , "C." )
if tcname [ 0 ] == '!' {
tcsystem = true
tcname = tcname [ 1 : ]
}
for j < len ( frames ) {
name := "?"
if frames [ j ] . Current . Fn != nil {
name = frames [ j ] . Current . Fn . Name
}
if name == tcname {
m [ i ] = j
if tcsystem != frames [ j ] . SystemStack {
t . Logf ( "system stack check failed for frame %d (expected %v got %v)" , j , tcsystem , frames [ j ] . SystemStack )
t . Logf ( "expected: %v\n" , tc )
return nil
}
break
}
j ++
}
if j >= len ( frames ) {
t . Logf ( "couldn't find frame %d %s" , i , tc )
t . Logf ( "expected: %v\n" , tc )
return nil
}
i ++
}
return m
}
func frameInFile ( frame proc . Stackframe , file string ) bool {
for _ , loc := range [ ] proc . Location { frame . Current , frame . Call } {
if ! strings . HasSuffix ( loc . File , "/" + file ) && ! strings . HasSuffix ( loc . File , "\\" + file ) {
return false
}
if loc . Line <= 0 {
return false
}
}
return true
}
func TestCgoStacktrace ( t * testing . T ) {
if runtime . GOOS == "windows" {
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) {
t . Skip ( "disabled on windows with go before version 1.9" )
}
}
if runtime . GOOS == "darwin" {
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 8 , - 1 , 0 , 0 , "" } ) {
t . Skip ( "disabled on macOS with go before version 1.8" )
}
}
// Tests that:
// a) we correctly identify the goroutine while we are executing cgo code
// b) that we can stitch together the system stack (where cgo code
// executes) and the normal goroutine stack
// Each test case describes how the stack trace should appear after a
// continue. The first function on each test case is the topmost function
// that should be found on the stack, the actual stack trace can have more
// frame than those listed here but all the frames listed must appear in
// the specified order.
testCases := [ ] [ ] string {
[ ] string { "main.main" } ,
[ ] string { "C.helloworld_pt2" , "C.helloworld" , "main.main" } ,
[ ] string { "main.helloWorldS" , "main.helloWorld" , "C.helloworld_pt2" , "C.helloworld" , "main.main" } ,
[ ] string { "C.helloworld_pt4" , "C.helloworld_pt3" , "main.helloWorldS" , "main.helloWorld" , "C.helloworld_pt2" , "C.helloworld" , "main.main" } ,
[ ] string { "main.helloWorld2" , "C.helloworld_pt4" , "C.helloworld_pt3" , "main.helloWorldS" , "main.helloWorld" , "C.helloworld_pt2" , "C.helloworld" , "main.main" } }
var gid int
frameOffs := map [ string ] int64 { }
framePointerOffs := map [ string ] int64 { }
withTestProcess ( "cgostacktest/" , t , func ( p proc . Process , fixture protest . Fixture ) {
for itidx , tc := range testCases {
assertNoError ( proc . Continue ( p ) , t , fmt . Sprintf ( "Continue at iteration step %d" , itidx ) )
g , err := proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , fmt . Sprintf ( "GetG at iteration step %d" , itidx ) )
if itidx == 0 {
gid = g . ID
} else {
if gid != g . ID {
t . Fatalf ( "wrong goroutine id at iteration step %d (expected %d got %d)" , itidx , gid , g . ID )
}
}
frames , err := g . Stacktrace ( 100 )
assertNoError ( err , t , fmt . Sprintf ( "Stacktrace at iteration step %d" , itidx ) )
t . Logf ( "iteration step %d" , itidx )
logStacktrace ( t , frames )
m := stacktraceCheck ( t , tc , frames )
mismatch := ( m == nil )
for i , j := range m {
if strings . HasPrefix ( tc [ i ] , "C.hellow" ) {
if ! frameInFile ( frames [ j ] , "hello.c" ) {
t . Logf ( "position in %q is %s:%d (call %s:%d)" , tc [ i ] , frames [ j ] . Current . File , frames [ j ] . Current . Line , frames [ j ] . Call . File , frames [ j ] . Call . Line )
mismatch = true
break
}
}
if frameOff , ok := frameOffs [ tc [ i ] ] ; ok {
if frameOff != frames [ j ] . FrameOffset ( ) {
t . Logf ( "frame %s offset mismatch" , tc [ i ] )
}
if framePointerOffs [ tc [ i ] ] != frames [ j ] . FramePointerOffset ( ) {
t . Logf ( "frame %s pointer offset mismatch" , tc [ i ] )
}
} else {
frameOffs [ tc [ i ] ] = frames [ j ] . FrameOffset ( )
framePointerOffs [ tc [ i ] ] = frames [ j ] . FramePointerOffset ( )
}
}
// also check that ThreadStacktrace produces the same list of frames
threadFrames , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 100 )
assertNoError ( err , t , fmt . Sprintf ( "ThreadStacktrace at iteration step %d" , itidx ) )
if len ( threadFrames ) != len ( frames ) {
mismatch = true
} else {
for j := range frames {
if frames [ j ] . Current . File != threadFrames [ j ] . Current . File || frames [ j ] . Current . Line != threadFrames [ j ] . Current . Line {
t . Logf ( "stack mismatch between goroutine stacktrace and thread stacktrace" )
t . Logf ( "thread stacktrace:" )
logStacktrace ( t , threadFrames )
mismatch = true
break
}
}
}
if mismatch {
t . Fatal ( "see previous loglines" )
}
}
} )
}
func TestCgoSources ( t * testing . T ) {
if runtime . GOOS == "windows" {
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) {
t . Skip ( "disabled on windows with go before version 1.9" )
}
}
withTestProcess ( "cgostacktest/" , t , func ( p proc . Process , fixture protest . Fixture ) {
sources := p . BinInfo ( ) . Sources
for _ , needle := range [ ] string { "main.go" , "hello.c" } {
found := false
for _ , k := range sources {
if strings . HasSuffix ( k , "/" + needle ) || strings . HasSuffix ( k , "\\" + needle ) {
found = true
break
}
}
if ! found {
t . Errorf ( "File %s not found" , needle )
}
}
} )
}
func TestSystemstackStacktrace ( t * testing . T ) {
// check that we can follow a stack switch initiated by runtime.systemstack()
withTestProcess ( "panic" , t , func ( p proc . Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "runtime.startpanic_m" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
assertNoError ( proc . Continue ( p ) , t , "first continue" )
assertNoError ( proc . Continue ( p ) , t , "second continue" )
g , err := proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG" )
frames , err := g . Stacktrace ( 100 )
assertNoError ( err , t , "stacktrace" )
logStacktrace ( t , frames )
2018-01-04 14:46:51 +00:00
m := stacktraceCheck ( t , [ ] string { "!runtime.startpanic_m" , "runtime.startpanic" , "main.main" } , frames )
if m == nil {
t . Fatal ( "see previous loglines" )
}
} )
}
func TestSystemstackOnRuntimeNewstack ( t * testing . T ) {
// The bug being tested here manifests as follows:
// - set a breakpoint somewhere or interrupt the program with Ctrl-C
// - try to look at stacktraces of other goroutines
// If one of the other goroutines is resizing its own stack the stack
// command won't work for it.
withTestProcess ( "binarytrees" , t , func ( p proc . Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.main" )
assertNoError ( err , t , "setFunctionBreakpoint(main.main)" )
assertNoError ( proc . Continue ( p ) , t , "first continue" )
2018-01-19 14:42:23 +00:00
2018-01-04 14:46:51 +00:00
g , err := proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG" )
2018-01-19 14:42:23 +00:00
mainGoroutineID := g . ID
_ , err = setFunctionBreakpoint ( p , "runtime.newstack" )
assertNoError ( err , t , "setFunctionBreakpoint(runtime.newstack)" )
for {
assertNoError ( proc . Continue ( p ) , t , "second continue" )
g , err = proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG" )
if g . ID == mainGoroutineID {
break
}
}
2018-01-04 14:46:51 +00:00
frames , err := g . Stacktrace ( 100 )
assertNoError ( err , t , "stacktrace" )
logStacktrace ( t , frames )
m := stacktraceCheck ( t , [ ] string { "!runtime.newstack" , "main.main" } , frames )
2017-09-01 13:34:13 +00:00
if m == nil {
t . Fatal ( "see previous loglines" )
}
} )
}
2017-12-04 10:05:05 +00:00
func TestIssue1034 ( t * testing . T ) {
// The external linker on macOS produces an abbrev for DW_TAG_subprogram
// without the "has children" flag, we should support this.
withTestProcess ( "cgostacktest/" , t , func ( p proc . Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.main" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
frames , err := p . SelectedGoroutine ( ) . Stacktrace ( 10 )
assertNoError ( err , t , "Stacktrace" )
2018-02-13 17:20:45 +00:00
scope := proc . FrameToScope ( p . BinInfo ( ) , p . CurrentThread ( ) , nil , frames [ 2 ] )
2017-12-04 10:05:05 +00:00
args , _ := scope . FunctionArguments ( normalLoadConfig )
assertNoError ( err , t , "FunctionArguments()" )
if len ( args ) > 0 {
t . Fatalf ( "wrong number of arguments for frame %v (%d)" , frames [ 2 ] , len ( args ) )
}
} )
}
2017-12-04 10:03:17 +00:00
func TestIssue1008 ( t * testing . T ) {
// The external linker on macOS inserts "end of sequence" extended opcodes
// in debug_line. which we should support correctly.
withTestProcess ( "cgostacktest/" , t , func ( p proc . Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.main" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
loc , err := p . CurrentThread ( ) . Location ( )
assertNoError ( err , t , "CurrentThread().Location()" )
t . Logf ( "location %v\n" , loc )
if ! strings . HasSuffix ( loc . File , "/main.go" ) {
t . Errorf ( "unexpected location %s:%d\n" , loc . File , loc . Line )
}
if loc . Line > 31 {
t . Errorf ( "unexpected location %s:%d (file only has 30 lines)\n" , loc . File , loc . Line )
}
} )
}
2017-09-08 10:31:03 +00:00
func TestDeclLine ( t * testing . T ) {
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major > 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
t . Skip ( "go 1.9 and prior versions do not emit DW_AT_decl_line" )
}
withTestProcess ( "decllinetest" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue" )
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
assertNoError ( err , t , "GoroutineScope (1)" )
vars , err := scope . LocalVariables ( normalLoadConfig )
assertNoError ( err , t , "LocalVariables (1)" )
if len ( vars ) != 1 {
t . Fatalf ( "wrong number of variables %d" , len ( vars ) )
}
assertNoError ( proc . Continue ( p ) , t , "Continue" )
scope , err = proc . GoroutineScope ( p . CurrentThread ( ) )
assertNoError ( err , t , "GoroutineScope (2)" )
scope . LocalVariables ( normalLoadConfig )
vars , err = scope . LocalVariables ( normalLoadConfig )
assertNoError ( err , t , "LocalVariables (2)" )
if len ( vars ) != 2 {
t . Fatalf ( "wrong number of variables %d" , len ( vars ) )
}
} )
}
2018-03-03 11:28:39 +00:00
func TestIssue1137 ( t * testing . T ) {
withTestProcess ( "dotpackagesiface" , t , func ( p proc . Process , fixture protest . Fixture ) {
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
v := evalVariable ( p , t , "iface" )
assertNoError ( v . Unreadable , t , "iface unreadable" )
v2 := evalVariable ( p , t , "iface2" )
assertNoError ( v2 . Unreadable , t , "iface2 unreadable" )
} )
}
2018-01-29 10:07:38 +00:00
func TestIssue1101 ( t * testing . T ) {
// If a breakpoint is hit close to process death on a thread that isn't the
// group leader the process could die while we are trying to stop it.
//
// This can be easily reproduced by having the goroutine that's executing
// main.main (which will almost always run on the thread group leader) wait
// for a second goroutine before exiting, then setting a breakpoint on the
// second goroutine and stepping through it (see TestIssue1101 in
// proc_test.go).
//
// When stepping over the return instruction of main.f the deferred
// wg.Done() call will be executed which will cause the main goroutine to
// resume and proceed to exit. Both the temporary breakpoint on wg.Done and
// the temporary breakpoint on the return address of main.f will be in
// close proximity to main.main calling os.Exit() and causing the death of
// the thread group leader.
withTestProcess ( "issue1101" , t , func ( p proc . Process , fixture protest . Fixture ) {
_ , err := setFunctionBreakpoint ( p , "main.f" )
assertNoError ( err , t , "setFunctionBreakpoint()" )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Next ( p ) , t , "Next() 1" )
assertNoError ( proc . Next ( p ) , t , "Next() 2" )
lastCmd := "Next() 3"
exitErr := proc . Next ( p )
if exitErr == nil {
lastCmd = "final Continue()"
exitErr = proc . Continue ( p )
}
if pexit , exited := exitErr . ( proc . ProcessExitedError ) ; exited {
if pexit . Status != 2 && testBackend != "lldb" {
// looks like there's a bug with debugserver on macOS that sometimes
// will report exit status 0 instead of the proper exit status.
t . Fatalf ( "process exited status %d (expected 2)" , pexit . Status )
}
} else {
assertNoError ( exitErr , t , lastCmd )
t . Fatalf ( "process did not exit after %s" , lastCmd )
}
} )
}
2018-03-07 10:14:34 +00:00
func TestIssue1145 ( t * testing . T ) {
withTestProcess ( "issue1145" , t , func ( p proc . Process , fixture protest . Fixture ) {
setFileBreakpoint ( p , t , fixture , 12 )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
resumeChan := make ( chan struct { } , 1 )
p . ResumeNotify ( resumeChan )
go func ( ) {
<- resumeChan
time . Sleep ( 100 * time . Millisecond )
p . RequestManualStop ( )
} ( )
assertNoError ( proc . Next ( p ) , t , "Next()" )
if p . Breakpoints ( ) . HasInternalBreakpoints ( ) {
t . Fatal ( "has internal breakpoints after manual stop request" )
}
} )
}