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"
2019-05-08 21:06:38 +00:00
"strconv"
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
2019-01-04 18:39:25 +00:00
"github.com/go-delve/delve/pkg/dwarf/frame"
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/gdbserial"
"github.com/go-delve/delve/pkg/proc/native"
protest "github.com/go-delve/delve/pkg/proc/test"
2014-06-25 19:06:04 +00:00
)
2014-05-30 15:12:18 +00:00
2018-10-29 11:22:03 +00:00
var normalLoadConfig = proc . LoadConfig { true , 1 , 64 , 64 , - 1 , 0 }
2018-05-29 15:01:51 +00:00
var testBackend , buildMode 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" )
2018-05-29 15:01:51 +00:00
flag . StringVar ( & buildMode , "test-buildmode" , "" , "selects build mode" )
var logConf string
flag . StringVar ( & logConf , "log" , "" , "configures logging" )
2017-02-10 14:11:40 +00:00
flag . Parse ( )
2018-10-01 08:19:06 +00:00
protest . DefaultTestBackend ( & testBackend )
2018-05-29 15:01:51 +00:00
if buildMode != "" && buildMode != "pie" {
fmt . Fprintf ( os . Stderr , "unknown build mode %q" , buildMode )
os . Exit ( 1 )
}
2019-03-27 21:58:36 +00:00
logflags . Setup ( logConf != "" , logConf , "" )
2015-06-21 03:47:44 +00:00
os . Exit ( protest . RunTestsWithFixtures ( m ) )
2015-05-04 13:31:50 +00:00
}
2020-01-21 20:41:24 +00:00
func withTestProcess ( name string , t testing . TB , fn func ( p * proc . Target , 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
}
2020-01-21 20:41:24 +00:00
func withTestProcessArgs ( name string , t testing . TB , wd string , args [ ] string , buildFlags protest . BuildFlags , fn func ( p * proc . Target , fixture protest . Fixture ) ) {
2018-05-29 15:01:51 +00:00
if buildMode == "pie" {
buildFlags |= protest . BuildModePIE
}
2018-01-20 15:18:39 +00:00
fixture := protest . BuildFixture ( name , buildFlags )
2020-01-21 20:41:24 +00:00
var p * proc . Target
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" :
2018-11-07 22:21:35 +00:00
p , err = native . Launch ( append ( [ ] string { fixture . Path } , args ... ) , wd , false , [ ] string { } )
2017-02-10 14:11:40 +00:00
case "lldb" :
2018-11-07 22:21:35 +00:00
p , err = gdbserial . LLDBLaunch ( append ( [ ] string { fixture . Path } , args ... ) , wd , false , [ ] string { } )
2017-05-05 22:17:52 +00:00
case "rr" :
protest . MustHaveRecordingAllowed ( t )
t . Log ( "recording" )
2018-11-07 22:21:35 +00:00
p , tracedir , err = gdbserial . RecordAndReplay ( append ( [ ] string { fixture . Path } , args ... ) , wd , true , [ ] string { } )
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 )
2016-04-21 21:20:38 +00:00
} ( )
fn ( p , fixture )
}
2020-01-21 20:41:24 +00:00
func getRegisters ( p * proc . Target , 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
}
}
2020-01-21 20:41:24 +00:00
func currentPC ( p * proc . Target , 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
}
2020-01-21 20:41:24 +00:00
func currentLineNumber ( p * proc . Target , 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
}
2020-01-21 20:41:24 +00:00
func assertLineNumber ( p * proc . Target , t * testing . T , lineno int , descr string ) ( string , int ) {
2018-01-20 15:18:39 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "continuetestprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2018-08-31 18:08:18 +00:00
pe , ok := err . ( proc . ErrProcessExited )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "continuetestprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.sayhi" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "First Continue()" )
2019-08-14 15:57:05 +00:00
err := proc . Continue ( p )
2018-08-31 18:08:18 +00:00
pe , ok := err . ( proc . ErrProcessExited )
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 )
}
} )
}
2020-01-21 20:41:24 +00:00
func setFunctionBreakpoint ( p * proc . Target , t testing . TB , fname string ) * proc . Breakpoint {
2019-08-14 15:57:05 +00:00
_ , f , l , _ := runtime . Caller ( 1 )
f = filepath . Base ( f )
2019-11-01 19:41:06 +00:00
addrs , err := proc . FindFunctionLocation ( p , fname , 0 )
2015-08-07 16:50:14 +00:00
if err != nil {
2019-08-14 15:57:05 +00:00
t . Fatalf ( "%s:%d: FindFunctionLocation(%s): %v" , f , l , fname , err )
2015-08-07 16:50:14 +00:00
}
2019-11-01 19:41:06 +00:00
if len ( addrs ) != 1 {
t . Fatalf ( "%s:%d: setFunctionBreakpoint(%s): too many results %v" , f , l , fname , addrs )
}
bp , err := p . SetBreakpoint ( addrs [ 0 ] , proc . UserBreakpoint , nil )
2019-08-14 15:57:05 +00:00
if err != nil {
t . Fatalf ( "%s:%d: FindFunctionLocation(%s): %v" , f , l , fname , err )
}
return bp
2015-08-07 16:50:14 +00:00
}
2020-01-23 16:55:08 +00:00
func setFileBreakpoint ( p * proc . Target , t testing . TB , path string , lineno int ) * proc . Breakpoint {
2019-08-14 15:57:05 +00:00
_ , f , l , _ := runtime . Caller ( 1 )
f = filepath . Base ( f )
2019-05-08 21:06:38 +00:00
2019-11-01 19:41:06 +00:00
addrs , err := proc . FindFileLocation ( p , path , lineno )
2016-04-14 09:58:59 +00:00
if err != nil {
2019-08-14 15:57:05 +00:00
t . Fatalf ( "%s:%d: FindFileLocation(%s, %d): %v" , f , l , path , lineno , err )
2016-04-14 09:58:59 +00:00
}
2019-11-01 19:41:06 +00:00
if len ( addrs ) != 1 {
2020-01-09 11:10:02 +00:00
t . Fatalf ( "%s:%d: setFileLineBreakpoint(%s, %d): too many (or not enough) results %v" , f , l , path , lineno , addrs )
2019-11-01 19:41:06 +00:00
}
bp , err := p . SetBreakpoint ( addrs [ 0 ] , proc . UserBreakpoint , nil )
2016-04-14 09:58:59 +00:00
if err != nil {
2019-08-14 15:57:05 +00:00
t . Fatalf ( "%s:%d: SetBreakpoint: %v" , f , l , err )
2016-04-14 09:58:59 +00:00
}
return bp
}
2020-01-21 20:41:24 +00:00
func findFunctionLocation ( p * proc . Target , t * testing . T , fnname string ) uint64 {
2019-11-01 19:41:06 +00:00
_ , f , l , _ := runtime . Caller ( 1 )
f = filepath . Base ( f )
addrs , err := proc . FindFunctionLocation ( p , fnname , 0 )
2019-08-14 15:57:05 +00:00
if err != nil {
t . Fatalf ( "%s:%d: FindFunctionLocation(%s): %v" , f , l , fnname , err )
}
2019-11-01 19:41:06 +00:00
if len ( addrs ) != 1 {
t . Fatalf ( "%s:%d: FindFunctionLocation(%s): too many results %v" , f , l , fnname , addrs )
}
return addrs [ 0 ]
2019-08-14 15:57:05 +00:00
}
2020-01-21 20:41:24 +00:00
func findFileLocation ( p * proc . Target , t * testing . T , file string , lineno int ) uint64 {
2019-11-01 19:41:06 +00:00
_ , f , l , _ := runtime . Caller ( 1 )
f = filepath . Base ( f )
addrs , err := proc . FindFileLocation ( p , file , lineno )
2019-08-14 15:57:05 +00:00
if err != nil {
t . Fatalf ( "%s:%d: FindFileLocation(%s, %d): %v" , f , l , file , lineno , err )
}
2019-11-01 19:41:06 +00:00
if len ( addrs ) != 1 {
t . Fatalf ( "%s:%d: FindFileLocation(%s, %d): too many results %v" , f , l , file , lineno , addrs )
}
return addrs [ 0 ]
2019-08-14 15:57:05 +00:00
}
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "loopprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.loop" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2020-01-21 20:41:24 +00:00
if p , ok := p . Process . ( * native . Process ) ; ok {
2017-04-21 06:55:53 +00:00
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.
2020-01-21 20:41:24 +00:00
if p , ok := p . Process . ( * native . Process ) ; ok {
2017-04-21 06:55:53 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.helloworld" )
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
2019-08-14 15:57:05 +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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.helloworld" )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testthreads" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.anotherthread" )
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testprog" , t , func ( p * proc . Target , 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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.sleepytime" )
2014-05-30 15:12:18 +00:00
2019-08-14 15:57:05 +00:00
_ , err := p . ClearBreakpoint ( bp . Addr )
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
2020-01-21 20:41:24 +00:00
func countBreakpoints ( p * proc . Target ) 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 {
2019-11-01 19:41:06 +00:00
if bp . LogicalID >= 0 {
2016-03-06 17:54:43 +00:00
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
2019-05-08 21:06:38 +00:00
pos interface { }
2018-01-20 15:18:39 +00:00
}
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 )
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( program , t , wd , args , buildFlags , func ( p * proc . Target , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
var bp * proc . Breakpoint
2016-07-26 13:34:31 +00:00
if initialLocation != "" {
2019-08-14 15:57:05 +00:00
bp = setFunctionBreakpoint ( p , t , initialLocation )
2018-01-20 15:18:39 +00:00
} else if testcases [ 0 ] . cf == contContinue {
2019-08-14 15:57:05 +00:00
bp = setFileBreakpoint ( p , t , fixture . Source , testcases [ 0 ] . pos . ( int ) )
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
}
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 )
2019-05-08 21:06:38 +00:00
fmt . Printf ( "at %#x %s:%d" , pc , f , ln )
2018-01-20 15:18:39 +00:00
}
2019-05-08 21:06:38 +00:00
switch pos := tc . pos . ( type ) {
case int :
if ln != pos {
t . Fatalf ( "Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)" , pos , filepath . Base ( f ) , ln , pc , i )
}
case string :
v := strings . Split ( pos , ":" )
tgtln , _ := strconv . Atoi ( v [ 1 ] )
if ! strings . HasSuffix ( f , v [ 0 ] ) || ( ln != tgtln ) {
t . Fatalf ( "Program did not continue to correct next location, expected %s was %s:%d (%#x) (testcase %d)" , 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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2015-08-20 14:28:11 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "parallel_next" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.sayhi" )
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 )
2019-08-14 15:57:05 +00:00
_ , err := p . ClearBreakpoint ( bp . Addr )
2015-11-18 09:07:08 +00:00
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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2015-11-18 09:07:08 +00:00
// 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "parallel_next" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.sayhi" )
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 } ,
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "testnextnethttp" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2015-07-16 00:57:54 +00:00
go func ( ) {
// Wait for program to start listening.
for {
2019-07-30 15:38:25 +00:00
conn , err := net . Dial ( "tcp" , "127.0.0.1:9191" )
2015-07-16 00:57:54 +00:00
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
}
2019-07-30 15:38:25 +00:00
http . Get ( "http://127.0.0.1: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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testruntimebreakpoint" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testnextprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 24 )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testreturnaddress" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2015-08-10 13:55:57 +00:00
fnName := "runtime.rt0_go"
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , fnName )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testnextprog" , t , func ( p * proc . Target , 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" )
}
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.main" )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgotest" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.main" )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Next ( p ) , t , "Next()" )
2015-08-28 08:06:22 +00:00
} )
}
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "stacktraceprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.stacktraceme" )
2015-06-17 17:11:57 +00:00
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "retstack" , t , func ( p * proc . Target , 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 ) {
2018-03-22 17:02:15 +00:00
mainStack := [ ] loc { { 14 , "main.stacktraceme" } , { 29 , "main.main" } }
2018-03-25 08:04:32 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
mainStack [ 0 ] . line = 15
}
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "goroutinestackprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.stacktraceme" )
2015-06-17 17:11:57 +00:00
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2015-06-17 17:11:57 +00:00
2018-11-19 14:18:10 +00:00
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
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 {
2019-09-25 17:21:20 +00:00
locations , err := g . Stacktrace ( 40 , 0 )
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
}
2019-11-12 14:58:54 +00:00
t . Logf ( "\t%s:%d %s (%#x) %x %v\n" , locations [ i ] . Call . File , locations [ i ] . Call . Line , name , locations [ i ] . Current . PC , locations [ i ] . FrameOffset ( ) , locations [ i ] . SystemStack )
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
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "testprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-05-05 17:04:32 +00:00
if err := p . Detach ( true ) ; err != nil {
t . Fatal ( err )
}
2018-06-21 11:07:37 +00:00
if valid , _ := p . Valid ( ) ; valid {
2017-05-05 17:04:32 +00:00
t . Fatal ( "expected process to have exited" )
}
if runtime . GOOS == "linux" {
2019-11-27 07:39:17 +00:00
if runtime . GOARCH == "arm64" {
//there is no any sync between signal sended(tracee handled) and open /proc/%d/. It may fail on arm64
return
}
2017-05-05 17:04:32 +00:00
_ , 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
2020-01-21 20:41:24 +00:00
func testGSupportFunc ( name string , t * testing . T , p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.main" )
2015-07-23 17:08:28 +00:00
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testprog" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgotest" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "integrationprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp1 := setFunctionBreakpoint ( p , t , "main.main" )
bp2 := setFunctionBreakpoint ( p , t , "main.sayhi" )
2015-07-09 16:41:03 +00:00
mainCount := 0
sayhiCount := 0
for {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2018-06-21 11:07:37 +00:00
if valid , _ := p . Valid ( ) ; ! valid {
2015-07-09 16:41:03 +00:00
break
}
assertNoError ( err , t , "Continue()" )
2019-11-01 19:41:06 +00:00
if bp := p . CurrentThread ( ) . Breakpoint ( ) ; bp . LogicalID == bp1 . LogicalID {
2015-07-09 16:41:03 +00:00
mainCount ++
}
2019-11-01 19:41:06 +00:00
if bp := p . CurrentThread ( ) . Breakpoint ( ) ; bp . LogicalID == bp2 . LogicalID {
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "sigchldprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2018-08-31 18:08:18 +00:00
_ , ok := err . ( proc . ErrProcessExited )
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "is sue239" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 17 )
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
2020-01-21 20:41:24 +00:00
func findFirstNonRuntimeFrame ( p * proc . Target ) ( proc . Stackframe , error ) {
2017-05-05 22:17:52 +00:00
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" )
}
2020-01-21 20:41:24 +00:00
func evalVariableOrError ( p * proc . Target , 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
}
2020-01-21 20:41:24 +00:00
func evalVariable ( p * proc . Target , t testing . TB , symbol string ) * proc . Variable {
2018-01-20 15:18:39 +00:00
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
}
2020-01-21 20:41:24 +00:00
func setVariable ( p * proc . Target , 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 } ,
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "goroutinestackprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.stacktraceme" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2015-10-18 17:37:13 +00:00
2018-04-13 14:43:56 +00:00
t . Logf ( "stopped on thread %d, goroutine: %#v" , p . CurrentThread ( ) . ThreadID ( ) , p . SelectedGoroutine ( ) )
2015-11-10 09:49:47 +00:00
// Testing evaluation on goroutines
2018-11-19 14:18:10 +00:00
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
2015-10-18 17:37:13 +00:00
assertNoError ( err , t , "GoroutinesInfo" )
found := make ( [ ] bool , 10 )
for _ , g := range gs {
frame := - 1
2019-09-25 17:21:20 +00:00
frames , err := g . Stacktrace ( 10 , 0 )
2017-02-16 13:16:00 +00:00
if err != nil {
t . Logf ( "could not stacktrace goroutine %d: %v\n" , g . ID , err )
continue
}
2020-02-11 01:31:54 +00:00
t . Logf ( "Goroutine %d %#v" , g . ID , g . Thread )
2018-07-06 07:37:31 +00:00
logStacktrace ( t , p . BinInfo ( ) , frames )
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
}
2018-07-10 10:15:11 +00:00
scope , err := proc . ConvertEvalScope ( p , g . ID , frame , 0 )
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 ++ {
2018-07-10 10:15:11 +00:00
scope , err := proc . ConvertEvalScope ( p , g . ID , i + 1 , 0 )
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "bpcountstest" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 12 )
2015-10-02 16:17:07 +00:00
for {
2017-04-21 06:55:53 +00:00
if err := proc . Continue ( p ) ; err != nil {
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; 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 ) )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , b , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "bpcountstest" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 12 )
2015-11-19 15:19:42 +00:00
for {
2017-04-21 06:55:53 +00:00
if err := proc . Continue ( p ) ; err != nil {
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; 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 ) )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , b , func ( p * proc . Target , 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 ) ) )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , b , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , b , func ( p * proc . Target , 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 ++ {
2020-01-21 20:41:24 +00:00
p . ClearAllGCache ( )
2018-11-19 14:18:10 +00:00
_ , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue262" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 11 )
2015-11-10 09:49:47 +00:00
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2019-08-14 15:57:05 +00:00
err := proc . Continue ( p )
2015-11-10 09:49:47 +00:00
if err == nil {
t . Fatalf ( "No error on second continue" )
}
2018-08-31 18:08:18 +00:00
_ , exited := err . ( proc . ErrProcessExited )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue305" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 5 )
2016-01-16 08:13:15 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables" , b , func ( p * proc . Target , 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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "parallel_next" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 9 )
2016-01-12 08:01:42 +00:00
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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "parallel_next" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 9 )
2016-01-12 08:01:42 +00:00
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "nonexistentvariable" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "7" } ,
}
2019-08-14 15:57:05 +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 {
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; ! 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststep" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue384" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 13 )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue332" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 8 )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue332" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 8 )
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 ( )
2019-08-14 15:57:05 +00:00
pcAfterPrologue := findFunctionLocation ( p , t , "main.changeMe" )
2019-06-25 20:50:05 +00:00
if pcAfterPrologue == p . BinInfo ( ) . LookupFunc [ "main.changeMe" ] . Entry {
2016-02-12 07:43:22 +00:00
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 )
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; ! 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 ) {
2019-06-30 17:34:47 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 13 ) {
// CL 161337 in Go 1.13 and later removes the autogenerated init function
// https://go-review.googlesource.com/c/go/+/161337
t . Skip ( "no autogenerated init function in Go 1.13 or later" )
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "callme" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
findFunctionLocation ( p , t , "main.init" )
2016-02-11 07:18:39 +00:00
} )
}
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "math" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 9 )
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 {
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables" , t , func ( p * proc . Target , 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 {
2018-05-29 15:01:51 +00:00
if v . Unreadable != nil && v . Unreadable . Error ( ) != "no location attribute Location" {
2016-02-21 16:26:13 +00:00
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
2020-01-21 20:41:24 +00:00
withTestProcess ( "break" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
findFileLocation ( p , t , fixture . Source , 8 )
2016-02-25 09:48:42 +00:00
} )
}
2016-03-06 17:54:43 +00:00
func TestPanicBreakpoint ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "panic" , t , func ( p * proc . Target , 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 ) {
2020-01-21 20:41:24 +00:00
expectSuccess := func ( p * proc . Target , 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
}
2018-08-31 18:08:18 +00:00
exit , exited := err . ( proc . ErrProcessExited )
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
}
}
}
2020-01-21 20:41:24 +00:00
expectPanic := func ( p * proc . Target , 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
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "testnextnethttp" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2016-03-18 08:32:17 +00:00
go func ( ) {
// Wait for program to start listening.
for {
2019-07-30 15:38:25 +00:00
conn , err := net . Dial ( "tcp" , "127.0.0.1:9191" )
2016-03-18 08:32:17 +00:00
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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "parallel_next" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.sayhi" )
2016-04-11 11:50:01 +00:00
// 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 )
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; exited {
2016-04-11 11:50:01 +00:00
t . Log ( "could not find parked goroutine" )
return
}
assertNoError ( err , t , "Continue()" )
2018-11-19 14:18:10 +00:00
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
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
}
2019-09-25 17:21:20 +00:00
frames , _ := g . Stacktrace ( 5 , 0 )
2017-08-14 05:57:49 +00:00
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
}
}
}
2020-01-21 20:41:24 +00:00
assertNoError ( p . SwitchGoroutine ( parkedg ) , t , "SwitchGoroutine()" )
2016-04-11 11:50:01 +00:00
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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "parallel_next" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.sayhi" )
2016-04-13 13:25:23 +00:00
// 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 )
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; exited {
2016-04-13 13:25:23 +00:00
t . Log ( "could not find parked goroutine" )
return
}
assertNoError ( err , t , "Continue()" )
2018-11-19 14:18:10 +00:00
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
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 )
2019-09-25 17:21:20 +00:00
frames , _ := parkedg . Stacktrace ( 20 , 0 )
2017-02-16 17:22:09 +00:00
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 )
}
2020-01-21 20:41:24 +00:00
assertNoError ( p . SwitchGoroutine ( parkedg ) , t , "SwitchGoroutine()" )
2016-04-13 13:25:23 +00:00
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" )
2018-10-01 08:19:06 +00:00
defer os . Remove ( exepath )
var err error
switch testBackend {
case "native" :
2018-11-07 22:21:35 +00:00
_ , err = native . Launch ( [ ] string { exepath } , "." , false , [ ] string { } )
2018-10-01 08:19:06 +00:00
case "lldb" :
2018-11-07 22:21:35 +00:00
_ , err = gdbserial . LLDBLaunch ( [ ] string { exepath } , "." , false , [ ] string { } )
2018-10-01 08:19:06 +00:00
default :
t . Skip ( "test not valid for this backend" )
}
2016-04-21 10:19:21 +00:00
if err == nil {
t . Fatalf ( "expected error but none was generated" )
}
2018-08-31 18:08:18 +00:00
if err != proc . ErrNotExecutable {
t . Fatalf ( "expected error \"%v\" got \"%v\"" , proc . ErrNotExecutable , err )
2016-04-21 10:19:21 +00:00
}
}
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
2020-01-21 20:41:24 +00:00
var p * proc . Target
2018-10-01 08:19:06 +00:00
switch testBackend {
case "native" :
2018-11-07 22:21:35 +00:00
p , err = native . Launch ( [ ] string { outfile } , "." , false , [ ] string { } )
2018-10-01 08:19:06 +00:00
case "lldb" :
2018-11-07 22:21:35 +00:00
p , err = gdbserial . LLDBLaunch ( [ ] string { outfile } , "." , false , [ ] string { } )
2018-10-01 08:19:06 +00:00
default :
t . Skip ( "test not valid for this backend" )
}
2016-04-21 10:19:21 +00:00
switch err {
2018-08-31 18:08:18 +00:00
case proc . ErrUnsupportedLinuxArch , proc . ErrUnsupportedWindowsArch , proc . ErrUnsupportedDarwinArch :
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue573" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.foo" )
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-06-25 20:50:05 +00:00
addrEntry := p . BinInfo ( ) . LookupFunc [ "main.main" ] . Entry
2019-08-14 15:57:05 +00:00
addrPrologue := findFunctionLocation ( p , t , "main.main" )
2016-09-06 17:27:07 +00:00
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
2018-03-20 12:37:07 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
testseq ( "defercall" , contNext , [ ] nextTest {
{ 15 , 16 } ,
{ 16 , 17 } ,
{ 17 , 18 } ,
{ 18 , 6 } } , "main.callAndPanic2" , t )
} else {
testseq ( "defercall" , contNext , [ ] nextTest {
{ 15 , 16 } ,
{ 16 , 17 } ,
{ 17 , 18 } ,
{ 18 , 5 } } , "main.callAndPanic2" , t )
}
2016-07-14 07:43:39 +00:00
}
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.
2018-03-20 12:37:07 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 9 , 10 } ,
{ 10 , 6 } ,
{ 6 , 7 } ,
{ 7 , 11 } } , "" , t )
} else {
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 9 , 10 } ,
{ 10 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 11 } } , "" , t )
}
2016-07-26 13:34:31 +00:00
}
func TestStepReturnAndPanic ( t * testing . T ) {
// Tests that Step works correctly when returning from functions
// and when a deferred function is called when panic'ing.
2018-03-20 12:37:07 +00:00
switch {
case goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) :
testseq ( "defercall" , contStep , [ ] nextTest {
{ 17 , 6 } ,
{ 6 , 7 } ,
{ 7 , 18 } ,
{ 18 , 6 } ,
{ 6 , 7 } } , "" , t )
case goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 10 ) :
testseq ( "defercall" , contStep , [ ] nextTest {
{ 17 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } ,
{ 7 , 18 } ,
{ 18 , 5 } ,
{ 5 , 6 } ,
{ 6 , 7 } } , "" , t )
case goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 9 ) :
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 )
2018-03-20 12:37:07 +00:00
default :
2017-05-03 07:10:12 +00:00
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.
2018-03-20 12:37:07 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
testseq ( "defercall" , contStep , [ ] nextTest {
{ 11 , 6 } ,
{ 6 , 7 } ,
{ 7 , 12 } ,
{ 12 , 13 } ,
{ 13 , 6 } ,
{ 6 , 7 } ,
{ 7 , 13 } ,
{ 13 , 28 } } , "" , t )
} else {
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 )
}
2016-07-26 13:34:31 +00:00
}
func TestStepIgnorePrivateRuntime ( t * testing . T ) {
// Tests that Step will ignore calls to private runtime functions
// (such as runtime.convT2E in this case)
2018-03-20 12:37:07 +00:00
switch {
case goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) :
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 21 , 14 } ,
{ 14 , 15 } ,
{ 15 , 22 } } , "" , t )
case goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 10 ) :
2016-07-26 13:34:31 +00:00
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 21 , 13 } ,
{ 13 , 14 } ,
{ 14 , 15 } ,
2018-03-20 12:37:07 +00:00
{ 15 , 22 } } , "" , t )
case goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 7 ) :
2017-10-13 20:13:43 +00:00
testseq ( "teststepprog" , contStep , [ ] nextTest {
{ 21 , 13 } ,
{ 13 , 14 } ,
{ 14 , 15 } ,
2018-03-20 12:37:07 +00:00
{ 15 , 14 } ,
{ 14 , 17 } ,
{ 17 , 22 } } , "" , t )
default :
2016-07-26 13:34:31 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue561" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 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
} )
}
2020-01-18 08:18:44 +00:00
func TestGoroutineLables ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "goroutineLabels" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2020-01-18 08:18:44 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
g , err := proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG()" )
2020-01-20 20:02:12 +00:00
if len ( g . Labels ( ) ) != 0 {
2020-01-18 08:18:44 +00:00
t . Fatalf ( "No labels expected" )
}
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
g , err = proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG()" )
2020-01-20 20:02:12 +00:00
labels := g . Labels ( )
if v := labels [ "k1" ] ; v != "v1" {
2020-01-18 08:18:44 +00:00
t . Errorf ( "Unexpected label value k1=%v" , v )
}
2020-01-20 20:02:12 +00:00
if v := labels [ "k2" ] ; v != "v2" {
2020-01-18 08:18:44 +00:00
t . Errorf ( "Unexpected label value k2=%v" , v )
}
} )
}
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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 37 )
2016-07-26 13:34:31 +00:00
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2019-08-14 15:57:05 +00:00
_ , err := p . ClearBreakpoint ( bp . Addr )
2016-07-26 13:34:31 +00:00
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 ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 24 )
2016-07-26 13:34:31 +00:00
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 )
2018-08-31 18:08:18 +00:00
_ , exited := err . ( proc . ErrProcessExited )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testnextdefer" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 9 )
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
}
2019-11-16 13:43:44 +00:00
var maxInstructionLength uint64
2017-04-21 06:55:53 +00:00
2016-07-26 13:34:31 +00:00
func TestStepOnCallPtrInstr ( t * testing . T ) {
2017-05-05 22:17:52 +00:00
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststepprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 10 )
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
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 ( )
2019-11-28 12:54:39 +00:00
text , err := proc . Disassemble ( p . CurrentThread ( ) , regs , p . Breakpoints ( ) , p . BinInfo ( ) , pc , pc + uint64 ( p . BinInfo ( ) . Arch . MaxInstructionLength ( ) ) )
2016-07-26 13:34:31 +00:00
assertNoError ( err , t , "Disassemble()" )
if text [ 0 ] . IsCall ( ) {
found = true
break
}
2020-01-21 20:41:24 +00:00
assertNoError ( proc . StepInstruction ( p ) , t , "StepInstruction()" )
2016-07-26 13:34:31 +00:00
}
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-03-20 12:37:07 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
assertLineNumber ( p , t , 6 , "Step continued to wrong line," )
} else {
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue594" , t , func ( p * proc . Target , 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-03-20 12:37:07 +00:00
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
testseq2 ( t , "defercall" , "" , [ ] seqTest {
{ contContinue , 17 } ,
{ contStepout , 6 } } )
} else {
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 )
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "workdir" , t , wd , [ ] string { } , 0 , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 14 )
2017-04-21 06:55:53 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue683" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.main" )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue664" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "traceperf" , b , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , b , "main.PerfCheck" )
2017-02-07 21:08:11 +00:00
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()" )
2018-10-29 11:22:03 +00:00
_ , err = s . FunctionArguments ( proc . LoadConfig { false , 0 , 64 , 0 , 3 , 0 } )
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "defercall" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "runtime.deferreturn" )
2017-04-21 06:55:53 +00:00
assertNoError ( proc . Continue ( p ) , t , "First Continue()" )
2018-03-20 11:34:02 +00:00
// Set a breakpoint on the deferred function so that the following loop
// can not step out of the runtime.deferreturn and all the way to the
// point where the target program panics.
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.sampleFunction" )
2017-02-08 13:14:57 +00:00
for i := 0 ; i < 20 ; i ++ {
2018-03-20 11:34:02 +00:00
loc , err := p . CurrentThread ( ) . Location ( )
assertNoError ( err , t , "CurrentThread().Location()" )
t . Logf ( "at %#x %s:%d" , loc . PC , loc . File , loc . Line )
if loc . Fn != nil && loc . Fn . Name == "main.sampleFunction" {
break
}
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" )
2020-01-21 20:41:24 +00:00
withTestProcess ( "binarytrees" , t , func ( p * proc . Target , 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
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "runtime.gcInstallStackBarrier" )
2017-02-08 13:14:57 +00:00
stackBarrierGoids := [ ] int { }
for len ( stackBarrierGoids ) == 0 {
2017-04-21 06:55:53 +00:00
err := proc . Continue ( p )
2018-08-31 18:08:18 +00:00
if _ , exited := err . ( proc . ErrProcessExited ) ; exited {
2017-02-21 18:38:13 +00:00
t . Logf ( "Could not run test" )
return
}
assertNoError ( err , t , "Continue()" )
2018-11-19 14:18:10 +00:00
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
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 {
2019-09-25 17:21:20 +00:00
stack , err := g . Stacktrace ( 50 , 0 )
2017-02-08 13:14:57 +00:00
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
2018-11-19 14:18:10 +00:00
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
2017-02-08 13:14:57 +00:00
assertNoError ( err , t , "GoroutinesInfo()" )
for _ , goid := range stackBarrierGoids {
g := getg ( goid , gs )
2019-09-25 17:21:20 +00:00
stack , err := g . Stacktrace ( 200 , 0 )
2017-02-08 13:14:57 +00:00
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
}
2018-05-29 15:01:51 +00:00
var buildFlags protest . BuildFlags
if buildMode == "pie" {
buildFlags |= protest . BuildModePIE
}
fixture := protest . BuildFixture ( "testnextnethttp" , buildFlags )
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 {
2019-07-30 15:38:25 +00:00
conn , err := net . Dial ( "tcp" , "127.0.0.1:9191" )
2017-03-28 16:30:27 +00:00
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
if time . Since ( t0 ) > 10 * time . Second {
t . Fatal ( "fixture did not start" )
}
}
2020-01-21 20:41:24 +00:00
var p * proc . Target
2017-02-10 14:11:40 +00:00
var err error
switch testBackend {
case "native" :
2018-11-07 22:21:35 +00:00
p , err = native . Attach ( cmd . Process . Pid , [ ] string { } )
2017-02-10 14:11:40 +00:00
case "lldb" :
path := ""
if runtime . GOOS == "darwin" {
path = fixture . Path
}
2018-11-07 22:21:35 +00:00
p , err = gdbserial . LLDBAttach ( cmd . Process . Pid , path , [ ] string { } )
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 )
2019-07-30 15:38:25 +00:00
http . Get ( "http://127.0.0.1:9191" )
2017-03-28 16:30:27 +00:00
} ( )
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" )
2019-07-30 15:38:25 +00:00
resp , err := http . Get ( "http://127.0.0.1:9191/nobp" )
2017-03-28 16:30:27 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-04-25 17:42:41 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "pkgrenames" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-04-25 17:42:41 +00:00
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" )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testenv" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-05-04 17:46:45 +00:00
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
2020-01-21 20:41:24 +00:00
func getFrameOff ( p * proc . Target , 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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "increment" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFunctionBreakpoint ( p , t , "main.Increment" )
2017-05-16 18:23:33 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2019-08-14 15:57:05 +00:00
_ , err := p . ClearBreakpoint ( bp . Addr )
2017-05-16 18:23:33 +00:00
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
}
2018-08-01 15:39:13 +00:00
if os . Getenv ( "TRAVIS" ) == "true" && runtime . GOOS == "darwin" {
// Something changed on Travis side that makes the Go compiler fail if
// DYLD_LIBRARY_PATH is set.
t . Skip ( "broken" )
}
2017-07-18 18:57:41 +00:00
const envval = "/usr/local/lib"
os . Setenv ( "DYLD_LIBRARY_PATH" , envval )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue877" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-07-18 18:57:41 +00:00
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 )
2020-01-21 20:41:24 +00:00
withTestProcess ( "increment" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-06-23 12:22:26 +00:00
err := proc . Next ( p )
if err == nil {
return
}
2018-08-31 18:08:18 +00:00
if _ , ok := err . ( * frame . ErrNoFDEForPC ) ; ok {
2017-06-23 12:22:26 +00:00
return
}
2018-08-31 18:08:18 +00:00
if _ , ok := err . ( proc . ErrThreadBlocked ) ; ok {
2017-07-24 08:58:12 +00:00
return
2017-10-05 07:26:19 +00:00
}
2018-08-31 18:08:18 +00:00
if _ , ok := err . ( * proc . ErrNoSourceForPC ) ; ok {
2017-10-05 07:26:19 +00:00
return
2017-07-24 08:58:12 +00:00
}
2018-08-31 18:08:18 +00:00
if _ , ok := err . ( proc . ErrProcessExited ) ; ok {
2017-10-13 20:13:43 +00:00
return
}
2017-06-23 12:22:26 +00:00
assertNoError ( err , t , "Next" )
} )
}
func TestStepInstructionNoGoroutine ( t * testing . T ) {
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "increment" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-06-23 12:22:26 +00:00
// Call StepInstruction immediately after launching the program, it should
// work even though no goroutine is selected.
2020-01-21 20:41:24 +00:00
assertNoError ( proc . StepInstruction ( p ) , t , "StepInstruction" )
2017-06-23 12:22:26 +00:00
} )
}
2017-06-13 08:38:45 +00:00
func TestIssue871 ( t * testing . T ) {
protest . AllowRecording ( t )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue871" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-06-13 08:38:45 +00:00
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
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "testshadow" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-05-04 14:35:31 +00:00
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
}
2018-05-29 15:01:51 +00:00
if buildMode != "" {
t . Skip ( "not enabled with buildmode=PIE" )
}
2017-08-15 06:21:24 +00:00
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 {
2019-07-30 15:38:25 +00:00
conn , err := net . Dial ( "tcp" , "127.0.0.1:9191" )
2017-08-15 06:21:24 +00:00
if err == nil {
conn . Close ( )
break
}
time . Sleep ( 50 * time . Millisecond )
if time . Since ( t0 ) > 10 * time . Second {
t . Fatal ( "fixture did not start" )
}
}
2020-01-21 20:41:24 +00:00
var p * proc . Target
2017-08-15 06:21:24 +00:00
var err error
switch testBackend {
case "native" :
2018-11-07 22:21:35 +00:00
p , err = native . Attach ( cmd . Process . Pid , [ ] string { } )
2017-08-15 06:21:24 +00:00
case "lldb" :
path := ""
if runtime . GOOS == "darwin" {
path = fixture . Path
}
2018-11-07 22:21:35 +00:00
p , err = gdbserial . LLDBAttach ( cmd . Process . Pid , path , [ ] string { } )
2017-08-15 06:21:24 +00:00
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.
2020-01-21 20:41:24 +00:00
withTestProcess ( "nextcond" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 9 )
condbp := setFileBreakpoint ( p , t , fixture . Source , 10 )
2017-09-25 06:29:13 +00:00
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
2018-07-06 07:37:31 +00:00
func logStacktrace ( t * testing . T , bi * proc . BinaryInfo , frames [ ] proc . Stackframe ) {
2017-09-01 13:34:13 +00:00
for j := range frames {
name := "?"
if frames [ j ] . Current . Fn != nil {
name = frames [ j ] . Current . Fn . Name
}
2019-12-30 09:42:20 +00:00
if frames [ j ] . Call . Fn != nil && frames [ j ] . Current . Fn != frames [ j ] . Call . Fn {
name = fmt . Sprintf ( "%s inlined in %s" , frames [ j ] . Call . Fn . Name , frames [ j ] . Current . Fn . Name )
}
2017-09-01 13:34:13 +00:00
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 )
2018-07-06 07:37:31 +00:00
if frames [ j ] . TopmostDefer != nil {
f , l , fn := bi . PCToLine ( frames [ j ] . TopmostDefer . DeferredPC )
fnname := ""
if fn != nil {
fnname = fn . Name
}
t . Logf ( "\t\ttopmost defer: %#x %s at %s:%d\n" , frames [ j ] . TopmostDefer . DeferredPC , fnname , f , l )
}
for deferIdx , _defer := range frames [ j ] . Defers {
f , l , fn := bi . PCToLine ( _defer . DeferredPC )
fnname := ""
if fn != nil {
fnname = fn . Name
}
t . Logf ( "\t\t%d defer: %#x %s at %s:%d\n" , deferIdx , _defer . DeferredPC , fnname , f , l )
}
2017-09-01 13:34:13 +00:00
}
}
// 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 { }
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgostacktest/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-09-01 13:34:13 +00:00
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 )
}
}
2019-09-25 17:21:20 +00:00
frames , err := g . Stacktrace ( 100 , 0 )
2017-09-01 13:34:13 +00:00
assertNoError ( err , t , fmt . Sprintf ( "Stacktrace at iteration step %d" , itidx ) )
t . Logf ( "iteration step %d" , itidx )
2018-07-06 07:37:31 +00:00
logStacktrace ( t , p . BinInfo ( ) , frames )
2017-09-01 13:34:13 +00:00
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:" )
2018-07-06 07:37:31 +00:00
logStacktrace ( t , p . BinInfo ( ) , threadFrames )
2017-09-01 13:34:13 +00:00
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" )
}
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgostacktest/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-09-01 13:34:13 +00:00
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()
2020-01-21 20:41:24 +00:00
withTestProcess ( "panic" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "runtime.startpanic_m" )
2017-09-01 13:34:13 +00:00
assertNoError ( proc . Continue ( p ) , t , "first continue" )
assertNoError ( proc . Continue ( p ) , t , "second continue" )
g , err := proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG" )
2019-09-25 17:21:20 +00:00
frames , err := g . Stacktrace ( 100 , 0 )
2017-09-01 13:34:13 +00:00
assertNoError ( err , t , "stacktrace" )
2018-07-06 07:37:31 +00:00
logStacktrace ( t , p . BinInfo ( ) , frames )
2018-03-20 11:34:02 +00:00
m := stacktraceCheck ( t , [ ] string { "!runtime.startpanic_m" , "runtime.gopanic" , "main.main" } , frames )
2018-01-04 14:46:51 +00:00
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.
2020-01-21 20:41:24 +00:00
withTestProcess ( "binarytrees" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.main" )
2018-01-04 14:46:51 +00:00
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
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "runtime.newstack" )
2018-01-19 14:42:23 +00:00
for {
assertNoError ( proc . Continue ( p ) , t , "second continue" )
g , err = proc . GetG ( p . CurrentThread ( ) )
assertNoError ( err , t , "GetG" )
if g . ID == mainGoroutineID {
break
}
}
2019-09-25 17:21:20 +00:00
frames , err := g . Stacktrace ( 100 , 0 )
2018-01-04 14:46:51 +00:00
assertNoError ( err , t , "stacktrace" )
2018-07-06 07:37:31 +00:00
logStacktrace ( t , p . BinInfo ( ) , frames )
2018-01-04 14:46:51 +00:00
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.
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgostacktest/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.main" )
2017-12-04 10:05:05 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2019-09-25 17:21:20 +00:00
frames , err := p . SelectedGoroutine ( ) . Stacktrace ( 10 , 0 )
2017-12-04 10:05:05 +00:00
assertNoError ( err , t , "Stacktrace" )
2018-04-14 13:44:39 +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.
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgostacktest/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.main" )
2017-12-04 10:03:17 +00:00
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" )
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "decllinetest" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-09-08 10:31:03 +00:00
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "dotpackagesiface" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-03-03 11:28:39 +00:00
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.
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1101" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.f" )
2018-01-29 10:07:38 +00:00
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 )
}
2018-08-31 18:08:18 +00:00
if pexit , exited := exitErr . ( proc . ErrProcessExited ) ; exited {
2018-01-29 10:07:38 +00:00
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 ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "sleep" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 18 )
2018-03-07 10:14:34 +00:00
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" )
}
} )
}
2017-10-26 11:37:19 +00:00
func TestDisassembleGlobalVars ( t * testing . T ) {
2019-11-08 14:17:57 +00:00
if runtime . GOARCH == "arm64" {
2019-11-28 13:12:59 +00:00
t . Skip ( "On ARM64 symLookup can't look up variables due to how they are loaded, see issue #1778" )
2019-11-08 14:17:57 +00:00
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2017-10-26 11:37:19 +00:00
mainfn := p . BinInfo ( ) . LookupFunc [ "main.main" ]
2019-08-08 18:54:56 +00:00
regs , _ := p . CurrentThread ( ) . Registers ( false )
text , err := proc . Disassemble ( p . CurrentThread ( ) , regs , p . Breakpoints ( ) , p . BinInfo ( ) , mainfn . Entry , mainfn . End )
2017-10-26 11:37:19 +00:00
assertNoError ( err , t , "Disassemble" )
found := false
for i := range text {
if strings . Index ( text [ i ] . Text ( proc . IntelFlavour , p . BinInfo ( ) ) , "main.v" ) > 0 {
found = true
break
}
}
if ! found {
t . Fatalf ( "could not find main.v reference in disassembly" )
}
} )
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
func checkFrame ( frame proc . Stackframe , fnname , file string , line int , inlined bool ) error {
if frame . Call . Fn == nil || frame . Call . Fn . Name != fnname {
return fmt . Errorf ( "wrong function name: %s" , fnname )
}
2019-12-30 09:42:20 +00:00
if file != "" {
if frame . Call . File != file || frame . Call . Line != line {
return fmt . Errorf ( "wrong file:line %s:%d" , frame . Call . File , frame . Call . Line )
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
}
if frame . Inlined != inlined {
if inlined {
return fmt . Errorf ( "not inlined" )
} else {
return fmt . Errorf ( "inlined" )
}
}
return nil
}
2019-06-28 02:39:15 +00:00
func TestAllPCsForFileLines ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "testinline" , t , "." , [ ] string { } , protest . EnableInlining , func ( p * proc . Target , fixture protest . Fixture ) {
2019-06-28 02:39:15 +00:00
l2pcs := p . BinInfo ( ) . AllPCsForFileLines ( fixture . Source , [ ] int { 7 , 20 } )
if len ( l2pcs ) != 2 {
t . Fatalf ( "expected two map entries for %s:{%d,%d} (got %d: %v)" , fixture . Source , 7 , 20 , len ( l2pcs ) , l2pcs )
}
pcs := l2pcs [ 20 ]
if len ( pcs ) < 1 {
t . Fatalf ( "expected at least one location for %s:%d (got %d: %#x)" , fixture . Source , 20 , len ( pcs ) , pcs )
}
pcs = l2pcs [ 7 ]
if len ( pcs ) < 2 {
t . Fatalf ( "expected at least two locations for %s:%d (got %d: %#x)" , fixture . Source , 7 , len ( pcs ) , pcs )
}
} )
}
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
func TestInlinedStacktraceAndVariables ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
firstCallCheck := & scopeCheck {
line : 7 ,
ok : false ,
varChecks : [ ] varCheck {
varCheck {
name : "a" ,
typ : "int" ,
kind : reflect . Int ,
hasVal : true ,
intVal : 3 ,
} ,
varCheck {
name : "z" ,
typ : "int" ,
kind : reflect . Int ,
hasVal : true ,
intVal : 9 ,
} ,
} ,
}
secondCallCheck := & scopeCheck {
line : 7 ,
ok : false ,
varChecks : [ ] varCheck {
varCheck {
name : "a" ,
typ : "int" ,
kind : reflect . Int ,
hasVal : true ,
intVal : 4 ,
} ,
varCheck {
name : "z" ,
typ : "int" ,
kind : reflect . Int ,
hasVal : true ,
intVal : 16 ,
} ,
} ,
}
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "testinline" , t , "." , [ ] string { } , protest . EnableInlining , func ( p * proc . Target , fixture protest . Fixture ) {
2019-11-01 19:41:06 +00:00
pcs , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 7 )
assertNoError ( err , t , "LineToPC" )
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
if len ( pcs ) < 2 {
2019-06-28 02:39:15 +00:00
t . Fatalf ( "expected at least two locations for %s:%d (got %d: %#x)" , fixture . Source , 7 , len ( pcs ) , pcs )
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
}
for _ , pc := range pcs {
2018-03-25 08:04:32 +00:00
t . Logf ( "setting breakpoint at %#x\n" , pc )
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
_ , err := p . SetBreakpoint ( pc , proc . UserBreakpoint , nil )
assertNoError ( err , t , fmt . Sprintf ( "SetBreakpoint(%#x)" , pc ) )
}
// first inlined call
assertNoError ( proc . Continue ( p ) , t , "Continue" )
frames , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 20 )
assertNoError ( err , t , "ThreadStacktrace" )
t . Logf ( "Stacktrace:\n" )
for i := range frames {
2018-03-25 08:04:32 +00:00
t . Logf ( "\t%s at %s:%d (%#x)\n" , frames [ i ] . Call . Fn . Name , frames [ i ] . Call . File , frames [ i ] . Call . Line , frames [ i ] . Current . PC )
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
}
if err := checkFrame ( frames [ 0 ] , "main.inlineThis" , fixture . Source , 7 , true ) ; err != nil {
t . Fatalf ( "Wrong frame 0: %v" , err )
}
if err := checkFrame ( frames [ 1 ] , "main.main" , fixture . Source , 18 , false ) ; err != nil {
t . Fatalf ( "Wrong frame 1: %v" , err )
}
if avar , _ := constant . Int64Val ( evalVariable ( p , t , "a" ) . Value ) ; avar != 3 {
t . Fatalf ( "value of 'a' variable is not 3 (%d)" , avar )
}
if zvar , _ := constant . Int64Val ( evalVariable ( p , t , "z" ) . Value ) ; zvar != 9 {
t . Fatalf ( "value of 'z' variable is not 9 (%d)" , zvar )
}
if _ , ok := firstCallCheck . checkLocalsAndArgs ( p , t ) ; ! ok {
t . Fatalf ( "exiting for past errors" )
}
// second inlined call
assertNoError ( proc . Continue ( p ) , t , "Continue" )
frames , err = proc . ThreadStacktrace ( p . CurrentThread ( ) , 20 )
assertNoError ( err , t , "ThreadStacktrace (2)" )
t . Logf ( "Stacktrace 2:\n" )
for i := range frames {
2018-03-25 08:04:32 +00:00
t . Logf ( "\t%s at %s:%d (%#x)\n" , frames [ i ] . Call . Fn . Name , frames [ i ] . Call . File , frames [ i ] . Call . Line , frames [ i ] . Current . PC )
proc: support inlining
Go 1.10 added inlined calls to debug_info, this commit adds support
for DW_TAG_inlined_call to delve, both for stack traces (where
inlined calls will appear as normal stack frames) and to correct
the behavior of next, step and stepout.
The calls to Next and Frame of stackIterator continue to work
unchanged and only return real stack frames, after reading each line
appendInlinedCalls is called to unpacked all the inlined calls that
involve the current PC.
The fake stack frames produced by appendInlinedCalls are
distinguished from real stack frames by having the Inlined attribute
set to true. Also their Current and Call locations are treated
differently. The Call location will be changed to represent the
position inside the inlined call, while the Current location will
always reference the real stack frame. This is done because:
* next, step and stepout need to access the debug_info entry of
the real function they are stepping through
* we are already manipulating Call in different ways while Current
is just what we read from the call stack
The strategy remains mostly the same, we disassemble the function
and we set a breakpoint on each instruction corresponding to a
different file:line. The function in question will be the one
corresponding to the first real (i.e. non-inlined) stack frame.
* If the current function contains inlined calls, 'next' will not
set any breakpoints on instructions that belong to inlined calls. We
do not do this for 'step'.
* If we are inside an inlined call that makes other inlined
functions, 'next' will not set any breakpoints that belong to
inlined calls that are children of the current inlined call.
* If the current function is inlined the breakpoint on the return
address won't be set, because inlined frames don't have a return
address.
* The code we use for stepout doesn't work at all if we are inside
an inlined call, instead we call 'next' but instruct it to remove
all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
}
if err := checkFrame ( frames [ 0 ] , "main.inlineThis" , fixture . Source , 7 , true ) ; err != nil {
t . Fatalf ( "Wrong frame 0: %v" , err )
}
if err := checkFrame ( frames [ 1 ] , "main.main" , fixture . Source , 19 , false ) ; err != nil {
t . Fatalf ( "Wrong frame 1: %v" , err )
}
if avar , _ := constant . Int64Val ( evalVariable ( p , t , "a" ) . Value ) ; avar != 4 {
t . Fatalf ( "value of 'a' variable is not 3 (%d)" , avar )
}
if zvar , _ := constant . Int64Val ( evalVariable ( p , t , "z" ) . Value ) ; zvar != 16 {
t . Fatalf ( "value of 'z' variable is not 9 (%d)" , zvar )
}
if bvar , err := evalVariableOrError ( p , "b" ) ; err == nil {
t . Fatalf ( "expected error evaluating 'b', but it succeeded instead: %v" , bvar )
}
if _ , ok := secondCallCheck . checkLocalsAndArgs ( p , t ) ; ! ok {
t . Fatalf ( "exiting for past errors" )
}
} )
}
func TestInlineStep ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
testseq2Args ( "." , [ ] string { } , protest . EnableInlining , t , "testinline" , "" , [ ] seqTest {
{ contContinue , 18 } ,
{ contStep , 6 } ,
{ contStep , 7 } ,
{ contStep , 18 } ,
{ contStep , 19 } ,
} )
}
func TestInlineNext ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
testseq2Args ( "." , [ ] string { } , protest . EnableInlining , t , "testinline" , "" , [ ] seqTest {
{ contContinue , 18 } ,
{ contStep , 6 } ,
{ contNext , 7 } ,
{ contNext , 18 } ,
{ contNext , 19 } ,
} )
}
func TestInlineStepOver ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
testseq2Args ( "." , [ ] string { } , protest . EnableInlining , t , "testinline" , "" , [ ] seqTest {
{ contContinue , 18 } ,
{ contNext , 19 } ,
{ contNext , 20 } ,
} )
}
func TestInlineStepOut ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
testseq2Args ( "." , [ ] string { } , protest . EnableInlining , t , "testinline" , "" , [ ] seqTest {
{ contContinue , 18 } ,
{ contStep , 6 } ,
{ contStepout , 18 } ,
} )
}
2018-04-15 12:11:27 +00:00
2018-08-07 01:08:25 +00:00
func TestInlineFunctionList ( t * testing . T ) {
// We should be able to list all functions, even inlined ones.
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "testinline" , t , "." , [ ] string { } , protest . EnableInlining | protest . EnableOptimization , func ( p * proc . Target , fixture protest . Fixture ) {
2018-08-07 01:08:25 +00:00
var found bool
for _ , fn := range p . BinInfo ( ) . Functions {
if strings . Contains ( fn . Name , "inlineThis" ) {
found = true
break
}
}
if ! found {
t . Fatal ( "inline function not returned" )
}
} )
}
func TestInlineBreakpoint ( t * testing . T ) {
// We should be able to set a breakpoint on the call site of an inlined function.
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
// Versions of go before 1.10 do not have DWARF information for inlined calls
t . Skip ( "inlining not supported" )
}
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "testinline" , t , "." , [ ] string { } , protest . EnableInlining | protest . EnableOptimization , func ( p * proc . Target , fixture protest . Fixture ) {
2019-11-01 19:41:06 +00:00
pcs , err := p . BinInfo ( ) . LineToPC ( fixture . Source , 17 )
t . Logf ( "%#v\n" , pcs )
if len ( pcs ) != 1 {
t . Fatalf ( "unable to get PC for inlined function call: %v" , pcs )
2018-08-07 01:08:25 +00:00
}
2019-11-01 19:41:06 +00:00
fn := p . BinInfo ( ) . PCToFunc ( pcs [ 0 ] )
2018-08-07 01:08:25 +00:00
expectedFn := "main.main"
if fn . Name != expectedFn {
t . Fatalf ( "incorrect function returned, expected %s, got %s" , expectedFn , fn . Name )
}
2019-11-01 19:41:06 +00:00
_ , err = p . SetBreakpoint ( pcs [ 0 ] , proc . UserBreakpoint , nil )
2018-08-07 01:08:25 +00:00
if err != nil {
t . Fatalf ( "unable to set breakpoint: %v" , err )
}
} )
}
2018-04-15 12:11:27 +00:00
func TestIssue951 ( t * testing . T ) {
if ver , _ := goversion . Parse ( runtime . Version ( ) ) ; ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 9 , - 1 , 0 , 0 , "" } ) {
t . Skip ( "scopes not implemented in <=go1.8" )
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue951" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-04-15 12:11:27 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
assertNoError ( err , t , "GoroutineScope" )
args , err := scope . FunctionArguments ( normalLoadConfig )
assertNoError ( err , t , "FunctionArguments" )
t . Logf ( "%#v" , args [ 0 ] )
if args [ 0 ] . Flags & proc . VariableShadowed == 0 {
t . Error ( "argument is not shadowed" )
}
vars , err := scope . LocalVariables ( normalLoadConfig )
assertNoError ( err , t , "LocalVariables" )
shadowed , notShadowed := 0 , 0
for i := range vars {
t . Logf ( "var %d: %#v\n" , i , vars [ i ] )
if vars [ i ] . Flags & proc . VariableShadowed != 0 {
shadowed ++
} else {
notShadowed ++
}
}
if shadowed != 1 || notShadowed != 1 {
t . Errorf ( "Wrong number of shadowed/non-shadowed local variables: %d %d" , shadowed , notShadowed )
}
} )
}
2018-05-25 11:59:40 +00:00
2018-05-22 12:23:04 +00:00
func TestDWZCompression ( t * testing . T ) {
2019-11-08 14:17:57 +00:00
if runtime . GOARCH == "arm64" {
t . Skip ( "test is not valid on ARM64" )
}
2018-05-22 12:23:04 +00:00
// If dwz is not available in the system, skip this test
if _ , err := exec . LookPath ( "dwz" ) ; err != nil {
t . Skip ( "dwz not installed" )
}
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "dwzcompression" , t , "." , [ ] string { } , protest . EnableDWZCompression , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "C.fortytwo" )
2018-05-22 12:23:04 +00:00
assertNoError ( proc . Continue ( p ) , t , "first Continue()" )
val := evalVariable ( p , t , "stdin" )
if val . RealType == nil {
t . Errorf ( "Can't find type for \"stdin\" global variable" )
}
} )
}
2018-05-25 11:59:40 +00:00
func TestMapLoadConfigWithReslice ( t * testing . T ) {
// Check that load configuration is respected for resliced maps.
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-05-25 11:59:40 +00:00
zolotovLoadCfg := proc . LoadConfig { FollowPointers : true , MaxStructFields : - 1 , MaxVariableRecurse : 3 , MaxStringLen : 10 , MaxArrayValues : 10 }
assertNoError ( proc . Continue ( p ) , t , "First Continue()" )
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
assertNoError ( err , t , "GoroutineScope" )
m1 , err := scope . EvalExpression ( "m1" , zolotovLoadCfg )
assertNoError ( err , t , "EvalVariable" )
t . Logf ( "m1 returned children %d (%d)" , len ( m1 . Children ) / 2 , m1 . Len )
expr := fmt . Sprintf ( "(*(*%q)(%d))[10:]" , m1 . DwarfType . String ( ) , m1 . Addr )
t . Logf ( "expr %q\n" , expr )
m1cont , err := scope . EvalExpression ( expr , zolotovLoadCfg )
assertNoError ( err , t , "EvalVariable" )
t . Logf ( "m1cont returned children %d" , len ( m1cont . Children ) / 2 )
if len ( m1cont . Children ) != 20 {
t . Fatalf ( "wrong number of children returned %d\n" , len ( m1cont . Children ) / 2 )
}
} )
}
2018-05-11 12:51:15 +00:00
func TestStepOutReturn ( t * testing . T ) {
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major >= 0 && ! ver . AfterOrEqual ( goversion . GoVersion { 1 , 10 , - 1 , 0 , 0 , "" } ) {
t . Skip ( "return variables aren't marked on 1.9 or earlier" )
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "stepoutret" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.stepout" )
2018-05-11 12:51:15 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
assertNoError ( proc . StepOut ( p ) , t , "StepOut" )
ret := p . CurrentThread ( ) . Common ( ) . ReturnValues ( normalLoadConfig )
if len ( ret ) != 2 {
t . Fatalf ( "wrong number of return values %v" , ret )
}
2018-11-10 17:35:37 +00:00
stridx := 0
numidx := 1
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 12 ) {
// in 1.11 and earlier the order of return values in DWARF is
// unspecified, in 1.11 and later it follows the order of definition
// specified by the user
for i := range ret {
if ret [ i ] . Name == "str" {
stridx = i
numidx = 1 - i
break
}
}
}
if ret [ stridx ] . Name != "str" {
t . Fatalf ( "(str) bad return value name %s" , ret [ stridx ] . Name )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ stridx ] . Kind != reflect . String {
t . Fatalf ( "(str) bad return value kind %v" , ret [ stridx ] . Kind )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if s := constant . StringVal ( ret [ stridx ] . Value ) ; s != "return 47" {
2018-05-11 12:51:15 +00:00
t . Fatalf ( "(str) bad return value %q" , s )
}
2018-11-10 17:35:37 +00:00
if ret [ numidx ] . Name != "num" {
t . Fatalf ( "(num) bad return value name %s" , ret [ numidx ] . Name )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if ret [ numidx ] . Kind != reflect . Int {
t . Fatalf ( "(num) bad return value kind %v" , ret [ numidx ] . Kind )
2018-05-11 12:51:15 +00:00
}
2018-11-10 17:35:37 +00:00
if n , _ := constant . Int64Val ( ret [ numidx ] . Value ) ; n != 48 {
2018-05-11 12:51:15 +00:00
t . Fatalf ( "(num) bad return value %d" , n )
}
} )
}
2018-06-20 08:06:12 +00:00
func TestOptimizationCheck ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "continuetestprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-06-20 08:06:12 +00:00
fn := p . BinInfo ( ) . LookupFunc [ "main.main" ]
if fn . Optimized ( ) {
t . Fatalf ( "main.main is optimized" )
}
} )
if goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 10 ) {
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "continuetestprog" , t , "." , [ ] string { } , protest . EnableOptimization | protest . EnableInlining , func ( p * proc . Target , fixture protest . Fixture ) {
2018-06-20 08:06:12 +00:00
fn := p . BinInfo ( ) . LookupFunc [ "main.main" ]
if ! fn . Optimized ( ) {
t . Fatalf ( "main.main is not optimized" )
}
} )
}
}
2018-07-07 08:31:56 +00:00
func TestIssue1264 ( t * testing . T ) {
// It should be possible to set a breakpoint condition that consists only
// of evaluating a single boolean variable.
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1264" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 8 )
2018-07-07 08:31:56 +00:00
bp . Cond = & ast . Ident { Name : "equalsTwo" }
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertLineNumber ( p , t , 8 , "after continue" )
} )
}
2018-07-06 07:37:31 +00:00
func TestReadDefer ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "deferstack" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-07-06 07:37:31 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
2019-09-25 17:21:20 +00:00
frames , err := p . SelectedGoroutine ( ) . Stacktrace ( 10 , proc . StacktraceReadDefers )
2018-07-06 07:37:31 +00:00
assertNoError ( err , t , "Stacktrace" )
logStacktrace ( t , p . BinInfo ( ) , frames )
examples := [ ] struct {
frameIdx int
topmostDefer string
defers [ ] string
} {
// main.call3 (defers nothing, topmost defer main.f2)
{ 0 , "main.f2" , [ ] string { } } ,
// main.call2 (defers main.f2, main.f3, topmost defer main.f2)
{ 1 , "main.f2" , [ ] string { "main.f2" , "main.f3" } } ,
// main.call1 (defers main.f1, main.f2, topmost defer main.f1)
{ 2 , "main.f1" , [ ] string { "main.f1" , "main.f2" } } ,
// main.main (defers nothing)
{ 3 , "" , [ ] string { } } }
defercheck := func ( d * proc . Defer , deferName , tgt string , frameIdx int ) {
if d == nil {
t . Fatalf ( "expected %q as %s of frame %d, got nothing" , tgt , deferName , frameIdx )
}
if d . Unreadable != nil {
t . Fatalf ( "expected %q as %s of frame %d, got unreadable defer: %v" , tgt , deferName , frameIdx , d . Unreadable )
}
2020-02-26 05:01:37 +00:00
dfn := p . BinInfo ( ) . PCToFunc ( d . DeferredPC )
2018-07-06 07:37:31 +00:00
if dfn == nil {
t . Fatalf ( "expected %q as %s of frame %d, got %#x" , tgt , deferName , frameIdx , d . DeferredPC )
}
if dfn . Name != tgt {
t . Fatalf ( "expected %q as %s of frame %d, got %q" , tgt , deferName , frameIdx , dfn . Name )
}
}
for _ , example := range examples {
frame := & frames [ example . frameIdx ]
if example . topmostDefer != "" {
defercheck ( frame . TopmostDefer , "topmost defer" , example . topmostDefer , example . frameIdx )
}
if len ( example . defers ) != len ( frames [ example . frameIdx ] . Defers ) {
t . Fatalf ( "expected %d defers for %d, got %v" , len ( example . defers ) , example . frameIdx , frame . Defers )
}
for deferIdx := range example . defers {
defercheck ( frame . Defers [ deferIdx ] , fmt . Sprintf ( "defer %d" , deferIdx ) , example . defers [ deferIdx ] , example . frameIdx )
}
}
} )
}
2018-08-27 05:51:48 +00:00
func TestNextUnknownInstr ( t * testing . T ) {
2019-11-16 13:43:44 +00:00
if runtime . GOARCH != "amd64" {
t . Skip ( "amd64 only" )
2019-11-08 14:17:57 +00:00
}
2018-08-27 05:51:48 +00:00
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 10 ) {
t . Skip ( "versions of Go before 1.10 can't assemble the instruction VPUNPCKLWD" )
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "nodisasm/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.asmFunc" )
2018-08-27 05:51:48 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertNoError ( proc . Next ( p ) , t , "Next()" )
} )
}
2018-07-10 10:15:11 +00:00
func TestReadDeferArgs ( t * testing . T ) {
2019-11-08 14:17:57 +00:00
if runtime . GOARCH == "arm64" {
2020-01-21 17:11:20 +00:00
t . Skip ( "arm64 does not support ReadDeferArgs for now" )
2019-11-08 14:17:57 +00:00
}
2018-07-10 10:15:11 +00:00
var tests = [ ] struct {
frame , deferCall int
a , b int64
} {
{ 1 , 1 , 42 , 61 } ,
{ 2 , 2 , 1 , - 1 } ,
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "deferstack" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-07-10 10:15:11 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
for _ , test := range tests {
scope , err := proc . ConvertEvalScope ( p , - 1 , test . frame , test . deferCall )
assertNoError ( err , t , fmt . Sprintf ( "ConvertEvalScope(-1, %d, %d)" , test . frame , test . deferCall ) )
if scope . Fn . Name != "main.f2" {
t . Fatalf ( "expected function \"main.f2\" got %q" , scope . Fn . Name )
}
avar , err := scope . EvalVariable ( "a" , normalLoadConfig )
if err != nil {
t . Fatal ( err )
}
bvar , err := scope . EvalVariable ( "b" , normalLoadConfig )
if err != nil {
t . Fatal ( err )
}
a , _ := constant . Int64Val ( avar . Value )
b , _ := constant . Int64Val ( bvar . Value )
if a != test . a {
t . Errorf ( "value of argument 'a' at frame %d, deferred call %d: %d (expected %d)" , test . frame , test . deferCall , a , test . a )
}
if b != test . b {
t . Errorf ( "value of argument 'b' at frame %d, deferred call %d: %d (expected %d)" , test . frame , test . deferCall , b , test . b )
}
}
} )
}
2018-10-16 08:15:31 +00:00
func TestIssue1374 ( t * testing . T ) {
2019-11-08 14:17:57 +00:00
if runtime . GOARCH == "arm64" {
2019-11-26 11:03:24 +00:00
t . Skip ( "arm64 does not support FunctionCall for now" )
2019-11-08 14:17:57 +00:00
}
2018-10-16 08:15:31 +00:00
// Continue did not work when stopped at a breakpoint immediately after calling CallFunction.
protest . MustSupportFunctionCalls ( t , testBackend )
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1374" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 7 )
2018-10-16 08:15:31 +00:00
assertNoError ( proc . Continue ( p ) , t , "First Continue" )
assertLineNumber ( p , t , 7 , "Did not continue to correct location (first continue)," )
2019-06-30 17:44:30 +00:00
assertNoError ( proc . EvalExpressionWithCalls ( p , p . SelectedGoroutine ( ) , "getNum()" , normalLoadConfig , true ) , t , "Call" )
2018-10-16 08:15:31 +00:00
err := proc . Continue ( p )
if _ , isexited := err . ( proc . ErrProcessExited ) ; ! isexited {
regs , _ := p . CurrentThread ( ) . Registers ( false )
f , l , _ := p . BinInfo ( ) . PCToLine ( regs . PC ( ) )
t . Fatalf ( "expected process exited error got %v at %s:%d" , err , f , l )
}
} )
}
2018-12-03 13:53:39 +00:00
func TestIssue1432 ( t * testing . T ) {
// Check that taking the address of a struct, casting it into a pointer to
// the struct's type and then accessing a member field will still:
// - perform auto-dereferencing on struct member access
// - yield a Variable that's ultimately assignable (i.e. has an address)
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1432" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2018-12-03 13:53:39 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
svar := evalVariable ( p , t , "s" )
t . Logf ( "%#x" , svar . Addr )
scope , err := proc . GoroutineScope ( p . CurrentThread ( ) )
assertNoError ( err , t , "GoroutineScope()" )
err = scope . SetVariable ( fmt . Sprintf ( "(*\"main.s\")(%#x).i" , svar . Addr ) , "10" )
assertNoError ( err , t , "SetVariable" )
} )
}
2018-12-06 08:28:38 +00:00
func TestGoroutinesInfoLimit ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 37 )
2018-12-06 08:28:38 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
gcount := 0
nextg := 0
const goroutinesInfoLimit = 10
for nextg >= 0 {
oldnextg := nextg
var gs [ ] * proc . G
var err error
gs , nextg , err = proc . GoroutinesInfo ( p , nextg , goroutinesInfoLimit )
assertNoError ( err , t , fmt . Sprintf ( "GoroutinesInfo(%d, %d)" , oldnextg , goroutinesInfoLimit ) )
gcount += len ( gs )
t . Logf ( "got %d goroutines\n" , len ( gs ) )
}
t . Logf ( "number of goroutines: %d\n" , gcount )
gs , _ , err := proc . GoroutinesInfo ( p , 0 , 0 )
assertNoError ( err , t , "GoroutinesInfo(0, 0)" )
t . Logf ( "number of goroutines (full scan): %d\n" , gcount )
if len ( gs ) != gcount {
t . Fatalf ( "mismatch in the number of goroutines %d %d\n" , gcount , len ( gs ) )
}
} )
}
2019-02-26 17:22:33 +00:00
func TestIssue1469 ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1469" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , fixture . Source , 13 )
2019-02-26 17:22:33 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
gid2thread := make ( map [ int ] [ ] proc . Thread )
for _ , thread := range p . ThreadList ( ) {
g , _ := proc . GetG ( thread )
if g == nil {
continue
}
gid2thread [ g . ID ] = append ( gid2thread [ g . ID ] , thread )
}
for gid := range gid2thread {
if len ( gid2thread [ gid ] ) > 1 {
t . Logf ( "too many threads running goroutine %d" , gid )
for _ , thread := range gid2thread [ gid ] {
t . Logf ( "\tThread %d" , thread . ThreadID ( ) )
frames , err := proc . ThreadStacktrace ( thread , 20 )
if err != nil {
t . Logf ( "\t\tcould not get stacktrace %v" , err )
}
for _ , frame := range frames {
t . Logf ( "\t\t%#x at %s:%d (systemstack: %v)" , frame . Call . PC , frame . Call . File , frame . Call . Line , frame . SystemStack )
}
}
}
}
} )
}
2019-02-27 22:28:25 +00:00
func TestDeadlockBreakpoint ( t * testing . T ) {
if buildMode == "pie" {
t . Skip ( "See https://github.com/golang/go/issues/29322" )
}
deadlockBp := proc . FatalThrow
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
deadlockBp = proc . UnrecoveredPanic
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "testdeadlock" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-02-27 22:28:25 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
bp := p . CurrentThread ( ) . Breakpoint ( )
if bp . Breakpoint == nil || bp . Name != deadlockBp {
t . Fatalf ( "did not stop at deadlock breakpoint %v" , bp )
}
} )
}
2019-03-20 17:32:51 +00:00
func TestListImages ( t * testing . T ) {
2019-06-30 17:34:47 +00:00
pluginFixtures := protest . WithPlugins ( t , protest . AllNonOptimized , "plugin1/" , "plugin2/" )
2019-03-20 17:32:51 +00:00
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "plugintest" , t , "." , [ ] string { pluginFixtures [ 0 ] . Path , pluginFixtures [ 1 ] . Path } , protest . AllNonOptimized , func ( p * proc . Target , fixture protest . Fixture ) {
2019-03-20 17:32:51 +00:00
assertNoError ( proc . Continue ( p ) , t , "first continue" )
2019-05-08 21:06:38 +00:00
f , l := currentLineNumber ( p , t )
2019-03-20 17:32:51 +00:00
plugin1Found := false
2019-05-08 21:06:38 +00:00
t . Logf ( "Libraries before %s:%d:" , f , l )
2019-03-20 17:32:51 +00:00
for _ , image := range p . BinInfo ( ) . Images {
2019-05-08 21:06:38 +00:00
t . Logf ( "\t%#x %q err:%v" , image . StaticBase , image . Path , image . LoadError ( ) )
2019-03-20 17:32:51 +00:00
if image . Path == pluginFixtures [ 0 ] . Path {
plugin1Found = true
}
}
if ! plugin1Found {
t . Fatalf ( "Could not find plugin1" )
}
assertNoError ( proc . Continue ( p ) , t , "second continue" )
2019-05-08 21:06:38 +00:00
f , l = currentLineNumber ( p , t )
2019-03-20 17:32:51 +00:00
plugin1Found , plugin2Found := false , false
2019-05-08 21:06:38 +00:00
t . Logf ( "Libraries after %s:%d:" , f , l )
2019-03-20 17:32:51 +00:00
for _ , image := range p . BinInfo ( ) . Images {
2019-05-08 21:06:38 +00:00
t . Logf ( "\t%#x %q err:%v" , image . StaticBase , image . Path , image . LoadError ( ) )
2019-03-20 17:32:51 +00:00
switch image . Path {
case pluginFixtures [ 0 ] . Path :
plugin1Found = true
case pluginFixtures [ 1 ] . Path :
plugin2Found = true
}
}
if ! plugin1Found {
t . Fatalf ( "Could not find plugin1" )
}
if ! plugin2Found {
t . Fatalf ( "Could not find plugin2" )
}
} )
}
2019-03-16 13:50:18 +00:00
func TestAncestors ( t * testing . T ) {
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 11 ) {
t . Skip ( "not supported on Go <= 1.10" )
}
savedGodebug := os . Getenv ( "GODEBUG" )
os . Setenv ( "GODEBUG" , "tracebackancestors=100" )
defer os . Setenv ( "GODEBUG" , savedGodebug )
2020-01-21 20:41:24 +00:00
withTestProcess ( "testnextprog" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFunctionBreakpoint ( p , t , "main.testgoroutine" )
2019-03-16 13:50:18 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2019-06-03 17:41:33 +00:00
as , err := proc . Ancestors ( p , p . SelectedGoroutine ( ) , 1000 )
2019-03-16 13:50:18 +00:00
assertNoError ( err , t , "Ancestors" )
t . Logf ( "ancestors: %#v\n" , as )
if len ( as ) != 1 {
t . Fatalf ( "expected only one ancestor got %d" , len ( as ) )
}
mainFound := false
for i , a := range as {
astack , err := a . Stack ( 100 )
assertNoError ( err , t , fmt . Sprintf ( "Ancestor %d stack" , i ) )
t . Logf ( "ancestor %d\n" , i )
logStacktrace ( t , p . BinInfo ( ) , astack )
for _ , frame := range astack {
if frame . Current . Fn != nil && frame . Current . Fn . Name == "main.main" {
mainFound = true
}
}
}
if ! mainFound {
t . Fatal ( "could not find main.main function in ancestors" )
}
} )
}
2019-04-25 16:45:37 +00:00
2020-01-21 20:41:24 +00:00
func testCallConcurrentCheckReturns ( p * proc . Target , t * testing . T , gid1 , gid2 int ) int {
2019-06-30 17:44:30 +00:00
found := 0
2019-04-25 16:45:37 +00:00
for _ , thread := range p . ThreadList ( ) {
g , _ := proc . GetG ( thread )
2019-06-30 17:44:30 +00:00
if g == nil || ( g . ID != gid1 && g . ID != gid2 ) {
2019-04-25 16:45:37 +00:00
continue
}
retvals := thread . Common ( ) . ReturnValues ( normalLoadConfig )
2019-06-30 17:44:30 +00:00
if len ( retvals ) == 0 {
continue
}
n , _ := constant . Int64Val ( retvals [ 0 ] . Value )
t . Logf ( "injection on goroutine %d (thread %d) returned %v\n" , g . ID , thread . ThreadID ( ) , n )
switch g . ID {
case gid1 :
if n != 11 {
t . Errorf ( "wrong return value for goroutine %d" , g . ID )
}
found ++
case gid2 :
if n != 12 {
t . Errorf ( "wrong return value for goroutine %d" , g . ID )
}
found ++
2019-04-25 16:45:37 +00:00
}
}
2019-06-30 17:44:30 +00:00
return found
2019-04-25 16:45:37 +00:00
}
func TestCallConcurrent ( t * testing . T ) {
2019-07-13 01:28:04 +00:00
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
2019-11-08 14:17:57 +00:00
if runtime . GOARCH == "arm64" {
2019-11-26 11:03:24 +00:00
t . Skip ( "arm64 does not support FunctionCall for now" )
2019-11-08 14:17:57 +00:00
}
2019-04-25 16:45:37 +00:00
protest . MustSupportFunctionCalls ( t , testBackend )
2020-01-21 20:41:24 +00:00
withTestProcess ( "teststepconcurrent" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 24 )
2019-04-25 16:45:37 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
2019-06-30 17:44:30 +00:00
//_, err := p.ClearBreakpoint(bp.Addr)
//assertNoError(err, t, "ClearBreakpoint() returned an error")
2019-04-25 16:45:37 +00:00
gid1 := p . SelectedGoroutine ( ) . ID
t . Logf ( "starting injection in %d / %d" , p . SelectedGoroutine ( ) . ID , p . CurrentThread ( ) . ThreadID ( ) )
2019-06-30 17:44:30 +00:00
assertNoError ( proc . EvalExpressionWithCalls ( p , p . SelectedGoroutine ( ) , "Foo(10, 1)" , normalLoadConfig , false ) , t , "EvalExpressionWithCalls()" )
2019-04-25 16:45:37 +00:00
2019-06-30 17:44:30 +00:00
returned := testCallConcurrentCheckReturns ( p , t , gid1 , - 1 )
2019-04-25 16:45:37 +00:00
curthread := p . CurrentThread ( )
2019-11-01 19:41:06 +00:00
if curbp := curthread . Breakpoint ( ) ; curbp . Breakpoint == nil || curbp . LogicalID != bp . LogicalID || returned > 0 {
2019-06-30 17:44:30 +00:00
t . Logf ( "skipping test, the call injection terminated before we hit a breakpoint in a different thread" )
2019-04-25 16:45:37 +00:00
return
}
2019-06-30 17:44:30 +00:00
_ , err := p . ClearBreakpoint ( bp . Addr )
assertNoError ( err , t , "ClearBreakpoint() returned an error" )
gid2 := p . SelectedGoroutine ( ) . ID
t . Logf ( "starting second injection in %d / %d" , p . SelectedGoroutine ( ) . ID , p . CurrentThread ( ) . ThreadID ( ) )
assertNoError ( proc . EvalExpressionWithCalls ( p , p . SelectedGoroutine ( ) , "Foo(10, 2)" , normalLoadConfig , false ) , t , "EvalExpressioniWithCalls" )
2019-04-25 16:45:37 +00:00
for {
2019-06-30 17:44:30 +00:00
returned += testCallConcurrentCheckReturns ( p , t , gid1 , gid2 )
if returned >= 2 {
2019-04-25 16:45:37 +00:00
break
}
2019-06-30 17:44:30 +00:00
t . Logf ( "Continuing... %d" , returned )
2019-04-25 16:45:37 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
}
proc . Continue ( p )
} )
}
2019-05-08 21:06:38 +00:00
func TestPluginStepping ( t * testing . T ) {
2019-06-30 17:34:47 +00:00
pluginFixtures := protest . WithPlugins ( t , protest . AllNonOptimized , "plugin1/" , "plugin2/" )
2019-05-08 21:06:38 +00:00
2019-06-30 17:34:47 +00:00
testseq2Args ( "." , [ ] string { pluginFixtures [ 0 ] . Path , pluginFixtures [ 1 ] . Path } , protest . AllNonOptimized , t , "plugintest2" , "" , [ ] seqTest {
2019-05-08 21:06:38 +00:00
{ contContinue , 41 } ,
{ contStep , "plugin1.go:9" } ,
{ contStep , "plugin1.go:10" } ,
{ contStep , "plugin1.go:11" } ,
{ contNext , "plugin1.go:12" } ,
{ contNext , "plugintest2.go:41" } ,
{ contNext , "plugintest2.go:42" } ,
{ contStep , "plugin2.go:22" } ,
{ contNext , "plugin2.go:23" } ,
{ contNext , "plugin2.go:26" } ,
{ contNext , "plugintest2.go:42" } } )
}
2019-07-08 17:24:56 +00:00
func TestIssue1601 ( t * testing . T ) {
//Tests that recursive types involving C qualifiers and typedefs are parsed correctly
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1601" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-07-08 17:24:56 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue" )
evalVariable ( p , t , "C.globalq" )
} )
}
2019-07-16 20:11:35 +00:00
func TestIssue1615 ( t * testing . T ) {
// A breakpoint condition that tests for string equality with a constant string shouldn't fail with 'string too long for comparison' error
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1615" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
bp := setFileBreakpoint ( p , t , fixture . Source , 19 )
2019-07-16 20:11:35 +00:00
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "s" } ,
Y : & ast . BasicLit { Kind : token . STRING , Value : ` "projects/my-gcp-project-id-string/locations/us-central1/queues/my-task-queue-name" ` } ,
}
assertNoError ( proc . Continue ( p ) , t , "Continue" )
assertLineNumber ( p , t , 19 , "" )
} )
}
2019-08-01 23:31:50 +00:00
func TestCgoStacktrace2 ( t * testing . T ) {
if runtime . GOOS == "windows" {
t . Skip ( "fixture crashes go runtime on windows" )
}
// If a panic happens during cgo execution the stacktrace should show the C
// function that caused the problem.
2020-01-21 20:41:24 +00:00
withTestProcess ( "cgosigsegvstack" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-01 23:31:50 +00:00
proc . Continue ( p )
frames , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 100 )
assertNoError ( err , t , "Stacktrace()" )
logStacktrace ( t , p . BinInfo ( ) , frames )
stacktraceCheck ( t , [ ] string { "C.sigsegv" , "C.testfn" , "main.main" } , frames )
} )
}
2019-08-12 22:11:19 +00:00
func TestIssue1656 ( t * testing . T ) {
2019-11-16 13:43:44 +00:00
if runtime . GOARCH != "amd64" {
t . Skip ( "amd64 only" )
2019-11-08 14:17:57 +00:00
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1656/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-08-14 15:57:05 +00:00
setFileBreakpoint ( p , t , filepath . ToSlash ( filepath . Join ( fixture . BuildDir , "main.s" ) ) , 5 )
2019-08-12 22:11:19 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
t . Logf ( "step1\n" )
assertNoError ( proc . Step ( p ) , t , "Step()" )
assertLineNumber ( p , t , 8 , "wrong line number after first step" )
t . Logf ( "step2\n" )
assertNoError ( proc . Step ( p ) , t , "Step()" )
assertLineNumber ( p , t , 9 , "wrong line number after second step" )
} )
}
2019-10-28 21:55:32 +00:00
func TestBreakpointConfusionOnResume ( t * testing . T ) {
// Checks that SetCurrentBreakpoint, (*Thread).StepInstruction and
// native.(*Thread).singleStep all agree on which breakpoint the thread is
// stopped at.
// This test checks for a regression introduced when fixing Issue #1656
if runtime . GOARCH != "amd64" {
t . Skip ( "amd64 only" )
}
2020-01-21 20:41:24 +00:00
withTestProcess ( "nopbreakpoint/" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-10-28 21:55:32 +00:00
maindots := filepath . ToSlash ( filepath . Join ( fixture . BuildDir , "main.s" ) )
maindotgo := filepath . ToSlash ( filepath . Join ( fixture . BuildDir , "main.go" ) )
setFileBreakpoint ( p , t , maindots , 5 ) // line immediately after the NOP
assertNoError ( proc . Continue ( p ) , t , "First Continue" )
assertLineNumber ( p , t , 5 , "not on main.s:5" )
setFileBreakpoint ( p , t , maindots , 4 ) // sets a breakpoint on the NOP line, which will be one byte before the breakpoint we currently are stopped at.
setFileBreakpoint ( p , t , maindotgo , 18 ) // set one extra breakpoint so that we can recover execution and check the global variable g
assertNoError ( proc . Continue ( p ) , t , "Second Continue" )
gvar := evalVariable ( p , t , "g" )
if n , _ := constant . Int64Val ( gvar . Value ) ; n != 1 {
t . Fatalf ( "wrong value of global variable 'g': %v (expected 1)" , gvar . Value )
}
} )
}
2019-10-26 15:15:33 +00:00
func TestIssue1736 ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "testvariables2" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-10-26 15:15:33 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
ch1BufVar := evalVariable ( p , t , "*(ch1.buf)" )
q := fmt . Sprintf ( "*(*%q)(%d)" , ch1BufVar . DwarfType . Common ( ) . Name , ch1BufVar . Addr )
t . Logf ( "%s" , q )
ch1BufVar2 := evalVariable ( p , t , q )
if ch1BufVar2 . Unreadable != nil {
t . Fatal ( ch1BufVar2 . Unreadable )
}
} )
}
2020-01-09 11:10:02 +00:00
func TestIssue1817 ( t * testing . T ) {
// Setting a breakpoint on a line that doesn't have any PC addresses marked
// is_stmt should work.
2020-01-21 20:41:24 +00:00
withTestProcess ( "issue1817" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2020-01-09 11:10:02 +00:00
setFileBreakpoint ( p , t , fixture . Source , 16 )
} )
}
2019-12-03 13:00:30 +00:00
func TestListPackagesBuildInfo ( t * testing . T ) {
2020-01-21 20:41:24 +00:00
withTestProcess ( "pkgrenames" , t , func ( p * proc . Target , fixture protest . Fixture ) {
2019-12-03 13:00:30 +00:00
pkgs := p . BinInfo ( ) . ListPackagesBuildInfo ( true )
t . Logf ( "returned %d" , len ( pkgs ) )
if len ( pkgs ) < 10 {
t . Errorf ( "very few packages returned" )
}
for _ , pkg := range pkgs {
t . Logf ( "%q %q" , pkg . ImportPath , pkg . DirectoryPath )
const _fixtures = "_fixtures"
fidx := strings . Index ( pkg . ImportPath , _fixtures )
if fidx < 0 {
continue
}
if ! strings . HasSuffix ( strings . Replace ( pkg . DirectoryPath , "\\" , "/" , - 1 ) , pkg . ImportPath [ fidx : ] ) {
t . Errorf ( "unexpected suffix: %q %q" , pkg . ImportPath , pkg . DirectoryPath )
}
}
} )
}
2019-12-30 09:42:20 +00:00
func TestIssue1795 ( t * testing . T ) {
// When doing midstack inlining the Go compiler sometimes (always?) emits
// the toplevel inlined call with ranges that do not cover the inlining of
// other nested inlined calls.
// For example if a function A calls B which calls C and both the calls to
// B and C are inlined the DW_AT_inlined_subroutine entry for A might have
// ranges that do not cover the ranges of the inlined call to C.
// This is probably a violation of the DWARF standard (it's unclear) but we
// might as well support it as best as possible anyway.
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 13 ) {
t . Skip ( "Test not relevant to Go < 1.13" )
}
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "issue1795" , t , "." , [ ] string { } , protest . EnableInlining | protest . EnableOptimization , func ( p * proc . Target , fixture protest . Fixture ) {
2019-12-30 09:42:20 +00:00
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertLineNumber ( p , t , 12 , "wrong line number after Continue," )
assertNoError ( proc . Next ( p ) , t , "Next()" )
assertLineNumber ( p , t , 13 , "wrong line number after Next," )
} )
2020-01-21 20:41:24 +00:00
withTestProcessArgs ( "issue1795" , t , "." , [ ] string { } , protest . EnableInlining | protest . EnableOptimization , func ( p * proc . Target , fixture protest . Fixture ) {
2019-12-30 09:42:20 +00:00
setFunctionBreakpoint ( p , t , "regexp.(*Regexp).doExecute" )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
assertLineNumber ( p , t , 12 , "wrong line number after Continue (1)," )
assertNoError ( proc . Continue ( p ) , t , "Continue()" )
frames , err := proc . ThreadStacktrace ( p . CurrentThread ( ) , 40 )
assertNoError ( err , t , "ThreadStacktrace()" )
logStacktrace ( t , p . BinInfo ( ) , frames )
if err := checkFrame ( frames [ 0 ] , "regexp.(*Regexp).doExecute" , "" , 0 , false ) ; err != nil {
t . Errorf ( "Wrong frame 0: %v" , err )
}
if err := checkFrame ( frames [ 1 ] , "regexp.(*Regexp).doMatch" , "" , 0 , true ) ; err != nil {
t . Errorf ( "Wrong frame 1: %v" , err )
}
if err := checkFrame ( frames [ 2 ] , "regexp.(*Regexp).MatchString" , "" , 0 , true ) ; err != nil {
t . Errorf ( "Wrong frame 2: %v" , err )
}
if err := checkFrame ( frames [ 3 ] , "main.main" , fixture . Source , 12 , false ) ; err != nil {
t . Errorf ( "Wrong frame 3: %v" , err )
}
} )
}
2020-01-23 16:55:08 +00:00
func BenchmarkConditionalBreakpoints ( b * testing . B ) {
b . N = 1
withTestProcess ( "issue1549" , b , func ( p * proc . Target , fixture protest . Fixture ) {
bp := setFileBreakpoint ( p , b , fixture . Source , 12 )
bp . Cond = & ast . BinaryExpr {
Op : token . EQL ,
X : & ast . Ident { Name : "value" } ,
Y : & ast . BasicLit { Kind : token . INT , Value : "-1" } ,
}
err := proc . Continue ( p )
if _ , exited := err . ( proc . ErrProcessExited ) ; ! exited {
b . Fatalf ( "Unexpected error on Continue(): %v" , err )
}
} )
}