2020-02-15 19:52:53 +00:00
package dap
import (
2021-01-06 17:07:47 +00:00
"bufio"
2020-02-15 19:52:53 +00:00
"flag"
2021-03-23 03:06:09 +00:00
"fmt"
2020-02-27 04:45:48 +00:00
"io"
2021-02-24 16:19:07 +00:00
"io/ioutil"
2021-04-02 16:19:16 +00:00
"math/rand"
2020-02-15 19:52:53 +00:00
"net"
"os"
2020-12-28 17:14:15 +00:00
"os/exec"
2020-03-04 17:22:51 +00:00
"path/filepath"
2020-08-11 15:34:27 +00:00
"regexp"
2020-08-17 07:07:53 +00:00
"runtime"
2021-10-06 01:29:46 +00:00
"strconv"
2020-03-10 19:29:06 +00:00
"strings"
2020-02-15 19:52:53 +00:00
"testing"
"time"
2020-08-17 07:07:53 +00:00
"github.com/go-delve/delve/pkg/goversion"
2020-02-15 19:52:53 +00:00
"github.com/go-delve/delve/pkg/logflags"
protest "github.com/go-delve/delve/pkg/proc/test"
"github.com/go-delve/delve/service"
2021-09-24 22:22:32 +00:00
"github.com/go-delve/delve/service/api"
2020-02-15 19:52:53 +00:00
"github.com/go-delve/delve/service/dap/daptest"
2021-09-24 11:43:46 +00:00
"github.com/go-delve/delve/service/debugger"
2020-02-15 19:52:53 +00:00
"github.com/google/go-dap"
)
2020-03-04 17:22:51 +00:00
const stopOnEntry bool = true
2020-08-11 15:34:27 +00:00
const hasChildren bool = true
const noChildren bool = false
2021-10-05 17:35:14 +00:00
const localsScope = 1000
const globalsScope = 1001
2020-03-04 17:22:51 +00:00
2020-08-24 17:21:51 +00:00
var testBackend string
2020-02-15 19:52:53 +00:00
func TestMain ( m * testing . M ) {
2021-05-04 19:53:42 +00:00
logOutputVal := ""
if _ , isTeamCityTest := os . LookupEnv ( "TEAMCITY_VERSION" ) ; isTeamCityTest {
logOutputVal = "debugger,dap"
}
2020-02-15 19:52:53 +00:00
var logOutput string
2021-05-04 19:53:42 +00:00
flag . StringVar ( & logOutput , "log-output" , logOutputVal , "configures log output" )
2020-02-15 19:52:53 +00:00
flag . Parse ( )
logflags . Setup ( logOutput != "" , logOutput , "" )
2020-08-24 17:21:51 +00:00
protest . DefaultTestBackend ( & testBackend )
2020-02-15 19:52:53 +00:00
os . Exit ( protest . RunTestsWithFixtures ( m ) )
}
2020-02-27 04:45:48 +00:00
// name is for _fixtures/<name>.go
func runTest ( t * testing . T , name string , test func ( c * daptest . Client , f protest . Fixture ) ) {
2021-05-17 16:21:15 +00:00
runTestBuildFlags ( t , name , test , protest . AllNonOptimized )
}
// name is for _fixtures/<name>.go
func runTestBuildFlags ( t * testing . T , name string , test func ( c * daptest . Client , f protest . Fixture ) , buildFlags protest . BuildFlags ) {
2020-02-27 04:45:48 +00:00
fixture := protest . BuildFixture ( name , buildFlags )
2021-05-06 07:56:29 +00:00
// Start the DAP server.
2021-09-01 16:59:51 +00:00
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
client := startDAPServerWithClient ( t , serverStopped )
2021-05-06 07:56:29 +00:00
defer client . Close ( )
test ( client , fixture )
2021-09-01 16:59:51 +00:00
<- serverStopped
2021-05-06 07:56:29 +00:00
}
2021-10-07 09:27:04 +00:00
func startDAPServerWithClient ( t * testing . T , serverStopped chan struct { } ) * daptest . Client {
server , _ := startDAPServer ( t , serverStopped )
2021-09-24 11:43:46 +00:00
client := daptest . NewClient ( server . config . Listener . Addr ( ) . String ( ) )
2021-09-01 16:59:51 +00:00
return client
}
2021-10-07 09:27:04 +00:00
func startDAPServer ( t * testing . T , serverStopped chan struct { } ) ( server * Server , forceStop chan struct { } ) {
2020-02-27 04:45:48 +00:00
// Start the DAP server.
2020-02-15 19:52:53 +00:00
listener , err := net . Listen ( "tcp" , ":0" )
if err != nil {
t . Fatal ( err )
}
2021-09-20 15:53:01 +00:00
disconnectChan := make ( chan struct { } )
2021-09-24 11:43:46 +00:00
server = NewServer ( & service . Config {
2020-02-15 19:52:53 +00:00
Listener : listener ,
2020-02-27 04:45:48 +00:00
DisconnectChan : disconnectChan ,
2020-02-15 19:52:53 +00:00
} )
server . Run ( )
// Give server time to start listening for clients
time . Sleep ( 100 * time . Millisecond )
2020-02-27 04:45:48 +00:00
// Run a goroutine that stops the server when disconnectChan is signaled.
// This helps us test that certain events cause the server to stop as
// expected.
2021-09-20 15:53:01 +00:00
forceStop = make ( chan struct { } )
2020-02-27 04:45:48 +00:00
go func ( ) {
2021-09-01 16:59:51 +00:00
defer func ( ) {
if serverStopped != nil {
close ( serverStopped )
}
} ( )
2021-09-20 15:53:01 +00:00
select {
case <- disconnectChan : // Stop triggered internally
case <- forceStop : // Stop triggered externally
}
2021-04-21 20:28:15 +00:00
server . Stop ( )
2020-02-27 04:45:48 +00:00
} ( )
2020-02-15 19:52:53 +00:00
2021-09-24 11:43:46 +00:00
return server , forceStop
2021-09-01 16:59:51 +00:00
}
2021-09-20 15:53:01 +00:00
func TestForceStopNoClient ( t * testing . T ) {
2021-09-01 16:59:51 +00:00
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
_ , forceStop := startDAPServer ( t , serverStopped )
2021-09-20 15:53:01 +00:00
close ( forceStop )
2021-09-01 16:59:51 +00:00
<- serverStopped
}
2021-09-20 15:53:01 +00:00
func TestForceStopNoTarget ( t * testing . T ) {
2021-09-01 16:59:51 +00:00
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
server , forceStop := startDAPServer ( t , serverStopped )
2021-09-24 11:43:46 +00:00
client := daptest . NewClient ( server . config . Listener . Addr ( ) . String ( ) )
defer client . Close ( )
2021-09-01 16:59:51 +00:00
client . InitializeRequest ( )
client . ExpectInitializeResponseAndCapabilities ( t )
2021-09-20 15:53:01 +00:00
close ( forceStop )
2021-09-01 16:59:51 +00:00
<- serverStopped
}
2021-09-20 15:53:01 +00:00
func TestForceStopWithTarget ( t * testing . T ) {
2021-09-01 16:59:51 +00:00
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
server , forceStop := startDAPServer ( t , serverStopped )
2021-09-24 11:43:46 +00:00
client := daptest . NewClient ( server . config . Listener . Addr ( ) . String ( ) )
defer client . Close ( )
2021-09-01 16:59:51 +00:00
client . InitializeRequest ( )
client . ExpectInitializeResponseAndCapabilities ( t )
fixture := protest . BuildFixture ( "increment" , protest . AllNonOptimized )
client . LaunchRequest ( "exec" , fixture . Path , stopOnEntry )
client . ExpectInitializedEvent ( t )
client . ExpectLaunchResponse ( t )
2021-09-20 15:53:01 +00:00
close ( forceStop )
<- serverStopped
}
func TestForceStopWhileStopping ( t * testing . T ) {
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
server , forceStop := startDAPServer ( t , serverStopped )
2021-09-24 11:43:46 +00:00
client := daptest . NewClient ( server . config . Listener . Addr ( ) . String ( ) )
2021-09-20 15:53:01 +00:00
client . InitializeRequest ( )
client . ExpectInitializeResponseAndCapabilities ( t )
fixture := protest . BuildFixture ( "increment" , protest . AllNonOptimized )
client . LaunchRequest ( "exec" , fixture . Path , stopOnEntry )
client . ExpectInitializedEvent ( t )
client . Close ( ) // depending on timing may trigger Stop()
time . Sleep ( time . Microsecond )
close ( forceStop ) // depending on timing may trigger Stop()
2021-09-01 16:59:51 +00:00
<- serverStopped
2020-02-15 19:52:53 +00:00
}
2020-12-28 17:14:15 +00:00
// TestLaunchStopOnEntry emulates the message exchange that can be observed with
// VS Code for the most basic launch debug session with "stopOnEntry" enabled:
2020-03-10 19:29:06 +00:00
// - User selects "Start Debugging": 1 >> initialize
// : 1 << initialize
// : 2 >> launch
// : << initialized event
// : 2 << launch
// : 3 >> setBreakpoints (empty)
// : 3 << setBreakpoints
// : 4 >> setExceptionBreakpoints (empty)
// : 4 << setExceptionBreakpoints
// : 5 >> configurationDone
// - Program stops upon launching : << stopped event
// : 5 << configurationDone
// : 6 >> threads
// : 6 << threads (Dummy)
// : 7 >> threads
// : 7 << threads (Dummy)
// : 8 >> stackTrace
2020-11-12 23:24:31 +00:00
// : 8 << error (Unable to produce stack trace)
2020-03-10 19:29:06 +00:00
// : 9 >> stackTrace
2020-11-12 23:24:31 +00:00
// : 9 << error (Unable to produce stack trace)
// - User evaluates bad expression : 10 >> evaluate
// : 10 << error (unable to find function context)
// - User evaluates good expression: 11 >> evaluate
// : 11 << evaluate
// - User selects "Continue" : 12 >> continue
// : 12 << continue
2020-03-10 19:29:06 +00:00
// - Program runs to completion : << terminated event
2020-11-12 23:24:31 +00:00
// : 13 >> disconnect
2021-04-12 21:50:15 +00:00
// : << output event (Process exited)
// : << output event (Detaching)
2020-11-12 23:24:31 +00:00
// : 13 << disconnect
2020-03-10 19:29:06 +00:00
// This test exhaustively tests Seq and RequestSeq on all messages from the
// server. Other tests do not necessarily need to repeat all these checks.
2020-12-28 17:14:15 +00:00
func TestLaunchStopOnEntry ( t * testing . T ) {
2020-02-15 19:52:53 +00:00
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-03-10 19:29:06 +00:00
// 1 >> initialize, << initialize
2020-02-15 19:52:53 +00:00
client . InitializeRequest ( )
2021-05-06 09:11:45 +00:00
initResp := client . ExpectInitializeResponseAndCapabilities ( t )
2020-03-10 19:29:06 +00:00
if initResp . Seq != 0 || initResp . RequestSeq != 1 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=1" , initResp )
2020-02-24 17:36:34 +00:00
}
2020-02-15 19:52:53 +00:00
2020-03-10 19:29:06 +00:00
// 2 >> launch, << initialized, << launch
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "exec" , fixture . Path , stopOnEntry )
2020-03-10 19:29:06 +00:00
initEvent := client . ExpectInitializedEvent ( t )
if initEvent . Seq != 0 {
t . Errorf ( "\ngot %#v\nwant Seq=0" , initEvent )
2020-02-24 17:36:34 +00:00
}
launchResp := client . ExpectLaunchResponse ( t )
2020-03-10 19:29:06 +00:00
if launchResp . Seq != 0 || launchResp . RequestSeq != 2 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=2" , launchResp )
}
// 3 >> setBreakpoints, << setBreakpoints
client . SetBreakpointsRequest ( fixture . Source , nil )
sbpResp := client . ExpectSetBreakpointsResponse ( t )
if sbpResp . Seq != 0 || sbpResp . RequestSeq != 3 || len ( sbpResp . Body . Breakpoints ) != 0 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0" , sbpResp )
2020-02-24 17:36:34 +00:00
}
2020-02-15 19:52:53 +00:00
2020-03-10 19:29:06 +00:00
// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
2020-02-15 19:52:53 +00:00
client . SetExceptionBreakpointsRequest ( )
2020-03-10 19:29:06 +00:00
sebpResp := client . ExpectSetExceptionBreakpointsResponse ( t )
if sebpResp . Seq != 0 || sebpResp . RequestSeq != 4 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=4" , sebpResp )
2020-02-24 17:36:34 +00:00
}
2020-02-15 19:52:53 +00:00
2020-03-10 19:29:06 +00:00
// 5 >> configurationDone, << stopped, << configurationDone
2020-02-15 19:52:53 +00:00
client . ConfigurationDoneRequest ( )
2020-02-24 17:36:34 +00:00
stopEvent := client . ExpectStoppedEvent ( t )
if stopEvent . Seq != 0 ||
2020-04-01 19:51:31 +00:00
stopEvent . Body . Reason != "entry" ||
2020-02-24 17:36:34 +00:00
stopEvent . Body . ThreadId != 1 ||
! stopEvent . Body . AllThreadsStopped {
2020-04-01 19:51:31 +00:00
t . Errorf ( "\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}" , stopEvent )
2020-02-24 17:36:34 +00:00
}
cdResp := client . ExpectConfigurationDoneResponse ( t )
2020-03-10 19:29:06 +00:00
if cdResp . Seq != 0 || cdResp . RequestSeq != 5 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=5" , cdResp )
}
// 6 >> threads, << threads
client . ThreadsRequest ( )
tResp := client . ExpectThreadsResponse ( t )
if tResp . Seq != 0 || tResp . RequestSeq != 6 || len ( tResp . Body . Threads ) != 1 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1" , tResp )
}
if tResp . Body . Threads [ 0 ] . Id != 1 || tResp . Body . Threads [ 0 ] . Name != "Dummy" {
t . Errorf ( "\ngot %#v\nwant Id=1, Name=\"Dummy\"" , tResp )
2020-02-24 17:36:34 +00:00
}
2020-02-15 19:52:53 +00:00
2020-03-10 19:29:06 +00:00
// 7 >> threads, << threads
client . ThreadsRequest ( )
tResp = client . ExpectThreadsResponse ( t )
if tResp . Seq != 0 || tResp . RequestSeq != 7 || len ( tResp . Body . Threads ) != 1 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=7 len(Threads)=1" , tResp )
}
2020-11-12 23:24:31 +00:00
// 8 >> stackTrace, << error
2020-07-01 18:01:17 +00:00
client . StackTraceRequest ( 1 , 0 , 20 )
2021-05-06 09:11:45 +00:00
stResp := client . ExpectInvisibleErrorResponse ( t )
2020-07-01 18:01:17 +00:00
if stResp . Seq != 0 || stResp . RequestSeq != 8 || stResp . Body . Error . Format != "Unable to produce stack trace: unknown goroutine 1" {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=8 Format=\"Unable to produce stack trace: unknown goroutine 1\"" , stResp )
2020-03-10 19:29:06 +00:00
}
2020-11-12 23:24:31 +00:00
// 9 >> stackTrace, << error
2020-07-01 18:01:17 +00:00
client . StackTraceRequest ( 1 , 0 , 20 )
2021-05-06 09:11:45 +00:00
stResp = client . ExpectInvisibleErrorResponse ( t )
2020-07-01 18:01:17 +00:00
if stResp . Seq != 0 || stResp . RequestSeq != 9 || stResp . Body . Error . Id != 2004 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=9 Id=2004" , stResp )
2020-03-10 19:29:06 +00:00
}
2020-11-12 23:24:31 +00:00
// 10 >> evaluate, << error
client . EvaluateRequest ( "foo" , 0 /*no frame specified*/ , "repl" )
2021-05-06 09:11:45 +00:00
erResp := client . ExpectInvisibleErrorResponse ( t )
2020-11-12 23:24:31 +00:00
if erResp . Seq != 0 || erResp . RequestSeq != 10 || erResp . Body . Error . Id != 2009 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=10 Id=2009" , erResp )
}
// 11 >> evaluate, << evaluate
client . EvaluateRequest ( "1+1" , 0 /*no frame specified*/ , "repl" )
evResp := client . ExpectEvaluateResponse ( t )
if evResp . Seq != 0 || evResp . RequestSeq != 11 || evResp . Body . Result != "2" {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2" , evResp )
}
// 12 >> continue, << continue, << terminated
2020-02-15 19:52:53 +00:00
client . ContinueRequest ( 1 )
2020-02-24 17:36:34 +00:00
contResp := client . ExpectContinueResponse ( t )
2020-11-12 23:24:31 +00:00
if contResp . Seq != 0 || contResp . RequestSeq != 12 || ! contResp . Body . AllThreadsContinued {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=12 Body.AllThreadsContinued=true" , contResp )
2020-03-10 19:29:06 +00:00
}
termEvent := client . ExpectTerminatedEvent ( t )
if termEvent . Seq != 0 {
t . Errorf ( "\ngot %#v\nwant Seq=0" , termEvent )
2020-02-24 17:36:34 +00:00
}
2020-11-12 23:24:31 +00:00
// 13 >> disconnect, << disconnect
2020-03-10 19:29:06 +00:00
client . DisconnectRequest ( )
2021-04-12 21:50:15 +00:00
oep := client . ExpectOutputEventProcessExited ( t , 0 )
if oep . Seq != 0 || oep . Body . Category != "console" {
t . Errorf ( "\ngot %#v\nwant Seq=0 Category='console'" , oep )
}
oed := client . ExpectOutputEventDetaching ( t )
2021-04-21 20:28:15 +00:00
if oed . Seq != 0 || oed . Body . Category != "console" {
2021-04-12 21:50:15 +00:00
t . Errorf ( "\ngot %#v\nwant Seq=0 Category='console'" , oed )
}
2020-03-10 19:29:06 +00:00
dResp := client . ExpectDisconnectResponse ( t )
2020-11-12 23:24:31 +00:00
if dResp . Seq != 0 || dResp . RequestSeq != 13 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=13" , dResp )
2020-02-24 17:36:34 +00:00
}
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2020-03-10 19:29:06 +00:00
} )
}
2020-02-15 19:52:53 +00:00
2020-12-28 17:14:15 +00:00
// TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request.
func TestAttachStopOnEntry ( t * testing . T ) {
if runtime . GOOS == "freebsd" {
t . SkipNow ( )
}
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
// Start the program to attach to
cmd := exec . Command ( fixture . Path )
2021-01-06 17:07:47 +00:00
stdout , err := cmd . StdoutPipe ( )
if err != nil {
t . Fatal ( err )
}
2020-12-28 17:14:15 +00:00
cmd . Stderr = os . Stderr
if err := cmd . Start ( ) ; err != nil {
t . Fatal ( err )
}
2021-01-06 17:07:47 +00:00
// Wait for output.
// This will give the target process time to initialize the runtime before we attach,
// so we can rely on having goroutines when they are requested on attach.
scanOut := bufio . NewScanner ( stdout )
scanOut . Scan ( )
if scanOut . Text ( ) != "past main" {
t . Errorf ( "expected loopprog.go to output \"past main\"" )
}
2020-12-28 17:14:15 +00:00
// 1 >> initialize, << initialize
client . InitializeRequest ( )
2021-05-06 09:11:45 +00:00
initResp := client . ExpectInitializeResponseAndCapabilities ( t )
2020-12-28 17:14:15 +00:00
if initResp . Seq != 0 || initResp . RequestSeq != 1 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=1" , initResp )
}
2021-01-06 17:07:47 +00:00
// 2 >> attach, << initialized, << attach
2020-12-28 17:14:15 +00:00
client . AttachRequest (
2021-07-18 09:37:41 +00:00
map [ string ] interface { } { "mode" : "local" , "processId" : cmd . Process . Pid , "stopOnEntry" : true , "backend" : "default" } )
2020-12-28 17:14:15 +00:00
initEvent := client . ExpectInitializedEvent ( t )
if initEvent . Seq != 0 {
t . Errorf ( "\ngot %#v\nwant Seq=0" , initEvent )
}
attachResp := client . ExpectAttachResponse ( t )
if attachResp . Seq != 0 || attachResp . RequestSeq != 2 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=2" , attachResp )
}
// 3 >> setBreakpoints, << setBreakpoints
client . SetBreakpointsRequest ( fixture . Source , nil )
sbpResp := client . ExpectSetBreakpointsResponse ( t )
if sbpResp . Seq != 0 || sbpResp . RequestSeq != 3 || len ( sbpResp . Body . Breakpoints ) != 0 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0" , sbpResp )
}
// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
client . SetExceptionBreakpointsRequest ( )
sebpResp := client . ExpectSetExceptionBreakpointsResponse ( t )
if sebpResp . Seq != 0 || sebpResp . RequestSeq != 4 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=4" , sebpResp )
}
// 5 >> configurationDone, << stopped, << configurationDone
client . ConfigurationDoneRequest ( )
stopEvent := client . ExpectStoppedEvent ( t )
if stopEvent . Seq != 0 ||
stopEvent . Body . Reason != "entry" ||
stopEvent . Body . ThreadId != 1 ||
! stopEvent . Body . AllThreadsStopped {
t . Errorf ( "\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}" , stopEvent )
}
cdResp := client . ExpectConfigurationDoneResponse ( t )
if cdResp . Seq != 0 || cdResp . RequestSeq != 5 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=5" , cdResp )
}
// 6 >> threads, << threads
client . ThreadsRequest ( )
tResp := client . ExpectThreadsResponse ( t )
2021-01-06 17:07:47 +00:00
// Expect main goroutine plus runtime at this point.
if tResp . Seq != 0 || tResp . RequestSeq != 6 || len ( tResp . Body . Threads ) < 2 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)>1" , tResp )
2020-12-28 17:14:15 +00:00
}
// 7 >> threads, << threads
client . ThreadsRequest ( )
2021-01-06 17:07:47 +00:00
client . ExpectThreadsResponse ( t )
2020-12-28 17:14:15 +00:00
2021-01-06 17:07:47 +00:00
// 8 >> stackTrace, << response
2020-12-28 17:14:15 +00:00
client . StackTraceRequest ( 1 , 0 , 20 )
2021-01-06 17:07:47 +00:00
client . ExpectStackTraceResponse ( t )
2020-12-28 17:14:15 +00:00
2021-01-06 17:07:47 +00:00
// 9 >> stackTrace, << response
2020-12-28 17:14:15 +00:00
client . StackTraceRequest ( 1 , 0 , 20 )
2021-01-06 17:07:47 +00:00
client . ExpectStackTraceResponse ( t )
2020-12-28 17:14:15 +00:00
// 10 >> evaluate, << error
client . EvaluateRequest ( "foo" , 0 /*no frame specified*/ , "repl" )
2021-05-06 09:11:45 +00:00
erResp := client . ExpectInvisibleErrorResponse ( t )
2020-12-28 17:14:15 +00:00
if erResp . Seq != 0 || erResp . RequestSeq != 10 || erResp . Body . Error . Id != 2009 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=10 Id=2009" , erResp )
}
// 11 >> evaluate, << evaluate
client . EvaluateRequest ( "1+1" , 0 /*no frame specified*/ , "repl" )
evResp := client . ExpectEvaluateResponse ( t )
if evResp . Seq != 0 || evResp . RequestSeq != 11 || evResp . Body . Result != "2" {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2" , evResp )
}
2021-05-04 19:49:52 +00:00
// 12 >> continue, << continue
client . ContinueRequest ( 1 )
cResp := client . ExpectContinueResponse ( t )
if cResp . Seq != 0 || cResp . RequestSeq != 12 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=12" , cResp )
}
2020-12-28 17:14:15 +00:00
// TODO(polina): once https://github.com/go-delve/delve/issues/2259 is
// fixed, test with kill=false.
2021-05-04 19:49:52 +00:00
// 13 >> disconnect, << disconnect
2020-12-28 17:14:15 +00:00
client . DisconnectRequestWithKillOption ( true )
2021-05-04 19:49:52 +00:00
2021-06-10 16:30:31 +00:00
// Disconnect consists of Halt + Detach.
// Halt interrupts command in progress, which triggers
// a stopped event in parallel with the disconnect
// sequence. It might arrive before or during the sequence
// or never if the server exits before it is sent.
2021-05-17 16:48:48 +00:00
msg := expectMessageFilterStopped ( t , client )
2021-05-26 15:15:39 +00:00
client . CheckOutputEvent ( t , msg )
2021-05-17 16:48:48 +00:00
msg = expectMessageFilterStopped ( t , client )
2021-05-26 15:15:39 +00:00
client . CheckDisconnectResponse ( t , msg )
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2021-05-26 15:15:39 +00:00
2021-05-17 16:48:48 +00:00
// If this call to KeepAlive isn't here there's a chance that stdout will
// be garbage collected (since it is no longer alive long before this
// point), when that happens, on unix-like OSes, the read end of the pipe
// will be closed by the finalizer and the target process will die by
// SIGPIPE, which the rest of this test does not expect.
runtime . KeepAlive ( stdout )
2020-12-28 17:14:15 +00:00
} )
}
2020-03-10 19:29:06 +00:00
// Like the test above, except the program is configured to continue on entry.
func TestContinueOnEntry ( t * testing . T ) {
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
// 1 >> initialize, << initialize
client . InitializeRequest ( )
2021-05-06 09:11:45 +00:00
client . ExpectInitializeResponseAndCapabilities ( t )
2020-03-10 19:29:06 +00:00
// 2 >> launch, << initialized, << launch
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
client . ExpectInitializedEvent ( t )
client . ExpectLaunchResponse ( t )
// 3 >> setBreakpoints, << setBreakpoints
client . SetBreakpointsRequest ( fixture . Source , nil )
client . ExpectSetBreakpointsResponse ( t )
// 4 >> setExceptionBreakpoints, << setExceptionBreakpoints
client . SetExceptionBreakpointsRequest ( )
client . ExpectSetExceptionBreakpointsResponse ( t )
// 5 >> configurationDone, << configurationDone
client . ConfigurationDoneRequest ( )
client . ExpectConfigurationDoneResponse ( t )
2021-05-04 19:49:52 +00:00
// "Continue" happens behind the scenes on another goroutine
2020-03-10 19:29:06 +00:00
2021-08-06 05:19:27 +00:00
client . ExpectTerminatedEvent ( t )
2021-05-04 19:49:52 +00:00
// 6 >> threads, << threads
client . ThreadsRequest ( )
2020-03-10 19:29:06 +00:00
tResp := client . ExpectThreadsResponse ( t )
2021-08-06 05:19:27 +00:00
if tResp . Seq != 0 || tResp . RequestSeq != 6 || len ( tResp . Body . Threads ) != 1 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1" , tResp )
2021-07-22 15:52:04 +00:00
}
if tResp . Body . Threads [ 0 ] . Id != 1 || tResp . Body . Threads [ 0 ] . Name != "Dummy" {
t . Errorf ( "\ngot %#v\nwant Id=1, Name=\"Dummy\"" , tResp )
2020-03-10 19:29:06 +00:00
}
2021-08-06 05:19:27 +00:00
// 7 >> disconnect, << disconnect
2020-02-15 19:52:53 +00:00
client . DisconnectRequest ( )
2021-04-12 21:50:15 +00:00
client . ExpectOutputEventProcessExited ( t , 0 )
client . ExpectOutputEventDetaching ( t )
2020-02-24 17:36:34 +00:00
dResp := client . ExpectDisconnectResponse ( t )
2021-08-06 05:19:27 +00:00
if dResp . Seq != 0 || dResp . RequestSeq != 7 {
t . Errorf ( "\ngot %#v\nwant Seq=0, RequestSeq=7" , dResp )
2020-02-24 17:36:34 +00:00
}
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2020-02-15 19:52:53 +00:00
} )
}
2020-10-02 16:18:33 +00:00
// TestPreSetBreakpoint corresponds to a debug session that is configured to
2020-03-10 19:29:06 +00:00
// continue on entry with a pre-set breakpoint.
2020-10-02 16:18:33 +00:00
func TestPreSetBreakpoint ( t * testing . T ) {
2020-02-15 19:52:53 +00:00
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
client . InitializeRequest ( )
2021-05-06 09:11:45 +00:00
client . ExpectInitializeResponseAndCapabilities ( t )
2020-02-15 19:52:53 +00:00
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
2020-02-24 17:36:34 +00:00
client . ExpectInitializedEvent ( t )
2020-03-10 19:29:06 +00:00
client . ExpectLaunchResponse ( t )
2020-02-15 19:52:53 +00:00
2020-10-02 16:18:33 +00:00
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 8 } )
2020-02-24 17:36:34 +00:00
sResp := client . ExpectSetBreakpointsResponse ( t )
if len ( sResp . Body . Breakpoints ) != 1 {
t . Errorf ( "got %#v, want len(Breakpoints)=1" , sResp )
}
bkpt0 := sResp . Body . Breakpoints [ 0 ]
2021-02-21 16:02:42 +00:00
if ! bkpt0 . Verified || bkpt0 . Line != 8 || bkpt0 . Id != 1 || bkpt0 . Source . Name != filepath . Base ( fixture . Source ) || bkpt0 . Source . Path != fixture . Source {
t . Errorf ( "got breakpoints[0] = %#v, want Verified=true, Line=8, Id=1, Path=%q" , bkpt0 , fixture . Source )
2020-02-24 17:36:34 +00:00
}
2020-02-15 19:52:53 +00:00
client . SetExceptionBreakpointsRequest ( )
2020-02-24 17:36:34 +00:00
client . ExpectSetExceptionBreakpointsResponse ( t )
2020-02-15 19:52:53 +00:00
client . ConfigurationDoneRequest ( )
2020-03-10 19:29:06 +00:00
client . ExpectConfigurationDoneResponse ( t )
2021-05-04 19:49:52 +00:00
// This triggers "continue" on a separate goroutine
2020-03-10 19:29:06 +00:00
2021-05-04 19:49:52 +00:00
client . ThreadsRequest ( )
// Since we are in async mode while running, we might receive messages in either order.
for i := 0 ; i < 2 ; i ++ {
msg := client . ExpectMessage ( t )
switch m := msg . ( type ) {
case * dap . ThreadsResponse :
2021-05-15 05:34:59 +00:00
// If the thread request arrived while the program was running, we expect to get the dummy response
// with a single goroutine "Current".
// If the thread request arrived after the stop, we should get the goroutine stopped at main.Increment.
if ( len ( m . Body . Threads ) != 1 || m . Body . Threads [ 0 ] . Id != - 1 || m . Body . Threads [ 0 ] . Name != "Current" ) &&
( len ( m . Body . Threads ) < 1 || m . Body . Threads [ 0 ] . Id != 1 || ! strings . HasPrefix ( m . Body . Threads [ 0 ] . Name , "* [Go 1] main.Increment" ) ) {
t . Errorf ( "\ngot %#v\nwant Id=-1, Name=\"Current\" or Id=1, Name=\"* [Go 1] main.Increment ...\"" , m . Body . Threads )
2021-05-04 19:49:52 +00:00
}
case * dap . StoppedEvent :
if m . Body . Reason != "breakpoint" || m . Body . ThreadId != 1 || ! m . Body . AllThreadsStopped {
t . Errorf ( "got %#v, want Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}" , m )
}
default :
t . Fatalf ( "got %#v, want ThreadsResponse or StoppedEvent" , m )
}
2020-02-24 17:36:34 +00:00
}
2020-03-10 19:29:06 +00:00
2021-05-04 19:49:52 +00:00
// Threads-StackTrace-Scopes-Variables request waterfall is
// triggered on stop event.
2020-03-10 19:29:06 +00:00
client . ThreadsRequest ( )
tResp := client . ExpectThreadsResponse ( t )
if len ( tResp . Body . Threads ) < 2 { // 1 main + runtime
t . Errorf ( "\ngot %#v\nwant len(Threads)>1" , tResp . Body . Threads )
}
2020-11-30 17:43:37 +00:00
reMain , _ := regexp . Compile ( ` \* \[Go 1\] main.Increment \(Thread [0-9]+\) ` )
wantMain := dap . Thread { Id : 1 , Name : "* [Go 1] main.Increment (Thread ...)" }
wantRuntime := dap . Thread { Id : 2 , Name : "[Go 2] runtime.gopark" }
2020-03-10 19:29:06 +00:00
for _ , got := range tResp . Body . Threads {
2021-07-19 15:09:18 +00:00
if got . Id != 1 && ! reMain . MatchString ( got . Name ) && ! ( strings . Contains ( got . Name , "runtime." ) || strings . Contains ( got . Name , "runtime/" ) ) {
2020-03-10 19:29:06 +00:00
t . Errorf ( "\ngot %#v\nwant []dap.Thread{%#v, %#v, ...}" , tResp . Body . Threads , wantMain , wantRuntime )
}
}
2020-07-01 18:01:17 +00:00
client . StackTraceRequest ( 1 , 0 , 20 )
stResp := client . ExpectStackTraceResponse ( t )
if stResp . Body . TotalFrames != 6 {
t . Errorf ( "\ngot %#v\nwant TotalFrames=6" , stResp . Body . TotalFrames )
}
if len ( stResp . Body . StackFrames ) != 6 {
t . Errorf ( "\ngot %#v\nwant len(StackFrames)=6" , stResp . Body . StackFrames )
} else {
2021-05-26 15:15:39 +00:00
checkFrame := func ( got dap . StackFrame , id int , name string , sourceName string , line int ) {
2020-07-01 18:01:17 +00:00
t . Helper ( )
if got . Id != id || got . Name != name {
2020-07-08 17:20:05 +00:00
t . Errorf ( "\ngot %#v\nwant Id=%d Name=%s" , got , id , name )
2020-07-01 18:01:17 +00:00
}
if ( sourceName != "" && got . Source . Name != sourceName ) || ( line > 0 && got . Line != line ) {
2020-07-08 17:20:05 +00:00
t . Errorf ( "\ngot %#v\nwant Source.Name=%s Line=%d" , got , sourceName , line )
2020-07-01 18:01:17 +00:00
}
}
2021-05-26 15:15:39 +00:00
checkFrame ( stResp . Body . StackFrames [ 0 ] , 1000 , "main.Increment" , "increment.go" , 8 )
checkFrame ( stResp . Body . StackFrames [ 1 ] , 1001 , "main.Increment" , "increment.go" , 11 )
checkFrame ( stResp . Body . StackFrames [ 2 ] , 1002 , "main.Increment" , "increment.go" , 11 )
checkFrame ( stResp . Body . StackFrames [ 3 ] , 1003 , "main.main" , "increment.go" , 17 )
checkFrame ( stResp . Body . StackFrames [ 4 ] , 1004 , "runtime.main" , "proc.go" , - 1 )
checkFrame ( stResp . Body . StackFrames [ 5 ] , 1005 , "runtime.goexit" , "" , - 1 )
2020-07-01 18:01:17 +00:00
}
2020-08-11 15:34:27 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
if len ( scopes . Body . Scopes ) > 1 {
t . Errorf ( "\ngot %#v\nwant len(Scopes)=1 (Locals)" , scopes )
2020-09-15 20:14:55 +00:00
}
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
2020-08-11 15:34:27 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-08-11 15:34:27 +00:00
args := client . ExpectVariablesResponse ( t )
2021-10-05 17:35:14 +00:00
checkChildren ( t , args , "Locals" , 2 )
2021-09-16 10:19:01 +00:00
checkVarExact ( t , args , 0 , "y" , "y" , "0 = 0x0" , "uint" , noChildren )
checkVarExact ( t , args , 1 , "~r1" , "" , "0 = 0x0" , "uint" , noChildren )
2020-08-11 15:34:27 +00:00
2020-07-01 18:01:17 +00:00
client . ContinueRequest ( 1 )
2020-08-24 17:21:51 +00:00
ctResp := client . ExpectContinueResponse ( t )
if ! ctResp . Body . AllThreadsContinued {
t . Errorf ( "\ngot %#v\nwant AllThreadsContinued=true" , ctResp . Body )
}
2020-07-01 18:01:17 +00:00
// "Continue" is triggered after the response is sent
client . ExpectTerminatedEvent ( t )
2021-05-17 17:37:15 +00:00
// Pause request after termination should result in an error.
// But in certain cases this request actually succeeds.
client . PauseRequest ( 1 )
switch r := client . ExpectMessage ( t ) . ( type ) {
case * dap . ErrorResponse :
if r . Message != "Unable to halt execution" {
t . Errorf ( "\ngot %#v\nwant Message='Unable to halt execution'" , r )
}
case * dap . PauseResponse :
default :
t . Fatalf ( "Unexpected response type: expect error or pause, got %#v" , r )
}
2020-07-01 18:01:17 +00:00
client . DisconnectRequest ( )
2021-04-12 21:50:15 +00:00
client . ExpectOutputEventProcessExited ( t , 0 )
client . ExpectOutputEventDetaching ( t )
2020-07-01 18:01:17 +00:00
client . ExpectDisconnectResponse ( t )
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2020-07-01 18:01:17 +00:00
} )
}
2021-07-29 17:34:01 +00:00
// checkStackFramesExact is a helper for verifying the values within StackTraceResponse.
2020-11-30 17:43:09 +00:00
// wantStartName - name of the first returned frame (ignored if "")
// wantStartLine - file line of the first returned frame (ignored if <0).
2020-07-08 17:20:05 +00:00
// wantStartID - id of the first frame returned (ignored if wantFrames is 0).
2020-11-30 17:43:09 +00:00
// wantFrames - number of frames returned (length of StackTraceResponse.Body.StackFrames array).
// wantTotalFrames - total number of stack frames available (StackTraceResponse.Body.TotalFrames).
2021-07-29 17:34:01 +00:00
func checkStackFramesExact ( t * testing . T , got * dap . StackTraceResponse ,
2020-11-30 17:43:09 +00:00
wantStartName string , wantStartLine , wantStartID , wantFrames , wantTotalFrames int ) {
t . Helper ( )
2021-07-29 17:34:01 +00:00
checkStackFramesNamed ( "" , t , got , wantStartName , wantStartLine , wantStartID , wantFrames , wantTotalFrames , true )
2020-11-30 17:43:09 +00:00
}
2021-07-29 17:34:01 +00:00
func checkStackFramesHasMore ( t * testing . T , got * dap . StackTraceResponse ,
2020-11-30 17:43:09 +00:00
wantStartName string , wantStartLine , wantStartID , wantFrames , wantTotalFrames int ) {
2020-07-08 17:20:05 +00:00
t . Helper ( )
2021-07-29 17:34:01 +00:00
checkStackFramesNamed ( "" , t , got , wantStartName , wantStartLine , wantStartID , wantFrames , wantTotalFrames , false )
}
func checkStackFramesNamed ( testName string , t * testing . T , got * dap . StackTraceResponse ,
wantStartName string , wantStartLine , wantStartID , wantFrames , wantTotalFrames int , totalExact bool ) {
t . Helper ( )
if totalExact && got . Body . TotalFrames != wantTotalFrames {
2020-11-30 17:43:09 +00:00
t . Errorf ( "%s\ngot %#v\nwant TotalFrames=%d" , testName , got . Body . TotalFrames , wantTotalFrames )
2021-07-29 17:34:01 +00:00
} else if ! totalExact && got . Body . TotalFrames < wantTotalFrames {
t . Errorf ( "%s\ngot %#v\nwant TotalFrames>=%d" , testName , got . Body . TotalFrames , wantTotalFrames )
2020-07-08 17:20:05 +00:00
}
2021-07-29 17:34:01 +00:00
2020-07-08 17:20:05 +00:00
if len ( got . Body . StackFrames ) != wantFrames {
2020-11-30 17:43:09 +00:00
t . Errorf ( "%s\ngot len(StackFrames)=%d\nwant %d" , testName , len ( got . Body . StackFrames ) , wantFrames )
2020-07-08 17:20:05 +00:00
} else {
// Verify that frame ids are consecutive numbers starting at wantStartID
for i := 0 ; i < wantFrames ; i ++ {
if got . Body . StackFrames [ i ] . Id != wantStartID + i {
2020-11-30 17:43:09 +00:00
t . Errorf ( "%s\ngot %#v\nwant Id=%d" , testName , got . Body . StackFrames [ i ] , wantStartID + i )
2020-07-08 17:20:05 +00:00
}
}
2020-11-30 17:43:09 +00:00
// Verify the name and line corresponding to the first returned frame (if any).
2020-07-08 17:20:05 +00:00
// This is useful when the first frame is the frame corresponding to the breakpoint at
2020-11-30 17:43:09 +00:00
// a predefined line. Line values < 0 are a signal to skip the check (which can be useful
2020-07-08 17:20:05 +00:00
// for frames in the third-party code, where we do not control the lines).
if wantFrames > 0 && wantStartLine > 0 && got . Body . StackFrames [ 0 ] . Line != wantStartLine {
2020-11-30 17:43:09 +00:00
t . Errorf ( "%s\ngot Line=%d\nwant %d" , testName , got . Body . StackFrames [ 0 ] . Line , wantStartLine )
}
if wantFrames > 0 && wantStartName != "" && got . Body . StackFrames [ 0 ] . Name != wantStartName {
t . Errorf ( "%s\ngot Name=%s\nwant %s" , testName , got . Body . StackFrames [ 0 ] . Name , wantStartName )
2020-07-08 17:20:05 +00:00
}
}
}
2021-05-26 15:15:39 +00:00
// checkScope is a helper for verifying the values within a ScopesResponse.
2020-08-11 15:34:27 +00:00
// i - index of the scope within ScopesRespose.Body.Scopes array
// name - name of the scope
// varRef - reference to retrieve variables of this scope
2021-05-26 15:15:39 +00:00
func checkScope ( t * testing . T , got * dap . ScopesResponse , i int , name string , varRef int ) {
2020-08-11 15:34:27 +00:00
t . Helper ( )
if len ( got . Body . Scopes ) <= i {
t . Errorf ( "\ngot %d\nwant len(Scopes)>%d" , len ( got . Body . Scopes ) , i )
}
goti := got . Body . Scopes [ i ]
if goti . Name != name || goti . VariablesReference != varRef || goti . Expensive {
t . Errorf ( "\ngot %#v\nwant Name=%q VariablesReference=%d Expensive=false" , goti , name , varRef )
}
}
2021-05-26 15:15:39 +00:00
// checkChildren is a helper for verifying the number of variables within a VariablesResponse.
2020-09-15 20:14:55 +00:00
// parentName - pseudoname of the enclosing variable or scope (used for error message only)
2020-08-11 15:34:27 +00:00
// numChildren - number of variables/fields/elements of this variable
2021-05-26 15:15:39 +00:00
func checkChildren ( t * testing . T , got * dap . VariablesResponse , parentName string , numChildren int ) {
2020-08-11 15:34:27 +00:00
t . Helper ( )
2021-07-15 21:15:49 +00:00
if got . Body . Variables == nil {
t . Errorf ( "\ngot %s children=%#v want []" , parentName , got . Body . Variables )
}
2020-08-11 15:34:27 +00:00
if len ( got . Body . Variables ) != numChildren {
2020-09-15 20:14:55 +00:00
t . Errorf ( "\ngot len(%s)=%d (children=%#v)\nwant len=%d" , parentName , len ( got . Body . Variables ) , got . Body . Variables , numChildren )
2020-08-11 15:34:27 +00:00
}
}
2021-05-26 15:15:39 +00:00
// checkVar is a helper for verifying the values within a VariablesResponse.
2020-08-11 15:34:27 +00:00
// i - index of the variable within VariablesRespose.Body.Variables array (-1 will search all vars for a match)
// name - name of the variable
2021-01-14 18:53:12 +00:00
// evalName - fully qualified variable name or alternative expression to load this variable
2020-08-11 15:34:27 +00:00
// value - the value of the variable
2021-01-14 18:53:12 +00:00
// useExactMatch - true if name, evalName and value are to be compared to exactly, false if to be used as regex
2020-08-11 15:34:27 +00:00
// hasRef - true if the variable should have children and therefore a non-0 variable reference
// ref - reference to retrieve children of this variable (0 if none)
2021-06-10 16:34:20 +00:00
func checkVar ( t * testing . T , got * dap . VariablesResponse , i int , name , evalName , value , typ string , useExactMatch , hasRef bool , indexed , named int ) ( ref int ) {
2020-08-11 15:34:27 +00:00
t . Helper ( )
if len ( got . Body . Variables ) <= i {
2020-09-15 20:14:55 +00:00
t . Errorf ( "\ngot len=%d (children=%#v)\nwant len>%d" , len ( got . Body . Variables ) , got . Body . Variables , i )
2020-08-11 15:34:27 +00:00
return
}
if i < 0 {
for vi , v := range got . Body . Variables {
if v . Name == name {
i = vi
break
}
}
}
if i < 0 {
2021-06-22 15:14:47 +00:00
t . Errorf ( "\ngot %#v\nwant Variables[i].Name=%q (not found)" , got , name )
2020-08-11 15:34:27 +00:00
return 0
}
goti := got . Body . Variables [ i ]
2021-01-08 17:17:54 +00:00
matchedName := false
if useExactMatch {
2021-08-22 14:12:07 +00:00
if strings . HasPrefix ( name , "~r" ) {
matchedName = strings . HasPrefix ( goti . Name , "~r" )
} else {
matchedName = ( goti . Name == name )
}
2021-01-08 17:17:54 +00:00
} else {
matchedName , _ = regexp . MatchString ( name , goti . Name )
}
if ! matchedName || ( goti . VariablesReference > 0 ) != hasRef {
2020-08-11 15:34:27 +00:00
t . Errorf ( "\ngot %#v\nwant Name=%q hasRef=%t" , goti , name , hasRef )
}
2021-01-14 18:53:12 +00:00
matchedEvalName := false
if useExactMatch {
matchedEvalName = ( goti . EvaluateName == evalName )
} else {
matchedEvalName , _ = regexp . MatchString ( evalName , goti . EvaluateName )
}
if ! matchedEvalName {
t . Errorf ( "\ngot %q\nwant EvaluateName=%q" , goti . EvaluateName , evalName )
}
2021-01-08 17:17:54 +00:00
matchedValue := false
2020-08-11 15:34:27 +00:00
if useExactMatch {
2021-01-08 17:17:54 +00:00
matchedValue = ( goti . Value == value )
2020-08-11 15:34:27 +00:00
} else {
2021-01-08 17:17:54 +00:00
matchedValue , _ = regexp . MatchString ( value , goti . Value )
2020-08-11 15:34:27 +00:00
}
2021-01-08 17:17:54 +00:00
if ! matchedValue {
2020-08-11 15:34:27 +00:00
t . Errorf ( "\ngot %s=%q\nwant %q" , name , goti . Value , value )
}
2021-05-10 18:34:42 +00:00
matchedType := false
if useExactMatch {
matchedType = ( goti . Type == typ )
} else {
matchedType , _ = regexp . MatchString ( typ , goti . Type )
}
if ! matchedType {
t . Errorf ( "\ngot %s=%q\nwant %q" , name , goti . Type , typ )
}
2021-06-10 16:34:20 +00:00
if indexed >= 0 && goti . IndexedVariables != indexed {
t . Errorf ( "\ngot %s=%d indexed\nwant %d indexed" , name , goti . IndexedVariables , indexed )
}
if named >= 0 && goti . NamedVariables != named {
t . Errorf ( "\ngot %s=%d named\nwant %d named" , name , goti . NamedVariables , named )
}
2020-08-11 15:34:27 +00:00
return goti . VariablesReference
}
2021-05-26 15:15:39 +00:00
// checkVarExact is a helper like checkVar that matches value exactly.
func checkVarExact ( t * testing . T , got * dap . VariablesResponse , i int , name , evalName , value , typ string , hasRef bool ) ( ref int ) {
2020-08-11 15:34:27 +00:00
t . Helper ( )
2021-06-10 16:34:20 +00:00
return checkVarExactIndexed ( t , got , i , name , evalName , value , typ , hasRef , - 1 , - 1 )
}
// checkVarExact is a helper like checkVar that matches value exactly.
func checkVarExactIndexed ( t * testing . T , got * dap . VariablesResponse , i int , name , evalName , value , typ string , hasRef bool , indexed , named int ) ( ref int ) {
t . Helper ( )
return checkVar ( t , got , i , name , evalName , value , typ , true , hasRef , indexed , named )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
// checkVarRegex is a helper like checkVar that treats value, evalName or name as a regex.
func checkVarRegex ( t * testing . T , got * dap . VariablesResponse , i int , name , evalName , value , typ string , hasRef bool ) ( ref int ) {
2020-08-11 15:34:27 +00:00
t . Helper ( )
2021-06-10 16:34:20 +00:00
return checkVarRegexIndexed ( t , got , i , name , evalName , value , typ , hasRef , - 1 , - 1 )
}
// checkVarRegex is a helper like checkVar that treats value, evalName or name as a regex.
func checkVarRegexIndexed ( t * testing . T , got * dap . VariablesResponse , i int , name , evalName , value , typ string , hasRef bool , indexed , named int ) ( ref int ) {
t . Helper ( )
return checkVar ( t , got , i , name , evalName , value , typ , false , hasRef , indexed , named )
2021-01-14 18:53:12 +00:00
}
2021-05-17 16:48:48 +00:00
func expectMessageFilterStopped ( t * testing . T , client * daptest . Client ) dap . Message {
msg := client . ExpectMessage ( t )
if _ , isStopped := msg . ( * dap . StoppedEvent ) ; isStopped {
msg = client . ExpectMessage ( t )
}
return msg
}
2021-01-14 18:53:12 +00:00
// validateEvaluateName issues an evaluate request with evaluateName of a variable and
// confirms that it succeeds and returns the same variable record as the original.
func validateEvaluateName ( t * testing . T , client * daptest . Client , got * dap . VariablesResponse , i int ) {
t . Helper ( )
original := got . Body . Variables [ i ]
client . EvaluateRequest ( original . EvaluateName , 1000 , "this context will be ignored" )
validated := client . ExpectEvaluateResponse ( t )
if original . VariablesReference == 0 && validated . Body . VariablesReference != 0 ||
original . VariablesReference != 0 && validated . Body . VariablesReference == 0 {
t . Errorf ( "\ngot varref=%d\nwant %d" , validated . Body . VariablesReference , original . VariablesReference )
}
2021-03-15 16:36:46 +00:00
// The variable might not be fully loaded, and when we reload it with an expression
// more of the subvalues might be revealed, so we must match the loaded prefix only.
if strings . Contains ( original . Value , "..." ) {
origLoaded := strings . Split ( original . Value , "..." ) [ 0 ]
if ! strings . HasPrefix ( validated . Body . Result , origLoaded ) {
t . Errorf ( "\ngot value=%q\nwant %q" , validated . Body . Result , original . Value )
}
} else if original . Value != validated . Body . Result {
2021-01-14 18:53:12 +00:00
t . Errorf ( "\ngot value=%q\nwant %q" , validated . Body . Result , original . Value )
}
2020-08-11 15:34:27 +00:00
}
2020-10-02 16:18:33 +00:00
// TestStackTraceRequest executes to a breakpoint and tests different
2020-07-01 18:01:17 +00:00
// good and bad configurations of 'stackTrace' requests.
func TestStackTraceRequest ( t * testing . T ) {
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
var stResp * dap . StackTraceResponse
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-07-08 17:20:05 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 8 , 18 } ,
2020-08-11 15:34:27 +00:00
[ ] onBreakpoint { {
2020-07-08 17:20:05 +00:00
// Stop at line 8
2020-08-11 15:34:27 +00:00
execute : func ( ) {
2020-11-30 17:43:09 +00:00
// Even though the stack frames do not change,
// repeated requests at the same breakpoint
// would assign next block of unique ids to them each time.
const NumFrames = 6
2021-07-29 17:34:01 +00:00
reqIndex := 0
frameID := func ( ) int {
return startHandle + reqIndex
2020-11-30 17:43:09 +00:00
}
2020-07-08 17:20:05 +00:00
2020-11-30 17:43:09 +00:00
tests := map [ string ] struct {
startFrame int
levels int
wantStartName string
wantStartLine int
wantStartFrame int
wantFramesReturned int
wantFramesAvailable int
2021-07-29 17:34:01 +00:00
exact bool
2020-11-30 17:43:09 +00:00
} {
2021-07-29 17:34:01 +00:00
"all frame levels from 0 to NumFrames" : { 0 , NumFrames , "main.Increment" , 8 , 0 , NumFrames , NumFrames , true } ,
"subset of frames from 1 to -1" : { 1 , NumFrames - 1 , "main.Increment" , 11 , 1 , NumFrames - 1 , NumFrames , true } ,
"load stack in pages: first half" : { 0 , NumFrames / 2 , "main.Increment" , 8 , 0 , NumFrames / 2 , NumFrames , false } ,
"load stack in pages: second half" : { NumFrames / 2 , NumFrames , "main.main" , 17 , NumFrames / 2 , NumFrames / 2 , NumFrames , true } ,
"zero levels means all levels" : { 0 , 0 , "main.Increment" , 8 , 0 , NumFrames , NumFrames , true } ,
"zero levels means all remaining levels" : { NumFrames / 2 , 0 , "main.main" , 17 , NumFrames / 2 , NumFrames / 2 , NumFrames , true } ,
"negative levels treated as 0 (all)" : { 0 , - 10 , "main.Increment" , 8 , 0 , NumFrames , NumFrames , true } ,
"OOB levels is capped at available len" : { 0 , NumFrames + 1 , "main.Increment" , 8 , 0 , NumFrames , NumFrames , true } ,
"OOB levels is capped at available len 1" : { 1 , NumFrames + 1 , "main.Increment" , 11 , 1 , NumFrames - 1 , NumFrames , true } ,
"negative startFrame treated as 0" : { - 10 , 0 , "main.Increment" , 8 , 0 , NumFrames , NumFrames , true } ,
"OOB startFrame returns empty trace" : { NumFrames , 0 , "main.Increment" , - 1 , - 1 , 0 , NumFrames , true } ,
2020-11-30 17:43:09 +00:00
}
for name , tc := range tests {
client . StackTraceRequest ( 1 , tc . startFrame , tc . levels )
stResp = client . ExpectStackTraceResponse ( t )
2021-05-26 15:15:39 +00:00
checkStackFramesNamed ( name , t , stResp ,
2021-07-29 17:34:01 +00:00
tc . wantStartName , tc . wantStartLine , frameID ( ) , tc . wantFramesReturned , tc . wantFramesAvailable , tc . exact )
reqIndex += len ( stResp . Body . StackFrames )
}
} ,
disconnect : false ,
} , {
// Stop at line 18
execute : func ( ) {
// Frame ids get reset at each breakpoint.
client . StackTraceRequest ( 1 , 0 , 0 )
stResp = client . ExpectStackTraceResponse ( t )
checkStackFramesExact ( t , stResp , "main.main" , 18 , startHandle , 3 , 3 )
} ,
disconnect : false ,
} } )
} )
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
var stResp * dap . StackTraceResponse
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 8 , 18 } ,
[ ] onBreakpoint { {
// Stop at line 8
execute : func ( ) {
// Even though the stack frames do not change,
// repeated requests at the same breakpoint
// would assign next block of unique ids to them each time.
const NumFrames = 6
var frames [ ] dap . StackFrame
for start , levels := 0 , 1 ; start < NumFrames ; {
client . StackTraceRequest ( 1 , start , levels )
stResp = client . ExpectStackTraceResponse ( t )
frames = append ( frames , stResp . Body . StackFrames ... )
if stResp . Body . TotalFrames < NumFrames {
t . Errorf ( "got %#v\nwant TotalFrames>=%d\n" , stResp . Body . TotalFrames , NumFrames )
}
if len ( stResp . Body . StackFrames ) < levels {
t . Errorf ( "got len(StackFrames)=%d\nwant >=%d\n" , len ( stResp . Body . StackFrames ) , levels )
}
start += len ( stResp . Body . StackFrames )
}
// TODO check all the frames.
want := [ ] struct {
wantName string
wantLine int
} {
{ "main.Increment" , 8 } ,
{ "main.Increment" , 11 } ,
{ "main.Increment" , 11 } ,
{ "main.main" , 17 } ,
{ "runtime.main" , 0 } ,
{ "runtime.goexit" , 0 } ,
}
for i , frame := range frames {
frameId := startHandle + i
if frame . Id != frameId {
t . Errorf ( "got %#v\nwant Id=%d\n" , frame , frameId )
}
// Verify the name and line corresponding to the first returned frame (if any).
// This is useful when the first frame is the frame corresponding to the breakpoint at
// a predefined line. Line values < 0 are a signal to skip the check (which can be useful
// for frames in the third-party code, where we do not control the lines).
if want [ i ] . wantLine > 0 && frame . Line != want [ i ] . wantLine {
t . Errorf ( "got Line=%d\nwant %d\n" , frame . Line , want [ i ] . wantLine )
}
if want [ i ] . wantName != "" && frame . Name != want [ i ] . wantName {
t . Errorf ( "got Name=%s\nwant %s\n" , frame . Name , want [ i ] . wantName )
}
2020-11-30 17:43:09 +00:00
}
2020-07-08 17:20:05 +00:00
} ,
2020-08-11 15:34:27 +00:00
disconnect : false ,
} , {
2020-07-08 17:20:05 +00:00
// Stop at line 18
2020-08-11 15:34:27 +00:00
execute : func ( ) {
2020-07-08 17:20:05 +00:00
// Frame ids get reset at each breakpoint.
client . StackTraceRequest ( 1 , 0 , 0 )
stResp = client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stResp , "main.main" , 18 , startHandle , 3 , 3 )
2020-08-11 15:34:27 +00:00
} ,
disconnect : false ,
} } )
} )
}
2021-09-01 16:58:42 +00:00
func TestSelectedThreadsRequest ( t * testing . T ) {
runTest ( t , "goroutinestackprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 20 } ,
[ ] onBreakpoint { {
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 20 )
defaultMaxGoroutines := maxGoroutines
defer func ( ) { maxGoroutines = defaultMaxGoroutines } ( )
maxGoroutines = 1
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 8 } )
client . ExpectSetBreakpointsResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se := client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || se . Body . ThreadId == 1 {
t . Errorf ( "got %#v, want Reason=%q, ThreadId!=1" , se , "breakpoint" )
}
client . ThreadsRequest ( )
oe := client . ExpectOutputEvent ( t )
if ! strings . HasPrefix ( oe . Body . Output , "Too many goroutines" ) {
t . Errorf ( "got %#v, expected Output=\"Too many goroutines...\"\n" , oe )
}
tr := client . ExpectThreadsResponse ( t )
if len ( tr . Body . Threads ) != 2 {
t . Errorf ( "got %d threads, expected 2\n" , len ( tr . Body . Threads ) )
}
var selectedFound bool
for _ , thread := range tr . Body . Threads {
if thread . Id == se . Body . ThreadId {
selectedFound = true
break
}
}
if ! selectedFound {
t . Errorf ( "got %#v, want ThreadId=%d\n" , tr . Body . Threads , se . Body . ThreadId )
}
} ,
disconnect : true ,
} } )
} )
}
2020-08-11 15:34:27 +00:00
// TestScopesAndVariablesRequests executes to a breakpoint and tests different
// configurations of 'scopes' and 'variables' requests.
func TestScopesAndVariablesRequests ( t * testing . T ) {
runTest ( t , "testvariables" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-08-11 15:34:27 +00:00
// Launch
func ( ) {
2020-09-15 20:14:55 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-07-18 09:37:41 +00:00
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true , "backend" : "default" ,
2020-09-15 20:14:55 +00:00
} )
2020-08-11 15:34:27 +00:00
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
2020-11-12 23:24:31 +00:00
// Stop at first breakpoint
2020-08-11 15:34:27 +00:00
execute : func ( ) {
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2020-08-17 07:07:53 +00:00
2021-03-08 17:41:47 +00:00
startLineno := 66
2020-08-17 07:07:53 +00:00
if runtime . GOOS == "windows" && goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 15 ) {
// Go1.15 on windows inserts a NOP after the call to
2020-10-30 12:53:54 +00:00
// runtime.Breakpoint and marks it same line as the
// runtime.Breakpoint call, making this flaky, so skip the line check.
startLineno = - 1
2020-08-17 07:07:53 +00:00
}
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.foobar" , startLineno , 1000 , 4 , 4 )
2020-08-11 15:34:27 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package main)" , globalsScope )
// Globals
client . VariablesRequest ( globalsScope )
globals := client . ExpectVariablesResponse ( t )
checkVarExact ( t , globals , 0 , "p1" , "main.p1" , "10" , "int" , noChildren )
2020-08-11 15:34:27 +00:00
2021-10-05 17:35:14 +00:00
// Locals
2020-08-11 15:34:27 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
locals := client . ExpectVariablesResponse ( t )
checkChildren ( t , locals , "Locals" , 33 )
checkVarExact ( t , locals , 0 , "baz" , "baz" , ` "bazburzum" ` , "string" , noChildren )
ref := checkVarExact ( t , locals , 1 , "bar" , "bar" , ` main.FooBar { Baz: 10, Bur: "lorem"} ` , "main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
bar := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , bar , "bar" , 2 )
checkVarExact ( t , bar , 0 , "Baz" , "bar.Baz" , "10" , "int" , noChildren )
checkVarExact ( t , bar , 1 , "Bur" , "bar.Bur" , ` "lorem" ` , "string" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , bar , 0 )
validateEvaluateName ( t , client , bar , 1 )
2020-08-11 15:34:27 +00:00
}
// reflect.Kind == Bool
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "b1" , "b1" , "true" , "bool" , noChildren )
checkVarExact ( t , locals , - 1 , "b2" , "b2" , "false" , "bool" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Int
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a2" , "a2" , "6" , "int" , noChildren )
checkVarExact ( t , locals , - 1 , "neg" , "neg" , "-1" , "int" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Int8
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "i8" , "i8" , "1" , "int8" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Int16 - see testvariables2
// reflect.Kind == Int32 - see testvariables2
// reflect.Kind == Int64 - see testvariables2
// reflect.Kind == Uint
// reflect.Kind == Uint8
2021-09-16 10:19:01 +00:00
checkVarExact ( t , locals , - 1 , "u8" , "u8" , "255 = 0xff" , "uint8" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Uint16
2021-09-16 10:19:01 +00:00
checkVarExact ( t , locals , - 1 , "u16" , "u16" , "65535 = 0xffff" , "uint16" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Uint32
2021-09-16 10:19:01 +00:00
checkVarExact ( t , locals , - 1 , "u32" , "u32" , "4294967295 = 0xffffffff" , "uint32" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Uint64
2021-09-16 10:19:01 +00:00
checkVarExact ( t , locals , - 1 , "u64" , "u64" , "18446744073709551615 = 0xffffffffffffffff" , "uint64" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Uintptr
2021-09-16 10:19:01 +00:00
checkVarExact ( t , locals , - 1 , "up" , "up" , "5 = 0x5" , "uintptr" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Float32
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "f32" , "f32" , "1.2" , "float32" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Float64
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a3" , "a3" , "7.23" , "float64" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Complex64
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "c64" , "c64" , "(1 + 2i)" , "complex64" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
c64 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , c64 , "c64" , 2 )
checkVarExact ( t , c64 , 0 , "real" , "" , "1" , "float32" , noChildren )
checkVarExact ( t , c64 , 1 , "imaginary" , "" , "2" , "float32" , noChildren )
2020-08-11 15:34:27 +00:00
}
// reflect.Kind == Complex128
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "c128" , "c128" , "(2 + 3i)" , "complex128" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
c128 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , c128 , "c128" , 2 )
checkVarExact ( t , c128 , 0 , "real" , "" , "2" , "float64" , noChildren )
checkVarExact ( t , c128 , 1 , "imaginary" , "" , "3" , "float64" , noChildren )
2020-08-11 15:34:27 +00:00
}
// reflect.Kind == Array
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a4" , "a4" , "[2]int [1,2]" , "[2]int" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a4 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a4 , "a4" , 2 )
checkVarExact ( t , a4 , 0 , "[0]" , "a4[0]" , "1" , "int" , noChildren )
checkVarExact ( t , a4 , 1 , "[1]" , "a4[1]" , "2" , "int" , noChildren )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a11" , "a11" , ` [3]main.FooBar [ { Baz: 1, Bur: "a"}, { Baz: 2, Bur: "b"}, { Baz: 3, Bur: "c"}] ` , "[3]main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a11 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a11 , "a11" , 3 )
checkVarExact ( t , a11 , 0 , "[0]" , "a11[0]" , ` main.FooBar { Baz: 1, Bur: "a"} ` , "main.FooBar" , hasChildren )
ref = checkVarExact ( t , a11 , 1 , "[1]" , "a11[1]" , ` main.FooBar { Baz: 2, Bur: "b"} ` , "main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a11_1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a11_1 , "a11[1]" , 2 )
checkVarExact ( t , a11_1 , 0 , "Baz" , "a11[1].Baz" , "2" , "int" , noChildren )
checkVarExact ( t , a11_1 , 1 , "Bur" , "a11[1].Bur" , ` "b" ` , "string" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , a11_1 , 0 )
validateEvaluateName ( t , client , a11_1 , 1 )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
checkVarExact ( t , a11 , 2 , "[2]" , "a11[2]" , ` main.FooBar { Baz: 3, Bur: "c"} ` , "main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
}
// reflect.Kind == Chan - see testvariables2
// reflect.Kind == Func - see testvariables2
// reflect.Kind == Interface - see testvariables2
// reflect.Kind == Map - see testvariables2
// reflect.Kind == Ptr
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a7" , "a7" , ` *main.FooBar { Baz: 5, Bur: "strum"} ` , "*main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a7 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a7 , "a7" , 1 )
ref = checkVarExact ( t , a7 , 0 , "" , "(*a7)" , ` main.FooBar { Baz: 5, Bur: "strum"} ` , "main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a7val := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a7val , "*a7" , 2 )
checkVarExact ( t , a7val , 0 , "Baz" , "(*a7).Baz" , "5" , "int" , noChildren )
checkVarExact ( t , a7val , 1 , "Bur" , "(*a7).Bur" , ` "strum" ` , "string" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , a7val , 0 )
validateEvaluateName ( t , client , a7val , 1 )
2020-08-11 15:34:27 +00:00
}
}
// TODO(polina): how to test for "nil" (without type) and "void"?
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a9" , "a9" , "*main.FooBar nil" , "*main.FooBar" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Slice
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a5" , "a5" , "[]int len: 5, cap: 5, [1,2,3,4,5]" , "[]int" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a5 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a5 , "a5" , 5 )
checkVarExact ( t , a5 , 0 , "[0]" , "a5[0]" , "1" , "int" , noChildren )
checkVarExact ( t , a5 , 4 , "[4]" , "a5[4]" , "5" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , a5 , 0 )
validateEvaluateName ( t , client , a5 , 1 )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a12" , "a12" , ` []main.FooBar len: 2, cap: 2, [ { Baz: 4, Bur: "d"}, { Baz: 5, Bur: "e"}] ` , "[]main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a12 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a12 , "a12" , 2 )
checkVarExact ( t , a12 , 0 , "[0]" , "a12[0]" , ` main.FooBar { Baz: 4, Bur: "d"} ` , "main.FooBar" , hasChildren )
ref = checkVarExact ( t , a12 , 1 , "[1]" , "a12[1]" , ` main.FooBar { Baz: 5, Bur: "e"} ` , "main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a12_1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a12_1 , "a12[1]" , 2 )
checkVarExact ( t , a12_1 , 0 , "Baz" , "a12[1].Baz" , "5" , "int" , noChildren )
checkVarExact ( t , a12_1 , 1 , "Bur" , "a12[1].Bur" , ` "e" ` , "string" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , a12_1 , 0 )
validateEvaluateName ( t , client , a12_1 , 1 )
2020-08-11 15:34:27 +00:00
}
}
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a13" , "a13" , ` []*main.FooBar len: 3, cap: 3, [* { Baz: 6, Bur: "f"},* { Baz: 7, Bur: "g"},* { Baz: 8, Bur: "h"}] ` , "[]*main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a13 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a13 , "a13" , 3 )
checkVarExact ( t , a13 , 0 , "[0]" , "a13[0]" , ` *main.FooBar { Baz: 6, Bur: "f"} ` , "*main.FooBar" , hasChildren )
checkVarExact ( t , a13 , 1 , "[1]" , "a13[1]" , ` *main.FooBar { Baz: 7, Bur: "g"} ` , "*main.FooBar" , hasChildren )
ref = checkVarExact ( t , a13 , 2 , "[2]" , "a13[2]" , ` *main.FooBar { Baz: 8, Bur: "h"} ` , "*main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a13_2 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a13_2 , "a13[2]" , 1 )
ref = checkVarExact ( t , a13_2 , 0 , "" , "(*a13[2])" , ` main.FooBar { Baz: 8, Bur: "h"} ` , "main.FooBar" , hasChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , a13_2 , 0 )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
val := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , val , "*a13[2]" , 2 )
checkVarExact ( t , val , 0 , "Baz" , "(*a13[2]).Baz" , "8" , "int" , noChildren )
checkVarExact ( t , val , 1 , "Bur" , "(*a13[2]).Bur" , ` "h" ` , "string" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , val , 0 )
validateEvaluateName ( t , client , val , 1 )
2020-08-11 15:34:27 +00:00
}
}
}
// reflect.Kind == String
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a1" , "a1" , ` "foofoofoofoofoofoo" ` , "string" , noChildren )
checkVarExact ( t , locals , - 1 , "a10" , "a10" , ` "ofo" ` , "string" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Struct
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a6" , "a6" , ` main.FooBar { Baz: 8, Bur: "word"} ` , "main.FooBar" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a6 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a6 , "a6" , 2 )
checkVarExact ( t , a6 , 0 , "Baz" , "a6.Baz" , "8" , "int" , noChildren )
checkVarExact ( t , a6 , 1 , "Bur" , "a6.Bur" , ` "word" ` , "string" , noChildren )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "a8" , "a8" , ` main.FooBar2 { Bur: 10, Baz: "feh"} ` , "main.FooBar2" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a8 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a8 , "a8" , 2 )
checkVarExact ( t , a8 , 0 , "Bur" , "a8.Bur" , "10" , "int" , noChildren )
checkVarExact ( t , a8 , 1 , "Baz" , "a8.Baz" , ` "feh" ` , "string" , noChildren )
2020-08-11 15:34:27 +00:00
}
// reflect.Kind == UnsafePointer - see testvariables2
} ,
disconnect : false ,
} , {
2020-11-12 23:24:31 +00:00
// Stop at second breakpoint
2020-08-11 15:34:27 +00:00
execute : func ( ) {
// Frame ids get reset at each breakpoint.
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.barfoo" , 27 , 1000 , 5 , 5 )
2020-08-11 15:34:27 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package main)" , globalsScope )
2020-08-11 15:34:27 +00:00
client . ScopesRequest ( 1111 )
2021-05-06 09:11:45 +00:00
erres := client . ExpectInvisibleErrorResponse ( t )
2020-08-11 15:34:27 +00:00
if erres . Body . Error . Format != "Unable to list locals: unknown frame id 1111" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to list locals: unknown frame id 1111\"" , erres )
}
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-08-11 15:34:27 +00:00
locals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , locals , "Locals" , 1 )
checkVarExact ( t , locals , - 1 , "a1" , "a1" , ` "bur" ` , "string" , noChildren )
2020-08-11 15:34:27 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( globalsScope )
2020-09-15 20:14:55 +00:00
globals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , globals , 0 , "p1" , "main.p1" , "10" , "int" , noChildren )
2020-09-15 20:14:55 +00:00
2020-08-11 15:34:27 +00:00
client . VariablesRequest ( 7777 )
2021-05-06 09:11:45 +00:00
erres = client . ExpectInvisibleErrorResponse ( t )
2020-08-11 15:34:27 +00:00
if erres . Body . Error . Format != "Unable to lookup variable: unknown reference 7777" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to lookup variable: unknown reference 7777\"" , erres )
}
} ,
disconnect : false ,
} } )
} )
}
// TestScopesAndVariablesRequests2 executes to a breakpoint and tests different
// configurations of 'scopes' and 'variables' requests.
func TestScopesAndVariablesRequests2 ( t * testing . T ) {
runTest ( t , "testvariables2" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-08-11 15:34:27 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
execute : func ( ) {
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.main" , - 1 , 1000 , 3 , 3 )
2020-08-11 15:34:27 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
2020-08-11 15:34:27 +00:00
} ,
disconnect : false ,
} , {
execute : func ( ) {
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.main" , - 1 , 1000 , 3 , 3 )
2020-08-11 15:34:27 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
if len ( scopes . Body . Scopes ) > 1 {
t . Errorf ( "\ngot %#v\nwant len(scopes)=1 (Argumes & Locals)" , scopes )
2020-09-15 20:14:55 +00:00
}
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
2020-08-11 15:34:27 +00:00
// Locals
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-08-11 15:34:27 +00:00
locals := client . ExpectVariablesResponse ( t )
// reflect.Kind == Bool - see testvariables
// reflect.Kind == Int - see testvariables
// reflect.Kind == Int8
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "ni8" , "ni8" , "-5" , "int8" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Int16
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "ni16" , "ni16" , "-5" , "int16" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Int32
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "ni32" , "ni32" , "-5" , "int32" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Int64
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "ni64" , "ni64" , "-5" , "int64" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Uint
// reflect.Kind == Uint8 - see testvariables
// reflect.Kind == Uint16 - see testvariables
// reflect.Kind == Uint32 - see testvariables
// reflect.Kind == Uint64 - see testvariables
// reflect.Kind == Uintptr - see testvariables
// reflect.Kind == Float32 - see testvariables
// reflect.Kind == Float64
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "pinf" , "pinf" , "+Inf" , "float64" , noChildren )
checkVarExact ( t , locals , - 1 , "ninf" , "ninf" , "-Inf" , "float64" , noChildren )
checkVarExact ( t , locals , - 1 , "nan" , "nan" , "NaN" , "float64" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Complex64 - see testvariables
// reflect.Kind == Complex128 - see testvariables
// reflect.Kind == Array
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a0" , "a0" , "[0]int []" , "[0]int" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Chan
2021-05-26 15:15:39 +00:00
ref := checkVarExact ( t , locals , - 1 , "ch1" , "ch1" , "chan int 4/11" , "chan int" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
ch1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , ch1 , "ch1" , 11 )
2021-09-16 10:19:01 +00:00
checkVarExact ( t , ch1 , 0 , "qcount" , "ch1.qcount" , "4 = 0x4" , "uint" , noChildren )
2021-05-26 15:15:39 +00:00
checkVarRegex ( t , ch1 , 10 , "lock" , "ch1.lock" , ` runtime\.mutex { .*key: 0.*} ` , ` runtime\.mutex ` , hasChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , ch1 , 0 )
validateEvaluateName ( t , client , ch1 , 10 )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "chnil" , "chnil" , "chan int nil" , "chan int" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Func
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "fn1" , "fn1" , "main.afunc" , "main.functype" , noChildren )
checkVarExact ( t , locals , - 1 , "fn2" , "fn2" , "nil" , "main.functype" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Interface
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "ifacenil" , "ifacenil" , "interface {} nil" , "interface {}" , noChildren )
ref = checkVarExact ( t , locals , - 1 , "iface2" , "iface2" , "interface {}(string) \"test\"" , "interface {}" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
iface2 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , iface2 , "iface2" , 1 )
checkVarExact ( t , iface2 , 0 , "data" , "iface2.(data)" , ` "test" ` , "string" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , iface2 , 0 )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "iface4" , "iface4" , "interface {}([]go/constant.Value) [4]" , "interface {}" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
iface4 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , iface4 , "iface4" , 1 )
ref = checkVarExact ( t , iface4 , 0 , "data" , "iface4.(data)" , "[]go/constant.Value len: 1, cap: 1, [4]" , "[]go/constant.Value" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
iface4data := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , iface4data , "iface4.data" , 1 )
ref = checkVarExact ( t , iface4data , 0 , "[0]" , "iface4.(data)[0]" , "go/constant.Value(go/constant.int64Val) 4" , "go/constant.Value" , hasChildren )
2021-01-14 18:53:12 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
iface4data0 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , iface4data0 , "iface4.data[0]" , 1 )
checkVarExact ( t , iface4data0 , 0 , "data" , "iface4.(data)[0].(data)" , "4" , "go/constant.int64Val" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , iface4data0 , 0 )
}
2020-08-11 15:34:27 +00:00
}
}
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "errnil" , "errnil" , "error nil" , "error" , noChildren )
ref = checkVarExact ( t , locals , - 1 , "err1" , "err1" , "error(*main.astruct) *{A: 1, B: 2}" , "error" , hasChildren )
2021-01-14 18:53:12 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
err1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , err1 , "err1" , 1 )
checkVarExact ( t , err1 , 0 , "data" , "err1.(data)" , "*main.astruct {A: 1, B: 2}" , "*main.astruct" , hasChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , err1 , 0 )
}
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "ptrinf" , "ptrinf" , "*interface {}(**interface {}) **..." , "*interface {}" , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
ptrinf_val := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , ptrinf_val , "*ptrinf" , 1 )
ref = checkVarExact ( t , ptrinf_val , 0 , "" , "(*ptrinf)" , "interface {}(**interface {}) **..." , "interface {}" , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
ptrinf_val_data := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , ptrinf_val_data , "(*ptrinf).data" , 1 )
checkVarExact ( t , ptrinf_val_data , 0 , "data" , "(*ptrinf).(data)" , "**interface {}(**interface {}) ..." , "**interface {}" , hasChildren )
2021-02-21 16:00:05 +00:00
validateEvaluateName ( t , client , ptrinf_val_data , 0 )
}
}
2020-08-11 15:34:27 +00:00
// reflect.Kind == Map
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "mnil" , "mnil" , "map[string]main.astruct nil" , "map[string]main.astruct" , noChildren )
2021-01-14 18:53:12 +00:00
// key - scalar, value - compound
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "m2" , "m2" , "map[int]*main.astruct [1: *{A: 10, B: 11}, ]" , "map[int]*main.astruct" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
m2 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkChildren ( t , m2 , "m2" , 2 ) // each key-value represented by a single child
checkVarExact ( t , m2 , 0 , "len()" , "len(m2)" , "1" , "int" , noChildren )
ref = checkVarExact ( t , m2 , 1 , "1" , "m2[1]" , "*main.astruct {A: 10, B: 11}" , "int: *main.astruct" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
2021-01-14 18:53:12 +00:00
m2kv1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , m2kv1 , "m2[1]" , 1 )
ref = checkVarExact ( t , m2kv1 , 0 , "" , "(*m2[1])" , "main.astruct {A: 10, B: 11}" , "main.astruct" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
2021-01-14 18:53:12 +00:00
m2kv1deref := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , m2kv1deref , "*m2[1]" , 2 )
checkVarExact ( t , m2kv1deref , 0 , "A" , "(*m2[1]).A" , "10" , "int" , noChildren )
checkVarExact ( t , m2kv1deref , 1 , "B" , "(*m2[1]).B" , "11" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , m2kv1deref , 0 )
validateEvaluateName ( t , client , m2kv1deref , 1 )
2020-08-11 15:34:27 +00:00
}
}
}
2021-01-14 18:53:12 +00:00
// key - compound, value - scalar
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "m3" , "m3" , "map[main.astruct]int [{A: 1, B: 1}: 42, {A: 2, B: 2}: 43, ]" , "map[main.astruct]int" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
m3 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkChildren ( t , m3 , "m3" , 3 ) // each key-value represented by a single child
checkVarExact ( t , m3 , 0 , "len()" , "len(m3)" , "2" , "int" , noChildren )
ref = checkVarRegex ( t , m3 , 1 , ` main\.astruct { A: 1, B: 1} ` , ` m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\] ` , "42" , "int" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
2021-01-14 18:53:12 +00:00
m3kv0 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , m3kv0 , "m3[0]" , 2 )
checkVarRegex ( t , m3kv0 , 0 , "A" , ` \(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A ` , "1" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , m3kv0 , 0 )
2020-08-11 15:34:27 +00:00
}
2021-07-16 16:50:37 +00:00
ref = checkVarRegex ( t , m3 , 2 , ` main\.astruct { A: 2, B: 2} ` , ` m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\] ` , "43" , "" , hasChildren )
2021-01-14 18:53:12 +00:00
if ref > 0 { // inspect another key from another key-value child
2020-08-11 15:34:27 +00:00
client . VariablesRequest ( ref )
2021-01-14 18:53:12 +00:00
m3kv1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , m3kv1 , "m3[1]" , 2 )
checkVarRegex ( t , m3kv1 , 1 , "B" , ` \(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B ` , "2" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , m3kv1 , 1 )
2020-08-11 15:34:27 +00:00
}
}
2021-01-14 18:53:12 +00:00
// key - compound, value - compound
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "m4" , "m4" , "map[main.astruct]main.astruct [{A: 1, B: 1}: {A: 11, B: 11}, {A: 2, B: 2}: {A: 22, B: 22}, ]" , "map[main.astruct]main.astruct" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
m4 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkChildren ( t , m4 , "m4" , 5 ) // each key and value represented by a child, so double the key-value count
checkVarExact ( t , m4 , 0 , "len()" , "len(m4)" , "2" , "int" , noChildren )
checkVarRegex ( t , m4 , 1 , ` \[key 0\] ` , ` \(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\) ` , ` main\.astruct { A: 1, B: 1} ` , ` main\.astruct ` , hasChildren )
checkVarRegex ( t , m4 , 2 , ` \[val 0\] ` , ` m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\] ` , ` main\.astruct { A: 11, B: 11} ` , ` main\.astruct ` , hasChildren )
ref = checkVarRegex ( t , m4 , 3 , ` \[key 1\] ` , ` \(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\) ` , ` main\.astruct { A: 2, B: 2} ` , ` main\.astruct ` , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
2020-09-15 20:14:55 +00:00
m4Key1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , m4Key1 , "m4Key1" , 2 )
checkVarRegex ( t , m4Key1 , 0 , "A" , ` \(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A ` , "2" , "int" , noChildren )
checkVarRegex ( t , m4Key1 , 1 , "B" , ` \(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B ` , "2" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , m4Key1 , 0 )
validateEvaluateName ( t , client , m4Key1 , 1 )
2020-08-11 15:34:27 +00:00
}
2021-07-16 16:50:37 +00:00
ref = checkVarRegex ( t , m4 , 4 , ` \[val 1\] ` , ` m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\] ` , ` main\.astruct { A: 22, B: 22} ` , "main.astruct" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
2020-09-15 20:14:55 +00:00
m4Val1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , m4Val1 , "m4Val1" , 2 )
checkVarRegex ( t , m4Val1 , 0 , "A" , ` m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.A ` , "22" , "int" , noChildren )
checkVarRegex ( t , m4Val1 , 1 , "B" , ` m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.B ` , "22" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , m4Val1 , 0 )
validateEvaluateName ( t , client , m4Val1 , 1 )
2020-08-11 15:34:27 +00:00
}
}
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "emptymap" , "emptymap" , "map[string]string []" , "map[string]string" , noChildren )
2021-02-21 16:00:05 +00:00
// reflect.Kind == Ptr
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "pp1" , "pp1" , "**1" , "**int" , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
pp1val := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , pp1val , "*pp1" , 1 )
ref = checkVarExact ( t , pp1val , 0 , "" , "(*pp1)" , "*1" , "*int" , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
pp1valval := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , pp1valval , "*(*pp1)" , 1 )
checkVarExact ( t , pp1valval , 0 , "" , "(*(*pp1))" , "1" , "int" , noChildren )
2021-02-21 16:00:05 +00:00
validateEvaluateName ( t , client , pp1valval , 0 )
}
}
2020-08-11 15:34:27 +00:00
// reflect.Kind == Slice
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "zsslice" , "zsslice" , "[]struct {} len: 3, cap: 3, [{},{},{}]" , "[]struct {}" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
zsslice := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , zsslice , "zsslice" , 3 )
checkVarExact ( t , zsslice , 2 , "[2]" , "zsslice[2]" , "struct {} {}" , "struct {}" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , zsslice , 2 )
2020-08-11 15:34:27 +00:00
}
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "emptyslice" , "emptyslice" , "[]string len: 0, cap: 0, []" , "[]string" , noChildren )
checkVarExact ( t , locals , - 1 , "nilslice" , "nilslice" , "[]int len: 0, cap: 0, nil" , "[]int" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == String
2021-06-22 15:14:47 +00:00
checkVarExact ( t , locals , - 1 , "longstr" , "longstr" , longstr , "string" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == Struct
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "zsvar" , "zsvar" , "struct {} {}" , "struct {}" , noChildren )
2020-08-11 15:34:27 +00:00
// reflect.Kind == UnsafePointer
// TODO(polina): how do I test for unsafe.Pointer(nil)?
2021-05-26 15:15:39 +00:00
checkVarRegex ( t , locals , - 1 , "upnil" , "upnil" , ` unsafe\.Pointer\(0x0\) ` , "int" , noChildren )
checkVarRegex ( t , locals , - 1 , "up1" , "up1" , ` unsafe\.Pointer\(0x[0-9a-f]+\) ` , "int" , noChildren )
2020-08-11 15:34:27 +00:00
// Test unreadable variable
2021-05-26 15:15:39 +00:00
ref = checkVarRegex ( t , locals , - 1 , "unread" , "unread" , ` \*\(unreadable .+\) ` , "int" , hasChildren )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
val := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , val , "*unread" , 1 )
checkVarRegex ( t , val , 0 , "^$" , ` \(\*unread\) ` , ` \(unreadable .+\) ` , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , val , 0 )
2020-08-11 15:34:27 +00:00
}
2021-02-23 16:29:06 +00:00
} ,
disconnect : true ,
} } )
} )
}
2021-05-17 16:21:15 +00:00
// TestScopesRequestsOptimized executes to a breakpoint and tests different
2021-10-05 17:35:14 +00:00
// that the name of the "Locals" scope is correctly annotated with
2021-05-17 16:21:15 +00:00
// a warning about debugging an optimized function.
func TestScopesRequestsOptimized ( t * testing . T ) {
runTestBuildFlags ( t , "testvariables" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
// Stop at first breakpoint
execute : func ( ) {
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
startLineno := 66
if runtime . GOOS == "windows" && goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 15 ) {
// Go1.15 on windows inserts a NOP after the call to
// runtime.Breakpoint and marks it same line as the
// runtime.Breakpoint call, making this flaky, so skip the line check.
startLineno = - 1
}
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.foobar" , startLineno , 1000 , 4 , 4 )
2021-05-17 16:21:15 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals (warning: optimized function)" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package main)" , globalsScope )
2021-05-17 16:21:15 +00:00
} ,
disconnect : false ,
} , {
// Stop at second breakpoint
execute : func ( ) {
// Frame ids get reset at each breakpoint.
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.barfoo" , 27 , 1000 , 5 , 5 )
2021-05-17 16:21:15 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals (warning: optimized function)" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package main)" , globalsScope )
2021-05-17 16:21:15 +00:00
} ,
disconnect : false ,
} } )
} ,
protest . EnableOptimization )
}
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
// TestVariablesLoading exposes test cases where variables might be partially or
2021-02-23 16:29:06 +00:00
// fully unloaded.
func TestVariablesLoading ( t * testing . T ) {
runTest ( t , "testvariables2" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
execute : func ( ) { } ,
disconnect : false ,
} , {
execute : func ( ) {
// Change default config values to trigger certain unloaded corner cases
2021-06-22 15:14:47 +00:00
saveDefaultConfig := DefaultLoadConfig
2021-02-23 16:29:06 +00:00
DefaultLoadConfig . MaxStructFields = 5
2021-06-22 15:14:47 +00:00
DefaultLoadConfig . MaxStringLen = 64
defer func ( ) {
DefaultLoadConfig = saveDefaultConfig
} ( )
2021-02-23 16:29:06 +00:00
client . StackTraceRequest ( 1 , 0 , 0 )
client . ExpectStackTraceResponse ( t )
2020-08-11 15:34:27 +00:00
2021-02-23 16:29:06 +00:00
client . ScopesRequest ( 1000 )
client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-02-23 16:29:06 +00:00
locals := client . ExpectVariablesResponse ( t )
// String partially missing based on LoadConfig.MaxStringLen
2021-06-22 15:14:47 +00:00
// See also TestVariableLoadingOfLongStrings
checkVarExact ( t , locals , - 1 , "longstr" , "longstr" , longstrLoaded64 , "string" , noChildren )
2021-02-23 16:29:06 +00:00
2021-06-10 16:34:20 +00:00
checkArrayChildren := func ( t * testing . T , longarr * dap . VariablesResponse , parentName string , start int ) {
t . Helper ( )
for i , child := range longarr . Body . Variables {
idx := start + i
if child . Name != fmt . Sprintf ( "[%d]" , idx ) || child . EvaluateName != fmt . Sprintf ( "%s[%d]" , parentName , idx ) {
t . Errorf ( "Expected %s[%d] to have Name=\"[%d]\" EvaluateName=\"%s[%d]\", got %#v" , parentName , idx , idx , parentName , idx , child )
}
}
}
// Array not fully loaded based on LoadConfig.MaxArrayValues.
// Expect to be able to load array by paging.
ref := checkVarExactIndexed ( t , locals , - 1 , "longarr" , "longarr" , "[100]int [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+36 more]" , "[100]int" , hasChildren , 100 , 0 )
2021-02-23 16:29:06 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
longarr := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , longarr , "longarr" , 64 )
2021-06-10 16:34:20 +00:00
checkArrayChildren ( t , longarr , "longarr" , 0 )
client . IndexedVariablesRequest ( ref , 0 , 100 )
longarr = client . ExpectVariablesResponse ( t )
checkChildren ( t , longarr , "longarr" , 100 )
checkArrayChildren ( t , longarr , "longarr" , 0 )
client . IndexedVariablesRequest ( ref , 50 , 50 )
longarr = client . ExpectVariablesResponse ( t )
checkChildren ( t , longarr , "longarr" , 50 )
checkArrayChildren ( t , longarr , "longarr" , 50 )
2021-02-23 16:29:06 +00:00
}
2021-06-10 16:34:20 +00:00
// Slice not fully loaded based on LoadConfig.MaxArrayValues.
// Expect to be able to load slice by paging.
ref = checkVarExactIndexed ( t , locals , - 1 , "longslice" , "longslice" , "[]int len: 100, cap: 100, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+36 more]" , "[]int" , hasChildren , 100 , 0 )
2021-02-23 16:29:06 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
longarr := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , longarr , "longslice" , 64 )
2021-06-10 16:34:20 +00:00
checkArrayChildren ( t , longarr , "longslice" , 0 )
client . IndexedVariablesRequest ( ref , 0 , 100 )
longarr = client . ExpectVariablesResponse ( t )
checkChildren ( t , longarr , "longslice" , 100 )
checkArrayChildren ( t , longarr , "longslice" , 0 )
client . IndexedVariablesRequest ( ref , 50 , 50 )
longarr = client . ExpectVariablesResponse ( t )
checkChildren ( t , longarr , "longslice" , 50 )
checkArrayChildren ( t , longarr , "longslice" , 50 )
2021-02-23 16:29:06 +00:00
}
2021-06-10 16:34:20 +00:00
// Map not fully loaded based on LoadConfig.MaxArrayValues
// Expect to be able to load map by paging.
2021-07-16 16:50:37 +00:00
ref = checkVarRegexIndexed ( t , locals , - 1 , "m1" , "m1" , ` map\[string\]main\.astruct \[.+\.\.\. ` , ` map\[string\]main\.astruct ` , hasChildren , 66 , 1 )
2020-08-11 15:34:27 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
m1 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkChildren ( t , m1 , "m1" , 65 )
2021-06-10 16:34:20 +00:00
client . IndexedVariablesRequest ( ref , 0 , 66 )
m1 = client . ExpectVariablesResponse ( t )
checkChildren ( t , m1 , "m1" , 66 )
client . IndexedVariablesRequest ( ref , 0 , 33 )
m1part1 := client . ExpectVariablesResponse ( t )
checkChildren ( t , m1part1 , "m1" , 33 )
client . IndexedVariablesRequest ( ref , 33 , 33 )
m1part2 := client . ExpectVariablesResponse ( t )
checkChildren ( t , m1part2 , "m1" , 33 )
if len ( m1part1 . Body . Variables ) + len ( m1part2 . Body . Variables ) == len ( m1 . Body . Variables ) {
for i , got := range m1part1 . Body . Variables {
want := m1 . Body . Variables [ i ]
if got . Name != want . Name || got . Value != want . Value {
t . Errorf ( "got %#v, want Name=%q Value=%q" , got , want . Name , want . Value )
}
}
for i , got := range m1part2 . Body . Variables {
want := m1 . Body . Variables [ i + len ( m1part1 . Body . Variables ) ]
if got . Name != want . Name || got . Value != want . Value {
t . Errorf ( "got %#v, want Name=%q Value=%q" , got , want . Name , want . Value )
}
}
}
2021-07-16 16:50:37 +00:00
client . NamedVariablesRequest ( ref )
named := client . ExpectVariablesResponse ( t )
checkChildren ( t , named , "m1" , 1 )
checkVarExact ( t , named , 0 , "len()" , "len(m1)" , "66" , "int" , noChildren )
2020-08-11 15:34:27 +00:00
}
2021-02-21 16:00:05 +00:00
2021-02-23 16:29:06 +00:00
// Struct partially missing based on LoadConfig.MaxStructFields
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "sd" , "sd" , "(loaded 5/6) main.D {u1: 0, u2: 0, u3: 0, u4: 0, u5: 0,...+1 more}" , "main.D" , hasChildren )
2021-02-23 16:29:06 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
sd := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , sd , "sd" , 5 )
2021-02-23 16:29:06 +00:00
}
2021-05-04 19:53:42 +00:00
// Fully missing struct auto-loaded when reaching LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
2021-05-26 15:15:39 +00:00
ref = checkVarRegex ( t , locals , - 1 , "c1" , "c1" , ` main\.cstruct { pb: \*main\.bstruct { a: \(\*main\.astruct\)\(0x[0-9a-f]+\)}, sa: []\*main\.astruct len: 3, cap: 3, [\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main.astruct\)\(0x[0-9a-f]+\)]} ` , ` main\.cstruct ` , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
c1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , c1 , "c1" , 2 )
ref = checkVarRegex ( t , c1 , 1 , "sa" , ` c1\.sa ` , ` \[\]\*main\.astruct len: 3, cap: 3, \[\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\),\*\(\*main\.astruct\)\(0x[0-9a-f]+\)\] ` , ` \[\]\*main\.astruct ` , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
c1sa := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , c1sa , "c1.sa" , 3 )
ref = checkVarRegex ( t , c1sa , 0 , ` \[0\] ` , ` c1\.sa\[0\] ` , ` \*\(\*main\.astruct\)\(0x[0-9a-f]+\) ` , ` \*main\.astruct ` , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
2021-05-04 19:53:42 +00:00
// Auto-loading of fully missing struc children happens here
2021-02-21 16:00:05 +00:00
client . VariablesRequest ( ref )
c1sa0 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , c1sa0 , "c1.sa[0]" , 1 )
2021-05-10 18:34:42 +00:00
// TODO(polina): there should be children here once we support auto loading
2021-05-26 15:15:39 +00:00
checkVarExact ( t , c1sa0 , 0 , "" , "(*c1.sa[0])" , "main.astruct {A: 1, B: 2}" , "main.astruct" , hasChildren )
2021-02-21 16:00:05 +00:00
}
}
}
2021-05-04 19:53:42 +00:00
// Fully missing struct auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluteName corner case)
2021-05-26 15:15:39 +00:00
ref = checkVarRegex ( t , locals , - 1 , "aas" , "aas" , ` \[\]main\.a len: 1, cap: 1, \[ { aas: \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]}\] ` , ` \[\]main\.a ` , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
aas := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , aas , "aas" , 1 )
ref = checkVarRegex ( t , aas , 0 , "[0]" , ` aas\[0\] ` , ` main\.a { aas: \[\]main.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]} ` , ` main\.a ` , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
aas0 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , aas0 , "aas[0]" , 1 )
ref = checkVarRegex ( t , aas0 , 0 , "aas" , ` aas\[0\]\.aas ` , ` \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\] ` , ` \[\]main\.a ` , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
2021-05-04 19:53:42 +00:00
// Auto-loading of fully missing struct children happens here
2021-02-21 16:00:05 +00:00
client . VariablesRequest ( ref )
aas0aas := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , aas0aas , "aas[0].aas" , 1 )
2021-05-10 18:34:42 +00:00
// TODO(polina): there should be a child here once we support auto loading - test for "aas[0].aas[0].aas"
2021-05-26 15:15:39 +00:00
ref = checkVarRegex ( t , aas0aas , 0 , "[0]" , ` aas\[0\]\.aas\[0\] ` , ` main\.a { aas: \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]} ` , "main.a" , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
aas0aas0 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , aas0aas , "aas[0].aas[0]" , 1 )
checkVarRegex ( t , aas0aas0 , 0 , "aas" , ` aas\[0\]\.aas\[0\]\.aas ` , ` \[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\] ` , ` \[\]main\.a ` , hasChildren )
2021-05-04 19:53:42 +00:00
}
2021-02-21 16:00:05 +00:00
}
}
}
2021-05-04 19:53:42 +00:00
// Fully missing map auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluateName corner case)
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , locals , - 1 , "tm" , "tm" , "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}" , "main.truncatedMap" , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
tm := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , tm , "tm" , 1 )
ref = checkVarExact ( t , tm , 0 , "v" , "tm.v" , "[]map[string]main.astruct len: 1, cap: 1, [[...]]" , "[]map[string]main.astruct" , hasChildren )
2021-02-21 16:00:05 +00:00
if ref > 0 {
2021-05-04 19:53:42 +00:00
// Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValuess
2021-02-21 16:00:05 +00:00
client . VariablesRequest ( ref )
2021-03-08 17:41:47 +00:00
tmV := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , tmV , "tm.v" , 1 )
2021-06-04 07:27:57 +00:00
ref = checkVarRegex ( t , tmV , 0 , ` \[0\] ` , ` tm\.v\[0\] ` , ` map\[string\]main\.astruct \[.+\.\.\. ` , ` map\[string\]main\.astruct ` , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
tmV0 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkChildren ( t , tmV0 , "tm.v[0]" , 65 )
2021-05-04 19:53:42 +00:00
}
2021-02-21 16:00:05 +00:00
}
}
2021-05-04 19:53:42 +00:00
// Auto-loading works with call return variables as well
protest . MustSupportFunctionCalls ( t , testBackend )
client . EvaluateRequest ( "call rettm()" , 1000 , "repl" )
got := client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEval ( t , got , "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}" , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
rv := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , rv , "rv" , 1 )
ref = checkVarExact ( t , rv , 0 , "~r0" , "" , "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}" , "main.truncatedMap" , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
tm := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , tm , "tm" , 1 )
ref = checkVarExact ( t , tm , 0 , "v" , "" , "[]map[string]main.astruct len: 1, cap: 1, [[...]]" , "[]map[string]main.astruct" , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
// Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValuess
client . VariablesRequest ( ref )
tmV := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , tmV , "tm.v" , 1 )
2021-05-04 19:53:42 +00:00
// TODO(polina): this evaluate name is not usable - it should be empty
2021-06-04 07:27:57 +00:00
ref = checkVarRegex ( t , tmV , 0 , ` \[0\] ` , ` \[0\] ` , ` map\[string\]main\.astruct \[.+\.\.\. ` , ` map\[string\]main\.astruct ` , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
tmV0 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkChildren ( t , tmV0 , "tm.v[0]" , 65 )
2021-05-04 19:53:42 +00:00
}
}
}
}
// TODO(polina): need fully missing array/slice test case
// Zero slices, structs and maps are not treated as fully missing
// See zsvar, zsslice,, emptyslice, emptymap, a0
2021-02-23 16:29:06 +00:00
} ,
disconnect : true ,
} } )
} )
2021-03-08 17:41:47 +00:00
runTest ( t , "testvariables" , func ( client * daptest . Client , fixture protest . Fixture ) {
2021-02-23 16:29:06 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
execute : func ( ) {
DefaultLoadConfig . FollowPointers = false
defer func ( ) { DefaultLoadConfig . FollowPointers = true } ( )
client . StackTraceRequest ( 1 , 0 , 0 )
client . ExpectStackTraceResponse ( t )
2021-03-08 17:41:47 +00:00
var loadvars = func ( frame int ) {
client . ScopesRequest ( frame )
scopes := client . ExpectScopesResponse ( t )
localsRef := 0
for _ , s := range scopes . Body . Scopes {
if s . Name == "Locals" {
localsRef = s . VariablesReference
}
}
2021-02-23 16:29:06 +00:00
2021-03-08 17:41:47 +00:00
client . VariablesRequest ( localsRef )
locals := client . ExpectVariablesResponse ( t )
// Interface auto-loaded when hitting LoadConfig.MaxVariableRecurse=1
2021-02-23 16:29:06 +00:00
2021-05-26 15:15:39 +00:00
ref := checkVarRegex ( t , locals , - 1 , "ni" , "ni" , ` \[\]interface { } len: 1, cap: 1, \[\[\]interface { } len: 1, cap: 1, \[\*\(\*interface { }\)\(0x[0-9a-f]+\)\]\] ` , ` \[\]interface { } ` , hasChildren )
2021-03-08 17:41:47 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
ni := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkVarRegex ( t , ni , 0 , ` \[0\] ` , ` ni\[0\] ` , ` interface \ { \}\(\[\]interface \ { \}\) \[\*\(\*interface \ { \}\)\(0x[0-9a-f]+\)\] ` , "interface {}" , hasChildren )
2021-03-08 17:41:47 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
niI1 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkVarRegex ( t , niI1 , 0 , "data" , ` ni\[0\]\.\(data\) ` , ` \[\]interface { } len: 1, cap: 1, \[\*\(\*interface { }\)\(0x[0-9a-f]+\) ` , ` \[\]interface { } ` , hasChildren )
2021-03-08 17:41:47 +00:00
if ref > 0 {
// Auto-loading happens here
client . VariablesRequest ( ref )
niI1Data := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkVarExact ( t , niI1Data , 0 , "[0]" , "ni[0].(data)[0]" , "interface {}(int) 123" , "interface {}" , hasChildren )
2021-03-08 17:41:47 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
niI1DataI2 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , niI1DataI2 , 0 , "data" , "ni[0].(data)[0].(data)" , "123" , "int" , noChildren )
2021-03-08 17:41:47 +00:00
}
}
}
}
2021-05-04 19:53:42 +00:00
// Pointer values loaded even with LoadConfig.FollowPointers=false
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a7" , "a7" , "*main.FooBar {Baz: 5, Bur: \"strum\"}" , "*main.FooBar" , hasChildren )
2021-05-04 19:53:42 +00:00
// Auto-loading works on results of evaluate expressions as well
client . EvaluateRequest ( "a7" , frame , "repl" )
2021-05-26 15:15:39 +00:00
checkEval ( t , client . ExpectEvaluateResponse ( t ) , "*main.FooBar {Baz: 5, Bur: \"strum\"}" , hasChildren )
2021-05-04 19:53:42 +00:00
client . EvaluateRequest ( "&a7" , frame , "repl" )
pa7 := client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEvalRegex ( t , pa7 , ` \*\(\*main\.FooBar\)\(0x[0-9a-f]+\) ` , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a7 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , a7 , 0 , "a7" , "(*(&a7))" , "*main.FooBar {Baz: 5, Bur: \"strum\"}" , "*main.FooBar" , hasChildren )
2021-05-04 19:53:42 +00:00
}
2021-03-08 17:41:47 +00:00
}
2021-05-04 19:53:42 +00:00
// Frame-independent loading expressions allow us to auto-load
// variables in any frame, not just topmost.
2021-03-08 17:41:47 +00:00
loadvars ( 1000 /*first topmost frame*/ )
// step into another function
client . StepInRequest ( 1 )
client . ExpectStepInResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.barfoo" , 24 )
2021-03-08 17:41:47 +00:00
loadvars ( 1001 /*second frame here is same as topmost above*/ )
2020-08-11 15:34:27 +00:00
} ,
disconnect : true ,
} } )
2020-07-08 17:20:05 +00:00
} )
}
2020-02-15 19:52:53 +00:00
2021-06-23 23:51:57 +00:00
// TestVariablesMetadata exposes test cases where variables contain metadata that
// can be accessed by requesting named variables.
func TestVariablesMetadata ( t * testing . T ) {
runTest ( t , "testvariables2" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
execute : func ( ) { } ,
disconnect : false ,
} , {
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 368 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-06-23 23:51:57 +00:00
locals := client . ExpectVariablesResponse ( t )
checkNamedChildren := func ( ref int , name , typeStr string , vals [ ] string , evaluate bool ) {
// byteslice, request named variables
client . NamedVariablesRequest ( ref )
named := client . ExpectVariablesResponse ( t )
checkChildren ( t , named , name , 1 )
checkVarExact ( t , named , 0 , "string()" , "" , "\"tèst\"" , "string" , false )
client . VariablesRequest ( ref )
all := client . ExpectVariablesResponse ( t )
checkChildren ( t , all , name , len ( vals ) + 1 )
checkVarExact ( t , all , 0 , "string()" , "" , "\"tèst\"" , "string" , false )
for i , v := range vals {
idx := fmt . Sprintf ( "[%d]" , i )
evalName := fmt . Sprintf ( "%s[%d]" , name , i )
if evaluate {
evalName = fmt . Sprintf ( "(%s)[%d]" , name , i )
}
checkVarExact ( t , all , i + 1 , idx , evalName , v , typeStr , false )
}
}
2021-09-16 10:19:01 +00:00
bytes := [ ] string { "116 = 0x74" , "195 = 0xc3" , "168 = 0xa8" , "115 = 0x73" , "116 = 0x74" }
runes := [ ] string { "116" , "232" , "115" , "116" }
2021-06-23 23:51:57 +00:00
// byteslice
ref := checkVarExactIndexed ( t , locals , - 1 , "byteslice" , "byteslice" , "[]uint8 len: 5, cap: 5, [116,195,168,115,116]" , "[]uint8" , true , 5 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "byteslice" , "uint8" , bytes , false )
2021-06-23 23:51:57 +00:00
client . EvaluateRequest ( "byteslice" , 0 , "" )
got := client . ExpectEvaluateResponse ( t )
ref = checkEvalIndexed ( t , got , "[]uint8 len: 5, cap: 5, [116,195,168,115,116]" , hasChildren , 5 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "byteslice" , "uint8" , bytes , true )
2021-06-23 23:51:57 +00:00
// runeslice
ref = checkVarExactIndexed ( t , locals , - 1 , "runeslice" , "runeslice" , "[]int32 len: 4, cap: 4, [116,232,115,116]" , "[]int32" , true , 4 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "runeslice" , "int32" , runes , false )
2021-06-23 23:51:57 +00:00
client . EvaluateRequest ( "runeslice" , 0 , "repl" )
got = client . ExpectEvaluateResponse ( t )
ref = checkEvalIndexed ( t , got , "[]int32 len: 4, cap: 4, [116,232,115,116]" , hasChildren , 4 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "runeslice" , "int32" , runes , true )
2021-06-23 23:51:57 +00:00
// bytearray
ref = checkVarExactIndexed ( t , locals , - 1 , "bytearray" , "bytearray" , "[5]uint8 [116,195,168,115,116]" , "[5]uint8" , true , 5 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "bytearray" , "uint8" , bytes , false )
2021-06-23 23:51:57 +00:00
client . EvaluateRequest ( "bytearray" , 0 , "hover" )
got = client . ExpectEvaluateResponse ( t )
ref = checkEvalIndexed ( t , got , "[5]uint8 [116,195,168,115,116]" , hasChildren , 5 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "bytearray" , "uint8" , bytes , true )
2021-06-23 23:51:57 +00:00
// runearray
ref = checkVarExactIndexed ( t , locals , - 1 , "runearray" , "runearray" , "[4]int32 [116,232,115,116]" , "[4]int32" , true , 4 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "runearray" , "int32" , runes , false )
2021-06-23 23:51:57 +00:00
client . EvaluateRequest ( "runearray" , 0 , "watch" )
got = client . ExpectEvaluateResponse ( t )
ref = checkEvalIndexed ( t , got , "[4]int32 [116,232,115,116]" , hasChildren , 4 , 1 )
2021-09-16 10:19:01 +00:00
checkNamedChildren ( ref , "runearray" , "int32" , runes , true )
2021-06-23 23:51:57 +00:00
} ,
disconnect : true ,
} } )
} )
}
2020-09-15 20:14:55 +00:00
// TestGlobalScopeAndVariables launches the program with showGlobalVariables
// arg set, executes to a breakpoint in the main package and tests that global
// package main variables got loaded. It then steps into a function
// in another package and tests that globals scope got updated to those vars.
func TestGlobalScopeAndVariables ( t * testing . T ) {
runTest ( t , "consts" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-09-15 20:14:55 +00:00
// Launch
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
// Stop at line 36
execute : func ( ) {
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.main" , 36 , 1000 , 3 , 3 )
2020-09-15 20:14:55 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package main)" , globalsScope )
2020-09-15 20:14:55 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( globalsScope )
2020-09-15 20:14:55 +00:00
client . ExpectVariablesResponse ( t )
// The program has no user-defined globals.
// Depending on the Go version, there might
// be some runtime globals (e.g. main..inittask)
// so testing for the total number is too fragile.
// Step into pkg.AnotherMethod()
client . StepInRequest ( 1 )
client . ExpectStepInResponse ( t )
client . ExpectStoppedEvent ( t )
client . StackTraceRequest ( 1 , 0 , 20 )
stack = client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "" , 13 , 1000 , 4 , 4 )
2020-09-15 20:14:55 +00:00
client . ScopesRequest ( 1000 )
scopes = client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package github.com/go-delve/delve/_fixtures/internal/dir0/pkg)" , globalsScope )
2020-09-15 20:14:55 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( globalsScope )
2020-09-15 20:14:55 +00:00
globals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , globals , "Globals" , 1 )
ref := checkVarExact ( t , globals , 0 , "SomeVar" , "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeVar" , "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType {X: 0}" , "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeType" , hasChildren )
2020-09-15 20:14:55 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
somevar := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , somevar , "SomeVar" , 1 )
2021-01-14 18:53:12 +00:00
// TODO(polina): unlike main.p, this prefix won't work
2021-05-26 15:15:39 +00:00
checkVarExact ( t , somevar , 0 , "X" , "github.com/go-delve/delve/_fixtures/internal/dir0/pkg.SomeVar.X" , "0" , "float64" , noChildren )
2020-09-15 20:14:55 +00:00
}
} ,
disconnect : false ,
} } )
} )
}
2021-04-19 18:14:50 +00:00
// TestShadowedVariables executes to a breakpoint and checks the shadowed
// variable is named correctly.
func TestShadowedVariables ( t * testing . T ) {
runTest ( t , "testshadow" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
// Breakpoints are set within the program
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
// Stop at line 13
execute : func ( ) {
client . StackTraceRequest ( 1 , 0 , 20 )
stack := client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesExact ( t , stack , "main.main" , 13 , 1000 , 3 , 3 )
2021-04-19 18:14:50 +00:00
client . ScopesRequest ( 1000 )
scopes := client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
checkScope ( t , scopes , 0 , "Locals" , localsScope )
checkScope ( t , scopes , 1 , "Globals (package main)" , globalsScope )
2021-04-19 18:14:50 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-04-19 18:14:50 +00:00
locals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , 0 , "(a)" , "a" , "0" , "int" , ! hasChildren )
checkVarExact ( t , locals , 1 , "a" , "a" , "1" , "int" , ! hasChildren )
2021-04-19 18:14:50 +00:00
// Check that the non-shadowed of "a" is returned from evaluate request.
validateEvaluateName ( t , client , locals , 1 )
} ,
disconnect : false ,
} } )
} )
}
2020-07-08 17:20:05 +00:00
// Tests that 'stackTraceDepth' from LaunchRequest is parsed and passed to
// stacktrace requests handlers.
func TestLaunchRequestWithStackTraceDepth ( t * testing . T ) {
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
var stResp * dap . StackTraceResponse
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-07-08 17:20:05 +00:00
// Launch
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "stackTraceDepth" : 1 ,
} )
} ,
// Set breakpoints
fixture . Source , [ ] int { 8 } ,
2020-08-11 15:34:27 +00:00
[ ] onBreakpoint { { // Stop at line 8
execute : func ( ) {
2020-07-08 17:20:05 +00:00
client . StackTraceRequest ( 1 , 0 , 0 )
stResp = client . ExpectStackTraceResponse ( t )
2021-07-29 17:34:01 +00:00
checkStackFramesHasMore ( t , stResp , "main.Increment" , 8 , 1000 , 1 /*returned*/ , 2 /*available*/ )
2020-08-11 15:34:27 +00:00
} ,
disconnect : false ,
} } )
2020-02-15 19:52:53 +00:00
} )
}
2021-05-17 16:17:00 +00:00
type Breakpoint struct {
line int
path string
verified bool
msgPrefix string
}
func expectSetBreakpointsResponse ( t * testing . T , client * daptest . Client , bps [ ] Breakpoint ) {
t . Helper ( )
2021-09-23 04:29:05 +00:00
checkSetBreakpointsResponse ( t , bps , client . ExpectSetBreakpointsResponse ( t ) )
2021-05-17 16:17:00 +00:00
}
2021-09-23 04:29:05 +00:00
func checkSetBreakpointsResponse ( t * testing . T , bps [ ] Breakpoint , got * dap . SetBreakpointsResponse ) {
2021-05-17 16:17:00 +00:00
t . Helper ( )
2021-09-23 04:29:05 +00:00
checkBreakpoints ( t , bps , got . Body . Breakpoints )
2021-05-20 17:00:51 +00:00
}
2021-09-23 04:29:05 +00:00
func checkBreakpoints ( t * testing . T , bps [ ] Breakpoint , breakpoints [ ] dap . Breakpoint ) {
2021-05-20 17:00:51 +00:00
t . Helper ( )
if len ( breakpoints ) != len ( bps ) {
t . Errorf ( "got %#v,\nwant len(Breakpoints)=%d" , breakpoints , len ( bps ) )
2021-05-17 16:17:00 +00:00
return
}
2021-05-20 17:00:51 +00:00
for i , bp := range breakpoints {
2021-05-28 18:21:53 +00:00
if bps [ i ] . line < 0 && ! bps [ i ] . verified {
2021-06-23 18:21:19 +00:00
if bp . Verified != bps [ i ] . verified || ! stringContainsCaseInsensitive ( bp . Message , bps [ i ] . msgPrefix ) {
2021-05-28 18:21:53 +00:00
t . Errorf ( "got breakpoints[%d] = %#v, \nwant %#v" , i , bp , bps [ i ] )
}
continue
}
2021-05-17 16:17:00 +00:00
if bp . Line != bps [ i ] . line || bp . Verified != bps [ i ] . verified || bp . Source . Path != bps [ i ] . path ||
! strings . HasPrefix ( bp . Message , bps [ i ] . msgPrefix ) {
t . Errorf ( "got breakpoints[%d] = %#v, \nwant %#v" , i , bp , bps [ i ] )
}
}
}
2020-10-02 16:18:33 +00:00
// TestSetBreakpoint executes to a breakpoint and tests different
// configurations of setBreakpoint requests.
func TestSetBreakpoint ( t * testing . T ) {
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-10-02 16:18:33 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 16 } , // b main.main
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 16 )
2020-10-02 16:18:33 +00:00
// Set two breakpoints at the next two lines in main
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 17 , 18 } )
2021-05-17 16:17:00 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 17 , fixture . Source , true , "" } , { 18 , fixture . Source , true , "" } } )
2020-10-02 16:18:33 +00:00
// Clear 17, reset 18
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 18 } )
2021-05-17 16:17:00 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 18 , fixture . Source , true , "" } } )
2020-10-02 16:18:33 +00:00
// Skip 17, continue to 18
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 18 )
2020-10-02 16:18:33 +00:00
// Set another breakpoint inside the loop in loop(), twice to trigger error
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 8 , 8 } )
2021-06-23 18:21:19 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 8 , fixture . Source , true , "" } , { - 1 , "" , false , "breakpoint exists" } } )
2020-10-02 16:18:33 +00:00
// Continue into the loop
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.loop" , 8 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-10-02 16:18:33 +00:00
locals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , 0 , "i" , "i" , "0" , "int" , noChildren ) // i == 0
2020-10-02 16:18:33 +00:00
// Edit the breakpoint to add a condition
2021-09-24 22:22:32 +00:00
client . SetBreakpointsRequestWithArgs ( fixture . Source , [ ] int { 8 } , map [ int ] string { 8 : "i == 3" } , nil , nil )
2021-05-17 16:17:00 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 8 , fixture . Source , true , "" } } )
2020-10-02 16:18:33 +00:00
// Continue until condition is hit
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.loop" , 8 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-10-02 16:18:33 +00:00
locals = client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , 0 , "i" , "i" , "3" , "int" , noChildren ) // i == 3
2020-10-02 16:18:33 +00:00
// Edit the breakpoint to remove a condition
2021-09-24 22:22:32 +00:00
client . SetBreakpointsRequestWithArgs ( fixture . Source , [ ] int { 8 } , map [ int ] string { 8 : "" } , nil , nil )
2021-05-17 16:17:00 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 8 , fixture . Source , true , "" } } )
2020-10-02 16:18:33 +00:00
// Continue for one more loop iteration
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.loop" , 8 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-10-02 16:18:33 +00:00
locals = client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , 0 , "i" , "i" , "4" , "int" , noChildren ) // i == 4
2020-10-02 16:18:33 +00:00
// Set at a line without a statement
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 1000 } )
2021-05-28 18:21:53 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { - 1 , "" , false , "could not find statement" } } ) // all cleared, none set
2020-10-02 16:18:33 +00:00
} ,
// The program has an infinite loop, so we must kill it by disconnecting.
disconnect : true ,
} } )
} )
}
2021-09-09 15:49:11 +00:00
func TestPauseAtStop ( t * testing . T ) {
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 16 } ,
[ ] onBreakpoint { {
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 16 )
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 6 , 8 } )
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 6 , fixture . Source , true , "" } , { 8 , fixture . Source , true , "" } } )
// Send a pause request while stopped on a cleared breakpoint.
client . PauseRequest ( 1 )
client . ExpectPauseResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
checkStop ( t , client , 1 , "main.loop" , 6 )
// Send a pause request while stopped on a breakpoint.
client . PauseRequest ( 1 )
client . ExpectPauseResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se := client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" {
t . Errorf ( "got %#v, expected breakpoint" , se )
}
checkStop ( t , client , 1 , "main.loop" , 8 )
// Send a pause request while stopped after stepping.
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
client . ExpectStoppedEvent ( t )
checkStop ( t , client , 1 , "main.loop" , 9 )
client . PauseRequest ( 1 )
client . ExpectPauseResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
checkStop ( t , client , 1 , "main.loop" , 8 )
} ,
// The program has an infinite loop, so we must kill it by disconnecting.
disconnect : true ,
} } )
} )
}
2021-07-08 05:08:52 +00:00
func checkHitBreakpointIds ( t * testing . T , se * dap . StoppedEvent , reason string , id int ) {
if se . Body . ThreadId != 1 || se . Body . Reason != reason || len ( se . Body . HitBreakpointIds ) != 1 || se . Body . HitBreakpointIds [ 0 ] != id {
t . Errorf ( "got %#v, want Reason=%q, ThreadId=1, HitBreakpointIds=[]int{%d}" , se , reason , id )
}
}
// TestHitBreakpointIds executes to a breakpoint and tests that
// the breakpoint ids in the stopped event are correct.
func TestHitBreakpointIds ( t * testing . T ) {
runTest ( t , "locationsprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 30 } , // b main.main
[ ] onBreakpoint { {
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 30 )
// Set two source breakpoints and two function breakpoints.
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 23 , 33 } )
sourceBps := client . ExpectSetBreakpointsResponse ( t ) . Body . Breakpoints
2021-09-23 04:29:05 +00:00
checkBreakpoints ( t , [ ] Breakpoint { { line : 23 , path : fixture . Source , verified : true } , { line : 33 , path : fixture . Source , verified : true } } , sourceBps )
2021-07-08 05:08:52 +00:00
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "anotherFunction" } ,
{ Name : "anotherFunction:1" } ,
} )
functionBps := client . ExpectSetFunctionBreakpointsResponse ( t ) . Body . Breakpoints
2021-09-23 04:29:05 +00:00
checkBreakpoints ( t , [ ] Breakpoint { { line : 26 , path : fixture . Source , verified : true } , { line : 27 , path : fixture . Source , verified : true } } , functionBps )
2021-07-08 05:08:52 +00:00
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se := client . ExpectStoppedEvent ( t )
checkHitBreakpointIds ( t , se , "breakpoint" , sourceBps [ 1 ] . Id )
checkStop ( t , client , 1 , "main.main" , 33 )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
checkHitBreakpointIds ( t , se , "breakpoint" , sourceBps [ 0 ] . Id )
checkStop ( t , client , 1 , "main.(*SomeType).SomeFunction" , 23 )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
checkHitBreakpointIds ( t , se , "function breakpoint" , functionBps [ 0 ] . Id )
checkStop ( t , client , 1 , "main.anotherFunction" , 26 )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
checkHitBreakpointIds ( t , se , "function breakpoint" , functionBps [ 1 ] . Id )
checkStop ( t , client , 1 , "main.anotherFunction" , 27 )
} ,
disconnect : true ,
} } )
} )
}
2021-06-23 18:21:19 +00:00
func stringContainsCaseInsensitive ( got , want string ) bool {
return strings . Contains ( strings . ToLower ( got ) , strings . ToLower ( want ) )
}
2021-05-18 17:25:16 +00:00
// TestSetFunctionBreakpoints is inspired by service/test.TestClientServer_FindLocations.
func TestSetFunctionBreakpoints ( t * testing . T ) {
runTest ( t , "locationsprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 30 } , // b main.main
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 30 )
2021-05-18 17:25:16 +00:00
type Breakpoint struct {
line int
sourceName string
verified bool
errMsg string
}
expectSetFunctionBreakpointsResponse := func ( bps [ ] Breakpoint ) {
t . Helper ( )
got := client . ExpectSetFunctionBreakpointsResponse ( t )
if len ( got . Body . Breakpoints ) != len ( bps ) {
t . Errorf ( "got %#v,\nwant len(Breakpoints)=%d" , got , len ( bps ) )
return
}
for i , bp := range got . Body . Breakpoints {
2021-05-28 18:21:53 +00:00
if bps [ i ] . line < 0 && ! bps [ i ] . verified {
2021-06-23 18:21:19 +00:00
if bp . Verified != bps [ i ] . verified || ! stringContainsCaseInsensitive ( bp . Message , bps [ i ] . errMsg ) {
2021-05-18 17:25:16 +00:00
t . Errorf ( "got breakpoints[%d] = %#v, \nwant %#v" , i , bp , bps [ i ] )
}
continue
}
// Some function breakpoints may be in packages that have been imported and we do not control, so
// we do not always want to check breakpoint lines.
if ( bps [ i ] . line >= 0 && bp . Line != bps [ i ] . line ) || bp . Verified != bps [ i ] . verified || bp . Source . Name != bps [ i ] . sourceName {
t . Errorf ( "got breakpoints[%d] = %#v, \nwant %#v" , i , bp , bps [ i ] )
}
}
}
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "anotherFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 26 , filepath . Base ( fixture . Source ) , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "main.anotherFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 26 , filepath . Base ( fixture . Source ) , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "SomeType.String" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "(*SomeType).String" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "main.SomeType.String" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "main.(*SomeType).String" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } } )
// Test line offsets
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "main.anotherFunction:1" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 27 , filepath . Base ( fixture . Source ) , true , "" } } )
// Test function names in imported package.
// Issue #275
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "io/ioutil.ReadFile" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "ioutil.go" , true , "" } } )
// Issue #296
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "/io/ioutil.ReadFile" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "ioutil.go" , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "ioutil.ReadFile" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "ioutil.go" , true , "" } } )
// Function Breakpoint name also accepts breakpoints that are specified as file:line.
// TODO(suzmue): We could return an error, but it probably is not necessary since breakpoints,
// and function breakpoints come in with different requests.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : fmt . Sprintf ( "%s:14" , fixture . Source ) } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : fmt . Sprintf ( "%s:14" , filepath . Base ( fixture . Source ) ) } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } } )
// Expect error for ambiguous function name.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "String" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "" , false , "Location \"String\" ambiguous" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "main.String" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "" , false , "Location \"main.String\" ambiguous" } } )
// Expect error for function that does not exist.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "fakeFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "" , false , "location \"fakeFunction\" not found" } } )
// Expect error for negative line number.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "main.anotherFunction:-1" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "" , false , "line offset negative or not a number" } } )
// Expect error when function name is regex.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : ` /^.*String.*$/ ` } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , "" , false , "breakpoint name \"/^.*String.*$/\" could not be parsed as a function" } } )
// Expect error when function name is an offset.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "+1" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , filepath . Base ( fixture . Source ) , false , "breakpoint name \"+1\" could not be parsed as a function" } } )
// Expect error when function name is a line number.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "14" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , filepath . Base ( fixture . Source ) , false , "breakpoint name \"14\" could not be parsed as a function" } } )
// Expect error when function name is an address.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "*b" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , filepath . Base ( fixture . Source ) , false , "breakpoint name \"*b\" could not be parsed as a function" } } )
// Expect error when function name is a relative path.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : fmt . Sprintf ( ".%s%s:14" , string ( filepath . Separator ) , filepath . Base ( fixture . Source ) ) } ,
} )
// This relative path could also be caught by the parser, so we should not match the error message.
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , filepath . Base ( fixture . Source ) , false , "" } } )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : fmt . Sprintf ( "..%s%s:14" , string ( filepath . Separator ) , filepath . Base ( fixture . Source ) ) } ,
} )
// This relative path could also be caught by the parser, so we should not match the error message.
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { - 1 , filepath . Base ( fixture . Source ) , false , "" } } )
// Test multiple function breakpoints.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "SomeType.String" } , { Name : "anotherFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } , { 26 , filepath . Base ( fixture . Source ) , true , "" } } )
// Test multiple breakpoints to the same location.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "SomeType.String" } , { Name : "(*SomeType).String" } ,
} )
2021-06-23 18:21:19 +00:00
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } , { - 1 , "" , false , "breakpoint exists" } } )
2021-05-18 17:25:16 +00:00
// Set two breakpoints at SomeType.String and SomeType.SomeFunction.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "SomeType.String" } , { Name : "SomeType.SomeFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 14 , filepath . Base ( fixture . Source ) , true , "" } , { 22 , filepath . Base ( fixture . Source ) , true , "" } } )
// Clear SomeType.String, reset SomeType.SomeFunction (SomeType.String is called before SomeType.SomeFunction).
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "SomeType.SomeFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 22 , filepath . Base ( fixture . Source ) , true , "" } } )
// Expect the next breakpoint to be at SomeType.SomeFunction.
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
if se := client . ExpectStoppedEvent ( t ) ; se . Body . Reason != "function breakpoint" || se . Body . ThreadId != 1 {
t . Errorf ( "got %#v, want Reason=\"function breakpoint\", ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.(*SomeType).SomeFunction" , 22 )
2021-05-18 17:25:16 +00:00
// Set a breakpoint at the next line in the program.
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 23 } )
got := client . ExpectSetBreakpointsResponse ( t )
if len ( got . Body . Breakpoints ) != 1 {
t . Errorf ( "got %#v,\nwant len(Breakpoints)=%d" , got , 1 )
return
}
bp := got . Body . Breakpoints [ 0 ]
if bp . Line != 23 || bp . Verified != true || bp . Source . Path != fixture . Source {
t . Errorf ( "got breakpoints[0] = %#v, \nwant Line=23 Verified=true Source.Path=%q" , bp , fixture . Source )
}
// Set a function breakpoint, this should not clear the breakpoint that was set in the previous setBreakpoints request.
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint {
{ Name : "anotherFunction" } ,
} )
expectSetFunctionBreakpointsResponse ( [ ] Breakpoint { { 26 , filepath . Base ( fixture . Source ) , true , "" } } )
// Expect the next breakpoint to be at line 23.
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
if se := client . ExpectStoppedEvent ( t ) ; se . Body . Reason != "breakpoint" || se . Body . ThreadId != 1 {
t . Errorf ( "got %#v, want Reason=\"breakpoint\", ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.(*SomeType).SomeFunction" , 23 )
2021-05-18 17:25:16 +00:00
// Set a breakpoint, this should not clear the breakpoint that was set in the previous setFunctionBreakpoints request.
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 37 } )
got = client . ExpectSetBreakpointsResponse ( t )
if len ( got . Body . Breakpoints ) != 1 {
t . Errorf ( "got %#v,\nwant len(Breakpoints)=%d" , got , 1 )
return
}
bp = got . Body . Breakpoints [ 0 ]
if bp . Line != 37 || bp . Verified != true || bp . Source . Path != fixture . Source {
t . Errorf ( "got breakpoints[0] = %#v, \nwant Line=23 Verified=true Source.Path=%q" , bp , fixture . Source )
}
// Expect the next breakpoint to be at line anotherFunction.
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
if se := client . ExpectStoppedEvent ( t ) ; se . Body . Reason != "function breakpoint" || se . Body . ThreadId != 1 {
t . Errorf ( "got %#v, want Reason=\"function breakpoint\", ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.anotherFunction" , 26 )
2021-05-18 17:25:16 +00:00
} ,
disconnect : true ,
} } )
} )
}
2021-09-24 22:22:32 +00:00
// TestLogPoints executes to a breakpoint and tests that log points
// send OutputEvents and do not halt program execution.
func TestLogPoints ( t * testing . T ) {
runTest ( t , "callme" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 23 } ,
[ ] onBreakpoint { {
// Stop at line 23
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 23 )
bps := [ ] int { 6 , 25 , 27 , 16 }
logMessages := map [ int ] string { 6 : "in callme!" , 16 : "in callme2!" }
client . SetBreakpointsRequestWithArgs ( fixture . Source , bps , nil , nil , logMessages )
client . ExpectSetBreakpointsResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
for i := 0 ; i < 5 ; i ++ {
se := client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || se . Body . ThreadId != 1 {
t . Errorf ( "got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1" , se )
}
checkStop ( t , client , 1 , "main.main" , 25 )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
2021-10-06 01:29:46 +00:00
checkLogMessage ( t , client . ExpectOutputEvent ( t ) , 1 , "in callme!" , fixture . Source , 6 )
2021-09-24 22:22:32 +00:00
}
se := client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || se . Body . ThreadId != 1 {
t . Errorf ( "got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1" , se )
}
checkStop ( t , client , 1 , "main.main" , 27 )
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
2021-10-06 01:29:46 +00:00
checkLogMessage ( t , client . ExpectOutputEvent ( t ) , 1 , "in callme2!" , fixture . Source , 16 )
2021-09-24 22:22:32 +00:00
se = client . ExpectStoppedEvent ( t )
if se . Body . Reason != "step" || se . Body . ThreadId != 1 {
t . Errorf ( "got stopped event = %#v, \nwant Reason=\"step\" ThreadId=1" , se )
}
checkStop ( t , client , 1 , "main.main" , 28 )
} ,
disconnect : true ,
} } )
} )
}
2021-10-06 01:29:46 +00:00
func checkLogMessage ( t * testing . T , oe * dap . OutputEvent , goid int , text , path string , line int ) {
t . Helper ( )
prefix := "> [Go "
if goid >= 0 {
prefix += strconv . Itoa ( goid ) + "]"
}
if oe . Body . Category != "stdout" || ! strings . HasPrefix ( oe . Body . Output , prefix ) || ! strings . HasSuffix ( oe . Body . Output , text + "\n" ) {
t . Errorf ( "got output event = %#v, \nwant Category=\"stdout\" Output=\"%s: %s\\n\"" , oe , prefix , text )
}
if oe . Body . Line != line || oe . Body . Source . Path != path {
t . Errorf ( "got output event = %#v, \nwant Line=%d Source.Path=%s" , oe , line , path )
}
}
2021-09-24 22:22:32 +00:00
// TestHaltPreventsAutoResume tests that a pause request issued while processing
// log messages will result in a real stop.
func TestHaltPreventsAutoResume ( t * testing . T ) {
runTest ( t , "callme" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" , // Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 23 } ,
[ ] onBreakpoint { {
execute : func ( ) {
savedResumeOnce := resumeOnceAndCheckStop
defer func ( ) {
resumeOnceAndCheckStop = savedResumeOnce
} ( )
checkStop ( t , client , 1 , "main.main" , 23 )
bps := [ ] int { 6 , 25 }
logMessages := map [ int ] string { 6 : "in callme!" }
client . SetBreakpointsRequestWithArgs ( fixture . Source , bps , nil , nil , logMessages )
client . ExpectSetBreakpointsResponse ( t )
for i := 0 ; i < 5 ; i ++ {
// Reset the handler to the default behavior.
resumeOnceAndCheckStop = savedResumeOnce
// Expect a pause request while stopped not to interrupt continue.
client . PauseRequest ( 1 )
client . ExpectPauseResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se := client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || se . Body . ThreadId != 1 {
t . Errorf ( "got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1" , se )
}
checkStop ( t , client , 1 , "main.main" , 25 )
pauseDoneChan := make ( chan struct { } , 1 )
outputDoneChan := make ( chan struct { } , 1 )
// Send a halt request when trying to resume the program after being
// interrupted. This should allow the log message to be processed,
// but keep the process from continuing beyond the line.
2021-10-04 16:13:17 +00:00
resumeOnceAndCheckStop = func ( s * Session , command string , allowNextStateChange chan struct { } ) ( * api . DebuggerState , error ) {
2021-09-24 22:22:32 +00:00
// This should trigger after the log message is sent, but before
// execution is resumed.
if command == api . DirectionCongruentContinue {
go func ( ) {
<- outputDoneChan
defer close ( pauseDoneChan )
client . PauseRequest ( 1 )
client . ExpectPauseResponse ( t )
} ( )
// Wait for the pause to be complete.
<- pauseDoneChan
}
return s . resumeOnceAndCheckStop ( command , allowNextStateChange )
}
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
2021-10-06 01:29:46 +00:00
checkLogMessage ( t , client . ExpectOutputEvent ( t ) , 1 , "in callme!" , fixture . Source , 6 )
2021-09-24 22:22:32 +00:00
// Signal that the output event has been received.
close ( outputDoneChan )
// Wait for the pause to be complete.
<- pauseDoneChan
se = client . ExpectStoppedEvent ( t )
if se . Body . Reason != "pause" {
t . Errorf ( "got stopped event = %#v, \nwant Reason=\"pause\"" , se )
}
checkStop ( t , client , 1 , "main.callme" , 6 )
}
} ,
disconnect : true ,
} } )
} )
}
// TestConcurrentBreakpointsLogPoints executes to a breakpoint and then tests
// that a breakpoint set in the main goroutine is hit the correct number of times
// and log points set in the children goroutines produce the correct number of
// output events.
func TestConcurrentBreakpointsLogPoints ( t * testing . T ) {
if runtime . GOOS == "freebsd" {
t . SkipNow ( )
}
runTest ( t , "goroutinestackprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 20 } ,
[ ] onBreakpoint { {
// Stop at line 20
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 20 )
bps := [ ] int { 8 , 23 }
logMessages := map [ int ] string { 8 : "hello" }
client . SetBreakpointsRequestWithArgs ( fixture . Source , bps , nil , nil , logMessages )
client . ExpectSetBreakpointsResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
// There may be up to 1 breakpoint and any number of log points that are
// hit concurrently. We should get a stopped event everytime the breakpoint
// is hit and an output event for each log point hit.
var oeCount , seCount int
for oeCount < 10 || seCount < 10 {
switch m := client . ExpectMessage ( t ) . ( type ) {
case * dap . StoppedEvent :
if m . Body . Reason != "breakpoint" || ! m . Body . AllThreadsStopped || m . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1" , m )
}
checkStop ( t , client , 1 , "main.main" , 23 )
seCount ++
client . ContinueRequest ( 1 )
case * dap . OutputEvent :
2021-10-06 01:29:46 +00:00
checkLogMessage ( t , m , - 1 , "hello" , fixture . Source , 8 )
2021-09-24 22:22:32 +00:00
oeCount ++
case * dap . ContinueResponse :
case * dap . TerminatedEvent :
t . Fatalf ( "\nexpected 10 output events and 10 stopped events, got %d output events and %d stopped events" , oeCount , seCount )
default :
t . Fatalf ( "Unexpected message type: expect StoppedEvent, OutputEvent, or ContinueResponse, got %#v" , m )
}
}
client . ExpectTerminatedEvent ( t )
} ,
disconnect : false ,
} } )
} )
}
2021-05-17 16:17:00 +00:00
func expectSetBreakpointsResponseAndStoppedEvent ( t * testing . T , client * daptest . Client ) ( se * dap . StoppedEvent , br * dap . SetBreakpointsResponse ) {
2021-08-05 17:16:15 +00:00
t . Helper ( )
var oe * dap . OutputEvent
for i := 0 ; i < 3 ; i ++ {
2021-05-17 16:17:00 +00:00
switch m := client . ExpectMessage ( t ) . ( type ) {
2021-08-05 17:16:15 +00:00
case * dap . OutputEvent :
oe = m
2021-05-17 16:17:00 +00:00
case * dap . StoppedEvent :
se = m
case * dap . SetBreakpointsResponse :
br = m
default :
2021-08-05 17:16:15 +00:00
t . Fatalf ( "Unexpected message type: expect OutputEvent, StoppedEvent or SetBreakpointsResponse, got %#v" , m )
2021-05-17 16:17:00 +00:00
}
}
2021-08-05 17:16:15 +00:00
if se == nil || br == nil || oe == nil {
t . Fatal ( "Expected OutputEvent, StoppedEvent and SetBreakpointsResponse" )
}
if ! strings . HasPrefix ( oe . Body . Output , "Execution halted to set breakpoints" ) || oe . Body . Category != "console" {
t . Errorf ( ` got %#v, want Category="console" Output="Execution halted to set breakpoints..." ` , oe )
2021-05-17 16:17:00 +00:00
}
return se , br
}
2021-05-20 17:00:51 +00:00
func expectSetFunctionBreakpointsResponseAndStoppedEvent ( t * testing . T , client * daptest . Client ) ( se * dap . StoppedEvent , br * dap . SetFunctionBreakpointsResponse ) {
2021-08-05 17:16:15 +00:00
var oe * dap . OutputEvent
for i := 0 ; i < 3 ; i ++ {
2021-05-20 17:00:51 +00:00
switch m := client . ExpectMessage ( t ) . ( type ) {
2021-08-05 17:16:15 +00:00
case * dap . OutputEvent :
oe = m
2021-05-20 17:00:51 +00:00
case * dap . StoppedEvent :
se = m
case * dap . SetFunctionBreakpointsResponse :
br = m
default :
2021-08-05 17:16:15 +00:00
t . Fatalf ( "Unexpected message type: expect OutputEvent, StoppedEvent or SetFunctionBreakpointsResponse, got %#v" , m )
2021-05-20 17:00:51 +00:00
}
}
2021-08-05 17:16:15 +00:00
if se == nil || br == nil || oe == nil {
t . Fatal ( "Expected OutputEvent, StoppedEvent and SetFunctionBreakpointsResponse" )
}
if ! strings . HasPrefix ( oe . Body . Output , "Execution halted to set breakpoints" ) || oe . Body . Category != "console" {
t . Errorf ( ` got %#v, want Category="console" Output="Execution halted to set breakpoints..." ` , oe )
2021-05-20 17:00:51 +00:00
}
return se , br
}
2021-05-17 16:17:00 +00:00
func TestSetBreakpointWhileRunning ( t * testing . T ) {
runTest ( t , "integrationprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 16 } ,
[ ] onBreakpoint { {
execute : func ( ) {
// The program loops 3 times over lines 14-15-8-9-10-16
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 16 ) // Line that sleeps for 1 second
2021-05-17 16:17:00 +00:00
// We can set breakpoints while nexting
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 15 } ) // [16,] => [15,]
se , br := expectSetBreakpointsResponseAndStoppedEvent ( t , client )
if se . Body . Reason != "pause" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 0 && se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='pause' AllThreadsStopped=true ThreadId=0/1" , se )
}
2021-09-23 04:29:05 +00:00
checkSetBreakpointsResponse ( t , [ ] Breakpoint { { 15 , fixture . Source , true , "" } } , br )
2021-05-17 16:17:00 +00:00
// Halt cancelled next, so if we continue we will not stop at line 14.
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 15 )
2021-05-17 16:17:00 +00:00
// We can set breakpoints while continuing
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 9 } ) // [15,] => [9,]
se , br = expectSetBreakpointsResponseAndStoppedEvent ( t , client )
if se . Body . Reason != "pause" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 0 && se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='pause' AllThreadsStopped=true ThreadId=0/1" , se )
}
2021-09-23 04:29:05 +00:00
checkSetBreakpointsResponse ( t , [ ] Breakpoint { { 9 , fixture . Source , true , "" } } , br )
2021-05-17 16:17:00 +00:00
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.sayhi" , 9 )
2021-05-17 16:17:00 +00:00
} ,
disconnect : true ,
} } )
} )
}
2021-05-20 17:00:51 +00:00
func TestSetFunctionBreakpointWhileRunning ( t * testing . T ) {
runTest ( t , "integrationprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 16 } ,
[ ] onBreakpoint { {
execute : func ( ) {
// The program loops 3 times over lines 14-15-8-9-10-16
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 16 ) // Line that sleeps for 1 second
2021-05-20 17:00:51 +00:00
// We can set breakpoints while nexting
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint { { Name : "main.sayhi" } } ) // [16,] => [16, 8]
se , br := expectSetFunctionBreakpointsResponseAndStoppedEvent ( t , client )
if se . Body . Reason != "pause" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 0 && se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='pause' AllThreadsStopped=true ThreadId=0/1" , se )
}
2021-09-23 04:29:05 +00:00
checkBreakpoints ( t , [ ] Breakpoint { { 8 , fixture . Source , true , "" } } , br . Body . Breakpoints )
2021-05-20 17:00:51 +00:00
client . SetBreakpointsRequest ( fixture . Source , [ ] int { } ) // [16,8] => [8]
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { } )
// Halt cancelled next, so if we continue we will not stop at line 14.
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
if se . Body . Reason != "function breakpoint" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.sayhi" , 8 )
2021-05-20 17:00:51 +00:00
// We can set breakpoints while continuing
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . SetFunctionBreakpointsRequest ( [ ] dap . FunctionBreakpoint { } ) // [8,] => []
se , br = expectSetFunctionBreakpointsResponseAndStoppedEvent ( t , client )
if se . Body . Reason != "pause" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 0 && se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='pause' AllThreadsStopped=true ThreadId=0/1" , se )
}
2021-09-23 04:29:05 +00:00
checkBreakpoints ( t , [ ] Breakpoint { } , br . Body . Breakpoints )
2021-05-20 17:00:51 +00:00
if se . Body . Reason != "pause" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 0 && se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='pause' AllThreadsStopped=true ThreadId=0/1" , se )
}
2021-09-23 04:29:05 +00:00
checkBreakpoints ( t , [ ] Breakpoint { } , br . Body . Breakpoints )
2021-05-20 17:00:51 +00:00
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 16 } ) // [] => [16]
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 16 , fixture . Source , true , "" } } )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
se = client . ExpectStoppedEvent ( t )
if se . Body . Reason != "breakpoint" || ! se . Body . AllThreadsStopped || se . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 16 )
2021-05-20 17:00:51 +00:00
} ,
disconnect : true ,
} } )
} )
}
2021-05-28 18:21:53 +00:00
func TestHitConditionBreakpoints ( t * testing . T ) {
runTest ( t , "break" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 4 } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-09-24 22:22:32 +00:00
client . SetBreakpointsRequestWithArgs ( fixture . Source , [ ] int { 7 } , nil , map [ int ] string { 7 : "4" } , nil )
2021-05-28 18:21:53 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 7 , fixture . Source , true , "" } } )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
checkStop ( t , client , 1 , "main.main" , 7 )
// Check that we are stopped at the correct value of i.
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-05-28 18:21:53 +00:00
locals := client . ExpectVariablesResponse ( t )
checkVarExact ( t , locals , 0 , "i" , "i" , "4" , "int" , noChildren )
// Change the hit condition.
2021-09-24 22:22:32 +00:00
client . SetBreakpointsRequestWithArgs ( fixture . Source , [ ] int { 7 } , nil , map [ int ] string { 7 : "% 2" } , nil )
2021-05-28 18:21:53 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 7 , fixture . Source , true , "" } } )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
checkStop ( t , client , 1 , "main.main" , 7 )
// Check that we are stopped at the correct value of i.
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-05-28 18:21:53 +00:00
locals = client . ExpectVariablesResponse ( t )
checkVarExact ( t , locals , 0 , "i" , "i" , "6" , "int" , noChildren )
// Expect an error if an assignment is passed.
2021-09-24 22:22:32 +00:00
client . SetBreakpointsRequestWithArgs ( fixture . Source , [ ] int { 7 } , nil , map [ int ] string { 7 : "= 2" } , nil )
2021-05-28 18:21:53 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { - 1 , "" , false , "" } } )
// Change the hit condition.
2021-09-24 22:22:32 +00:00
client . SetBreakpointsRequestWithArgs ( fixture . Source , [ ] int { 7 } , nil , map [ int ] string { 7 : "< 8" } , nil )
2021-05-28 18:21:53 +00:00
expectSetBreakpointsResponse ( t , client , [ ] Breakpoint { { 7 , fixture . Source , true , "" } } )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
checkStop ( t , client , 1 , "main.main" , 7 )
// Check that we are stopped at the correct value of i.
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-05-28 18:21:53 +00:00
locals = client . ExpectVariablesResponse ( t )
checkVarExact ( t , locals , 0 , "i" , "i" , "7" , "int" , noChildren )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectTerminatedEvent ( t )
} ,
disconnect : false ,
} } )
} )
}
2021-05-20 17:00:51 +00:00
2021-04-15 23:35:37 +00:00
// TestLaunchSubstitutePath sets a breakpoint using a path
// that does not exist and expects the substitutePath attribute
// in the launch configuration to take care of the mapping.
func TestLaunchSubstitutePath ( t * testing . T ) {
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
substitutePathTestHelper ( t , fixture , client , "launch" , map [ string ] interface { } { "mode" : "exec" , "program" : fixture . Path } )
} )
}
// TestAttachSubstitutePath sets a breakpoint using a path
// that does not exist and expects the substitutePath attribute
// in the launch configuration to take care of the mapping.
func TestAttachSubstitutePath ( t * testing . T ) {
if runtime . GOOS == "freebsd" {
t . SkipNow ( )
}
if runtime . GOOS == "windows" {
t . Skip ( "test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details" )
}
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
cmd := execFixture ( t , fixture )
substitutePathTestHelper ( t , fixture , client , "attach" , map [ string ] interface { } { "mode" : "local" , "processId" : cmd . Process . Pid } )
} )
}
func substitutePathTestHelper ( t * testing . T , fixture protest . Fixture , client * daptest . Client , request string , launchAttachConfig map [ string ] interface { } ) {
t . Helper ( )
nonexistentDir := filepath . Join ( string ( filepath . Separator ) , "path" , "that" , "does" , "not" , "exist" )
if runtime . GOOS == "windows" {
nonexistentDir = "C:" + nonexistentDir
}
launchAttachConfig [ "stopOnEntry" ] = false
// The rules in 'substitutePath' will be applied as follows:
// - mapping paths from client to server:
// The first rule["from"] to match a prefix of 'path' will be applied:
// strings.Replace(path, rule["from"], rule["to"], 1)
// - mapping paths from server to client:
// The first rule["to"] to match a prefix of 'path' will be applied:
// strings.Replace(path, rule["to"], rule["from"], 1)
launchAttachConfig [ "substitutePath" ] = [ ] map [ string ] string {
{ "from" : nonexistentDir , "to" : filepath . Dir ( fixture . Source ) } ,
// Since the path mappings are ordered, when converting from client path to
// server path, this mapping will not apply, because nonexistentDir appears in
// an earlier rule.
{ "from" : nonexistentDir , "to" : "this_is_a_bad_path" } ,
// Since the path mappings are ordered, when converting from server path to
// client path, this mapping will not apply, because filepath.Dir(fixture.Source)
// appears in an earlier rule.
{ "from" : "this_is_a_bad_path" , "to" : filepath . Dir ( fixture . Source ) } ,
}
runDebugSessionWithBPs ( t , client , request ,
func ( ) {
switch request {
case "attach" :
client . AttachRequest ( launchAttachConfig )
case "launch" :
client . LaunchRequestWithArgs ( launchAttachConfig )
default :
t . Fatalf ( "invalid request: %s" , request )
}
} ,
// Set breakpoints
filepath . Join ( nonexistentDir , "loopprog.go" ) , [ ] int { 8 } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.loop" , 8 )
2021-04-15 23:35:37 +00:00
} ,
disconnect : true ,
} } )
}
// execFixture runs the binary fixture.Path and hooks up stdout and stderr
// to os.Stdout and os.Stderr.
func execFixture ( t * testing . T , fixture protest . Fixture ) * exec . Cmd {
t . Helper ( )
// TODO(polina): do I need to sanity check testBackend and runtime.GOOS?
cmd := exec . Command ( fixture . Path )
cmd . Stdout = os . Stdout
cmd . Stderr = os . Stderr
if err := cmd . Start ( ) ; err != nil {
t . Fatal ( err )
}
return cmd
}
2021-03-23 03:06:09 +00:00
// TestWorkingDir executes to a breakpoint and tests that the specified
// working directory is the one used to run the program.
func TestWorkingDir ( t * testing . T ) {
runTest ( t , "workdir" , func ( client * daptest . Client , fixture protest . Fixture ) {
wd := os . TempDir ( )
// For Darwin `os.TempDir()` returns `/tmp` which is symlink to `/private/tmp`.
if runtime . GOOS == "darwin" {
wd = "/private/tmp"
}
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" ,
"program" : fixture . Path ,
"stopOnEntry" : false ,
2021-04-20 07:40:34 +00:00
"cwd" : wd ,
2021-03-23 03:06:09 +00:00
} )
} ,
// Set breakpoints
fixture . Source , [ ] int { 10 } , // b main.main
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 10 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-03-23 03:06:09 +00:00
locals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , locals , "Locals" , 2 )
2021-07-08 15:47:53 +00:00
for i := range locals . Body . Variables {
switch locals . Body . Variables [ i ] . Name {
case "pwd" :
checkVarExact ( t , locals , i , "pwd" , "pwd" , fmt . Sprintf ( "%q" , wd ) , "string" , noChildren )
case "err" :
checkVarExact ( t , locals , i , "err" , "err" , "error nil" , "error" , noChildren )
}
}
2021-03-23 03:06:09 +00:00
} ,
disconnect : false ,
} } )
} )
}
2021-05-26 15:15:39 +00:00
// checkEval is a helper for verifying the values within an EvaluateResponse.
2020-11-12 23:24:31 +00:00
// value - the value of the evaluated expression
// hasRef - true if the evaluated expression should have children and therefore a non-0 variable reference
// ref - reference to retrieve children of this evaluated expression (0 if none)
2021-05-26 15:15:39 +00:00
func checkEval ( t * testing . T , got * dap . EvaluateResponse , value string , hasRef bool ) ( ref int ) {
2020-11-12 23:24:31 +00:00
t . Helper ( )
if got . Body . Result != value || ( got . Body . VariablesReference > 0 ) != hasRef {
t . Errorf ( "\ngot %#v\nwant Result=%q hasRef=%t" , got , value , hasRef )
}
return got . Body . VariablesReference
}
2021-06-10 16:34:20 +00:00
// checkEvalIndexed is a helper for verifying the values within an EvaluateResponse.
// value - the value of the evaluated expression
// hasRef - true if the evaluated expression should have children and therefore a non-0 variable reference
// ref - reference to retrieve children of this evaluated expression (0 if none)
func checkEvalIndexed ( t * testing . T , got * dap . EvaluateResponse , value string , hasRef bool , indexed , named int ) ( ref int ) {
t . Helper ( )
if got . Body . Result != value || ( got . Body . VariablesReference > 0 ) != hasRef || got . Body . IndexedVariables != indexed || got . Body . NamedVariables != named {
t . Errorf ( "\ngot %#v\nwant Result=%q hasRef=%t IndexedVariables=%d NamedVariables=%d" , got , value , hasRef , indexed , named )
}
return got . Body . VariablesReference
}
2021-05-26 15:15:39 +00:00
func checkEvalRegex ( t * testing . T , got * dap . EvaluateResponse , valueRegex string , hasRef bool ) ( ref int ) {
2021-03-15 16:36:46 +00:00
t . Helper ( )
matched , _ := regexp . MatchString ( valueRegex , got . Body . Result )
if ! matched || ( got . Body . VariablesReference > 0 ) != hasRef {
t . Errorf ( "\ngot %#v\nwant Result=%q hasRef=%t" , got , valueRegex , hasRef )
}
return got . Body . VariablesReference
}
2020-11-12 23:24:31 +00:00
func TestEvaluateRequest ( t * testing . T ) {
runTest ( t , "testvariables" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-11-12 23:24:31 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
fixture . Source , [ ] int { } , // Breakpoint set in the program
[ ] onBreakpoint { { // Stop at first breakpoint
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.foobar" , 66 )
2020-11-12 23:24:31 +00:00
// Variable lookup
client . EvaluateRequest ( "a2" , 1000 , "this context will be ignored" )
got := client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "6" , noChildren )
2020-11-12 23:24:31 +00:00
client . EvaluateRequest ( "a5" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref := checkEval ( t , got , "[]int len: 5, cap: 5, [1,2,3,4,5]" , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
a5 := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , a5 , "a5" , 5 )
checkVarExact ( t , a5 , 0 , "[0]" , "(a5)[0]" , "1" , "int" , noChildren )
checkVarExact ( t , a5 , 4 , "[4]" , "(a5)[4]" , "5" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , a5 , 0 )
validateEvaluateName ( t , client , a5 , 4 )
2020-11-12 23:24:31 +00:00
}
2021-02-23 16:29:06 +00:00
// Variable lookup that's not fully loaded
client . EvaluateRequest ( "ba" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-06-10 16:34:20 +00:00
checkEvalIndexed ( t , got , "[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]" , hasChildren , 200 , 0 )
2021-02-23 16:29:06 +00:00
2020-11-12 23:24:31 +00:00
// All (binary and unary) on basic types except <-, ++ and --
client . EvaluateRequest ( "1+1" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "2" , noChildren )
2020-11-12 23:24:31 +00:00
// Comparison operators on any type
client . EvaluateRequest ( "1<2" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "true" , noChildren )
2020-11-12 23:24:31 +00:00
// Type casts between numeric types
client . EvaluateRequest ( "int(2.3)" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "2" , noChildren )
2020-11-12 23:24:31 +00:00
// Type casts of integer constants into any pointer type and vice versa
client . EvaluateRequest ( "(*int)(2)" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEvalRegex ( t , got , ` \*\(unreadable .+\) ` , hasChildren )
2021-05-04 19:53:42 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
val := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , val , "*(*int)(2)" , 1 )
checkVarRegex ( t , val , 0 , "^$" , ` \(\*\(\(\*int\)\(2\)\)\) ` , ` \(unreadable .+\) ` , "int" , noChildren )
2021-05-04 19:53:42 +00:00
validateEvaluateName ( t , client , val , 0 )
}
2021-02-23 16:29:06 +00:00
2020-11-12 23:24:31 +00:00
// Type casts between string, []byte and []rune
client . EvaluateRequest ( "[]byte(\"ABC€\")" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-06-10 16:34:20 +00:00
checkEvalIndexed ( t , got , "[]uint8 len: 6, cap: 6, [65,66,67,226,130,172]" , noChildren , 6 , 0 )
2020-11-12 23:24:31 +00:00
// Struct member access (i.e. somevar.memberfield)
client . EvaluateRequest ( "ms.Nest.Level" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "1" , noChildren )
2020-11-12 23:24:31 +00:00
// Slicing and indexing operators on arrays, slices and strings
client . EvaluateRequest ( "a5[4]" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "5" , noChildren )
2020-11-12 23:24:31 +00:00
// Map access
client . EvaluateRequest ( "mp[1]" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEval ( t , got , "interface {}(int) 42" , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
expr := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , expr , "mp[1]" , 1 )
checkVarExact ( t , expr , 0 , "data" , "(mp[1]).(data)" , "42" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , expr , 0 )
2020-11-12 23:24:31 +00:00
}
// Pointer dereference
client . EvaluateRequest ( "*ms.Nest" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEvalRegex ( t , got , ` main\.Nest { Level: 1, Nest: \*main.Nest { Level: 2, Nest: \*\(\*main\.Nest\)\(0x[0-9a-f]+\)}} ` , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
expr := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , expr , "*ms.Nest" , 2 )
checkVarExact ( t , expr , 0 , "Level" , "(*ms.Nest).Level" , "1" , "int" , noChildren )
2021-01-14 18:53:12 +00:00
validateEvaluateName ( t , client , expr , 0 )
2020-11-12 23:24:31 +00:00
}
// Calls to builtin functions: cap, len, complex, imag and real
client . EvaluateRequest ( "len(a5)" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "5" , noChildren )
2020-11-12 23:24:31 +00:00
// Type assertion on interface variables (i.e. somevar.(concretetype))
client . EvaluateRequest ( "mp[1].(int)" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "42" , noChildren )
2020-11-12 23:24:31 +00:00
} ,
disconnect : false ,
} , { // Stop at second breakpoint
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.barfoo" , 27 )
2020-11-12 23:24:31 +00:00
// Top-most frame
client . EvaluateRequest ( "a1" , 1000 , "this context will be ignored" )
got := client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "\"bur\"" , noChildren )
2020-11-12 23:24:31 +00:00
// No frame defaults to top-most frame
client . EvaluateRequest ( "a1" , 0 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "\"bur\"" , noChildren )
2020-11-12 23:24:31 +00:00
// Next frame
client . EvaluateRequest ( "a1" , 1001 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "\"foofoofoofoofoofoo\"" , noChildren )
2020-11-12 23:24:31 +00:00
// Next frame
client . EvaluateRequest ( "a1" , 1002 , "any context but watch" )
erres := client . ExpectVisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: could not find symbol value for a1" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"" , erres )
}
client . EvaluateRequest ( "a1" , 1002 , "watch" )
2021-05-06 09:11:45 +00:00
erres = client . ExpectInvisibleErrorResponse ( t )
2020-11-12 23:24:31 +00:00
if erres . Body . Error . Format != "Unable to evaluate expression: could not find symbol value for a1" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"" , erres )
}
2021-05-19 18:17:36 +00:00
client . EvaluateRequest ( "a1" , 1002 , "repl" )
erres = client . ExpectInvisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: could not find symbol value for a1" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"" , erres )
}
client . EvaluateRequest ( "a1" , 1002 , "hover" )
erres = client . ExpectInvisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: could not find symbol value for a1" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"" , erres )
}
2021-06-04 07:27:57 +00:00
client . EvaluateRequest ( "a1" , 1002 , "clipboard" )
erres = client . ExpectVisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: could not find symbol value for a1" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"" , erres )
}
2020-11-12 23:24:31 +00:00
} ,
disconnect : false ,
} } )
} )
}
2021-06-16 20:27:38 +00:00
// From testvariables2 fixture
const (
// As defined in the code
2021-06-22 15:14:47 +00:00
longstr = ` "very long string 0123456789a0123456789b0123456789c0123456789d0123456789e0123456789f0123456789g012345678h90123456789i0123456789j0123456789" `
2021-06-16 20:27:38 +00:00
// Loaded with MaxStringLen=64
longstrLoaded64 = ` "very long string 0123456789a0123456789b0123456789c0123456789d012...+73 more" `
longstrLoaded64re = ` \"very long string 0123456789a0123456789b0123456789c0123456789d012\.\.\.\+73 more\" `
)
// TestVariableValueTruncation tests that in certain cases
// we truncate the loaded variable values to make display more user-friendly.
func TestVariableValueTruncation ( t * testing . T ) {
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
runTest ( t , "testvariables2" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
2021-06-16 20:27:38 +00:00
// Breakpoint set within the program
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-06-16 20:27:38 +00:00
checkStop ( t , client , 1 , "main.main" , - 1 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2021-06-16 20:27:38 +00:00
locals := client . ExpectVariablesResponse ( t )
// Compound variable values may be truncated
m1Full := ` map\[string\]main\.astruct \[(\"[A-Za-z]+\": { A: [0-9]+, B: [0-9]+}, )+,\.\.\.\+2 more\] `
m1Part := ` map\[string\]main\.astruct \[(\"[A-Za-z]+\": { A: [0-9]+, B: [0-9]+}, )+.+\.\.\. `
// In variable responses
checkVarRegex ( t , locals , - 1 , "m1" , "m1" , m1Part , ` map\[string\]main\.astruct ` , hasChildren )
// In evaluate responses (select contexts only)
tests := [ ] struct {
context string
want string
} {
{ "" , m1Part } ,
{ "watch" , m1Part } ,
{ "repl" , m1Part } ,
{ "hover" , m1Part } ,
{ "variables" , m1Full } , // used for copy
{ "clipboard" , m1Full } , // used for copy
{ "somethingelse" , m1Part } ,
}
for _ , tc := range tests {
t . Run ( tc . context , func ( t * testing . T ) {
client . EvaluateRequest ( "m1" , 0 , tc . context )
checkEvalRegex ( t , client . ExpectEvaluateResponse ( t ) , tc . want , hasChildren )
} )
}
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
2021-06-16 20:27:38 +00:00
// Compound map keys may be truncated even further
// As the keys are always inside of a map container,
2021-06-22 15:14:47 +00:00
// this applies to variables requests only, not evalute requests.
2021-06-16 20:27:38 +00:00
// key - compound, value - scalar (inlined key:value display) => truncate key if too long
2021-06-22 15:14:47 +00:00
ref := checkVarExact ( t , locals , - 1 , "m5" , "m5" , "map[main.C]int [{s: " + longstr + "}: 1, ]" , "map[main.C]int" , hasChildren )
2021-06-16 20:27:38 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
// Key format: <truncated>... @<address>
2021-07-16 16:50:37 +00:00
checkVarRegex ( t , client . ExpectVariablesResponse ( t ) , 1 , ` main\.C { s: "very long string 0123456789.+\.\.\. @ 0x[0-9a-f]+ ` , ` m5\[\(\*\(\*"main\.C"\)\(0x[0-9a-f]+\)\)\] ` , "1" , ` int ` , hasChildren )
2021-06-16 20:27:38 +00:00
}
2021-06-22 15:14:47 +00:00
// key - scalar, value - scalar (inlined key:value display) => key not truncated
ref = checkVarExact ( t , locals , - 1 , "m6" , "m6" , "map[string]int [" + longstr + ": 123, ]" , "map[string]int" , hasChildren )
2021-06-16 20:27:38 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
2021-07-16 16:50:37 +00:00
checkVarExact ( t , client . ExpectVariablesResponse ( t ) , 1 , longstr , ` m6[ ` + longstr + ` ] ` , "123" , "string: int" , noChildren )
2021-06-16 20:27:38 +00:00
}
2021-06-22 15:14:47 +00:00
// key - compound, value - compound (array-like display) => key not truncated
ref = checkVarExact ( t , locals , - 1 , "m7" , "m7" , "map[main.C]main.C [{s: " + longstr + "}: {s: \"hello\"}, ]" , "map[main.C]main.C" , hasChildren )
2021-06-16 20:27:38 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
m7 := client . ExpectVariablesResponse ( t )
2021-07-16 16:50:37 +00:00
checkVarRegex ( t , m7 , 1 , "[key 0]" , ` \(\*\(\*\"main\.C\"\)\(0x[0-9a-f]+\)\) ` , ` main\.C { s: ` + longstr + ` } ` , ` main\.C ` , hasChildren )
2021-06-16 20:27:38 +00:00
}
} ,
disconnect : true ,
} } )
} )
}
2021-06-22 15:14:47 +00:00
// TestVariableLoadingOfLongStrings tests that different string loading limits
2021-06-16 20:27:38 +00:00
// apply that depending on the context.
2021-06-22 15:14:47 +00:00
func TestVariableLoadingOfLongStrings ( t * testing . T ) {
protest . MustSupportFunctionCalls ( t , testBackend )
runTest ( t , "longstrings" , func ( client * daptest . Client , fixture protest . Fixture ) {
2021-06-16 20:27:38 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
2021-06-22 15:14:47 +00:00
// Breakpoint set within the program
2021-06-16 20:27:38 +00:00
fixture . Source , [ ] int { } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , - 1 )
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
locals := client . ExpectVariablesResponse ( t )
2021-06-22 15:14:47 +00:00
// Limits vary for evaluate requests with different contexts
tests := [ ] struct {
context string
limit int
} {
{ "" , DefaultLoadConfig . MaxStringLen } ,
{ "watch" , DefaultLoadConfig . MaxStringLen } ,
{ "repl" , maxSingleStringLen } ,
{ "hover" , maxSingleStringLen } ,
{ "variables" , maxSingleStringLen } ,
{ "clipboard" , maxSingleStringLen } ,
{ "somethingelse" , DefaultLoadConfig . MaxStringLen } ,
}
for _ , tc := range tests {
t . Run ( tc . context , func ( t * testing . T ) {
// Long string by itself (limits vary)
client . EvaluateRequest ( "s4097" , 0 , tc . context )
want := fmt . Sprintf ( ` "x+\.\.\.\+%d more" ` , 4097 - tc . limit )
checkEvalRegex ( t , client . ExpectEvaluateResponse ( t ) , want , noChildren )
// Evaluated container variables return values with minimally loaded
// strings, which are further truncated for displaying, so we
// can't test for loading limit except in contexts where an untruncated
// value is returned.
client . EvaluateRequest ( "&s4097" , 0 , tc . context )
switch tc . context {
case "variables" , "clipboard" :
want = fmt . Sprintf ( ` \*"x+\.\.\.\+%d more ` , 4097 - DefaultLoadConfig . MaxStringLen )
default :
want = fmt . Sprintf ( ` \*"x { %d}\.\.\. ` , maxVarValueLen - 2 )
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
}
2021-06-22 15:14:47 +00:00
checkEvalRegex ( t , client . ExpectEvaluateResponse ( t ) , want , hasChildren )
} )
}
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
2021-06-22 15:14:47 +00:00
// Long strings returned from calls are subject to a different limit,
// same limit regardless of context
for _ , context := range [ ] string { "" , "watch" , "repl" , "variables" , "hover" , "clipboard" , "somethingelse" } {
t . Run ( context , func ( t * testing . T ) {
client . EvaluateRequest ( ` call buildString(4097) ` , 1000 , context )
want := fmt . Sprintf ( ` "x+\.\.\.\+%d more" ` , 4097 - maxStringLenInCallRetVars )
got := client . ExpectEvaluateResponse ( t )
checkEvalRegex ( t , got , want , hasChildren )
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
} )
}
2021-06-16 20:27:38 +00:00
2021-06-22 15:14:47 +00:00
// Variables requests use the most conservative loading limit
checkVarRegex ( t , locals , - 1 , "s513" , "s513" , ` "x { 512}\.\.\.\+1 more" ` , "string" , noChildren )
// Container variables are subject to additional stricter value truncation that drops +more part
checkVarRegex ( t , locals , - 1 , "nested" , "nested" , ` map\[int\]string \[513: \"x+\.\.\. ` , "string" , hasChildren )
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
} ,
disconnect : true ,
} } )
} )
}
2020-11-12 23:24:31 +00:00
func TestEvaluateCallRequest ( t * testing . T ) {
protest . MustSupportFunctionCalls ( t , testBackend )
runTest ( t , "fncall" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-11-12 23:24:31 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
fixture . Source , [ ] int { 88 } ,
[ ] onBreakpoint { { // Stop in makeclos()
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.makeclos" , 88 )
2020-11-12 23:24:31 +00:00
// Topmost frame: both types of expressions should work
client . EvaluateRequest ( "callstacktrace" , 1000 , "this context will be ignored" )
client . ExpectEvaluateResponse ( t )
client . EvaluateRequest ( "call callstacktrace()" , 1000 , "this context will be ignored" )
client . ExpectEvaluateResponse ( t )
// Next frame: only non-call expressions will work
client . EvaluateRequest ( "callstacktrace" , 1001 , "this context will be ignored" )
client . ExpectEvaluateResponse ( t )
client . EvaluateRequest ( "call callstacktrace()" , 1001 , "not watch" )
erres := client . ExpectVisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: call is only supported with topmost stack frame" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: call is only supported with topmost stack frame\"" , erres )
}
// A call can stop on a breakpoint
client . EvaluateRequest ( "call callbreak()" , 1000 , "not watch" )
s := client . ExpectStoppedEvent ( t )
if s . Body . Reason != "hardcoded breakpoint" {
t . Errorf ( "\ngot %#v\nwant Reason=\"hardcoded breakpoint\"" , s )
}
erres = client . ExpectVisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: call stopped" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: call stopped\"" , erres )
}
// A call during a call causes an error
client . EvaluateRequest ( "call callstacktrace()" , 1000 , "not watch" )
erres = client . ExpectVisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: cannot call function while another function call is already in progress" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: cannot call function while another function call is already in progress\"" , erres )
}
// Complete the call and get back to original breakpoint in makeclos()
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.makeclos" , 88 )
2020-11-12 23:24:31 +00:00
// Inject a call for the same function that is stopped at breakpoint:
// it might stop at the exact same breakpoint on the same goroutine,
// but we should still detect that its an injected call that stopped
// and not the return to the original point of injection after it
// completed.
client . EvaluateRequest ( "call makeclos(nil)" , 1000 , "not watch" )
2020-12-27 23:11:02 +00:00
stopped := client . ExpectStoppedEvent ( t )
2020-11-12 23:24:31 +00:00
erres = client . ExpectVisibleErrorResponse ( t )
if erres . Body . Error . Format != "Unable to evaluate expression: call stopped" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: call stopped\"" , erres )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , stopped . Body . ThreadId , "main.makeclos" , 88 )
2020-11-12 23:24:31 +00:00
// Complete the call and get back to original breakpoint in makeclos()
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
client . ExpectStoppedEvent ( t )
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.makeclos" , 88 )
2020-11-12 23:24:31 +00:00
} ,
disconnect : false ,
} , { // Stop at runtime breakpoint
execute : func ( ) {
2021-07-08 15:47:53 +00:00
checkStop ( t , client , 1 , "main.main" , - 1 )
2020-11-12 23:24:31 +00:00
// No return values
client . EvaluateRequest ( "call call0(1, 2)" , 1000 , "this context will be ignored" )
got := client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "" , noChildren )
2020-11-12 23:24:31 +00:00
// One unnamed return value
client . EvaluateRequest ( "call call1(one, two)" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref := checkEval ( t , got , "3" , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
rv := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , rv , "rv" , 1 )
checkVarExact ( t , rv , 0 , "~r2" , "" , "3" , "int" , noChildren )
2020-11-12 23:24:31 +00:00
}
// One named return value
// Panic doesn't panic, but instead returns the error as a named return variable
client . EvaluateRequest ( "call callpanic()" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEval ( t , got , ` interface { }(string) "callpanic panicked" ` , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
rv := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , rv , "rv" , 1 )
ref = checkVarExact ( t , rv , 0 , "~panic" , "" , ` interface { }(string) "callpanic panicked" ` , "interface {}" , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
p := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , p , "~panic" , 1 )
checkVarExact ( t , p , 0 , "data" , "" , "\"callpanic panicked\"" , "string" , noChildren )
2020-11-12 23:24:31 +00:00
}
}
// Multiple return values
client . EvaluateRequest ( "call call2(one, two)" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
ref = checkEval ( t , got , "1, 2" , hasChildren )
2020-11-12 23:24:31 +00:00
if ref > 0 {
client . VariablesRequest ( ref )
rvs := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , rvs , "rvs" , 2 )
checkVarExact ( t , rvs , 0 , "~r2" , "" , "1" , "int" , noChildren )
checkVarExact ( t , rvs , 1 , "~r3" , "" , "2" , "int" , noChildren )
2020-11-12 23:24:31 +00:00
}
// No frame defaults to top-most frame
client . EvaluateRequest ( "call call1(one, two)" , 0 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "3" , hasChildren )
2020-11-12 23:24:31 +00:00
// Extra spaces don't matter
client . EvaluateRequest ( " call call1(one, one) " , 0 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "2" , hasChildren )
2020-11-12 23:24:31 +00:00
// Just 'call', even with extra space, is treated as {expression}
client . EvaluateRequest ( "call " , 1000 , "watch" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "\"this is a variable named `call`\"" , noChildren )
dap: use larger string type variable load limits in 'repl', 'variables' context (#2418)
* dap: use larger variable load limits in 'repl', 'variables' context
When evaluate requests are triggered in the context of 'repl'
(DEBUG CONSOLE in VSCode) or 'variables' (copy values from VARIABLES
section in VSCode), they are the result of human action and have
more rooms to display. So it is not too bad to apply longer limits.
Variable auto-loading for strings or arrays is nice but currently
it's unclear to me how this should be integrated in the DEBUG
CONSOLE or with the Copy Value feature. Until we have better ideas
and tools, let's go with these larger limits.
Unfortunately, the "Copy Value" from WATCH section triggers evaluate
requests with "watch" context and we don't want to load large data
automatically for "watch". So, users who want to query a large value
should first copy the expression to DEBUG CONSOLE and evaluate it.
Not ideal but not the end of the world either.
Updates golang/vscode-go#1318
* dap: apply large limit only to the string type result
* dap: move string reload logic to convertVariable* where other reload logic is
Currently we are thinking string reload for evaluation as a temporary
workaround until we figure out an intutitive way to present long strings.
So, I hope moving this logic near other reload logic may be better.
And, use the address based expression when reloading - when handling the
function return values, we may not have an expression to use.
* dap: make deep source check happy
* dap: move string reevaluation logic back to onEvaluateRequest
Reloading string variables is tricky if they are in registers.
We don't attempt to reload them but for clarity, move this up
to the onEvaluateRequest handler.
For function call, use a generous limit for string load
since the results are volatile.
* dap: check variable isn't affected by evaluate in other context
2021-05-25 17:23:41 +00:00
2020-11-12 23:24:31 +00:00
// Call error
client . EvaluateRequest ( "call call1(one)" , 1000 , "watch" )
2021-05-06 09:11:45 +00:00
erres := client . ExpectInvisibleErrorResponse ( t )
2020-11-12 23:24:31 +00:00
if erres . Body . Error . Format != "Unable to evaluate expression: not enough arguments" {
t . Errorf ( "\ngot %#v\nwant Format=\"Unable to evaluate expression: not enough arguments\"" , erres )
}
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// Assignment - expect no error, but no return value.
client . EvaluateRequest ( "call one = two" , 1000 , "this context will be ignored" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "" , noChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// Check one=two was applied.
client . EvaluateRequest ( "one" , 1000 , "repl" )
got = client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , got , "2" , noChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// Call can exit.
2020-11-12 23:24:31 +00:00
client . EvaluateRequest ( "call callexit()" , 1000 , "this context will be ignored" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
client . ExpectTerminatedEvent ( t )
if res := client . ExpectVisibleErrorResponse ( t ) ; ! strings . Contains ( res . Body . Error . Format , "terminated" ) {
t . Errorf ( "\ngot %#v\nwant Format=.*terminated.*" , res )
}
2020-11-12 23:24:31 +00:00
} ,
2021-04-12 21:50:15 +00:00
terminated : true ,
2020-11-12 23:24:31 +00:00
disconnect : true ,
} } )
} )
}
2020-08-24 17:21:51 +00:00
func TestNextAndStep ( t * testing . T ) {
runTest ( t , "testinline" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-08-24 17:21:51 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 11 } ,
[ ] onBreakpoint { { // Stop at line 11
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.initialize" , 11 )
2020-08-24 17:21:51 +00:00
2020-11-12 23:24:31 +00:00
expectStop := func ( fun string , line int ) {
2020-08-24 17:21:51 +00:00
t . Helper ( )
se := client . ExpectStoppedEvent ( t )
if se . Body . Reason != "step" || se . Body . ThreadId != 1 || ! se . Body . AllThreadsStopped {
t . Errorf ( "got %#v, want Reason=\"step\", ThreadId=1, AllThreadsStopped=true" , se )
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , fun , line )
2020-08-24 17:21:51 +00:00
}
client . StepOutRequest ( 1 )
client . ExpectStepOutResponse ( t )
2020-11-12 23:24:31 +00:00
expectStop ( "main.main" , 18 )
2020-08-24 17:21:51 +00:00
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
2020-11-12 23:24:31 +00:00
expectStop ( "main.main" , 19 )
2020-08-24 17:21:51 +00:00
client . StepInRequest ( 1 )
client . ExpectStepInResponse ( t )
2020-11-12 23:24:31 +00:00
expectStop ( "main.inlineThis" , 5 )
2020-08-24 17:21:51 +00:00
2021-04-21 20:39:19 +00:00
client . NextRequest ( - 1000 )
2020-08-24 17:21:51 +00:00
client . ExpectNextResponse ( t )
2021-04-21 20:39:19 +00:00
if se := client . ExpectStoppedEvent ( t ) ; se . Body . Reason != "error" || se . Body . Text != "unknown goroutine -1000" {
2021-05-18 17:25:16 +00:00
t . Errorf ( "got %#v, want Reason=\"error\", Text=\"unknown goroutine -1000\"" , se )
2021-04-21 20:39:19 +00:00
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.inlineThis" , 5 )
2020-08-24 17:21:51 +00:00
} ,
disconnect : false ,
} } )
} )
}
2021-04-02 16:19:16 +00:00
func TestNextParked ( t * testing . T ) {
if runtime . GOOS == "freebsd" {
t . SkipNow ( )
}
runTest ( t , "parallel_next" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 15 } ,
[ ] onBreakpoint { { // Stop at line 15
execute : func ( ) {
2021-08-04 21:12:58 +00:00
if goroutineId := testStepParkedHelper ( t , client , fixture ) ; goroutineId >= 0 {
2021-04-02 16:19:16 +00:00
2021-08-04 21:12:58 +00:00
client . NextRequest ( goroutineId )
client . ExpectNextResponse ( t )
2021-04-02 16:19:16 +00:00
2021-08-04 21:12:58 +00:00
se := client . ExpectStoppedEvent ( t )
if se . Body . ThreadId != goroutineId {
t . Fatalf ( "Next did not continue on the selected goroutine, expected %d got %d" , goroutineId , se . Body . ThreadId )
}
2021-04-02 16:19:16 +00:00
}
} ,
disconnect : false ,
} } )
} )
}
func testStepParkedHelper ( t * testing . T , client * daptest . Client , fixture protest . Fixture ) int {
t . Helper ( )
// Set a breakpoint at main.sayHi
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 8 } )
client . ExpectSetBreakpointsResponse ( t )
var goroutineId = - 1
for goroutineId < 0 {
client . ContinueRequest ( 1 )
2021-08-04 21:12:58 +00:00
contResp := client . ExpectMessage ( t )
switch contResp . ( type ) {
case * dap . ContinueResponse :
// ok
case * dap . TerminatedEvent :
return - 1
}
2021-04-02 16:19:16 +00:00
se := client . ExpectStoppedEvent ( t )
client . ThreadsRequest ( )
threads := client . ExpectThreadsResponse ( t )
// 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
// 3. is not the currently selected goroutine
for _ , g := range threads . Body . Threads {
// We do not need to check the thread that the program
// is currently stopped on.
if g . Id == se . Body . ThreadId {
continue
}
client . StackTraceRequest ( g . Id , 0 , 5 )
frames := client . ExpectStackTraceResponse ( t )
for _ , frame := range frames . Body . StackFrames {
// line 11 is the line where wg.Done is called
if frame . Name == "main.sayhi" && frame . Line < 11 {
goroutineId = g . Id
break
}
}
if goroutineId >= 0 {
break
}
}
}
// Clear all breakpoints.
client . SetBreakpointsRequest ( fixture . Source , [ ] int { } )
client . ExpectSetBreakpointsResponse ( t )
return goroutineId
}
2021-04-07 08:00:14 +00:00
// TestStepOutPreservesGoroutine is inspired by proc_test.TestStepOutPreservesGoroutine
// and checks that StepOut preserves the currently selected goroutine.
2021-04-02 16:19:16 +00:00
func TestStepOutPreservesGoroutine ( t * testing . T ) {
// Checks that StepOut preserves the currently selected goroutine.
if runtime . GOOS == "freebsd" {
t . SkipNow ( )
}
rand . Seed ( time . Now ( ) . Unix ( ) )
runTest ( t , "issue2113" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 25 } ,
[ ] onBreakpoint { { // Stop at line 25
execute : func ( ) {
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
// The program contains runtime.Breakpoint()
se := client . ExpectStoppedEvent ( t )
client . ThreadsRequest ( )
gs := client . ExpectThreadsResponse ( t )
candg := [ ] int { }
bestg := [ ] int { }
for _ , g := range gs . Body . Threads {
// We do not need to check the thread that the program
// is currently stopped on.
if g . Id == se . Body . ThreadId {
continue
}
client . StackTraceRequest ( g . Id , 0 , 20 )
frames := client . ExpectStackTraceResponse ( t )
for _ , frame := range frames . Body . StackFrames {
if frame . Name == "main.coroutine" {
candg = append ( candg , g . Id )
if strings . HasPrefix ( frames . Body . StackFrames [ 0 ] . Name , "runtime." ) {
bestg = append ( bestg , g . Id )
}
break
}
}
}
var goroutineId int
if len ( bestg ) > 0 {
goroutineId = bestg [ rand . Intn ( len ( bestg ) ) ]
t . Logf ( "selected goroutine %d (best)\n" , goroutineId )
2021-07-08 15:47:53 +00:00
} else if len ( candg ) > 0 {
2021-04-02 16:19:16 +00:00
goroutineId = candg [ rand . Intn ( len ( candg ) ) ]
t . Logf ( "selected goroutine %d\n" , goroutineId )
}
2021-07-08 15:47:53 +00:00
if goroutineId != 0 {
client . StepOutRequest ( goroutineId )
client . ExpectStepOutResponse ( t )
} else {
client . ContinueRequest ( - 1 )
client . ExpectContinueResponse ( t )
}
2021-04-02 16:19:16 +00:00
2021-04-08 09:09:41 +00:00
switch e := client . ExpectMessage ( t ) . ( type ) {
2021-04-07 08:00:14 +00:00
case * dap . StoppedEvent :
if e . Body . ThreadId != goroutineId {
t . Fatalf ( "StepOut did not continue on the selected goroutine, expected %d got %d" , goroutineId , e . Body . ThreadId )
}
case * dap . TerminatedEvent :
t . Logf ( "program terminated" )
default :
t . Fatalf ( "Unexpected event type: expect stopped or terminated event, got %#v" , e )
2021-04-02 16:19:16 +00:00
}
} ,
disconnect : false ,
} } )
} )
}
2021-07-29 17:27:49 +00:00
func checkStopOnNextWhileNextingError ( t * testing . T , client * daptest . Client , threadID int ) {
t . Helper ( )
oe := client . ExpectOutputEvent ( t )
if oe . Body . Category != "console" || oe . Body . Output != fmt . Sprintf ( "invalid command: %s\n" , BetterNextWhileNextingError ) {
t . Errorf ( "\ngot %#v\nwant Category=\"console\" Output=\"invalid command: %s\\n\"" , oe , BetterNextWhileNextingError )
}
se := client . ExpectStoppedEvent ( t )
if se . Body . ThreadId != threadID || se . Body . Reason != "exception" || se . Body . Description != "invalid command" || se . Body . Text != BetterNextWhileNextingError {
t . Errorf ( "\ngot %#v\nwant ThreadId=%d Reason=\"exception\" Description=\"invalid command\" Text=\"%s\"" , se , threadID , BetterNextWhileNextingError )
}
client . ExceptionInfoRequest ( 1 )
eInfo := client . ExpectExceptionInfoResponse ( t )
if eInfo . Body . ExceptionId != "invalid command" || eInfo . Body . Description != BetterNextWhileNextingError {
t . Errorf ( "\ngot %#v\nwant ExceptionId=\"invalid command\" Text=\"%s\"" , eInfo , BetterNextWhileNextingError )
}
}
2021-04-02 16:19:16 +00:00
2020-08-24 17:21:51 +00:00
func TestBadAccess ( t * testing . T ) {
if runtime . GOOS != "darwin" || testBackend != "lldb" {
t . Skip ( "not applicable" )
}
runTest ( t , "issue2078" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-08-24 17:21:51 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 4 } ,
[ ] onBreakpoint { { // Stop at line 4
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 4 )
2020-08-24 17:21:51 +00:00
expectStoppedOnError := func ( errorPrefix string ) {
t . Helper ( )
2021-05-04 19:49:52 +00:00
se := client . ExpectStoppedEvent ( t )
2021-06-04 15:35:50 +00:00
if se . Body . ThreadId != 1 || se . Body . Reason != "exception" || se . Body . Description != "runtime error" || ! strings . HasPrefix ( se . Body . Text , errorPrefix ) {
t . Errorf ( "\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"runtime error\" Text=\"%s\"" , se , errorPrefix )
2021-05-17 16:25:41 +00:00
}
client . ExceptionInfoRequest ( 1 )
eInfo := client . ExpectExceptionInfoResponse ( t )
if eInfo . Body . ExceptionId != "runtime error" || ! strings . HasPrefix ( eInfo . Body . Description , errorPrefix ) {
t . Errorf ( "\ngot %#v\nwant ExceptionId=\"runtime error\" Text=\"%s\"" , eInfo , errorPrefix )
2021-05-04 19:49:52 +00:00
}
2020-08-24 17:21:51 +00:00
}
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
expectStoppedOnError ( "invalid memory address or nil pointer dereference" )
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
expectStoppedOnError ( "invalid memory address or nil pointer dereference" )
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
2021-07-29 17:27:49 +00:00
checkStopOnNextWhileNextingError ( t , client , 1 )
2020-08-24 17:21:51 +00:00
client . StepInRequest ( 1 )
client . ExpectStepInResponse ( t )
2021-07-29 17:27:49 +00:00
checkStopOnNextWhileNextingError ( t , client , 1 )
2020-08-24 17:21:51 +00:00
client . StepOutRequest ( 1 )
client . ExpectStepOutResponse ( t )
2021-07-29 17:27:49 +00:00
checkStopOnNextWhileNextingError ( t , client , 1 )
} ,
disconnect : true ,
} } )
} )
}
// TestNextWhileNexting is inspired by command_test.TestIssue387 and tests
// that when 'next' is interrupted by a 'breakpoint', calling 'next'
// again will produce an error with a helpful message, and 'continue'
// will resume the program.
func TestNextWhileNexting ( t * testing . T ) {
if runtime . GOOS == "freebsd" {
t . Skip ( "test is not valid on FreeBSD" )
}
// a breakpoint triggering during a 'next' operation will interrupt 'next''
// Unlike the test for the terminal package, we cannot be certain
// of the number of breakpoints we expect to hit, since multiple
2021-10-07 09:27:04 +00:00
// breakpoints being hit at the same time is not supported in DAP stopped
2021-07-29 17:27:49 +00:00
// events.
runTest ( t , "issue387" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 15 } ,
[ ] onBreakpoint { { // Stop at line 15
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 15 )
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 8 } )
client . ExpectSetBreakpointsResponse ( t )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
bpSe := client . ExpectStoppedEvent ( t )
threadID := bpSe . Body . ThreadId
checkStop ( t , client , threadID , "main.dostuff" , 8 )
for pos := 9 ; pos < 11 ; pos ++ {
client . NextRequest ( threadID )
client . ExpectNextResponse ( t )
stepInProgress := true
for stepInProgress {
m := client . ExpectStoppedEvent ( t )
switch m . Body . Reason {
case "step" :
if ! m . Body . AllThreadsStopped {
t . Errorf ( "got %#v, want Reason=\"step\", AllThreadsStopped=true" , m )
}
checkStop ( t , client , m . Body . ThreadId , "main.dostuff" , pos )
stepInProgress = false
case "breakpoint" :
if ! m . Body . AllThreadsStopped {
t . Errorf ( "got %#v, want Reason=\"breakpoint\", AllThreadsStopped=true" , m )
}
if stepInProgress {
// We encountered a breakpoint on a different thread. We should have to resume execution
// using continue.
oe := client . ExpectOutputEvent ( t )
if oe . Body . Category != "console" || ! strings . Contains ( oe . Body . Output , "Step interrupted by a breakpoint." ) {
t . Errorf ( "\ngot %#v\nwant Category=\"console\" Output=\"Step interrupted by a breakpoint.\"" , oe )
}
client . NextRequest ( m . Body . ThreadId )
client . ExpectNextResponse ( t )
checkStopOnNextWhileNextingError ( t , client , m . Body . ThreadId )
// Continue since we have not finished the step request.
client . ContinueRequest ( threadID )
client . ExpectContinueResponse ( t )
} else {
checkStop ( t , client , m . Body . ThreadId , "main.dostuff" , 8 )
// Switch to stepping on this thread instead.
pos = 8
threadID = m . Body . ThreadId
}
default :
t . Fatalf ( "got %#v, want StoppedEvent on step or breakpoint" , m )
}
}
}
2020-08-24 17:21:51 +00:00
} ,
disconnect : true ,
} } )
} )
}
2020-10-07 15:24:40 +00:00
func TestPanicBreakpointOnContinue ( t * testing . T ) {
runTest ( t , "panic" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-10-07 15:24:40 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 5 } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 5 )
2020-10-07 15:24:40 +00:00
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
2021-07-13 15:38:28 +00:00
text := "\"BOOM!\""
2020-10-07 15:24:40 +00:00
se := client . ExpectStoppedEvent ( t )
2021-07-13 15:38:28 +00:00
if se . Body . ThreadId != 1 || se . Body . Reason != "exception" || se . Body . Description != "panic" || se . Body . Text != text {
t . Errorf ( "\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"panic\" Text=%q" , se , text )
2021-05-17 16:25:41 +00:00
}
client . ExceptionInfoRequest ( 1 )
eInfo := client . ExpectExceptionInfoResponse ( t )
2021-07-13 15:38:28 +00:00
if eInfo . Body . ExceptionId != "panic" || eInfo . Body . Description != text {
t . Errorf ( "\ngot %#v\nwant ExceptionId=\"panic\" Description=%q" , eInfo , text )
2020-10-07 15:24:40 +00:00
}
2021-06-10 17:59:24 +00:00
client . StackTraceRequest ( se . Body . ThreadId , 0 , 20 )
st := client . ExpectStackTraceResponse ( t )
for i , frame := range st . Body . StackFrames {
if strings . HasPrefix ( frame . Name , "runtime." ) {
if frame . Source . PresentationHint != "deemphasize" {
t . Errorf ( "\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"deemphasize\"" , i , frame )
}
} else if frame . Source . PresentationHint != "" {
t . Errorf ( "\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"" , i , frame )
}
}
2020-10-07 15:24:40 +00:00
} ,
disconnect : true ,
} } )
} )
}
func TestPanicBreakpointOnNext ( t * testing . T ) {
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 14 ) {
// In Go 1.13, 'next' will step into the defer in the runtime
// main function, instead of the next line in the main program.
t . SkipNow ( )
}
runTest ( t , "panic" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-10-07 15:24:40 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 5 } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 5 )
2020-10-07 15:24:40 +00:00
client . NextRequest ( 1 )
client . ExpectNextResponse ( t )
2021-07-13 15:38:28 +00:00
text := "\"BOOM!\""
2020-10-07 15:24:40 +00:00
se := client . ExpectStoppedEvent ( t )
2021-07-13 15:38:28 +00:00
if se . Body . ThreadId != 1 || se . Body . Reason != "exception" || se . Body . Description != "panic" || se . Body . Text != text {
t . Errorf ( "\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"panic\" Text=%q" , se , text )
2021-05-17 16:25:41 +00:00
}
2020-10-07 15:24:40 +00:00
2021-05-17 16:25:41 +00:00
client . ExceptionInfoRequest ( 1 )
eInfo := client . ExpectExceptionInfoResponse ( t )
2021-07-13 15:38:28 +00:00
if eInfo . Body . ExceptionId != "panic" || eInfo . Body . Description != text {
t . Errorf ( "\ngot %#v\nwant ExceptionId=\"panic\" Description=%q" , eInfo , text )
2020-10-07 15:24:40 +00:00
}
} ,
disconnect : true ,
} } )
} )
}
func TestFatalThrowBreakpoint ( t * testing . T ) {
2021-06-28 15:39:34 +00:00
runTest ( t , "fatalerror" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 3 } ,
[ ] onBreakpoint { {
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 3 )
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
2021-07-13 15:38:28 +00:00
var text string
// This does not work for Go 1.16.
ver , _ := goversion . Parse ( runtime . Version ( ) )
if ver . Major != 1 || ver . Minor != 16 {
text = "\"go of nil func value\""
}
2021-06-28 15:39:34 +00:00
se := client . ExpectStoppedEvent ( t )
2021-07-13 15:38:28 +00:00
if se . Body . ThreadId != 1 || se . Body . Reason != "exception" || se . Body . Description != "fatal error" || se . Body . Text != text {
t . Errorf ( "\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"fatal error\" Text=%q" , se , text )
2021-06-28 15:39:34 +00:00
}
2021-07-13 15:38:28 +00:00
// This does not work for Go 1.16.
errorPrefix := text
if errorPrefix == "" {
2021-06-28 15:39:34 +00:00
errorPrefix = "Throw reason unavailable, see https://github.com/golang/go/issues/46425"
}
client . ExceptionInfoRequest ( 1 )
eInfo := client . ExpectExceptionInfoResponse ( t )
if eInfo . Body . ExceptionId != "fatal error" || ! strings . HasPrefix ( eInfo . Body . Description , errorPrefix ) {
t . Errorf ( "\ngot %#v\nwant ExceptionId=\"runtime error\" Text=%s" , eInfo , errorPrefix )
}
} ,
disconnect : true ,
} } )
} )
2020-10-07 15:24:40 +00:00
runTest ( t , "testdeadlock" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "launch" ,
2020-10-07 15:24:40 +00:00
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 3 } ,
[ ] onBreakpoint { {
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , 3 )
2020-10-07 15:24:40 +00:00
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
2021-07-28 06:58:02 +00:00
// This does not work for Go 1.16 so skip by detecting versions before or after 1.16.
2021-07-13 15:38:28 +00:00
var text string
2021-07-28 06:58:02 +00:00
if ! goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 16 ) || goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 17 ) {
2021-07-13 15:38:28 +00:00
text = "\"all goroutines are asleep - deadlock!\""
}
2020-10-07 15:24:40 +00:00
se := client . ExpectStoppedEvent ( t )
2021-07-13 15:38:28 +00:00
if se . Body . Reason != "exception" || se . Body . Description != "fatal error" || se . Body . Text != text {
t . Errorf ( "\ngot %#v\nwant Reason=\"exception\" Description=\"fatal error\" Text=%q" , se , text )
2020-10-07 15:24:40 +00:00
}
2021-06-28 15:39:34 +00:00
// TODO(suzmue): Get the exception info for the thread and check the description
// includes "all goroutines are asleep - deadlock!".
// Stopped events with no selected goroutines need to be supported to test deadlock.
2020-10-07 15:24:40 +00:00
} ,
disconnect : true ,
} } )
} )
}
2021-05-17 17:37:15 +00:00
func verifyStopLocation ( t * testing . T , client * daptest . Client , thread int , name string , line int ) {
2020-08-24 17:21:51 +00:00
t . Helper ( )
client . StackTraceRequest ( thread , 0 , 20 )
st := client . ExpectStackTraceResponse ( t )
2020-11-12 23:24:31 +00:00
if len ( st . Body . StackFrames ) < 1 {
t . Errorf ( "\ngot %#v\nwant len(stackframes) => 1" , st )
} else {
if line != - 1 && st . Body . StackFrames [ 0 ] . Line != line {
t . Errorf ( "\ngot %#v\nwant Line=%d" , st , line )
}
if st . Body . StackFrames [ 0 ] . Name != name {
t . Errorf ( "\ngot %#v\nwant Name=%q" , st , name )
}
2020-08-24 17:21:51 +00:00
}
2021-05-17 17:37:15 +00:00
}
2021-05-26 15:15:39 +00:00
// checkStop covers the standard sequence of requests issued by
2021-05-17 17:37:15 +00:00
// a client at a breakpoint or another non-terminal stop event.
// The details have been tested by other tests,
// so this is just a sanity check.
// Skips line check if line is -1.
2021-05-26 15:15:39 +00:00
func checkStop ( t * testing . T , client * daptest . Client , thread int , name string , line int ) {
2021-05-17 17:37:15 +00:00
t . Helper ( )
client . ThreadsRequest ( )
client . ExpectThreadsResponse ( t )
verifyStopLocation ( t , client , thread , name , line )
2020-08-24 17:21:51 +00:00
client . ScopesRequest ( 1000 )
client . ExpectScopesResponse ( t )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-08-24 17:21:51 +00:00
client . ExpectVariablesResponse ( t )
}
2020-08-11 15:34:27 +00:00
// onBreakpoint specifies what the test harness should simulate at
// a stopped breakpoint. First execute() is to be called to test
// specified editor-driven or user-driven requests. Then if
// disconnect is true, the test harness will abort the program
// execution. Otherwise, a continue will be issued and the
// program will continue to the next breakpoint or termination.
2021-04-12 21:50:15 +00:00
// If terminated is true, we expect requests at this breakpoint
// to result in termination.
2020-08-11 15:34:27 +00:00
type onBreakpoint struct {
execute func ( )
disconnect bool
2021-04-12 21:50:15 +00:00
terminated bool
2020-08-11 15:34:27 +00:00
}
// runDebugSessionWithBPs is a helper for executing the common init and shutdown
2020-03-10 19:29:06 +00:00
// sequences for a program that does not stop on entry
2020-12-28 17:14:15 +00:00
// while specifying breakpoints and unique launch/attach criteria via parameters.
// cmd - "launch" or "attach"
// cmdRequest - a function that sends a launch or attach request,
// so the test author has full control of its arguments.
// Note that he rest of the test sequence assumes that
// stopOnEntry is false.
2021-08-23 18:27:49 +00:00
// source - source file path, needed to set breakpoints, "" if none to be set.
2020-07-08 17:20:05 +00:00
// breakpoints - list of lines, where breakpoints are to be set
2021-08-23 18:27:49 +00:00
// onBPs - list of test sequences to execute at each of the set breakpoints.
2020-12-28 17:14:15 +00:00
func runDebugSessionWithBPs ( t * testing . T , client * daptest . Client , cmd string , cmdRequest func ( ) , source string , breakpoints [ ] int , onBPs [ ] onBreakpoint ) {
2020-03-04 17:22:51 +00:00
client . InitializeRequest ( )
2021-05-06 09:11:45 +00:00
client . ExpectInitializeResponseAndCapabilities ( t )
2020-03-04 17:22:51 +00:00
2020-12-28 17:14:15 +00:00
cmdRequest ( )
2020-03-04 17:22:51 +00:00
client . ExpectInitializedEvent ( t )
2020-12-28 17:14:15 +00:00
if cmd == "launch" {
client . ExpectLaunchResponse ( t )
} else if cmd == "attach" {
client . ExpectAttachResponse ( t )
} else {
panic ( "expected launch or attach command" )
}
2020-03-04 17:22:51 +00:00
2021-08-23 18:27:49 +00:00
if source != "" {
client . SetBreakpointsRequest ( source , breakpoints )
client . ExpectSetBreakpointsResponse ( t )
}
2020-07-08 17:20:05 +00:00
2020-03-10 19:29:06 +00:00
// Skip no-op setExceptionBreakpoints
2020-03-04 17:22:51 +00:00
client . ConfigurationDoneRequest ( )
client . ExpectConfigurationDoneResponse ( t )
2020-07-08 17:20:05 +00:00
// Program automatically continues to breakpoint or completion
2020-08-11 15:34:27 +00:00
// TODO(polina): See if we can make this more like withTestProcessArgs in proc_test:
// a single function pointer gets called here and then if it wants to continue it calls
// client.ContinueRequest/client.ExpectContinueResponse/client.ExpectStoppedEvent
// (possibly using a helper function).
for _ , onBP := range onBPs {
2020-07-08 17:20:05 +00:00
client . ExpectStoppedEvent ( t )
2020-08-11 15:34:27 +00:00
onBP . execute ( )
if onBP . disconnect {
2020-12-28 17:14:15 +00:00
client . DisconnectRequestWithKillOption ( true )
2021-04-12 21:50:15 +00:00
if onBP . terminated {
client . ExpectOutputEventProcessExited ( t , 0 )
client . ExpectOutputEventDetaching ( t )
} else {
client . ExpectOutputEventDetachingKill ( t )
}
2020-08-11 15:34:27 +00:00
client . ExpectDisconnectResponse ( t )
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2020-08-11 15:34:27 +00:00
return
}
2020-07-08 17:20:05 +00:00
client . ContinueRequest ( 1 )
client . ExpectContinueResponse ( t )
// "Continue" is triggered after the response is sent
}
2020-03-10 19:29:06 +00:00
2020-12-28 17:14:15 +00:00
if cmd == "launch" { // Let the program run to completion
client . ExpectTerminatedEvent ( t )
}
client . DisconnectRequestWithKillOption ( true )
2021-04-12 21:50:15 +00:00
if cmd == "launch" {
client . ExpectOutputEventProcessExited ( t , 0 )
client . ExpectOutputEventDetaching ( t )
} else if cmd == "attach" {
client . ExpectOutputEventDetachingKill ( t )
}
2020-03-04 17:22:51 +00:00
client . ExpectDisconnectResponse ( t )
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2020-03-04 17:22:51 +00:00
}
2020-10-02 16:18:33 +00:00
// runDebugSession is a helper for executing the standard init and shutdown
2020-07-08 17:20:05 +00:00
// sequences for a program that does not stop on entry
// while specifying unique launch criteria via parameters.
2021-08-23 18:27:49 +00:00
func runDebugSession ( t * testing . T , client * daptest . Client , cmd string , cmdRequest func ( ) ) {
runDebugSessionWithBPs ( t , client , cmd , cmdRequest , "" , nil , nil )
2020-07-08 17:20:05 +00:00
}
2020-03-04 17:22:51 +00:00
func TestLaunchDebugRequest ( t * testing . T ) {
2021-02-24 16:19:07 +00:00
rescueStderr := os . Stderr
r , w , _ := os . Pipe ( )
os . Stderr = w
2021-04-14 07:22:40 +00:00
tmpBin := "__tmpBin"
2020-02-15 19:52:53 +00:00
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-05-28 21:01:51 +00:00
// We reuse the harness that builds, but ignore the built binary,
// only relying on the source to be built in response to LaunchRequest.
2020-12-28 17:14:15 +00:00
runDebugSession ( t , client , "launch" , func ( ) {
2020-03-04 17:22:51 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-08-17 18:40:31 +00:00
"mode" : "debug" , "program" : fixture . Source , "output" : tmpBin } )
2021-08-23 18:27:49 +00:00
} )
2020-03-04 17:22:51 +00:00
} )
2021-02-24 16:19:07 +00:00
// Wait for the test to finish to capture all stderr
time . Sleep ( 100 * time . Millisecond )
w . Close ( )
err , _ := ioutil . ReadAll ( r )
t . Log ( string ( err ) )
os . Stderr = rescueStderr
rmErrRe , _ := regexp . Compile ( ` could not remove .*\n ` )
rmErr := rmErrRe . FindString ( string ( err ) )
if rmErr != "" {
// On Windows, a file in use cannot be removed, resulting in "Access is denied".
// When the process exits, Delve releases the binary by calling
// BinaryInfo.Close(), but it appears that it is still in use (by Windows?)
2021-03-15 16:34:26 +00:00
// shortly after. gobuild.Remove has a delay to address this, but
// to avoid any test flakiness we guard against this failure here as well.
2021-06-23 18:21:19 +00:00
if runtime . GOOS != "windows" || ! stringContainsCaseInsensitive ( rmErr , "Access is denied" ) {
2021-03-15 16:34:26 +00:00
t . Fatalf ( "Binary removal failure:\n%s\n" , rmErr )
}
2021-04-14 07:22:40 +00:00
} else {
2021-08-17 18:40:31 +00:00
tmpBin = cleanExeName ( tmpBin )
2021-04-14 07:22:40 +00:00
// We did not get a removal error, but did we even try to remove before exiting?
// Confirm that the binary did get removed.
if _ , err := os . Stat ( tmpBin ) ; err == nil || os . IsExist ( err ) {
t . Fatal ( "Failed to remove temp binary" , tmpBin )
}
2021-02-24 16:19:07 +00:00
}
2020-03-04 17:22:51 +00:00
}
2020-02-24 17:36:34 +00:00
2021-03-08 17:42:54 +00:00
// TestLaunchRequestDefaults tests defaults for launch attribute that are explicit in other tests.
func TestLaunchRequestDefaults ( t * testing . T ) {
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSession ( t , client , "launch" , func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-03-24 18:02:22 +00:00
"mode" : "" /*"debug" by default*/ , "program" : fixture . Source , "output" : "__mybin" } )
2021-08-23 18:27:49 +00:00
} )
2021-03-08 17:42:54 +00:00
} )
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSession ( t , client , "launch" , func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-03-24 18:02:22 +00:00
/*"mode":"debug" by default*/ "program" : fixture . Source , "output" : "__mybin" } )
2021-08-23 18:27:49 +00:00
} )
2021-03-08 17:42:54 +00:00
} )
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSession ( t , client , "launch" , func ( ) {
2021-10-05 11:40:17 +00:00
// Use the temporary output binary.
2021-03-08 17:42:54 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "debug" , "program" : fixture . Source } )
2021-08-23 18:27:49 +00:00
} )
2021-03-08 17:42:54 +00:00
} )
dap: change how noDebug launch request is served (#2407)
* dap: change how noDebug launch request is served
PR #2400 added support for noDebug launch requests - that was done
by directly starting the target program using os/exec.Cmd and blocking
the launch request indefinitely until the program terminates or
is stopped with a disconnect request (when dlv dap can support it).
Even though the approach seemed to work for user, that adds an extra
control flow and complexity to the codebase.
This change takes a different approach to implement the noDebug
launch feature. Instead of using os/exec.Cmd, this uses the existing
debug launch path, but avoids setting breakpoints. Finally, the program
will start upon receiving onConfigurationDoneRequest and blocks there
until the program terminates. This simplifies the code.
The DAP spec does not explicitly specify what to do about other
requests. It seems like VSCode can issue evaluate requests or other
requests after the configuration done request. Currently they are
blocked because the program is in running state. We can consider checking
s.noDebug and responding with an error in the future if we want/need to.
See the log below for a typical DAP request/response sequence:
2021-03-29T01:42:53-04:00 debug layer=dap building binary at /Users/hakim/projects/s/__debug_bin
2021-03-29T01:42:55-04:00 info layer=debugger launching process with args: [/Users/hakim/projects/s/__debug_bin]
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"initialized"}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":2,"success":true,"command":"launch"}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":3,"type":"request","command":"setBreakpoints","arguments":{"source":{"name":"main.go","path":"/Users/hakim/projects/s/main.go"},"breakpoints":[{"line":9}],"lines":[9]}}
2021-03-29T01:42:55-04:00 error layer=dap Unable to set or clear breakpoints: running in noDebug mode
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":3,"success":false,"command":"setBreakpoints","message":"Unable to set or clear breakpoints","body":{"error":{"id":2002,"format":"Unable to set or clear breakpoints: running in noDebug mode"}}}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":4,"type":"request","command":"configurationDone","arguments":{}}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":4,"success":true,"command":"configurationDone"}
2021-03-29T01:42:55-04:00 debug layer=debugger continuing
Hello
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"terminated","body":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":5,"type":"request","command":"threads"}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":5,"success":true,"command":"threads","body":{"threads":null}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":6,"type":"request","command":"disconnect","arguments":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":6,"success":true,"command":"disconnect"}
2021-03-29T01:43:00-04:00 debug layer=debugger halting
2021-03-29T01:43:00-04:00 error layer=dap Process 27219 has exited with status 0
2021-03-29T01:43:00-04:00 debug layer=debugger detaching
Updates https://github.com/golang/vscode-go/issues/1111
* service/dap: address polina's comments for noDebug logic
Reworked so the noDebug launch request again blocks launch request
handler after responding with the launch response. By skipping
the initialized event, it should prevent well-behaving clients from
sending any further requests. Currently, doesn't matter because
the handler goroutine will be blocked until the program termination
anyway.
Placed mutex back, since I noticed that's the only way to prevent
racing between Stop and the handler goroutine. The synchronization
lotic (in particular, during teardown) should be revisited after
asynchronous request support work is done.
* dap: address more comments
2021-04-05 18:44:02 +00:00
}
2021-08-17 18:40:31 +00:00
// TestLaunchRequestOutputPath verifies that relative output binary path
// is mapped to server's, not target's, working directory.
func TestLaunchRequestOutputPath ( t * testing . T ) {
runTest ( t , "testargs" , func ( client * daptest . Client , fixture protest . Fixture ) {
inrel := "__somebin"
wd , _ := os . Getwd ( )
outabs := cleanExeName ( filepath . Join ( wd , inrel ) )
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "debug" , "program" : fixture . Source , "output" : inrel ,
"cwd" : filepath . Dir ( wd ) } )
} ,
// Set breakpoints
fixture . Source , [ ] int { 12 } ,
[ ] onBreakpoint { {
execute : func ( ) {
checkStop ( t , client , 1 , "main.main" , 12 )
client . EvaluateRequest ( "os.Args[0]" , 1000 , "repl" )
checkEval ( t , client . ExpectEvaluateResponse ( t ) , fmt . Sprintf ( "%q" , outabs ) , noChildren )
} ,
disconnect : true ,
} } )
} )
}
2021-09-24 22:18:28 +00:00
func TestExitNonZeroStatus ( t * testing . T ) {
runTest ( t , "pr1055" , func ( client * daptest . Client , fixture protest . Fixture ) {
client . InitializeRequest ( )
client . ExpectInitializeResponseAndCapabilities ( t )
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
client . ExpectInitializedEvent ( t )
client . ExpectLaunchResponse ( t )
client . ConfigurationDoneRequest ( )
client . ExpectConfigurationDoneResponse ( t )
client . ExpectTerminatedEvent ( t )
client . DisconnectRequest ( )
// Check that the process exit status is 2.
oep := client . ExpectOutputEventProcessExited ( t , 2 )
if oep . Body . Category != "console" {
t . Errorf ( "\ngot %#v\nwant Category='console'" , oep )
}
oed := client . ExpectOutputEventDetaching ( t )
if oed . Body . Category != "console" {
t . Errorf ( "\ngot %#v\nwant Category='console'" , oed )
}
client . ExpectDisconnectResponse ( t )
client . ExpectTerminatedEvent ( t )
} )
}
2021-08-17 18:40:31 +00:00
2021-08-16 15:51:23 +00:00
func TestNoDebug_GoodExitStatus ( t * testing . T ) {
2021-03-23 19:10:21 +00:00
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
2021-08-16 15:51:23 +00:00
runNoDebugSession ( t , client , func ( ) {
2021-03-23 19:10:21 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-08-16 15:51:23 +00:00
"noDebug" : true , "mode" : "debug" , "program" : fixture . Source , "output" : "__mybin" } )
} , 0 )
2021-04-12 21:50:15 +00:00
} )
}
2021-08-16 15:51:23 +00:00
func TestNoDebug_BadExitStatus ( t * testing . T ) {
2021-04-12 21:50:15 +00:00
runTest ( t , "issue1101" , func ( client * daptest . Client , fixture protest . Fixture ) {
2021-08-16 15:51:23 +00:00
runNoDebugSession ( t , client , func ( ) {
2021-04-12 21:50:15 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-08-16 15:51:23 +00:00
"noDebug" : true , "mode" : "exec" , "program" : fixture . Path } )
} , 2 )
2021-03-23 19:10:21 +00:00
} )
}
2021-08-16 15:51:23 +00:00
// runNoDebugSession tests the session started with noDebug=true runs
// to completion and logs termination status.
func runNoDebugSession ( t * testing . T , client * daptest . Client , launchRequest func ( ) , exitStatus int ) {
2021-03-23 19:10:21 +00:00
client . InitializeRequest ( )
2021-05-06 09:11:45 +00:00
client . ExpectInitializeResponseAndCapabilities ( t )
2021-03-23 19:10:21 +00:00
2021-08-16 15:51:23 +00:00
launchRequest ( )
dap: change how noDebug launch request is served (#2407)
* dap: change how noDebug launch request is served
PR #2400 added support for noDebug launch requests - that was done
by directly starting the target program using os/exec.Cmd and blocking
the launch request indefinitely until the program terminates or
is stopped with a disconnect request (when dlv dap can support it).
Even though the approach seemed to work for user, that adds an extra
control flow and complexity to the codebase.
This change takes a different approach to implement the noDebug
launch feature. Instead of using os/exec.Cmd, this uses the existing
debug launch path, but avoids setting breakpoints. Finally, the program
will start upon receiving onConfigurationDoneRequest and blocks there
until the program terminates. This simplifies the code.
The DAP spec does not explicitly specify what to do about other
requests. It seems like VSCode can issue evaluate requests or other
requests after the configuration done request. Currently they are
blocked because the program is in running state. We can consider checking
s.noDebug and responding with an error in the future if we want/need to.
See the log below for a typical DAP request/response sequence:
2021-03-29T01:42:53-04:00 debug layer=dap building binary at /Users/hakim/projects/s/__debug_bin
2021-03-29T01:42:55-04:00 info layer=debugger launching process with args: [/Users/hakim/projects/s/__debug_bin]
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"initialized"}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":2,"success":true,"command":"launch"}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":3,"type":"request","command":"setBreakpoints","arguments":{"source":{"name":"main.go","path":"/Users/hakim/projects/s/main.go"},"breakpoints":[{"line":9}],"lines":[9]}}
2021-03-29T01:42:55-04:00 error layer=dap Unable to set or clear breakpoints: running in noDebug mode
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":3,"success":false,"command":"setBreakpoints","message":"Unable to set or clear breakpoints","body":{"error":{"id":2002,"format":"Unable to set or clear breakpoints: running in noDebug mode"}}}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":4,"type":"request","command":"configurationDone","arguments":{}}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":4,"success":true,"command":"configurationDone"}
2021-03-29T01:42:55-04:00 debug layer=debugger continuing
Hello
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"terminated","body":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":5,"type":"request","command":"threads"}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":5,"success":true,"command":"threads","body":{"threads":null}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":6,"type":"request","command":"disconnect","arguments":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":6,"success":true,"command":"disconnect"}
2021-03-29T01:43:00-04:00 debug layer=debugger halting
2021-03-29T01:43:00-04:00 error layer=dap Process 27219 has exited with status 0
2021-03-29T01:43:00-04:00 debug layer=debugger detaching
Updates https://github.com/golang/vscode-go/issues/1111
* service/dap: address polina's comments for noDebug logic
Reworked so the noDebug launch request again blocks launch request
handler after responding with the launch response. By skipping
the initialized event, it should prevent well-behaving clients from
sending any further requests. Currently, doesn't matter because
the handler goroutine will be blocked until the program termination
anyway.
Placed mutex back, since I noticed that's the only way to prevent
racing between Stop and the handler goroutine. The synchronization
lotic (in particular, during teardown) should be revisited after
asynchronous request support work is done.
* dap: address more comments
2021-04-05 18:44:02 +00:00
// no initialized event.
// noDebug mode applies only to "launch" requests.
client . ExpectLaunchResponse ( t )
2021-08-16 15:51:23 +00:00
client . ExpectOutputEventProcessExited ( t , exitStatus )
2021-03-23 19:10:21 +00:00
client . ExpectTerminatedEvent ( t )
dap: change how noDebug launch request is served (#2407)
* dap: change how noDebug launch request is served
PR #2400 added support for noDebug launch requests - that was done
by directly starting the target program using os/exec.Cmd and blocking
the launch request indefinitely until the program terminates or
is stopped with a disconnect request (when dlv dap can support it).
Even though the approach seemed to work for user, that adds an extra
control flow and complexity to the codebase.
This change takes a different approach to implement the noDebug
launch feature. Instead of using os/exec.Cmd, this uses the existing
debug launch path, but avoids setting breakpoints. Finally, the program
will start upon receiving onConfigurationDoneRequest and blocks there
until the program terminates. This simplifies the code.
The DAP spec does not explicitly specify what to do about other
requests. It seems like VSCode can issue evaluate requests or other
requests after the configuration done request. Currently they are
blocked because the program is in running state. We can consider checking
s.noDebug and responding with an error in the future if we want/need to.
See the log below for a typical DAP request/response sequence:
2021-03-29T01:42:53-04:00 debug layer=dap building binary at /Users/hakim/projects/s/__debug_bin
2021-03-29T01:42:55-04:00 info layer=debugger launching process with args: [/Users/hakim/projects/s/__debug_bin]
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"initialized"}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":2,"success":true,"command":"launch"}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":3,"type":"request","command":"setBreakpoints","arguments":{"source":{"name":"main.go","path":"/Users/hakim/projects/s/main.go"},"breakpoints":[{"line":9}],"lines":[9]}}
2021-03-29T01:42:55-04:00 error layer=dap Unable to set or clear breakpoints: running in noDebug mode
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":3,"success":false,"command":"setBreakpoints","message":"Unable to set or clear breakpoints","body":{"error":{"id":2002,"format":"Unable to set or clear breakpoints: running in noDebug mode"}}}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":4,"type":"request","command":"configurationDone","arguments":{}}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":4,"success":true,"command":"configurationDone"}
2021-03-29T01:42:55-04:00 debug layer=debugger continuing
Hello
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"terminated","body":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":5,"type":"request","command":"threads"}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":5,"success":true,"command":"threads","body":{"threads":null}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":6,"type":"request","command":"disconnect","arguments":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":6,"success":true,"command":"disconnect"}
2021-03-29T01:43:00-04:00 debug layer=debugger halting
2021-03-29T01:43:00-04:00 error layer=dap Process 27219 has exited with status 0
2021-03-29T01:43:00-04:00 debug layer=debugger detaching
Updates https://github.com/golang/vscode-go/issues/1111
* service/dap: address polina's comments for noDebug logic
Reworked so the noDebug launch request again blocks launch request
handler after responding with the launch response. By skipping
the initialized event, it should prevent well-behaving clients from
sending any further requests. Currently, doesn't matter because
the handler goroutine will be blocked until the program termination
anyway.
Placed mutex back, since I noticed that's the only way to prevent
racing between Stop and the handler goroutine. The synchronization
lotic (in particular, during teardown) should be revisited after
asynchronous request support work is done.
* dap: address more comments
2021-04-05 18:44:02 +00:00
client . DisconnectRequestWithKillOption ( true )
client . ExpectDisconnectResponse ( t )
2021-07-14 21:32:46 +00:00
client . ExpectTerminatedEvent ( t )
2021-03-23 19:10:21 +00:00
}
2021-08-16 15:51:23 +00:00
func TestNoDebug_AcceptNoRequestsButDisconnect ( t * testing . T ) {
runTest ( t , "http_server" , func ( client * daptest . Client , fixture protest . Fixture ) {
client . InitializeRequest ( )
client . ExpectInitializeResponseAndCapabilities ( t )
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"noDebug" : true , "mode" : "exec" , "program" : fixture . Path } )
client . ExpectLaunchResponse ( t )
// Anything other than disconnect should get rejected
var ExpectNoDebugError = func ( cmd string ) {
er := client . ExpectErrorResponse ( t )
if er . Body . Error . Format != fmt . Sprintf ( "noDebug mode: unable to process '%s' request" , cmd ) {
t . Errorf ( "\ngot %#v\nwant 'noDebug mode: unable to process '%s' request'" , er , cmd )
}
}
client . SetBreakpointsRequest ( fixture . Source , [ ] int { 8 } )
ExpectNoDebugError ( "setBreakpoints" )
client . SetFunctionBreakpointsRequest ( nil )
ExpectNoDebugError ( "setFunctionBreakpoints" )
client . PauseRequest ( 1 )
ExpectNoDebugError ( "pause" )
client . RestartRequest ( )
client . ExpectUnsupportedCommandErrorResponse ( t )
// Disconnect request is ok
client . DisconnectRequestWithKillOption ( true )
client . ExpectOutputEventTerminating ( t )
client . ExpectDisconnectResponse ( t )
client . ExpectTerminatedEvent ( t )
} )
}
2021-08-23 18:27:49 +00:00
func TestLaunchRequestWithRelativeBuildPath ( t * testing . T ) {
2021-09-01 16:59:51 +00:00
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
client := startDAPServerWithClient ( t , serverStopped )
2021-09-01 16:59:51 +00:00
defer client . Close ( )
2021-08-23 18:27:49 +00:00
fixdir := protest . FindFixturesDir ( )
if filepath . IsAbs ( fixdir ) {
t . Fatal ( "this test requires relative program path" )
}
program := filepath . Join ( protest . FindFixturesDir ( ) , "buildtest" )
// Use different working dir for target than dlv.
// Program path will be interpreted relative to dlv's.
dlvwd , _ := os . Getwd ( )
runDebugSession ( t , client , "launch" , func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "debug" , "program" : program , "cwd" : filepath . Dir ( dlvwd ) } )
} )
2021-09-01 16:59:51 +00:00
<- serverStopped
2021-08-23 18:27:49 +00:00
}
func TestLaunchRequestWithRelativeExecPath ( t * testing . T ) {
2020-03-04 17:22:51 +00:00
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
2021-08-23 18:27:49 +00:00
symlink := "./__thisexe"
err := os . Symlink ( fixture . Path , symlink )
defer os . Remove ( symlink )
if err != nil {
t . Fatal ( "unable to create relative symlink:" , err )
}
2020-12-28 17:14:15 +00:00
runDebugSession ( t , client , "launch" , func ( ) {
2020-03-04 17:22:51 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-08-23 18:27:49 +00:00
"mode" : "exec" , "program" : symlink } )
} )
} )
}
func TestLaunchTestRequest ( t * testing . T ) {
2021-10-05 11:40:17 +00:00
orgWD , _ := os . Getwd ( )
fixtures := protest . FindFixturesDir ( ) // relative to current working directory.
absoluteFixturesDir , _ := filepath . Abs ( fixtures )
absolutePkgDir , _ := filepath . Abs ( filepath . Join ( fixtures , "buildtest" ) )
testFile := filepath . Join ( absolutePkgDir , "main_test.go" )
for _ , tc := range [ ] struct {
name string
dlvWD string
launchArgs map [ string ] interface { }
wantWD string
} { {
name : "default" ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : absolutePkgDir ,
} ,
wantWD : absolutePkgDir ,
} , {
name : "output" ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : absolutePkgDir , "output" : "test.out" ,
} ,
wantWD : absolutePkgDir ,
} , {
name : "delveCWD" ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : absolutePkgDir , "delveCWD" : "." ,
} ,
wantWD : absolutePkgDir ,
} , {
name : "delveCWD2" ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : "." , "delveCWD" : absolutePkgDir ,
} ,
wantWD : absolutePkgDir ,
} , {
name : "cwd" ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : absolutePkgDir , "cwd" : fixtures , // fixtures is relative to the current working directory.
} ,
wantWD : absoluteFixturesDir ,
} , {
name : "dlv runs outside of module" ,
dlvWD : os . TempDir ( ) ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : absolutePkgDir , "delveCWD" : absoluteFixturesDir ,
} ,
wantWD : absolutePkgDir ,
} , {
name : "dlv builds in delveCWD but runs in cwd" ,
dlvWD : fixtures ,
launchArgs : map [ string ] interface { } {
"mode" : "test" , "program" : absolutePkgDir , "delveCWD" : absolutePkgDir , "cwd" : ".." , // relative to delveCWD.
} ,
wantWD : absoluteFixturesDir ,
} } {
t . Run ( tc . name , func ( t * testing . T ) {
// Some test cases with delveCWD or dlvWD change process working directory.
defer os . Chdir ( orgWD )
if tc . dlvWD != "" {
os . Chdir ( tc . dlvWD )
defer os . Chdir ( orgWD )
}
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
client := startDAPServerWithClient ( t , serverStopped )
2021-10-05 11:40:17 +00:00
defer client . Close ( )
runDebugSessionWithBPs ( t , client , "launch" ,
func ( ) { // Launch
client . LaunchRequestWithArgs ( tc . launchArgs )
} ,
testFile , [ ] int { 14 } ,
[ ] onBreakpoint { {
execute : func ( ) {
checkStop ( t , client , - 1 , "github.com/go-delve/delve/_fixtures/buildtest.TestCurrentDirectory" , 14 )
client . VariablesRequest ( 1001 ) // Locals
locals := client . ExpectVariablesResponse ( t )
checkChildren ( t , locals , "Locals" , 1 )
for i := range locals . Body . Variables {
switch locals . Body . Variables [ i ] . Name {
case "wd" : // The test's working directory is the package directory by default.
checkVarExact ( t , locals , i , "wd" , "wd" , fmt . Sprintf ( "%q" , tc . wantWD ) , "string" , noChildren )
}
}
} } } )
<- serverStopped
} )
}
2020-03-04 17:22:51 +00:00
}
2020-05-28 21:01:51 +00:00
// Tests that 'args' from LaunchRequest are parsed and passed to the target
// program. The target program exits without an error on success, and
// panics on error, causing an unexpected StoppedEvent instead of
// Terminated Event.
2020-05-11 16:52:26 +00:00
func TestLaunchRequestWithArgs ( t * testing . T ) {
runTest ( t , "testargs" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSession ( t , client , "launch" , func ( ) {
2020-05-11 16:52:26 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path ,
"args" : [ ] string { "test" , "pass flag" } } )
2021-08-23 18:27:49 +00:00
} )
2020-05-11 16:52:26 +00:00
} )
}
2020-05-28 21:01:51 +00:00
// Tests that 'buildFlags' from LaunchRequest are parsed and passed to the
// compiler. The target program exits without an error on success, and
// panics on error, causing an unexpected StoppedEvent instead of
// TerminatedEvent.
func TestLaunchRequestWithBuildFlags ( t * testing . T ) {
runTest ( t , "buildflagtest" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-12-28 17:14:15 +00:00
runDebugSession ( t , client , "launch" , func ( ) {
2020-05-28 21:01:51 +00:00
// We reuse the harness that builds, but ignore the built binary,
// only relying on the source to be built in response to LaunchRequest.
client . LaunchRequestWithArgs ( map [ string ] interface { } {
2021-03-24 18:02:22 +00:00
"mode" : "debug" , "program" : fixture . Source , "output" : "__mybin" ,
2020-05-28 21:01:51 +00:00
"buildFlags" : "-ldflags '-X main.Hello=World'" } )
2021-08-23 18:27:49 +00:00
} )
2020-05-28 21:01:51 +00:00
} )
}
2020-12-28 17:14:15 +00:00
func TestAttachRequest ( t * testing . T ) {
if runtime . GOOS == "freebsd" {
t . SkipNow ( )
}
2021-01-18 15:48:06 +00:00
if runtime . GOOS == "windows" {
t . Skip ( "test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details" )
}
2020-12-28 17:14:15 +00:00
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
// Start the program to attach to
2021-04-15 23:35:37 +00:00
cmd := execFixture ( t , fixture )
2020-12-28 17:14:15 +00:00
runDebugSessionWithBPs ( t , client , "attach" ,
// Attach
func ( ) {
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } {
/*"mode": "local" by default*/ "processId" : cmd . Process . Pid , "stopOnEntry" : false } )
2020-12-28 17:14:15 +00:00
} ,
// Set breakpoints
fixture . Source , [ ] int { 8 } ,
[ ] onBreakpoint { {
// Stop at line 8
execute : func ( ) {
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.loop" , 8 )
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( localsScope )
2020-12-28 17:14:15 +00:00
locals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkChildren ( t , locals , "Locals" , 1 )
checkVarRegex ( t , locals , 0 , "i" , "i" , "[0-9]+" , "int" , noChildren )
2020-12-28 17:14:15 +00:00
} ,
disconnect : true ,
} } )
} )
}
2021-09-24 11:43:46 +00:00
// Since we are in async mode while running, we might receive thee messages after pause request
// in either order.
func expectPauseResponseAndStoppedEvent ( t * testing . T , client * daptest . Client ) {
t . Helper ( )
for i := 0 ; i < 2 ; i ++ {
msg := client . ExpectMessage ( t )
switch m := msg . ( type ) {
case * dap . StoppedEvent :
if m . Body . Reason != "pause" || m . Body . ThreadId != 0 && m . Body . ThreadId != 1 {
t . Errorf ( "\ngot %#v\nwant ThreadId=0/1 Reason='pause'" , m )
}
case * dap . PauseResponse :
default :
t . Fatalf ( "got %#v, want StoppedEvent or PauseResponse" , m )
}
}
}
2021-05-17 17:37:15 +00:00
func TestPauseAndContinue ( t * testing . T ) {
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
// Launch
func ( ) {
client . LaunchRequest ( "exec" , fixture . Path , ! stopOnEntry )
} ,
// Set breakpoints
fixture . Source , [ ] int { 6 } ,
[ ] onBreakpoint { {
execute : func ( ) {
verifyStopLocation ( t , client , 1 , "main.loop" , 6 )
// Continue resumes all goroutines, so thread id is ignored
client . ContinueRequest ( 12345 )
client . ExpectContinueResponse ( t )
time . Sleep ( time . Second )
// Halt pauses all goroutines, so thread id is ignored
client . PauseRequest ( 56789 )
2021-09-24 11:43:46 +00:00
expectPauseResponseAndStoppedEvent ( t , client )
2021-05-17 17:37:15 +00:00
// Pause will be a no-op at a pause: there will be no additional stopped events
client . PauseRequest ( 1 )
client . ExpectPauseResponse ( t )
} ,
// The program has an infinite loop, so we must kill it by disconnecting.
disconnect : true ,
} } )
} )
}
2020-05-01 19:24:44 +00:00
func TestUnupportedCommandResponses ( t * testing . T ) {
var got * dap . ErrorResponse
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
seqCnt := 1
expectUnsupportedCommand := func ( cmd string ) {
t . Helper ( )
got = client . ExpectUnsupportedCommandErrorResponse ( t )
if got . RequestSeq != seqCnt || got . Command != cmd {
t . Errorf ( "\ngot %#v\nwant RequestSeq=%d Command=%s" , got , seqCnt , cmd )
}
seqCnt ++
}
client . RestartFrameRequest ( )
expectUnsupportedCommand ( "restartFrame" )
client . GotoRequest ( )
expectUnsupportedCommand ( "goto" )
client . SourceRequest ( )
expectUnsupportedCommand ( "source" )
client . TerminateThreadsRequest ( )
expectUnsupportedCommand ( "terminateThreads" )
client . StepInTargetsRequest ( )
expectUnsupportedCommand ( "stepInTargets" )
client . GotoTargetsRequest ( )
expectUnsupportedCommand ( "gotoTargets" )
client . CompletionsRequest ( )
expectUnsupportedCommand ( "completions" )
client . DataBreakpointInfoRequest ( )
expectUnsupportedCommand ( "dataBreakpointInfo" )
client . SetDataBreakpointsRequest ( )
expectUnsupportedCommand ( "setDataBreakpoints" )
client . BreakpointLocationsRequest ( )
expectUnsupportedCommand ( "breakpointLocations" )
client . ModulesRequest ( )
expectUnsupportedCommand ( "modules" )
2021-09-01 16:59:51 +00:00
client . DisconnectRequest ( )
client . ExpectDisconnectResponse ( t )
2020-05-01 19:24:44 +00:00
} )
}
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
type helperForSetVariable struct {
t * testing . T
c * daptest . Client
}
func ( h * helperForSetVariable ) expectSetVariable ( ref int , name , value string ) {
h . t . Helper ( )
h . expectSetVariable0 ( ref , name , value , false )
}
func ( h * helperForSetVariable ) failSetVariable ( ref int , name , value , wantErrInfo string ) {
h . t . Helper ( )
h . failSetVariable0 ( ref , name , value , wantErrInfo , false )
}
func ( h * helperForSetVariable ) failSetVariableAndStop ( ref int , name , value , wantErrInfo string ) {
h . t . Helper ( )
h . failSetVariable0 ( ref , name , value , wantErrInfo , true )
}
func ( h * helperForSetVariable ) evaluate ( expr , want string , hasRef bool ) {
h . t . Helper ( )
h . c . EvaluateRequest ( expr , 1000 , "whatever" )
got := h . c . ExpectEvaluateResponse ( h . t )
2021-05-26 15:15:39 +00:00
checkEval ( h . t , got , want , hasRef )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
}
func ( h * helperForSetVariable ) evaluateRegex ( expr , want string , hasRef bool ) {
h . t . Helper ( )
h . c . EvaluateRequest ( expr , 1000 , "whatever" )
got := h . c . ExpectEvaluateResponse ( h . t )
2021-05-26 15:15:39 +00:00
checkEvalRegex ( h . t , got , want , hasRef )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
}
func ( h * helperForSetVariable ) expectSetVariable0 ( ref int , name , value string , wantStop bool ) {
h . t . Helper ( )
h . c . SetVariableRequest ( ref , name , value )
if wantStop {
h . c . ExpectStoppedEvent ( h . t )
}
if got , want := h . c . ExpectSetVariableResponse ( h . t ) , value ; got . Success != true || got . Body . Value != want {
h . t . Errorf ( "SetVariableRequest(%v, %v)=%#v, want {Success=true, Body.Value=%q" , name , value , got , want )
}
}
func ( h * helperForSetVariable ) failSetVariable0 ( ref int , name , value , wantErrInfo string , wantStop bool ) {
h . t . Helper ( )
h . c . SetVariableRequest ( ref , name , value )
if wantStop {
h . c . ExpectStoppedEvent ( h . t )
}
resp := h . c . ExpectErrorResponse ( h . t )
2021-06-23 18:21:19 +00:00
if got := resp . Body . Error . Format ; ! stringContainsCaseInsensitive ( got , wantErrInfo ) {
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
h . t . Errorf ( "got %#v, want error string containing %v" , got , wantErrInfo )
}
}
func ( h * helperForSetVariable ) variables ( ref int ) * dap . VariablesResponse {
h . t . Helper ( )
h . c . VariablesRequest ( ref )
return h . c . ExpectVariablesResponse ( h . t )
}
// TestSetVariable tests SetVariable features that do not need function call support.
func TestSetVariable ( t * testing . T ) {
runTest ( t , "testvariables" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
fixture . Source , [ ] int { } , // breakpoints are set within the program.
[ ] onBreakpoint { {
execute : func ( ) {
tester := & helperForSetVariable { t , client }
startLineno := 66 // after runtime.Breakpoint
if runtime . GOOS == "windows" && goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 15 ) {
// Go1.15 on windows inserts a NOP after the call to
// runtime.Breakpoint and marks it same line as the
// runtime.Breakpoint call, making this flaky, so skip the line check.
startLineno = - 1
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.foobar" , startLineno )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
2021-10-05 17:35:14 +00:00
// Local variables
locals := tester . variables ( localsScope )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
2021-10-05 17:35:14 +00:00
// Args of foobar(baz string, bar FooBar)
checkVarExact ( t , locals , 1 , "bar" , "bar" , ` main.FooBar { Baz: 10, Bur: "lorem"} ` , "main.FooBar" , hasChildren )
tester . failSetVariable ( localsScope , "bar" , ` main.FooBar { Baz: 42, Bur: "ipsum"} ` , "*ast.CompositeLit not implemented" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// Nested field.
2021-10-05 17:35:14 +00:00
barRef := checkVarExact ( t , locals , 1 , "bar" , "bar" , ` main.FooBar { Baz: 10, Bur: "lorem"} ` , "main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( barRef , "Baz" , "42" )
tester . evaluate ( "bar" , ` main.FooBar { Baz: 42, Bur: "lorem"} ` , hasChildren )
tester . failSetVariable ( barRef , "Baz" , ` "string" ` , "can not convert" )
// int
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a2" , "a2" , "6" , "int" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "a2" , "42" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "a2" , "42" , noChildren )
2021-10-05 17:35:14 +00:00
tester . failSetVariable ( localsScope , "a2" , "false" , "can not convert" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// float
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a3" , "a3" , "7.23" , "float64" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "a3" , "-0.1" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "a3" , "-0.1" , noChildren )
// array of int
2021-05-26 15:15:39 +00:00
a4Ref := checkVarExact ( t , locals , - 1 , "a4" , "a4" , "[2]int [1,2]" , "[2]int" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( a4Ref , "[1]" , "-7" )
tester . evaluate ( "a4" , "[2]int [1,-7]" , hasChildren )
2021-10-05 17:35:14 +00:00
tester . failSetVariable ( localsScope , "a4" , "[2]int{3, 4}" , "not implemented" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// slice of int
2021-05-26 15:15:39 +00:00
a5Ref := checkVarExact ( t , locals , - 1 , "a5" , "a5" , "[]int len: 5, cap: 5, [1,2,3,4,5]" , "[]int" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( a5Ref , "[3]" , "100" )
tester . evaluate ( "a5" , "[]int len: 5, cap: 5, [1,2,3,100,5]" , hasChildren )
// composite literal and its nested fields.
2021-05-26 15:15:39 +00:00
a7Ref := checkVarExact ( t , locals , - 1 , "a7" , "a7" , ` *main.FooBar { Baz: 5, Bur: "strum"} ` , "*main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
a7Val := tester . variables ( a7Ref )
2021-05-26 15:15:39 +00:00
a7ValRef := checkVarExact ( t , a7Val , - 1 , "" , "(*a7)" , ` main.FooBar { Baz: 5, Bur: "strum"} ` , "main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( a7ValRef , "Baz" , "7" )
tester . evaluate ( "(*a7)" , ` main.FooBar { Baz: 7, Bur: "strum"} ` , hasChildren )
// pointer
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a9" , "a9" , ` *main.FooBar nil ` , "*main.FooBar" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "a9" , "&a6" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "a9" , ` *main.FooBar { Baz: 8, Bur: "word"} ` , hasChildren )
// slice of pointers
2021-05-26 15:15:39 +00:00
a13Ref := checkVarExact ( t , locals , - 1 , "a13" , "a13" , ` []*main.FooBar len: 3, cap: 3, [* { Baz: 6, Bur: "f"},* { Baz: 7, Bur: "g"},* { Baz: 8, Bur: "h"}] ` , "[]*main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
a13 := tester . variables ( a13Ref )
2021-05-26 15:15:39 +00:00
a13c0Ref := checkVarExact ( t , a13 , - 1 , "[0]" , "a13[0]" , ` *main.FooBar { Baz: 6, Bur: "f"} ` , "*main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
a13c0 := tester . variables ( a13c0Ref )
2021-05-26 15:15:39 +00:00
a13c0valRef := checkVarExact ( t , a13c0 , - 1 , "" , "(*a13[0])" , ` main.FooBar { Baz: 6, Bur: "f"} ` , "main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( a13c0valRef , "Baz" , "777" )
tester . evaluate ( "a13[0]" , ` *main.FooBar { Baz: 777, Bur: "f"} ` , hasChildren )
// complex
tester . evaluate ( "c64" , ` (1 + 2i) ` , hasChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "c64" , "(2 + 3i)" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "c64" , ` (2 + 3i) ` , hasChildren )
// note: complex's real, imaginary part can't be directly mutable.
//
// Global variables
// p1 = 10
2021-10-05 17:35:14 +00:00
client . VariablesRequest ( globalsScope )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
globals := client . ExpectVariablesResponse ( t )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , globals , - 1 , "p1" , "main.p1" , "10" , "int" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( globalsScope , "p1" , "-10" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "p1" , "-10" , noChildren )
2021-10-05 17:35:14 +00:00
tester . failSetVariable ( globalsScope , "p1" , "0.1" , "can not convert" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
} ,
disconnect : true ,
} } )
} )
runTest ( t , "testvariables2" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
fixture . Source , [ ] int { } , // breakpoints are set within the program.
[ ] onBreakpoint { {
execute : func ( ) {
tester := & helperForSetVariable { t , client }
2021-06-16 20:27:38 +00:00
startLineno := 360 // after runtime.Breakpoint
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
if runtime . GOOS == "windows" && goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 15 ) {
startLineno = - 1
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.main" , startLineno )
2021-10-05 17:35:14 +00:00
locals := tester . variables ( localsScope )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// channel
tester . evaluate ( "chnil" , "chan int nil" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "chnil" , "ch1" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "chnil" , "chan int 4/11" , hasChildren )
// func
tester . evaluate ( "fn2" , "nil" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "fn2" , "fn1" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "fn2" , "main.afunc" , noChildren )
// interface
tester . evaluate ( "ifacenil" , "interface {} nil" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "ifacenil" , "iface1" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "ifacenil" , "interface {}(*main.astruct) *{A: 1, B: 2}" , hasChildren )
// interface.(data)
2021-05-26 15:15:39 +00:00
iface1Ref := checkVarExact ( t , locals , - 1 , "iface1" , "iface1" , "interface {}(*main.astruct) *{A: 1, B: 2}" , "interface {}" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
iface1 := tester . variables ( iface1Ref )
2021-05-26 15:15:39 +00:00
iface1DataRef := checkVarExact ( t , iface1 , - 1 , "data" , "iface1.(data)" , "*main.astruct {A: 1, B: 2}" , "*main.astruct" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
iface1Data := tester . variables ( iface1DataRef )
2021-05-26 15:15:39 +00:00
iface1DataValueRef := checkVarExact ( t , iface1Data , - 1 , "" , "(*iface1.(data))" , "main.astruct {A: 1, B: 2}" , "main.astruct" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( iface1DataValueRef , "A" , "2021" )
tester . evaluate ( "iface1" , "interface {}(*main.astruct) *{A: 2021, B: 2}" , hasChildren )
// map: string -> struct
tester . evaluate ( ` m1["Malone"] ` , "main.astruct {A: 2, B: 3}" , hasChildren )
2021-05-26 15:15:39 +00:00
m1Ref := checkVarRegex ( t , locals , - 1 , "m1" , "m1" , ` .*map\[string\]main\.astruct.* ` , ` map\[string\]main\.astruct ` , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
m1 := tester . variables ( m1Ref )
2021-07-16 16:50:37 +00:00
elem1 := m1 . Body . Variables [ 1 ]
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( elem1 . VariablesReference , "A" , "-9999" )
tester . expectSetVariable ( elem1 . VariablesReference , "B" , "10000" )
tester . evaluate ( elem1 . EvaluateName , "main.astruct {A: -9999, B: 10000}" , hasChildren )
// map: struct -> int
2021-05-26 15:15:39 +00:00
m3Ref := checkVarExact ( t , locals , - 1 , "m3" , "m3" , "map[main.astruct]int [{A: 1, B: 1}: 42, {A: 2, B: 2}: 43, ]" , "map[main.astruct]int" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( m3Ref , "main.astruct {A: 1, B: 1}" , "8888" )
// note: updating keys is possible, but let's not promise anything.
tester . evaluateRegex ( "m3" , ` .*\[\ { A: 1, B: 1\}: 8888,.* ` , hasChildren )
// map: struct -> struct
2021-05-26 15:15:39 +00:00
m4Ref := checkVarRegex ( t , locals , - 1 , "m4" , "m4" , ` map\[main\.astruct]main\.astruct.*\[\ { A: 1, B: 1\}: \ { A: 11, B: 11\}.* ` , ` map\[main\.astruct\]main\.astruct ` , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
m4 := tester . variables ( m4Ref )
2021-05-26 15:15:39 +00:00
m4Val1Ref := checkVarRegex ( t , m4 , - 1 , "[val 0]" , ` .*0x[0-9a-f]+.* ` , ` main.astruct.* ` , ` main\.astruct ` , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( m4Val1Ref , "A" , "-9999" )
tester . evaluateRegex ( "m4" , ` .*A: -9999,.* ` , hasChildren )
// unsigned pointer
2021-05-26 15:15:39 +00:00
checkVarRegex ( t , locals , - 1 , "up1" , "up1" , ` unsafe\.Pointer\(0x[0-9a-f]+\) ` , "unsafe.Pointer" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "up1" , "unsafe.Pointer(0x0)" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "up1" , "unsafe.Pointer(0x0)" , noChildren )
// val := A{val: 1}
2021-05-26 15:15:39 +00:00
valRef := checkVarExact ( t , locals , - 1 , "val" , "val" , ` main.A { val: 1} ` , "main.A" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( valRef , "val" , "3" )
tester . evaluate ( "val" , ` main.A { val: 3} ` , hasChildren )
} ,
disconnect : true ,
} } )
} )
}
// TestSetVariableWithCall tests SetVariable features that do not depend on function calls support.
func TestSetVariableWithCall ( t * testing . T ) {
protest . MustSupportFunctionCalls ( t , testBackend )
runTest ( t , "testvariables" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
fixture . Source , [ ] int { 66 , 67 } ,
[ ] onBreakpoint { {
execute : func ( ) {
tester := & helperForSetVariable { t , client }
startLineno := 66
if runtime . GOOS == "windows" && goversion . VersionAfterOrEqual ( runtime . Version ( ) , 1 , 15 ) {
// Go1.15 on windows inserts a NOP after the call to
// runtime.Breakpoint and marks it same line as the
// runtime.Breakpoint call, making this flaky, so skip the line check.
startLineno = - 1
}
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.foobar" , startLineno )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
2021-10-05 17:35:14 +00:00
// Local variables
locals := tester . variables ( localsScope )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
2021-10-05 17:35:14 +00:00
// Args of foobar(baz string, bar FooBar)
checkVarExact ( t , locals , 0 , "baz" , "baz" , ` "bazburzum" ` , "string" , noChildren )
tester . expectSetVariable ( localsScope , "baz" , ` "BazBurZum" ` )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "baz" , ` "BazBurZum" ` , noChildren )
2021-10-05 17:35:14 +00:00
barRef := checkVarExact ( t , locals , 1 , "bar" , "bar" , ` main.FooBar { Baz: 10, Bur: "lorem"} ` , "main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . expectSetVariable ( barRef , "Bur" , ` "ipsum" ` )
tester . evaluate ( "bar" , ` main.FooBar { Baz: 10, Bur: "ipsum"} ` , hasChildren )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a1" , "a1" , ` "foofoofoofoofoofoo" ` , "string" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "a1" , ` "barbarbar" ` )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "a1" , ` "barbarbar" ` , noChildren )
2021-05-26 15:15:39 +00:00
a6Ref := checkVarExact ( t , locals , - 1 , "a6" , "a6" , ` main.FooBar { Baz: 8, Bur: "word"} ` , "main.FooBar" , hasChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . failSetVariable ( a6Ref , "Bur" , "false" , "can not convert" )
tester . expectSetVariable ( a6Ref , "Bur" , ` "sentence" ` )
tester . evaluate ( "a6" , ` main.FooBar { Baz: 8, Bur: "sentence"} ` , hasChildren )
} ,
} , {
// Stop at second breakpoint and set a1.
execute : func ( ) {
tester := & helperForSetVariable { t , client }
2021-05-26 15:15:39 +00:00
checkStop ( t , client , 1 , "main.barfoo" , - 1 )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// Test: set string 'a1' in main.barfoo.
// This shouldn't affect 'a1' in main.foobar - we will check that in the next breakpoint.
2021-10-05 17:35:14 +00:00
locals := tester . variables ( localsScope )
2021-05-26 15:15:39 +00:00
checkVarExact ( t , locals , - 1 , "a1" , "a1" , ` "bur" ` , "string" , noChildren )
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "a1" , ` "fur" ` )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluate ( "a1" , ` "fur" ` , noChildren )
// We will check a1 in main.foobar isn't affected from the next breakpoint.
client . StackTraceRequest ( 1 , 1 , 20 )
res := client . ExpectStackTraceResponse ( t )
if len ( res . Body . StackFrames ) < 1 {
t . Fatalf ( "stack trace response = %#v, wanted at least one stack frame" , res )
}
outerFrame := res . Body . StackFrames [ 0 ] . Id
client . EvaluateRequest ( "a1" , outerFrame , "whatever_context" )
evalRes := client . ExpectEvaluateResponse ( t )
2021-05-26 15:15:39 +00:00
checkEval ( t , evalRes , ` "barbarbar" ` , noChildren )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
} ,
disconnect : true ,
} } )
} )
runTest ( t , "fncall" , func ( client * daptest . Client , fixture protest . Fixture ) {
runDebugSessionWithBPs ( t , client , "launch" ,
func ( ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"mode" : "exec" , "program" : fixture . Path , "showGlobalVariables" : true ,
} )
} ,
fixture . Source , [ ] int { } , // breakpoints are set within the program.
[ ] onBreakpoint { {
// Stop at second breakpoint and set a1.
execute : func ( ) {
tester := & helperForSetVariable { t , client }
2021-07-08 15:47:53 +00:00
checkStop ( t , client , 1 , "main.main" , - 1 )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
2021-10-05 17:35:14 +00:00
_ = tester . variables ( localsScope )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// successful variable set using a function call.
2021-10-05 17:35:14 +00:00
tester . expectSetVariable ( localsScope , "str" , ` callstacktrace() ` )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
tester . evaluateRegex ( "str" , ` .*in main.callstacktrace at.* ` , noChildren )
2021-10-05 17:35:14 +00:00
tester . failSetVariableAndStop ( localsScope , "str" , ` callpanic() ` , ` callpanic panicked ` )
2021-07-08 15:47:53 +00:00
checkStop ( t , client , 1 , "main.main" , - 1 )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// breakpoint during a function call.
2021-10-05 17:35:14 +00:00
tester . failSetVariableAndStop ( localsScope , "str" , ` callbreak() ` , "call stopped" )
dap: handle SetVariable requests (#2440)
* dap: handle SetVariable requests
The handler invokes debugger.SetVariableInScope, except for
string type variables. For which, we rely on the `call` command.
Moved the call expression handling logic to the new `doCall`
function, so it can be reused by the SetVariable requenst
handler.
With this PR, every successful SetVariable request triggers
a StoppedEvent - that's a hack to reset the variablesHandle
map internally and notify the client of this change. It will
be nice if we can just update cached data corresponding to
the updated variable. But I cannot find an easy and safe way
to achieve this yet.
Also fixed a small bug in the call expression evaluation -
Previously, dlv dap returned an error "Unable to evaluate
expression: call stopped" if the call expression is for
variable assignment. (e.g. "call animal = "rabbit").
* dap: address comments from aarzilli
resetHandlesForStop & sendStoppedEvent unconditionally after
call command is left as a TODO - This is an existing code path
(just refactored) and an preexisting bug. Fixing it here
requires updates in TestEvaluateCallRequest and I prefer
addressing it in a separate cl.
Disabled call injection testing on arm64. Separated TestSetVariable
into two, one that doesn't involve call injection and another that
may involve call injection.
Fixed variableByName by removing unnecessary recursion.
* dap: address polina's comments
- removed the hard reset for every variable set
- added tests for various variable types
- added tests that involves interrupted function calls. (breakpoint/panic)
And,
- changed to utilize EvalVariableInScope to access the variable instead
of searching the children by name.
- changed to utilize evaluate requests when verifying whether the variable
is changed as expected in testing. Since now we avoid resetting the variable
handles after variable reset, either we need to trigger scope changes
explicitly, or stop depending on the variables request.
* dap: address comments
- Discuss the problem around the current doCall implementation
and the implication.
- Refine the description on how VS Code handles after setVariable
and evaluate request (there could be followup scopes/evaluate requests).
- Use the explicit line numbers for breakpoints in the SetVariable tests.
- Do not use errors.Is - we could've used golang.org/x/xerrors polyfill
but that's an additional dependency, and we will remove this check once
tests that depend on old behavior are fixed.
* dap: remove errTerminated and adjust the test
* dap: evaluate in the outer frame, instead of advancing to the next bp
2021-05-20 17:05:47 +00:00
// TODO(hyangah): continue after this causes runtime error while resuming
// unfinished injected call.
// runtime error: can not convert %!s(<nil>) constant to string
// This can be reproducible with dlv cli. (`call str = callbreak(); continue`)
} ,
disconnect : true ,
} } )
} )
}
2020-05-01 19:24:44 +00:00
func TestOptionalNotYetImplementedResponses ( t * testing . T ) {
var got * dap . ErrorResponse
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
seqCnt := 1
expectNotYetImplemented := func ( cmd string ) {
t . Helper ( )
got = client . ExpectNotYetImplementedErrorResponse ( t )
if got . RequestSeq != seqCnt || got . Command != cmd {
t . Errorf ( "\ngot %#v\nwant RequestSeq=%d Command=%s" , got , seqCnt , cmd )
}
seqCnt ++
}
client . TerminateRequest ( )
expectNotYetImplemented ( "terminate" )
client . RestartRequest ( )
expectNotYetImplemented ( "restart" )
client . SetExpressionRequest ( )
expectNotYetImplemented ( "setExpression" )
client . LoadedSourcesRequest ( )
expectNotYetImplemented ( "loadedSources" )
client . ReadMemoryRequest ( )
expectNotYetImplemented ( "readMemory" )
client . DisassembleRequest ( )
expectNotYetImplemented ( "disassemble" )
client . CancelRequest ( )
expectNotYetImplemented ( "cancel" )
2021-09-01 16:59:51 +00:00
client . DisconnectRequest ( )
client . ExpectDisconnectResponse ( t )
2020-05-01 19:24:44 +00:00
} )
}
2020-03-04 17:22:51 +00:00
func TestBadLaunchRequests ( t * testing . T ) {
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
2020-03-10 19:29:06 +00:00
seqCnt := 1
2021-05-26 15:15:39 +00:00
checkFailedToLaunch := func ( response * dap . ErrorResponse ) {
2020-02-24 17:36:34 +00:00
t . Helper ( )
2020-03-04 17:22:51 +00:00
if response . RequestSeq != seqCnt {
t . Errorf ( "RequestSeq got %d, want %d" , seqCnt , response . RequestSeq )
2020-02-24 17:36:34 +00:00
}
if response . Command != "launch" {
t . Errorf ( "Command got %q, want \"launch\"" , response . Command )
}
if response . Message != "Failed to launch" {
t . Errorf ( "Message got %q, want \"Failed to launch\"" , response . Message )
}
if response . Body . Error . Id != 3000 {
t . Errorf ( "Id got %d, want 3000" , response . Body . Error . Id )
}
2020-03-04 17:22:51 +00:00
seqCnt ++
2020-02-24 17:36:34 +00:00
}
2021-05-26 15:15:39 +00:00
checkFailedToLaunchWithMessage := func ( response * dap . ErrorResponse , errmsg string ) {
2020-03-04 17:22:51 +00:00
t . Helper ( )
2021-05-26 15:15:39 +00:00
checkFailedToLaunch ( response )
2020-03-04 17:22:51 +00:00
if response . Body . Error . Format != errmsg {
t . Errorf ( "\ngot %q\nwant %q" , response . Body . Error . Format , errmsg )
}
2020-02-15 19:52:53 +00:00
}
2020-03-04 17:22:51 +00:00
// Test for the DAP-specific detailed error message.
client . LaunchRequest ( "exec" , "" , stopOnEntry )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2020-03-04 17:22:51 +00:00
"Failed to launch: The program attribute is missing in debug configuration." )
2021-03-08 17:42:54 +00:00
// Bad "program"
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : 12345 } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"program\" of type string" )
2020-03-04 17:22:51 +00:00
2021-03-08 17:42:54 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : nil } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2020-03-04 17:22:51 +00:00
"Failed to launch: The program attribute is missing in debug configuration." )
2021-03-08 17:42:54 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2020-03-04 17:22:51 +00:00
"Failed to launch: The program attribute is missing in debug configuration." )
2021-03-08 17:42:54 +00:00
// Bad "mode"
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "remote" , fixture . Path , stopOnEntry )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"remote\"" )
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "notamode" , fixture . Path , stopOnEntry )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"notamode\"" )
2020-03-04 17:22:51 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : 12345 , "program" : fixture . Path } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"mode\" of type string" )
2020-03-04 17:22:51 +00:00
2021-03-08 17:42:54 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "" } ) // empty mode defaults to "debug" (not an error)
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-03-08 17:42:54 +00:00
"Failed to launch: The program attribute is missing in debug configuration." )
client . LaunchRequestWithArgs ( map [ string ] interface { } { } ) // missing mode defaults to "debug" (not an error)
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-03-08 17:42:54 +00:00
"Failed to launch: The program attribute is missing in debug configuration." )
// Bad "args"
2021-08-26 12:42:58 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "exec" , "program" : fixture . Path , "args" : "foobar" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal string into \"args\" of type []string" )
2020-05-11 16:52:26 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "exec" , "program" : fixture . Path , "args" : 12345 } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"args\" of type []string" )
2020-05-11 16:52:26 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "exec" , "program" : fixture . Path , "args" : [ ] int { 1 , 2 } } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"args\" of type string" )
2020-05-11 16:52:26 +00:00
2021-03-08 17:42:54 +00:00
// Bad "buildFlags"
2020-05-28 21:01:51 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "buildFlags" : 123 } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"buildFlags\" of type string" )
2020-05-28 21:01:51 +00:00
2021-07-18 09:37:41 +00:00
// Bad "backend"
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "backend" : 123 } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"backend\" of type string" )
2021-09-23 04:12:48 +00:00
2021-07-18 09:37:41 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "backend" : "foo" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-18 09:37:41 +00:00
"Failed to launch: could not launch process: unknown backend \"foo\"" )
// Bad "substitutePath"
2021-04-15 23:35:37 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "substitutePath" : 123 } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"substitutePath\" of type {\"from\":string, \"to\":string}" )
2021-04-15 23:35:37 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "substitutePath" : [ ] interface { } { 123 } } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot use 123 as 'substitutePath' of type {\"from\":string, \"to\":string}" )
2021-04-15 23:35:37 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "substitutePath" : [ ] interface { } { map [ string ] interface { } { "to" : "path2" } } } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - 'substitutePath' requires both 'from' and 'to' entries" )
2021-04-15 23:35:37 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "substitutePath" : [ ] interface { } { map [ string ] interface { } { "from" : "path1" , "to" : 123 } } } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot use {\"from\":\"path1\",\"to\":123} as 'substitutePath' of type {\"from\":string, \"to\":string}" )
2021-07-18 09:37:41 +00:00
// Bad "cwd"
2021-04-20 07:40:34 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "cwd" : 123 } )
2021-09-24 11:43:46 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to launch: invalid debug configuration - cannot unmarshal number into \"cwd\" of type string" )
2021-03-23 03:06:09 +00:00
2020-02-15 19:52:53 +00:00
// Skip detailed message checks for potentially different OS-specific errors.
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "exec" , fixture . Path + "_does_not_exist" , stopOnEntry )
2021-09-23 04:12:48 +00:00
checkFailedToLaunch ( client . ExpectVisibleErrorResponse ( t ) ) // No such file or directory
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "debug" , fixture . Path + "_does_not_exist" , stopOnEntry )
2021-05-17 16:13:25 +00:00
oe := client . ExpectOutputEvent ( t )
if ! strings . HasPrefix ( oe . Body . Output , "Build Error: " ) || oe . Body . Category != "stderr" {
t . Errorf ( "got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"" , oe )
}
2021-05-26 15:15:39 +00:00
checkFailedToLaunch ( client . ExpectInvisibleErrorResponse ( t ) )
2021-03-08 17:42:54 +00:00
2021-08-26 12:42:58 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } {
"request" : "launch" ,
/* mode: debug by default*/
"program" : fixture . Path + "_does_not_exist" ,
"stopOnEntry" : stopOnEntry ,
} )
2021-05-17 16:13:25 +00:00
oe = client . ExpectOutputEvent ( t )
if ! strings . HasPrefix ( oe . Body . Output , "Build Error: " ) || oe . Body . Category != "stderr" {
t . Errorf ( "got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"" , oe )
}
2021-05-26 15:15:39 +00:00
checkFailedToLaunch ( client . ExpectInvisibleErrorResponse ( t ) )
2020-02-15 19:52:53 +00:00
2020-03-04 17:22:51 +00:00
client . LaunchRequest ( "exec" , fixture . Source , stopOnEntry )
2021-09-23 04:12:48 +00:00
checkFailedToLaunch ( client . ExpectVisibleErrorResponse ( t ) ) // Not an executable
2020-02-15 19:52:53 +00:00
2021-04-26 17:31:59 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "buildFlags" : "-bad -flags" } )
2021-05-17 16:13:25 +00:00
oe = client . ExpectOutputEvent ( t )
if ! strings . HasPrefix ( oe . Body . Output , "Build Error: " ) || oe . Body . Category != "stderr" {
t . Errorf ( "got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"" , oe )
}
2021-05-26 15:15:39 +00:00
checkFailedToLaunchWithMessage ( client . ExpectInvisibleErrorResponse ( t ) , "Failed to launch: Build error: Check the debug console for details." )
2021-04-26 17:31:59 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "noDebug" : true , "buildFlags" : "-bad -flags" } )
2021-05-17 16:13:25 +00:00
oe = client . ExpectOutputEvent ( t )
if ! strings . HasPrefix ( oe . Body . Output , "Build Error: " ) || oe . Body . Category != "stderr" {
t . Errorf ( "got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"" , oe )
}
2021-05-26 15:15:39 +00:00
checkFailedToLaunchWithMessage ( client . ExpectInvisibleErrorResponse ( t ) , "Failed to launch: Build error: Check the debug console for details." )
dap: change how noDebug launch request is served (#2407)
* dap: change how noDebug launch request is served
PR #2400 added support for noDebug launch requests - that was done
by directly starting the target program using os/exec.Cmd and blocking
the launch request indefinitely until the program terminates or
is stopped with a disconnect request (when dlv dap can support it).
Even though the approach seemed to work for user, that adds an extra
control flow and complexity to the codebase.
This change takes a different approach to implement the noDebug
launch feature. Instead of using os/exec.Cmd, this uses the existing
debug launch path, but avoids setting breakpoints. Finally, the program
will start upon receiving onConfigurationDoneRequest and blocks there
until the program terminates. This simplifies the code.
The DAP spec does not explicitly specify what to do about other
requests. It seems like VSCode can issue evaluate requests or other
requests after the configuration done request. Currently they are
blocked because the program is in running state. We can consider checking
s.noDebug and responding with an error in the future if we want/need to.
See the log below for a typical DAP request/response sequence:
2021-03-29T01:42:53-04:00 debug layer=dap building binary at /Users/hakim/projects/s/__debug_bin
2021-03-29T01:42:55-04:00 info layer=debugger launching process with args: [/Users/hakim/projects/s/__debug_bin]
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"initialized"}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":2,"success":true,"command":"launch"}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":3,"type":"request","command":"setBreakpoints","arguments":{"source":{"name":"main.go","path":"/Users/hakim/projects/s/main.go"},"breakpoints":[{"line":9}],"lines":[9]}}
2021-03-29T01:42:55-04:00 error layer=dap Unable to set or clear breakpoints: running in noDebug mode
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":3,"success":false,"command":"setBreakpoints","message":"Unable to set or clear breakpoints","body":{"error":{"id":2002,"format":"Unable to set or clear breakpoints: running in noDebug mode"}}}
2021-03-29T01:42:55-04:00 debug layer=dap [<- from client]{"seq":4,"type":"request","command":"configurationDone","arguments":{}}
2021-03-29T01:42:55-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":4,"success":true,"command":"configurationDone"}
2021-03-29T01:42:55-04:00 debug layer=debugger continuing
Hello
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"event","event":"terminated","body":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":5,"type":"request","command":"threads"}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":5,"success":true,"command":"threads","body":{"threads":null}}
2021-03-29T01:43:00-04:00 debug layer=dap [<- from client]{"seq":6,"type":"request","command":"disconnect","arguments":{}}
2021-03-29T01:43:00-04:00 debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":6,"success":true,"command":"disconnect"}
2021-03-29T01:43:00-04:00 debug layer=debugger halting
2021-03-29T01:43:00-04:00 error layer=dap Process 27219 has exited with status 0
2021-03-29T01:43:00-04:00 debug layer=debugger detaching
Updates https://github.com/golang/vscode-go/issues/1111
* service/dap: address polina's comments for noDebug logic
Reworked so the noDebug launch request again blocks launch request
handler after responding with the launch response. By skipping
the initialized event, it should prevent well-behaving clients from
sending any further requests. Currently, doesn't matter because
the handler goroutine will be blocked until the program termination
anyway.
Placed mutex back, since I noticed that's the only way to prevent
racing between Stop and the handler goroutine. The synchronization
lotic (in particular, during teardown) should be revisited after
asynchronous request support work is done.
* dap: address more comments
2021-04-05 18:44:02 +00:00
2021-07-18 09:37:41 +00:00
// Bad "cwd"
2021-04-20 07:40:34 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "noDebug" : false , "cwd" : "dir/invalid" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunch ( client . ExpectVisibleErrorResponse ( t ) ) // invalid directory, the error message is system-dependent.
2021-04-20 07:40:34 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "noDebug" : true , "cwd" : "dir/invalid" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunch ( client . ExpectVisibleErrorResponse ( t ) ) // invalid directory, the error message is system-dependent.
2021-03-05 09:07:23 +00:00
2021-08-26 12:42:58 +00:00
// Bad "noDebug"
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "debug" , "program" : fixture . Source , "noDebug" : "true" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) , "Failed to launch: invalid debug configuration - cannot unmarshal string into \"noDebug\" of type bool" )
2021-07-23 21:04:53 +00:00
// Bad "replay" parameters
// These errors come from dap layer
2021-07-21 14:38:04 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "replay" , "traceDirPath" : "" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-21 14:38:04 +00:00
"Failed to launch: The 'traceDirPath' attribute is missing in debug configuration." )
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "replay" , "program" : fixture . Source , "traceDirPath" : "" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-21 14:38:04 +00:00
"Failed to launch: The 'traceDirPath' attribute is missing in debug configuration." )
2021-07-23 21:04:53 +00:00
// These errors come from debugger layer
if _ , err := exec . LookPath ( "rr" ) ; err != nil {
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "replay" , "backend" : "ignored" , "traceDirPath" : ".." } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-23 21:04:53 +00:00
"Failed to launch: backend unavailable" )
}
// Bad "core" parameters
// These errors come from dap layer
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "core" , "coreFilePath" : "" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-23 21:04:53 +00:00
"Failed to launch: The program attribute is missing in debug configuration." )
2021-07-21 14:38:04 +00:00
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "core" , "program" : fixture . Source , "coreFilePath" : "" } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-21 14:38:04 +00:00
"Failed to launch: The 'coreFilePath' attribute is missing in debug configuration." )
2021-07-23 21:04:53 +00:00
// These errors come from debugger layer
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : "core" , "backend" : "ignored" , "program" : fixture . Source , "coreFilePath" : fixture . Source } )
2021-09-23 04:12:48 +00:00
checkFailedToLaunchWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-23 21:04:53 +00:00
"Failed to launch: unrecognized core format" )
2021-07-21 14:38:04 +00:00
2020-02-15 19:52:53 +00:00
// We failed to launch the program. Make sure shutdown still works.
client . DisconnectRequest ( )
2020-02-24 17:36:34 +00:00
dresp := client . ExpectDisconnectResponse ( t )
2020-03-04 17:22:51 +00:00
if dresp . RequestSeq != seqCnt {
t . Errorf ( "got %#v, want RequestSeq=%d" , dresp , seqCnt )
2020-02-24 17:36:34 +00:00
}
2020-02-15 19:52:53 +00:00
} )
}
2020-02-27 04:45:48 +00:00
2020-12-28 17:14:15 +00:00
func TestBadAttachRequest ( t * testing . T ) {
runTest ( t , "loopprog" , func ( client * daptest . Client , fixture protest . Fixture ) {
seqCnt := 1
2021-05-26 15:15:39 +00:00
checkFailedToAttach := func ( response * dap . ErrorResponse ) {
2020-12-28 17:14:15 +00:00
t . Helper ( )
if response . RequestSeq != seqCnt {
t . Errorf ( "RequestSeq got %d, want %d" , seqCnt , response . RequestSeq )
}
if response . Command != "attach" {
t . Errorf ( "Command got %q, want \"attach\"" , response . Command )
}
if response . Message != "Failed to attach" {
t . Errorf ( "Message got %q, want \"Failed to attach\"" , response . Message )
}
if response . Body . Error . Id != 3001 {
t . Errorf ( "Id got %d, want 3001" , response . Body . Error . Id )
}
seqCnt ++
}
2021-05-26 15:15:39 +00:00
checkFailedToAttachWithMessage := func ( response * dap . ErrorResponse , errmsg string ) {
2020-12-28 17:14:15 +00:00
t . Helper ( )
2021-05-26 15:15:39 +00:00
checkFailedToAttach ( response )
2020-12-28 17:14:15 +00:00
if response . Body . Error . Format != errmsg {
t . Errorf ( "\ngot %q\nwant %q" , response . Body . Error . Format , errmsg )
}
}
2021-03-08 17:42:54 +00:00
// Bad "mode"
2020-12-28 17:14:15 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "blah blah blah" } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to attach: invalid debug configuration - unsupported 'mode' attribute \"blah blah blah\"" )
2020-12-28 17:14:15 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : 123 } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to attach: invalid debug configuration - cannot unmarshal number into \"mode\" of type string" )
2020-12-28 17:14:15 +00:00
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "" } ) // empty mode defaults to "local" (not an error)
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-03-08 17:42:54 +00:00
"Failed to attach: The 'processId' attribute is missing in debug configuration" )
client . AttachRequest ( map [ string ] interface { } { } ) // no mode defaults to "local" (not an error)
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-03-08 17:42:54 +00:00
"Failed to attach: The 'processId' attribute is missing in debug configuration" )
// Bad "processId"
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2020-12-28 17:14:15 +00:00
"Failed to attach: The 'processId' attribute is missing in debug configuration" )
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : nil } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2020-12-28 17:14:15 +00:00
"Failed to attach: The 'processId' attribute is missing in debug configuration" )
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : 0 } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2020-12-28 17:14:15 +00:00
"Failed to attach: The 'processId' attribute is missing in debug configuration" )
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : "1" } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to attach: invalid debug configuration - cannot unmarshal string into \"processId\" of type int" )
2020-12-28 17:14:15 +00:00
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : 1 } )
2020-12-28 17:14:15 +00:00
// The exact message varies on different systems, so skip that check
2021-09-23 04:12:48 +00:00
checkFailedToAttach ( client . ExpectVisibleErrorResponse ( t ) ) // could not attach to pid 1
2020-12-28 17:14:15 +00:00
// This will make debugger.(*Debugger) panic, which we will catch as an internal error.
2021-03-08 17:42:54 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : - 1 } )
2021-05-06 09:11:45 +00:00
er := client . ExpectInvisibleErrorResponse ( t )
2020-12-28 17:14:15 +00:00
if er . RequestSeq != seqCnt {
t . Errorf ( "RequestSeq got %d, want %d" , seqCnt , er . RequestSeq )
}
seqCnt ++
if er . Command != "" {
t . Errorf ( "Command got %q, want \"attach\"" , er . Command )
}
if er . Body . Error . Format != "Internal Error: runtime error: index out of range [0] with length 0" {
t . Errorf ( "Message got %q, want \"Internal Error: runtime error: index out of range [0] with length 0\"" , er . Message )
}
if er . Body . Error . Id != 8888 {
t . Errorf ( "Id got %d, want 8888" , er . Body . Error . Id )
}
2021-07-18 09:37:41 +00:00
// Bad "backend"
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : 1 , "backend" : 123 } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-08-26 12:42:58 +00:00
"Failed to attach: invalid debug configuration - cannot unmarshal number into \"backend\" of type string" )
2021-09-23 04:12:48 +00:00
2021-07-18 09:37:41 +00:00
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : 1 , "backend" : "foo" } )
2021-09-23 04:12:48 +00:00
checkFailedToAttachWithMessage ( client . ExpectVisibleErrorResponse ( t ) ,
2021-07-18 09:37:41 +00:00
"Failed to attach: could not attach to pid 1: unknown backend \"foo\"" )
// We failed to attach to the program. Make sure shutdown still works.
2020-12-28 17:14:15 +00:00
client . DisconnectRequest ( )
dresp := client . ExpectDisconnectResponse ( t )
if dresp . RequestSeq != seqCnt {
t . Errorf ( "got %#v, want RequestSeq=%d" , dresp , seqCnt )
}
} )
}
2021-09-24 11:43:46 +00:00
// TODO(polina): also add launchDebuggerWithTargetRunning
func launchDebuggerWithTargetHalted ( t * testing . T , fixture string ) * debugger . Debugger {
t . Helper ( )
bin := protest . BuildFixture ( fixture , protest . AllNonOptimized )
cfg := service . Config {
ProcessArgs : [ ] string { bin . Path } ,
Debugger : debugger . Config { Backend : "default" } ,
}
dbg , err := debugger . New ( & cfg . Debugger , cfg . ProcessArgs ) // debugger halts process on entry
if err != nil {
t . Fatal ( "failed to start debugger:" , err )
}
return dbg
}
// runTestWithDebugger starts the server and sets its debugger, initializes a debug session,
// runs test, then disconnects. Expects the process at the end of test() to be halted
func runTestWithDebugger ( t * testing . T , dbg * debugger . Debugger , test func ( c * daptest . Client ) ) {
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
server , _ := startDAPServer ( t , serverStopped )
2021-09-24 11:43:46 +00:00
client := daptest . NewClient ( server . listener . Addr ( ) . String ( ) )
2021-10-04 16:13:17 +00:00
time . Sleep ( 100 * time . Millisecond ) // Give time for connection to be set as dap.Session
// TODO(polina): update once the server interface is refactored to take debugger as arg
server . sessionMu . Lock ( )
if server . session == nil {
2021-10-07 09:27:04 +00:00
t . Fatal ( "DAP session is not ready" )
2021-10-04 16:13:17 +00:00
}
server . session . debugger = dbg
server . sessionMu . Unlock ( )
2021-09-24 11:43:46 +00:00
defer client . Close ( )
client . InitializeRequest ( )
client . ExpectInitializeResponseAndCapabilities ( t )
test ( client )
client . DisconnectRequest ( )
if server . config . AcceptMulti {
// TODO(polina): support multi-client mode
t . Fatal ( "testing of accept-multiclient not yet supporteed" )
} else if server . config . Debugger . AttachPid == 0 { // launched target
client . ExpectOutputEventDetachingKill ( t )
} else { // attached to target
client . ExpectOutputEventDetachingNoKill ( t )
}
client . ExpectDisconnectResponse ( t )
client . ExpectTerminatedEvent ( t )
<- serverStopped
}
func TestAttachRemoteToHaltedTargetStopOnEntry ( t * testing . T ) {
// Halted + stop on entry
runTestWithDebugger ( t , launchDebuggerWithTargetHalted ( t , "increment" ) , func ( client * daptest . Client ) {
client . AttachRequest ( map [ string ] interface { } { "mode" : "remote" , "stopOnEntry" : true } )
client . ExpectInitializedEvent ( t )
client . ExpectAttachResponse ( t )
client . ConfigurationDoneRequest ( )
client . ExpectStoppedEvent ( t )
client . ExpectConfigurationDoneResponse ( t )
} )
}
func TestAttachRemoteToHaltedTargetContinueOnEntry ( t * testing . T ) {
// Halted + continue on entry
runTestWithDebugger ( t , launchDebuggerWithTargetHalted ( t , "http_server" ) , func ( client * daptest . Client ) {
client . AttachRequest ( map [ string ] interface { } { "mode" : "remote" , "stopOnEntry" : false } )
client . ExpectInitializedEvent ( t )
client . ExpectAttachResponse ( t )
client . ConfigurationDoneRequest ( )
client . ExpectConfigurationDoneResponse ( t )
// Continuing
time . Sleep ( time . Second )
// Halt to make the disconnect sequence more predictable.
client . PauseRequest ( 1 )
expectPauseResponseAndStoppedEvent ( t , client )
} )
}
// TODO(polina): Running + stop/continue on entry
func TestLaunchAttachErrorWhenDebugInProgress ( t * testing . T ) {
runTestWithDebugger ( t , launchDebuggerWithTargetHalted ( t , "increment" ) , func ( client * daptest . Client ) {
client . AttachRequest ( map [ string ] interface { } { "mode" : "local" , "processId" : 100 } )
er := client . ExpectVisibleErrorResponse ( t )
msg := "Failed to attach: debugger already started - use remote mode to connect"
if er . Body . Error . Id != 3001 || er . Body . Error . Format != msg {
t . Errorf ( "got %#v, want Id=3001 Format=%q" , er , msg )
}
tests := [ ] string { "debug" , "test" , "exec" , "replay" , "core" }
for _ , mode := range tests {
t . Run ( mode , func ( t * testing . T ) {
client . LaunchRequestWithArgs ( map [ string ] interface { } { "mode" : mode } )
er := client . ExpectVisibleErrorResponse ( t )
msg := "Failed to launch: debugger already started - use remote attach to connect to a server with an active debug session"
if er . Body . Error . Id != 3000 || er . Body . Error . Format != msg {
t . Errorf ( "got %#v, want Id=3001 Format=%q" , er , msg )
}
} )
}
} )
}
2021-05-06 07:56:29 +00:00
func TestBadInitializeRequest ( t * testing . T ) {
runInitializeTest := func ( args dap . InitializeRequestArguments , err string ) {
t . Helper ( )
// Only one initialize request is allowed, so use a new server
// for each test.
2021-09-01 16:59:51 +00:00
serverStopped := make ( chan struct { } )
2021-10-07 09:27:04 +00:00
client := startDAPServerWithClient ( t , serverStopped )
2021-05-06 07:56:29 +00:00
defer client . Close ( )
client . InitializeRequestWithArgs ( args )
response := client . ExpectErrorResponse ( t )
if response . Command != "initialize" {
t . Errorf ( "Command got %q, want \"launch\"" , response . Command )
}
if response . Message != "Failed to initialize" {
t . Errorf ( "Message got %q, want \"Failed to launch\"" , response . Message )
}
if response . Body . Error . Id != 3002 {
t . Errorf ( "Id got %d, want 3002" , response . Body . Error . Id )
}
if response . Body . Error . Format != err {
t . Errorf ( "\ngot %q\nwant %q" , response . Body . Error . Format , err )
}
2021-09-01 16:59:51 +00:00
client . DisconnectRequest ( )
client . ExpectDisconnectResponse ( t )
<- serverStopped
2021-05-06 07:56:29 +00:00
}
// Bad path format.
runInitializeTest ( dap . InitializeRequestArguments {
AdapterID : "go" ,
PathFormat : "url" , // unsupported 'pathFormat'
LinesStartAt1 : true ,
ColumnsStartAt1 : true ,
Locale : "en-us" ,
} ,
"Failed to initialize: Unsupported 'pathFormat' value 'url'." ,
)
// LinesStartAt1 must be true.
runInitializeTest ( dap . InitializeRequestArguments {
AdapterID : "go" ,
PathFormat : "path" ,
LinesStartAt1 : false , // only 1-based line numbers are supported
ColumnsStartAt1 : true ,
Locale : "en-us" ,
} ,
"Failed to initialize: Only 1-based line numbers are supported." ,
)
// ColumnsStartAt1 must be true.
runInitializeTest ( dap . InitializeRequestArguments {
AdapterID : "go" ,
PathFormat : "path" ,
LinesStartAt1 : true ,
ColumnsStartAt1 : false , // only 1-based column numbers are supported
Locale : "en-us" ,
} ,
"Failed to initialize: Only 1-based column numbers are supported." ,
)
}
2020-02-27 04:45:48 +00:00
func TestBadlyFormattedMessageToServer ( t * testing . T ) {
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
// Send a badly formatted message to the server, and expect it to close the
// connection.
2021-07-08 05:27:54 +00:00
client . BadRequest ( )
2020-02-27 04:45:48 +00:00
time . Sleep ( 100 * time . Millisecond )
_ , err := client . ReadMessage ( )
if err != io . EOF {
t . Errorf ( "got err=%v, want io.EOF" , err )
}
} )
2021-07-08 05:27:54 +00:00
runTest ( t , "increment" , func ( client * daptest . Client , fixture protest . Fixture ) {
// Send an unknown request message to the server, and expect it to send
// an error response.
client . UnknownRequest ( )
err := client . ExpectErrorResponse ( t )
if err . Body . Error . Format != "Internal Error: Request command 'unknown' is not supported (seq: 1)" || err . RequestSeq != 1 {
t . Errorf ( "got %v, want RequestSeq=1 Error=\"Internal Error: Request command 'unknown' is not supported (seq: 1)\"" , err )
}
// Make sure that the unknown request did not kill the server.
client . InitializeRequest ( )
client . ExpectInitializeResponse ( t )
2021-09-01 16:59:51 +00:00
client . DisconnectRequest ( )
client . ExpectDisconnectResponse ( t )
2021-07-08 05:27:54 +00:00
} )
2020-02-27 04:45:48 +00:00
}