github.com/emad-elsaid/delve@v0.0.0-20220828190829-96212cb354b1/service/dap/server_test.go (about) 1 package dap 2 3 import ( 4 "bufio" 5 "flag" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "math" 10 "math/rand" 11 "net" 12 "os" 13 "os/exec" 14 "path/filepath" 15 "reflect" 16 "regexp" 17 "runtime" 18 "strconv" 19 "strings" 20 "testing" 21 "time" 22 23 "github.com/emad-elsaid/delve/pkg/goversion" 24 "github.com/emad-elsaid/delve/pkg/logflags" 25 "github.com/emad-elsaid/delve/pkg/proc" 26 protest "github.com/emad-elsaid/delve/pkg/proc/test" 27 "github.com/emad-elsaid/delve/service" 28 "github.com/emad-elsaid/delve/service/api" 29 "github.com/emad-elsaid/delve/service/dap/daptest" 30 "github.com/emad-elsaid/delve/service/debugger" 31 "github.com/google/go-dap" 32 ) 33 34 const stopOnEntry bool = true 35 const hasChildren bool = true 36 const noChildren bool = false 37 38 const localsScope = 1000 39 const globalsScope = 1001 40 41 var testBackend string 42 43 func TestMain(m *testing.M) { 44 logOutputVal := "" 45 if _, isTeamCityTest := os.LookupEnv("TEAMCITY_VERSION"); isTeamCityTest { 46 logOutputVal = "debugger,dap" 47 } 48 var logOutput string 49 flag.StringVar(&logOutput, "log-output", logOutputVal, "configures log output") 50 flag.Parse() 51 logflags.Setup(logOutput != "", logOutput, "") 52 protest.DefaultTestBackend(&testBackend) 53 os.Exit(protest.RunTestsWithFixtures(m)) 54 } 55 56 // name is for _fixtures/<name>.go 57 func runTest(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture)) { 58 runTestBuildFlags(t, name, test, protest.AllNonOptimized, false) 59 } 60 61 // name is for _fixtures/<name>.go 62 func runTestBuildFlags(t *testing.T, name string, test func(c *daptest.Client, f protest.Fixture), buildFlags protest.BuildFlags, defaultDebugInfoDirs bool) { 63 fixture := protest.BuildFixture(name, buildFlags) 64 65 // Start the DAP server. 66 serverStopped := make(chan struct{}) 67 client := startDAPServerWithClient(t, defaultDebugInfoDirs, serverStopped) 68 defer client.Close() 69 70 test(client, fixture) 71 <-serverStopped 72 } 73 74 func startDAPServerWithClient(t *testing.T, defaultDebugInfoDirs bool, serverStopped chan struct{}) *daptest.Client { 75 server, _ := startDAPServer(t, defaultDebugInfoDirs, serverStopped) 76 client := daptest.NewClient(server.config.Listener.Addr().String()) 77 return client 78 } 79 80 // Starts an empty server and a stripped down config just to establish a client connection. 81 // To mock a server created by dap.NewServer(config) or serving dap.NewSession(conn, config, debugger) 82 // set those arg fields manually after the server creation. 83 func startDAPServer(t *testing.T, defaultDebugInfoDirs bool, serverStopped chan struct{}) (server *Server, forceStop chan struct{}) { 84 // Start the DAP server. 85 listener, err := net.Listen("tcp", ":0") 86 if err != nil { 87 t.Fatal(err) 88 } 89 debugInfoDirs := []string{} 90 if defaultDebugInfoDirs { 91 debugInfoDirs = []string{"/usr/lib/debug/.build-id"} 92 } 93 disconnectChan := make(chan struct{}) 94 server = NewServer(&service.Config{ 95 Listener: listener, 96 DisconnectChan: disconnectChan, 97 Debugger: debugger.Config{DebugInfoDirectories: debugInfoDirs}, 98 }) 99 server.Run() 100 // Give server time to start listening for clients 101 time.Sleep(100 * time.Millisecond) 102 103 // Run a goroutine that stops the server when disconnectChan is signaled. 104 // This helps us test that certain events cause the server to stop as 105 // expected. 106 forceStop = make(chan struct{}) 107 go func() { 108 defer func() { 109 if serverStopped != nil { 110 close(serverStopped) 111 } 112 }() 113 select { 114 case <-disconnectChan: 115 t.Log("server stop triggered internally") 116 case <-forceStop: 117 t.Log("server stop triggered externally") 118 } 119 server.Stop() 120 }() 121 122 return server, forceStop 123 } 124 125 func verifyServerStopped(t *testing.T, server *Server) { 126 t.Helper() 127 if server.listener != nil { 128 if server.listener.Close() == nil { 129 t.Error("server should have closed listener after shutdown") 130 } 131 } 132 verifySessionStopped(t, server.session) 133 } 134 135 func verifySessionStopped(t *testing.T, session *Session) { 136 t.Helper() 137 if session == nil { 138 return 139 } 140 if session.conn == nil { 141 t.Error("session must always have a set connection") 142 } 143 verifyConnStopped(t, session.conn) 144 if session.debugger != nil { 145 t.Error("session should have no pointer to debugger after shutdown") 146 } 147 if session.binaryToRemove != "" { 148 t.Error("session should have no binary to remove after shutdown") 149 } 150 } 151 152 func verifyConnStopped(t *testing.T, conn io.ReadWriteCloser) { 153 t.Helper() 154 if conn.Close() == nil { 155 t.Error("client connection should be closed after shutdown") 156 } 157 } 158 159 func TestStopNoClient(t *testing.T) { 160 for name, triggerStop := range map[string]func(s *Server, forceStop chan struct{}){ 161 "force": func(s *Server, forceStop chan struct{}) { close(forceStop) }, 162 "accept error": func(s *Server, forceStop chan struct{}) { s.config.Listener.Close() }, 163 } { 164 t.Run(name, func(t *testing.T) { 165 serverStopped := make(chan struct{}) 166 server, forceStop := startDAPServer(t, false, serverStopped) 167 triggerStop(server, forceStop) 168 <-serverStopped 169 verifyServerStopped(t, server) 170 }) 171 } 172 } 173 174 func TestStopNoTarget(t *testing.T) { 175 for name, triggerStop := range map[string]func(c *daptest.Client, forceStop chan struct{}){ 176 "force": func(c *daptest.Client, forceStop chan struct{}) { close(forceStop) }, 177 "read error": func(c *daptest.Client, forceStop chan struct{}) { c.Close() }, 178 "disconnect": func(c *daptest.Client, forceStop chan struct{}) { c.DisconnectRequest() }, 179 } { 180 t.Run(name, func(t *testing.T) { 181 serverStopped := make(chan struct{}) 182 server, forceStop := startDAPServer(t, false, serverStopped) 183 client := daptest.NewClient(server.config.Listener.Addr().String()) 184 defer client.Close() 185 186 client.InitializeRequest() 187 client.ExpectInitializeResponseAndCapabilities(t) 188 triggerStop(client, forceStop) 189 <-serverStopped 190 verifyServerStopped(t, server) 191 }) 192 } 193 } 194 195 func TestStopWithTarget(t *testing.T) { 196 for name, triggerStop := range map[string]func(c *daptest.Client, forceStop chan struct{}){ 197 "force": func(c *daptest.Client, forceStop chan struct{}) { close(forceStop) }, 198 "read error": func(c *daptest.Client, forceStop chan struct{}) { c.Close() }, 199 "disconnect before exit": func(c *daptest.Client, forceStop chan struct{}) { c.DisconnectRequest() }, 200 "disconnect after exit": func(c *daptest.Client, forceStop chan struct{}) { 201 c.ContinueRequest(1) 202 c.ExpectContinueResponse(t) 203 c.ExpectTerminatedEvent(t) 204 c.DisconnectRequest() 205 }, 206 } { 207 t.Run(name, func(t *testing.T) { 208 serverStopped := make(chan struct{}) 209 server, forceStop := startDAPServer(t, false, serverStopped) 210 client := daptest.NewClient(server.config.Listener.Addr().String()) 211 defer client.Close() 212 213 client.InitializeRequest() 214 client.ExpectInitializeResponseAndCapabilities(t) 215 fixture := protest.BuildFixture("increment", protest.AllNonOptimized) 216 client.LaunchRequest("debug", fixture.Source, stopOnEntry) 217 client.ExpectInitializedEvent(t) 218 client.ExpectLaunchResponse(t) 219 triggerStop(client, forceStop) 220 <-serverStopped 221 verifyServerStopped(t, server) 222 }) 223 } 224 } 225 226 func TestSessionStop(t *testing.T) { 227 verifySessionState := func(t *testing.T, s *Session, binaryToRemoveSet bool, debuggerSet bool, disconnectChanSet bool) { 228 t.Helper() 229 if binaryToRemoveSet && s.binaryToRemove == "" || !binaryToRemoveSet && s.binaryToRemove != "" { 230 t.Errorf("binaryToRemove: got %s, want set=%v", s.binaryToRemove, binaryToRemoveSet) 231 } 232 if debuggerSet && s.debugger == nil || !debuggerSet && s.debugger != nil { 233 t.Errorf("debugger: got %v, want set=%v", s.debugger, debuggerSet) 234 } 235 if disconnectChanSet && s.config.DisconnectChan == nil || !disconnectChanSet && s.config.DisconnectChan != nil { 236 t.Errorf("disconnectChan: got %v, want set=%v", s.config.DisconnectChan, disconnectChanSet) 237 } 238 } 239 for name, stopSession := range map[string]func(s *Session, c *daptest.Client, serveDone chan struct{}){ 240 "force": func(s *Session, c *daptest.Client, serveDone chan struct{}) { 241 s.Close() 242 <-serveDone 243 verifySessionState(t, s, false /*binaryToRemoveSet*/, false /*debuggerSet*/, false /*disconnectChanSet*/) 244 }, 245 "read error": func(s *Session, c *daptest.Client, serveDone chan struct{}) { 246 c.Close() 247 <-serveDone 248 verifyConnStopped(t, s.conn) 249 verifySessionState(t, s, true /*binaryToRemoveSet*/, true /*debuggerSet*/, false /*disconnectChanSet*/) 250 s.Close() 251 }, 252 "disconnect before exit": func(s *Session, c *daptest.Client, serveDone chan struct{}) { 253 c.DisconnectRequest() 254 <-serveDone 255 verifyConnStopped(t, s.conn) 256 verifySessionState(t, s, true /*binaryToRemoveSet*/, false /*debuggerSet*/, false /*disconnectChanSet*/) 257 s.Close() 258 }, 259 "disconnect after exit": func(s *Session, c *daptest.Client, serveDone chan struct{}) { 260 c.ContinueRequest(1) 261 c.ExpectContinueResponse(t) 262 c.ExpectTerminatedEvent(t) 263 c.DisconnectRequest() 264 <-serveDone 265 verifyConnStopped(t, s.conn) 266 verifySessionState(t, s, true /*binaryToRemoveSet*/, false /*debuggerSet*/, false /*disconnectChanSet*/) 267 s.Close() 268 }, 269 } { 270 t.Run(name, func(t *testing.T) { 271 listener, err := net.Listen("tcp", ":0") 272 if err != nil { 273 t.Fatalf("cannot setup listener required for testing: %v", err) 274 } 275 defer listener.Close() 276 acceptDone := make(chan struct{}) 277 var conn net.Conn 278 go func() { 279 conn, err = listener.Accept() 280 close(acceptDone) 281 }() 282 time.Sleep(10 * time.Millisecond) // give time to start listening 283 client := daptest.NewClient(listener.Addr().String()) 284 defer client.Close() 285 <-acceptDone 286 if err != nil { 287 t.Fatalf("cannot accept client requireed for testing: %v", err) 288 } 289 session := NewSession(conn, &Config{ 290 Config: &service.Config{DisconnectChan: make(chan struct{})}, 291 StopTriggered: make(chan struct{})}, nil) 292 serveDAPCodecDone := make(chan struct{}) 293 go func() { 294 session.ServeDAPCodec() 295 close(serveDAPCodecDone) 296 }() 297 time.Sleep(10 * time.Millisecond) // give time to start reading 298 client.InitializeRequest() 299 client.ExpectInitializeResponseAndCapabilities(t) 300 fixture := protest.BuildFixture("increment", protest.AllNonOptimized) 301 client.LaunchRequest("debug", fixture.Source, stopOnEntry) 302 client.ExpectInitializedEvent(t) 303 client.ExpectLaunchResponse(t) 304 stopSession(session, client, serveDAPCodecDone) 305 verifySessionStopped(t, session) 306 }) 307 } 308 } 309 310 func TestForceStopWhileStopping(t *testing.T) { 311 serverStopped := make(chan struct{}) 312 server, forceStop := startDAPServer(t, false, serverStopped) 313 client := daptest.NewClient(server.config.Listener.Addr().String()) 314 315 client.InitializeRequest() 316 client.ExpectInitializeResponseAndCapabilities(t) 317 fixture := protest.BuildFixture("increment", protest.AllNonOptimized) 318 client.LaunchRequest("exec", fixture.Path, stopOnEntry) 319 client.ExpectInitializedEvent(t) 320 client.Close() // depending on timing may trigger Stop() 321 time.Sleep(time.Microsecond) 322 close(forceStop) // depending on timing may trigger Stop() 323 <-serverStopped 324 verifyServerStopped(t, server) 325 } 326 327 // TestLaunchStopOnEntry emulates the message exchange that can be observed with 328 // VS Code for the most basic launch debug session with "stopOnEntry" enabled: 329 // 330 // User selects "Start Debugging": 1 >> initialize 331 // : 1 << initialize 332 // : 2 >> launch 333 // : << initialized event 334 // : 2 << launch 335 // : 3 >> setBreakpoints (empty) 336 // : 3 << setBreakpoints 337 // : 4 >> setExceptionBreakpoints (empty) 338 // : 4 << setExceptionBreakpoints 339 // : 5 >> configurationDone 340 // Program stops upon launching : << stopped event 341 // : 5 << configurationDone 342 // : 6 >> threads 343 // : 6 << threads (Dummy) 344 // : 7 >> threads 345 // : 7 << threads (Dummy) 346 // : 8 >> stackTrace 347 // : 8 << error (Unable to produce stack trace) 348 // : 9 >> stackTrace 349 // : 9 << error (Unable to produce stack trace) 350 // User evaluates bad expression : 10 >> evaluate 351 // : 10 << error (unable to find function context) 352 // User evaluates good expression: 11 >> evaluate 353 // : 11 << evaluate 354 // User selects "Continue" : 12 >> continue 355 // : 12 << continue 356 // Program runs to completion : << terminated event 357 // : 13 >> disconnect 358 // : << output event (Process exited) 359 // : << output event (Detaching) 360 // : 13 << disconnect 361 // 362 // This test exhaustively tests Seq and RequestSeq on all messages from the 363 // server. Other tests do not necessarily need to repeat all these checks. 364 func TestLaunchStopOnEntry(t *testing.T) { 365 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 366 // 1 >> initialize, << initialize 367 client.InitializeRequest() 368 initResp := client.ExpectInitializeResponseAndCapabilities(t) 369 if initResp.Seq != 0 || initResp.RequestSeq != 1 { 370 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp) 371 } 372 373 // 2 >> launch, << initialized, << launch 374 client.LaunchRequest("exec", fixture.Path, stopOnEntry) 375 initEvent := client.ExpectInitializedEvent(t) 376 if initEvent.Seq != 0 { 377 t.Errorf("\ngot %#v\nwant Seq=0", initEvent) 378 } 379 launchResp := client.ExpectLaunchResponse(t) 380 if launchResp.Seq != 0 || launchResp.RequestSeq != 2 { 381 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", launchResp) 382 } 383 384 // 3 >> setBreakpoints, << setBreakpoints 385 client.SetBreakpointsRequest(fixture.Source, nil) 386 sbpResp := client.ExpectSetBreakpointsResponse(t) 387 if sbpResp.Seq != 0 || sbpResp.RequestSeq != 3 || len(sbpResp.Body.Breakpoints) != 0 { 388 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0", sbpResp) 389 } 390 391 // 4 >> setExceptionBreakpoints, << setExceptionBreakpoints 392 client.SetExceptionBreakpointsRequest() 393 sebpResp := client.ExpectSetExceptionBreakpointsResponse(t) 394 if sebpResp.Seq != 0 || sebpResp.RequestSeq != 4 { 395 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=4", sebpResp) 396 } 397 398 // 5 >> configurationDone, << stopped, << configurationDone 399 client.ConfigurationDoneRequest() 400 stopEvent := client.ExpectStoppedEvent(t) 401 if stopEvent.Seq != 0 || 402 stopEvent.Body.Reason != "entry" || 403 stopEvent.Body.ThreadId != 1 || 404 !stopEvent.Body.AllThreadsStopped { 405 t.Errorf("\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}", stopEvent) 406 } 407 cdResp := client.ExpectConfigurationDoneResponse(t) 408 if cdResp.Seq != 0 || cdResp.RequestSeq != 5 { 409 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp) 410 } 411 412 // 6 >> threads, << threads 413 client.ThreadsRequest() 414 tResp := client.ExpectThreadsResponse(t) 415 if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 1 { 416 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1", tResp) 417 } 418 if tResp.Body.Threads[0].Id != 1 || tResp.Body.Threads[0].Name != "Dummy" { 419 t.Errorf("\ngot %#v\nwant Id=1, Name=\"Dummy\"", tResp) 420 } 421 422 // 7 >> threads, << threads 423 client.ThreadsRequest() 424 tResp = client.ExpectThreadsResponse(t) 425 if tResp.Seq != 0 || tResp.RequestSeq != 7 || len(tResp.Body.Threads) != 1 { 426 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7 len(Threads)=1", tResp) 427 } 428 429 // 8 >> stackTrace, << error 430 client.StackTraceRequest(1, 0, 20) 431 stResp := client.ExpectInvisibleErrorResponse(t) 432 if stResp.Seq != 0 || stResp.RequestSeq != 8 || stResp.Body.Error.Format != "Unable to produce stack trace: unknown goroutine 1" { 433 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Format=\"Unable to produce stack trace: unknown goroutine 1\"", stResp) 434 } 435 436 // 9 >> stackTrace, << error 437 client.StackTraceRequest(1, 0, 20) 438 stResp = client.ExpectInvisibleErrorResponse(t) 439 if stResp.Seq != 0 || stResp.RequestSeq != 9 || stResp.Body.Error.Id != UnableToProduceStackTrace { 440 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Id=%d", stResp, UnableToProduceStackTrace) 441 } 442 443 // 10 >> evaluate, << error 444 client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl") 445 erResp := client.ExpectInvisibleErrorResponse(t) 446 if erResp.Seq != 0 || erResp.RequestSeq != 10 || erResp.Body.Error.Id != UnableToEvaluateExpression { 447 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=%d", erResp, UnableToEvaluateExpression) 448 } 449 450 // 11 >> evaluate, << evaluate 451 client.EvaluateRequest("1+1", 0 /*no frame specified*/, "repl") 452 evResp := client.ExpectEvaluateResponse(t) 453 if evResp.Seq != 0 || evResp.RequestSeq != 11 || evResp.Body.Result != "2" { 454 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2", evResp) 455 } 456 457 // 12 >> continue, << continue, << terminated 458 client.ContinueRequest(1) 459 contResp := client.ExpectContinueResponse(t) 460 if contResp.Seq != 0 || contResp.RequestSeq != 12 || !contResp.Body.AllThreadsContinued { 461 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12 Body.AllThreadsContinued=true", contResp) 462 } 463 termEvent := client.ExpectTerminatedEvent(t) 464 if termEvent.Seq != 0 { 465 t.Errorf("\ngot %#v\nwant Seq=0", termEvent) 466 } 467 468 // 13 >> disconnect, << disconnect 469 client.DisconnectRequest() 470 oep := client.ExpectOutputEventProcessExited(t, 0) 471 if oep.Seq != 0 || oep.Body.Category != "console" { 472 t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oep) 473 } 474 oed := client.ExpectOutputEventDetaching(t) 475 if oed.Seq != 0 || oed.Body.Category != "console" { 476 t.Errorf("\ngot %#v\nwant Seq=0 Category='console'", oed) 477 } 478 dResp := client.ExpectDisconnectResponse(t) 479 if dResp.Seq != 0 || dResp.RequestSeq != 13 { 480 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=13", dResp) 481 } 482 client.ExpectTerminatedEvent(t) 483 }) 484 } 485 486 // TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request. 487 func TestAttachStopOnEntry(t *testing.T) { 488 if runtime.GOOS == "freebsd" { 489 t.SkipNow() 490 } 491 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 492 // Start the program to attach to 493 cmd := exec.Command(fixture.Path) 494 stdout, err := cmd.StdoutPipe() 495 if err != nil { 496 t.Fatal(err) 497 } 498 cmd.Stderr = os.Stderr 499 if err := cmd.Start(); err != nil { 500 t.Fatal(err) 501 } 502 // Wait for output. 503 // This will give the target process time to initialize the runtime before we attach, 504 // so we can rely on having goroutines when they are requested on attach. 505 scanOut := bufio.NewScanner(stdout) 506 scanOut.Scan() 507 if scanOut.Text() != "past main" { 508 t.Errorf("expected loopprog.go to output \"past main\"") 509 } 510 511 // 1 >> initialize, << initialize 512 client.InitializeRequest() 513 initResp := client.ExpectInitializeResponseAndCapabilities(t) 514 if initResp.Seq != 0 || initResp.RequestSeq != 1 { 515 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=1", initResp) 516 } 517 518 // 2 >> attach, << initialized, << attach 519 client.AttachRequest( 520 map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid, "stopOnEntry": true, "backend": "default"}) 521 client.ExpectCapabilitiesEventSupportTerminateDebuggee(t) 522 initEvent := client.ExpectInitializedEvent(t) 523 if initEvent.Seq != 0 { 524 t.Errorf("\ngot %#v\nwant Seq=0", initEvent) 525 } 526 attachResp := client.ExpectAttachResponse(t) 527 if attachResp.Seq != 0 || attachResp.RequestSeq != 2 { 528 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=2", attachResp) 529 } 530 531 // 3 >> setBreakpoints, << setBreakpoints 532 client.SetBreakpointsRequest(fixture.Source, nil) 533 sbpResp := client.ExpectSetBreakpointsResponse(t) 534 if sbpResp.Seq != 0 || sbpResp.RequestSeq != 3 || len(sbpResp.Body.Breakpoints) != 0 { 535 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=3, len(Breakpoints)=0", sbpResp) 536 } 537 538 // 4 >> setExceptionBreakpoints, << setExceptionBreakpoints 539 client.SetExceptionBreakpointsRequest() 540 sebpResp := client.ExpectSetExceptionBreakpointsResponse(t) 541 if sebpResp.Seq != 0 || sebpResp.RequestSeq != 4 { 542 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=4", sebpResp) 543 } 544 545 // 5 >> configurationDone, << stopped, << configurationDone 546 client.ConfigurationDoneRequest() 547 stopEvent := client.ExpectStoppedEvent(t) 548 if stopEvent.Seq != 0 || 549 stopEvent.Body.Reason != "entry" || 550 stopEvent.Body.ThreadId != 1 || 551 !stopEvent.Body.AllThreadsStopped { 552 t.Errorf("\ngot %#v\nwant Seq=0, Body={Reason=\"entry\", ThreadId=1, AllThreadsStopped=true}", stopEvent) 553 } 554 cdResp := client.ExpectConfigurationDoneResponse(t) 555 if cdResp.Seq != 0 || cdResp.RequestSeq != 5 { 556 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=5", cdResp) 557 } 558 559 // 6 >> threads, << threads 560 client.ThreadsRequest() 561 tResp := client.ExpectThreadsResponse(t) 562 // Expect main goroutine plus runtime at this point. 563 if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) < 2 { 564 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)>1", tResp) 565 } 566 567 // 7 >> threads, << threads 568 client.ThreadsRequest() 569 client.ExpectThreadsResponse(t) 570 571 // 8 >> stackTrace, << response 572 client.StackTraceRequest(1, 0, 20) 573 client.ExpectStackTraceResponse(t) 574 575 // 9 >> stackTrace, << response 576 client.StackTraceRequest(1, 0, 20) 577 client.ExpectStackTraceResponse(t) 578 579 // 10 >> evaluate, << error 580 client.EvaluateRequest("foo", 0 /*no frame specified*/, "repl") 581 erResp := client.ExpectInvisibleErrorResponse(t) 582 if erResp.Seq != 0 || erResp.RequestSeq != 10 || erResp.Body.Error.Id != UnableToEvaluateExpression { 583 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Id=%d", erResp, UnableToEvaluateExpression) 584 } 585 586 // 11 >> evaluate, << evaluate 587 client.EvaluateRequest("1+1", 0 /*no frame specified*/, "repl") 588 evResp := client.ExpectEvaluateResponse(t) 589 if evResp.Seq != 0 || evResp.RequestSeq != 11 || evResp.Body.Result != "2" { 590 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=10 Result=2", evResp) 591 } 592 593 // 12 >> continue, << continue 594 client.ContinueRequest(1) 595 cResp := client.ExpectContinueResponse(t) 596 if cResp.Seq != 0 || cResp.RequestSeq != 12 { 597 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=12", cResp) 598 } 599 600 // TODO(polina): once https://github.com/emad-elsaid/delve/issues/2259 is 601 // fixed, test with kill=false. 602 603 // 13 >> disconnect, << disconnect 604 client.DisconnectRequestWithKillOption(true) 605 606 // Disconnect consists of Halt + Detach. 607 // Halt interrupts command in progress, which triggers 608 // a stopped event in parallel with the disconnect 609 // sequence. It might arrive before or during the sequence 610 // or never if the server exits before it is sent. 611 msg := expectMessageFilterStopped(t, client) 612 client.CheckOutputEvent(t, msg) 613 msg = expectMessageFilterStopped(t, client) 614 client.CheckDisconnectResponse(t, msg) 615 client.ExpectTerminatedEvent(t) 616 617 // If this call to KeepAlive isn't here there's a chance that stdout will 618 // be garbage collected (since it is no longer alive long before this 619 // point), when that happens, on unix-like OSes, the read end of the pipe 620 // will be closed by the finalizer and the target process will die by 621 // SIGPIPE, which the rest of this test does not expect. 622 runtime.KeepAlive(stdout) 623 }) 624 } 625 626 // Like the test above, except the program is configured to continue on entry. 627 func TestContinueOnEntry(t *testing.T) { 628 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 629 // 1 >> initialize, << initialize 630 client.InitializeRequest() 631 client.ExpectInitializeResponseAndCapabilities(t) 632 633 // 2 >> launch, << initialized, << launch 634 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 635 client.ExpectInitializedEvent(t) 636 client.ExpectLaunchResponse(t) 637 638 // 3 >> setBreakpoints, << setBreakpoints 639 client.SetBreakpointsRequest(fixture.Source, nil) 640 client.ExpectSetBreakpointsResponse(t) 641 642 // 4 >> setExceptionBreakpoints, << setExceptionBreakpoints 643 client.SetExceptionBreakpointsRequest() 644 client.ExpectSetExceptionBreakpointsResponse(t) 645 646 // 5 >> configurationDone, << configurationDone 647 client.ConfigurationDoneRequest() 648 client.ExpectConfigurationDoneResponse(t) 649 // "Continue" happens behind the scenes on another goroutine 650 651 client.ExpectTerminatedEvent(t) 652 653 // 6 >> threads, << threads 654 client.ThreadsRequest() 655 tResp := client.ExpectThreadsResponse(t) 656 if tResp.Seq != 0 || tResp.RequestSeq != 6 || len(tResp.Body.Threads) != 1 { 657 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=6 len(Threads)=1", tResp) 658 } 659 if tResp.Body.Threads[0].Id != 1 || tResp.Body.Threads[0].Name != "Dummy" { 660 t.Errorf("\ngot %#v\nwant Id=1, Name=\"Dummy\"", tResp) 661 } 662 663 // 7 >> disconnect, << disconnect 664 client.DisconnectRequest() 665 client.ExpectOutputEventProcessExited(t, 0) 666 client.ExpectOutputEventDetaching(t) 667 dResp := client.ExpectDisconnectResponse(t) 668 if dResp.Seq != 0 || dResp.RequestSeq != 7 { 669 t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=7", dResp) 670 } 671 client.ExpectTerminatedEvent(t) 672 }) 673 } 674 675 // TestPreSetBreakpoint corresponds to a debug session that is configured to 676 // continue on entry with a pre-set breakpoint. 677 func TestPreSetBreakpoint(t *testing.T) { 678 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 679 client.InitializeRequest() 680 client.ExpectInitializeResponseAndCapabilities(t) 681 682 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 683 client.ExpectInitializedEvent(t) 684 client.ExpectLaunchResponse(t) 685 686 client.SetBreakpointsRequest(fixture.Source, []int{8}) 687 sResp := client.ExpectSetBreakpointsResponse(t) 688 if len(sResp.Body.Breakpoints) != 1 { 689 t.Errorf("got %#v, want len(Breakpoints)=1", sResp) 690 } 691 bkpt0 := sResp.Body.Breakpoints[0] 692 if !bkpt0.Verified || bkpt0.Line != 8 || bkpt0.Id != 1 || bkpt0.Source.Name != filepath.Base(fixture.Source) || bkpt0.Source.Path != fixture.Source { 693 t.Errorf("got breakpoints[0] = %#v, want Verified=true, Line=8, Id=1, Path=%q", bkpt0, fixture.Source) 694 } 695 696 client.SetExceptionBreakpointsRequest() 697 client.ExpectSetExceptionBreakpointsResponse(t) 698 699 client.ConfigurationDoneRequest() 700 client.ExpectConfigurationDoneResponse(t) 701 // This triggers "continue" on a separate goroutine 702 703 client.ThreadsRequest() 704 // Since we are in async mode while running, we might receive messages in either order. 705 for i := 0; i < 2; i++ { 706 msg := client.ExpectMessage(t) 707 switch m := msg.(type) { 708 case *dap.ThreadsResponse: 709 // If the thread request arrived while the program was running, we expect to get the dummy response 710 // with a single goroutine "Current". 711 // If the thread request arrived after the stop, we should get the goroutine stopped at main.Increment. 712 if (len(m.Body.Threads) != 1 || m.Body.Threads[0].Id != -1 || m.Body.Threads[0].Name != "Current") && 713 (len(m.Body.Threads) < 1 || m.Body.Threads[0].Id != 1 || !strings.HasPrefix(m.Body.Threads[0].Name, "* [Go 1] main.Increment")) { 714 t.Errorf("\ngot %#v\nwant Id=-1, Name=\"Current\" or Id=1, Name=\"* [Go 1] main.Increment ...\"", m.Body.Threads) 715 } 716 case *dap.StoppedEvent: 717 if m.Body.Reason != "breakpoint" || m.Body.ThreadId != 1 || !m.Body.AllThreadsStopped { 718 t.Errorf("got %#v, want Body={Reason=\"breakpoint\", ThreadId=1, AllThreadsStopped=true}", m) 719 } 720 default: 721 t.Fatalf("got %#v, want ThreadsResponse or StoppedEvent", m) 722 } 723 } 724 725 // Threads-StackTrace-Scopes-Variables request waterfall is 726 // triggered on stop event. 727 client.ThreadsRequest() 728 tResp := client.ExpectThreadsResponse(t) 729 if len(tResp.Body.Threads) < 2 { // 1 main + runtime 730 t.Errorf("\ngot %#v\nwant len(Threads)>1", tResp.Body.Threads) 731 } 732 reMain, _ := regexp.Compile(`\* \[Go 1\] main.Increment \(Thread [0-9]+\)`) 733 wantMain := dap.Thread{Id: 1, Name: "* [Go 1] main.Increment (Thread ...)"} 734 wantRuntime := dap.Thread{Id: 2, Name: "[Go 2] runtime.gopark"} 735 for _, got := range tResp.Body.Threads { 736 if got.Id != 1 && !reMain.MatchString(got.Name) && !(strings.Contains(got.Name, "runtime.") || strings.Contains(got.Name, "runtime/")) { 737 t.Errorf("\ngot %#v\nwant []dap.Thread{%#v, %#v, ...}", tResp.Body.Threads, wantMain, wantRuntime) 738 } 739 } 740 741 client.StackTraceRequest(1, 0, 20) 742 stResp := client.ExpectStackTraceResponse(t) 743 744 if stResp.Body.TotalFrames != 6 { 745 t.Errorf("\ngot %#v\nwant TotalFrames=6", stResp.Body.TotalFrames) 746 } 747 if len(stResp.Body.StackFrames) != 6 { 748 t.Errorf("\ngot %#v\nwant len(StackFrames)=6", stResp.Body.StackFrames) 749 } else { 750 checkFrame := func(got dap.StackFrame, id int, name string, sourceName string, line int) { 751 t.Helper() 752 if got.Id != id || got.Name != name { 753 t.Errorf("\ngot %#v\nwant Id=%d Name=%s", got, id, name) 754 } 755 if (sourceName != "" && got.Source.Name != sourceName) || (line > 0 && got.Line != line) { 756 t.Errorf("\ngot %#v\nwant Source.Name=%s Line=%d", got, sourceName, line) 757 } 758 } 759 checkFrame(stResp.Body.StackFrames[0], 1000, "main.Increment", "increment.go", 8) 760 checkFrame(stResp.Body.StackFrames[1], 1001, "main.Increment", "increment.go", 11) 761 checkFrame(stResp.Body.StackFrames[2], 1002, "main.Increment", "increment.go", 11) 762 checkFrame(stResp.Body.StackFrames[3], 1003, "main.main", "increment.go", 17) 763 checkFrame(stResp.Body.StackFrames[4], 1004, "runtime.main", "proc.go", -1) 764 checkFrame(stResp.Body.StackFrames[5], 1005, "runtime.goexit", "", -1) 765 } 766 767 client.ScopesRequest(1000) 768 scopes := client.ExpectScopesResponse(t) 769 if len(scopes.Body.Scopes) > 1 { 770 t.Errorf("\ngot %#v\nwant len(Scopes)=1 (Locals)", scopes) 771 } 772 checkScope(t, scopes, 0, "Locals", localsScope) 773 774 client.VariablesRequest(localsScope) 775 args := client.ExpectVariablesResponse(t) 776 checkChildren(t, args, "Locals", 2) 777 checkVarExact(t, args, 0, "y", "y", "0 = 0x0", "uint", noChildren) 778 checkVarExact(t, args, 1, "~r1", "", "0 = 0x0", "uint", noChildren) 779 780 client.ContinueRequest(1) 781 ctResp := client.ExpectContinueResponse(t) 782 if !ctResp.Body.AllThreadsContinued { 783 t.Errorf("\ngot %#v\nwant AllThreadsContinued=true", ctResp.Body) 784 } 785 // "Continue" is triggered after the response is sent 786 787 client.ExpectTerminatedEvent(t) 788 789 // Pause request after termination should result in an error. 790 // But in certain cases this request actually succeeds. 791 client.PauseRequest(1) 792 switch r := client.ExpectMessage(t).(type) { 793 case *dap.ErrorResponse: 794 if r.Message != "Unable to halt execution" { 795 t.Errorf("\ngot %#v\nwant Message='Unable to halt execution'", r) 796 } 797 case *dap.PauseResponse: 798 default: 799 t.Fatalf("Unexpected response type: expect error or pause, got %#v", r) 800 } 801 802 client.DisconnectRequest() 803 client.ExpectOutputEventProcessExited(t, 0) 804 client.ExpectOutputEventDetaching(t) 805 client.ExpectDisconnectResponse(t) 806 client.ExpectTerminatedEvent(t) 807 }) 808 } 809 810 // checkStackFramesExact is a helper for verifying the values within StackTraceResponse. 811 // 812 // wantStartName - name of the first returned frame (ignored if "") 813 // wantStartLine - file line of the first returned frame (ignored if <0). 814 // wantStartID - id of the first frame returned (ignored if wantFrames is 0). 815 // wantFrames - number of frames returned (length of StackTraceResponse.Body.StackFrames array). 816 // wantTotalFrames - total number of stack frames available (StackTraceResponse.Body.TotalFrames). 817 func checkStackFramesExact(t *testing.T, got *dap.StackTraceResponse, 818 wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int) { 819 t.Helper() 820 checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, true) 821 } 822 823 func TestFilterGoroutines(t *testing.T) { 824 tt := []struct { 825 name string 826 filter string 827 want []string 828 wantLen int 829 wantErr bool 830 }{ 831 { 832 name: "user goroutines", 833 filter: "-with user", 834 want: []string{"main.main", "main.agoroutine"}, 835 wantLen: 11, 836 }, 837 { 838 name: "filter by user loc", 839 filter: "-with userloc main.main", 840 want: []string{"main.main"}, 841 wantLen: 1, 842 }, 843 { 844 name: "multiple filters", 845 filter: "-with user -with userloc main.agoroutine", 846 want: []string{"main.agoroutine"}, 847 wantLen: 10, 848 }, 849 { 850 name: "system goroutines", 851 filter: "-without user", 852 want: []string{"runtime."}, 853 }, 854 // Filters that should return all goroutines. 855 { 856 name: "empty filter string", 857 filter: "", 858 want: []string{"main.main", "main.agoroutine", "runtime."}, 859 wantLen: -1, 860 }, 861 { 862 name: "bad filter string", 863 filter: "not parsable to filters", 864 want: []string{"main.main", "main.agoroutine", "runtime."}, 865 wantLen: -1, 866 wantErr: true, 867 }, 868 // Filters that should produce none. 869 { 870 name: "no match to user loc", 871 filter: "-with userloc main.NotAUserFrame", 872 want: []string{"Dummy"}, 873 wantLen: 1, 874 }, 875 { 876 name: "no match to user and not user", 877 filter: "-with user -without user", 878 want: []string{"Dummy"}, 879 wantLen: 1, 880 }, 881 } 882 runTest(t, "goroutinestackprog", func(client *daptest.Client, fixture protest.Fixture) { 883 runDebugSessionWithBPs(t, client, "launch", 884 // Launch 885 func() { 886 client.LaunchRequestWithArgs(map[string]interface{}{ 887 "mode": "exec", 888 "program": fixture.Path, 889 "stopOnEntry": !stopOnEntry}) 890 }, 891 // Set breakpoints 892 fixture.Source, []int{30}, 893 []onBreakpoint{{ 894 // Stop at line 30 895 execute: func() { 896 for _, tc := range tt { 897 command := fmt.Sprintf("dlv config goroutineFilters %s", tc.filter) 898 client.EvaluateRequest(command, 1000, "repl") 899 client.ExpectInvalidatedEvent(t) 900 client.ExpectEvaluateResponse(t) 901 902 client.ThreadsRequest() 903 if tc.wantErr { 904 client.ExpectOutputEvent(t) 905 } 906 tr := client.ExpectThreadsResponse(t) 907 if tc.wantLen > 0 && len(tr.Body.Threads) != tc.wantLen { 908 t.Errorf("got Threads=%#v, want Len=%d\n", tr.Body.Threads, tc.wantLen) 909 } 910 for i, frame := range tr.Body.Threads { 911 var found bool 912 for _, wantName := range tc.want { 913 if strings.Contains(frame.Name, wantName) { 914 found = true 915 break 916 } 917 } 918 if !found { 919 t.Errorf("got Threads[%d]=%#v, want Name=%v\n", i, frame, tc.want) 920 } 921 } 922 } 923 }, 924 disconnect: false, 925 }}) 926 927 }) 928 } 929 930 func checkStackFramesHasMore(t *testing.T, got *dap.StackTraceResponse, 931 wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int) { 932 t.Helper() 933 checkStackFramesNamed("", t, got, wantStartName, wantStartLine, wantStartID, wantFrames, wantTotalFrames, false) 934 } 935 func checkStackFramesNamed(testName string, t *testing.T, got *dap.StackTraceResponse, 936 wantStartName string, wantStartLine, wantStartID, wantFrames, wantTotalFrames int, totalExact bool) { 937 t.Helper() 938 if totalExact && got.Body.TotalFrames != wantTotalFrames { 939 t.Errorf("%s\ngot %#v\nwant TotalFrames=%d", testName, got.Body.TotalFrames, wantTotalFrames) 940 } else if !totalExact && got.Body.TotalFrames < wantTotalFrames { 941 t.Errorf("%s\ngot %#v\nwant TotalFrames>=%d", testName, got.Body.TotalFrames, wantTotalFrames) 942 } 943 944 if len(got.Body.StackFrames) != wantFrames { 945 t.Errorf("%s\ngot len(StackFrames)=%d\nwant %d", testName, len(got.Body.StackFrames), wantFrames) 946 } else { 947 // Verify that frame ids are consecutive numbers starting at wantStartID 948 for i := 0; i < wantFrames; i++ { 949 if got.Body.StackFrames[i].Id != wantStartID+i { 950 t.Errorf("%s\ngot %#v\nwant Id=%d", testName, got.Body.StackFrames[i], wantStartID+i) 951 } 952 } 953 // Verify the name and line corresponding to the first returned frame (if any). 954 // This is useful when the first frame is the frame corresponding to the breakpoint at 955 // a predefined line. Line values < 0 are a signal to skip the check (which can be useful 956 // for frames in the third-party code, where we do not control the lines). 957 if wantFrames > 0 && wantStartLine > 0 && got.Body.StackFrames[0].Line != wantStartLine { 958 t.Errorf("%s\ngot Line=%d\nwant %d", testName, got.Body.StackFrames[0].Line, wantStartLine) 959 } 960 if wantFrames > 0 && wantStartName != "" && got.Body.StackFrames[0].Name != wantStartName { 961 t.Errorf("%s\ngot Name=%s\nwant %s", testName, got.Body.StackFrames[0].Name, wantStartName) 962 } 963 } 964 } 965 966 // checkScope is a helper for verifying the values within a ScopesResponse. 967 // 968 // i - index of the scope within ScopesRespose.Body.Scopes array 969 // name - name of the scope 970 // varRef - reference to retrieve variables of this scope. If varRef is negative, the reference is not checked. 971 func checkScope(t *testing.T, got *dap.ScopesResponse, i int, name string, varRef int) { 972 t.Helper() 973 if len(got.Body.Scopes) <= i { 974 t.Errorf("\ngot %d\nwant len(Scopes)>%d", len(got.Body.Scopes), i) 975 } 976 goti := got.Body.Scopes[i] 977 if goti.Name != name || (varRef >= 0 && goti.VariablesReference != varRef) || goti.Expensive { 978 t.Errorf("\ngot %#v\nwant Name=%q VariablesReference=%d Expensive=false", goti, name, varRef) 979 } 980 } 981 982 // checkChildren is a helper for verifying the number of variables within a VariablesResponse. 983 // 984 // parentName - pseudoname of the enclosing variable or scope (used for error message only) 985 // numChildren - number of variables/fields/elements of this variable 986 func checkChildren(t *testing.T, got *dap.VariablesResponse, parentName string, numChildren int) { 987 t.Helper() 988 if got.Body.Variables == nil { 989 t.Errorf("\ngot %s children=%#v want []", parentName, got.Body.Variables) 990 } 991 if len(got.Body.Variables) != numChildren { 992 t.Errorf("\ngot len(%s)=%d (children=%#v)\nwant len=%d", parentName, len(got.Body.Variables), got.Body.Variables, numChildren) 993 } 994 } 995 996 // checkVar is a helper for verifying the values within a VariablesResponse. 997 // 998 // i - index of the variable within VariablesRespose.Body.Variables array (-1 will search all vars for a match) 999 // name - name of the variable 1000 // evalName - fully qualified variable name or alternative expression to load this variable 1001 // value - the value of the variable 1002 // useExactMatch - true if name, evalName and value are to be compared to exactly, false if to be used as regex 1003 // hasRef - true if the variable should have children and therefore a non-0 variable reference 1004 // ref - reference to retrieve children of this variable (0 if none) 1005 func checkVar(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, useExactMatch, hasRef bool, indexed, named int) (ref int) { 1006 t.Helper() 1007 if len(got.Body.Variables) <= i { 1008 t.Errorf("\ngot len=%d (children=%#v)\nwant len>%d", len(got.Body.Variables), got.Body.Variables, i) 1009 return 1010 } 1011 if i < 0 { 1012 for vi, v := range got.Body.Variables { 1013 if v.Name == name { 1014 i = vi 1015 break 1016 } 1017 } 1018 } 1019 if i < 0 { 1020 t.Errorf("\ngot %#v\nwant Variables[i].Name=%q (not found)", got, name) 1021 return 0 1022 } 1023 1024 goti := got.Body.Variables[i] 1025 matchedName := false 1026 if useExactMatch { 1027 if strings.HasPrefix(name, "~r") { 1028 matchedName = strings.HasPrefix(goti.Name, "~r") 1029 } else { 1030 matchedName = (goti.Name == name) 1031 } 1032 } else { 1033 matchedName, _ = regexp.MatchString(name, goti.Name) 1034 } 1035 if !matchedName || (goti.VariablesReference > 0) != hasRef { 1036 t.Errorf("\ngot %#v\nwant Name=%q hasRef=%t", goti, name, hasRef) 1037 } 1038 matchedEvalName := false 1039 if useExactMatch { 1040 matchedEvalName = (goti.EvaluateName == evalName) 1041 } else { 1042 matchedEvalName, _ = regexp.MatchString(evalName, goti.EvaluateName) 1043 } 1044 if !matchedEvalName { 1045 t.Errorf("\ngot %q\nwant EvaluateName=%q", goti.EvaluateName, evalName) 1046 } 1047 matchedValue := false 1048 if useExactMatch { 1049 matchedValue = (goti.Value == value) 1050 } else { 1051 matchedValue, _ = regexp.MatchString(value, goti.Value) 1052 } 1053 if !matchedValue { 1054 t.Errorf("\ngot %s=%q\nwant %q", name, goti.Value, value) 1055 } 1056 matchedType := false 1057 if useExactMatch { 1058 matchedType = (goti.Type == typ) 1059 } else { 1060 matchedType, _ = regexp.MatchString(typ, goti.Type) 1061 } 1062 if !matchedType { 1063 t.Errorf("\ngot %s=%q\nwant %q", name, goti.Type, typ) 1064 } 1065 if indexed >= 0 && goti.IndexedVariables != indexed { 1066 t.Errorf("\ngot %s=%d indexed\nwant %d indexed", name, goti.IndexedVariables, indexed) 1067 } 1068 if named >= 0 && goti.NamedVariables != named { 1069 t.Errorf("\ngot %s=%d named\nwant %d named", name, goti.NamedVariables, named) 1070 } 1071 return goti.VariablesReference 1072 } 1073 1074 // checkVarExact is a helper like checkVar that matches value exactly. 1075 func checkVarExact(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool) (ref int) { 1076 t.Helper() 1077 return checkVarExactIndexed(t, got, i, name, evalName, value, typ, hasRef, -1, -1) 1078 } 1079 1080 // checkVarExact is a helper like checkVar that matches value exactly. 1081 func checkVarExactIndexed(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool, indexed, named int) (ref int) { 1082 t.Helper() 1083 return checkVar(t, got, i, name, evalName, value, typ, true, hasRef, indexed, named) 1084 } 1085 1086 // checkVarRegex is a helper like checkVar that treats value, evalName or name as a regex. 1087 func checkVarRegex(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool) (ref int) { 1088 t.Helper() 1089 return checkVarRegexIndexed(t, got, i, name, evalName, value, typ, hasRef, -1, -1) 1090 } 1091 1092 // checkVarRegex is a helper like checkVar that treats value, evalName or name as a regex. 1093 func checkVarRegexIndexed(t *testing.T, got *dap.VariablesResponse, i int, name, evalName, value, typ string, hasRef bool, indexed, named int) (ref int) { 1094 t.Helper() 1095 return checkVar(t, got, i, name, evalName, value, typ, false, hasRef, indexed, named) 1096 } 1097 1098 func expectMessageFilterStopped(t *testing.T, client *daptest.Client) dap.Message { 1099 msg := client.ExpectMessage(t) 1100 if _, isStopped := msg.(*dap.StoppedEvent); isStopped { 1101 msg = client.ExpectMessage(t) 1102 } 1103 return msg 1104 } 1105 1106 // validateEvaluateName issues an evaluate request with evaluateName of a variable and 1107 // confirms that it succeeds and returns the same variable record as the original. 1108 func validateEvaluateName(t *testing.T, client *daptest.Client, got *dap.VariablesResponse, i int) { 1109 t.Helper() 1110 original := got.Body.Variables[i] 1111 client.EvaluateRequest(original.EvaluateName, 1000, "this context will be ignored") 1112 validated := client.ExpectEvaluateResponse(t) 1113 if original.VariablesReference == 0 && validated.Body.VariablesReference != 0 || 1114 original.VariablesReference != 0 && validated.Body.VariablesReference == 0 { 1115 t.Errorf("\ngot varref=%d\nwant %d", validated.Body.VariablesReference, original.VariablesReference) 1116 } 1117 // The variable might not be fully loaded, and when we reload it with an expression 1118 // more of the subvalues might be revealed, so we must match the loaded prefix only. 1119 if strings.Contains(original.Value, "...") { 1120 origLoaded := strings.Split(original.Value, "...")[0] 1121 if !strings.HasPrefix(validated.Body.Result, origLoaded) { 1122 t.Errorf("\ngot value=%q\nwant %q", validated.Body.Result, original.Value) 1123 } 1124 } else if original.Value != validated.Body.Result { 1125 t.Errorf("\ngot value=%q\nwant %q", validated.Body.Result, original.Value) 1126 } 1127 } 1128 1129 // TestStackTraceRequest executes to a breakpoint and tests different 1130 // good and bad configurations of 'stackTrace' requests. 1131 func TestStackTraceRequest(t *testing.T) { 1132 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 1133 var stResp *dap.StackTraceResponse 1134 runDebugSessionWithBPs(t, client, "launch", 1135 // Launch 1136 func() { 1137 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 1138 }, 1139 // Set breakpoints 1140 fixture.Source, []int{8, 18}, 1141 []onBreakpoint{{ 1142 // Stop at line 8 1143 execute: func() { 1144 // Even though the stack frames do not change, 1145 // repeated requests at the same breakpoint 1146 // would assign next block of unique ids to them each time. 1147 const NumFrames = 6 1148 reqIndex := 0 1149 frameID := func() int { 1150 return startHandle + reqIndex 1151 } 1152 1153 tests := map[string]struct { 1154 startFrame int 1155 levels int 1156 wantStartName string 1157 wantStartLine int 1158 wantStartFrame int 1159 wantFramesReturned int 1160 wantFramesAvailable int 1161 exact bool 1162 }{ 1163 "all frame levels from 0 to NumFrames": {0, NumFrames, "main.Increment", 8, 0, NumFrames, NumFrames, true}, 1164 "subset of frames from 1 to -1": {1, NumFrames - 1, "main.Increment", 11, 1, NumFrames - 1, NumFrames, true}, 1165 "load stack in pages: first half": {0, NumFrames / 2, "main.Increment", 8, 0, NumFrames / 2, NumFrames, false}, 1166 "load stack in pages: second half": {NumFrames / 2, NumFrames, "main.main", 17, NumFrames / 2, NumFrames / 2, NumFrames, true}, 1167 "zero levels means all levels": {0, 0, "main.Increment", 8, 0, NumFrames, NumFrames, true}, 1168 "zero levels means all remaining levels": {NumFrames / 2, 0, "main.main", 17, NumFrames / 2, NumFrames / 2, NumFrames, true}, 1169 "negative levels treated as 0 (all)": {0, -10, "main.Increment", 8, 0, NumFrames, NumFrames, true}, 1170 "OOB levels is capped at available len": {0, NumFrames + 1, "main.Increment", 8, 0, NumFrames, NumFrames, true}, 1171 "OOB levels is capped at available len 1": {1, NumFrames + 1, "main.Increment", 11, 1, NumFrames - 1, NumFrames, true}, 1172 "negative startFrame treated as 0": {-10, 0, "main.Increment", 8, 0, NumFrames, NumFrames, true}, 1173 "OOB startFrame returns empty trace": {NumFrames, 0, "main.Increment", -1, -1, 0, NumFrames, true}, 1174 } 1175 for name, tc := range tests { 1176 client.StackTraceRequest(1, tc.startFrame, tc.levels) 1177 stResp = client.ExpectStackTraceResponse(t) 1178 checkStackFramesNamed(name, t, stResp, 1179 tc.wantStartName, tc.wantStartLine, frameID(), tc.wantFramesReturned, tc.wantFramesAvailable, tc.exact) 1180 reqIndex += len(stResp.Body.StackFrames) 1181 } 1182 }, 1183 disconnect: false, 1184 }, { 1185 // Stop at line 18 1186 execute: func() { 1187 // Frame ids get reset at each breakpoint. 1188 client.StackTraceRequest(1, 0, 0) 1189 stResp = client.ExpectStackTraceResponse(t) 1190 checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3) 1191 1192 }, 1193 disconnect: false, 1194 }}) 1195 }) 1196 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 1197 var stResp *dap.StackTraceResponse 1198 runDebugSessionWithBPs(t, client, "launch", 1199 // Launch 1200 func() { 1201 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 1202 }, 1203 // Set breakpoints 1204 fixture.Source, []int{8, 18}, 1205 []onBreakpoint{{ 1206 // Stop at line 8 1207 execute: func() { 1208 // Even though the stack frames do not change, 1209 // repeated requests at the same breakpoint 1210 // would assign next block of unique ids to them each time. 1211 const NumFrames = 6 1212 1213 var frames []dap.StackFrame 1214 1215 for start, levels := 0, 1; start < NumFrames; { 1216 client.StackTraceRequest(1, start, levels) 1217 stResp = client.ExpectStackTraceResponse(t) 1218 frames = append(frames, stResp.Body.StackFrames...) 1219 if stResp.Body.TotalFrames < NumFrames { 1220 t.Errorf("got %#v\nwant TotalFrames>=%d\n", stResp.Body.TotalFrames, NumFrames) 1221 } 1222 1223 if len(stResp.Body.StackFrames) < levels { 1224 t.Errorf("got len(StackFrames)=%d\nwant >=%d\n", len(stResp.Body.StackFrames), levels) 1225 } 1226 1227 start += len(stResp.Body.StackFrames) 1228 } 1229 1230 // TODO check all the frames. 1231 want := []struct { 1232 wantName string 1233 wantLine int 1234 }{ 1235 {"main.Increment", 8}, 1236 {"main.Increment", 11}, 1237 {"main.Increment", 11}, 1238 {"main.main", 17}, 1239 {"runtime.main", 0}, 1240 {"runtime.goexit", 0}, 1241 } 1242 for i, frame := range frames { 1243 frameId := startHandle + i 1244 if frame.Id != frameId { 1245 t.Errorf("got %#v\nwant Id=%d\n", frame, frameId) 1246 } 1247 1248 // Verify the name and line corresponding to the first returned frame (if any). 1249 // This is useful when the first frame is the frame corresponding to the breakpoint at 1250 // a predefined line. Line values < 0 are a signal to skip the check (which can be useful 1251 // for frames in the third-party code, where we do not control the lines). 1252 if want[i].wantLine > 0 && frame.Line != want[i].wantLine { 1253 t.Errorf("got Line=%d\nwant %d\n", frame.Line, want[i].wantLine) 1254 } 1255 if want[i].wantName != "" && frame.Name != want[i].wantName { 1256 t.Errorf("got Name=%s\nwant %s\n", frame.Name, want[i].wantName) 1257 } 1258 } 1259 }, 1260 disconnect: false, 1261 }, { 1262 // Stop at line 18 1263 execute: func() { 1264 // Frame ids get reset at each breakpoint. 1265 client.StackTraceRequest(1, 0, 0) 1266 stResp = client.ExpectStackTraceResponse(t) 1267 checkStackFramesExact(t, stResp, "main.main", 18, startHandle, 3, 3) 1268 }, 1269 disconnect: false, 1270 }}) 1271 }) 1272 } 1273 1274 func TestSelectedThreadsRequest(t *testing.T) { 1275 runTest(t, "goroutinestackprog", func(client *daptest.Client, fixture protest.Fixture) { 1276 runDebugSessionWithBPs(t, client, "launch", 1277 // Launch 1278 func() { 1279 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 1280 }, 1281 // Set breakpoints 1282 fixture.Source, []int{20}, 1283 []onBreakpoint{{ 1284 execute: func() { 1285 checkStop(t, client, 1, "main.main", 20) 1286 1287 defaultMaxGoroutines := maxGoroutines 1288 defer func() { maxGoroutines = defaultMaxGoroutines }() 1289 1290 maxGoroutines = 1 1291 client.SetBreakpointsRequest(fixture.Source, []int{8}) 1292 client.ExpectSetBreakpointsResponse(t) 1293 1294 client.ContinueRequest(1) 1295 client.ExpectContinueResponse(t) 1296 1297 se := client.ExpectStoppedEvent(t) 1298 if se.Body.Reason != "breakpoint" || se.Body.ThreadId == 1 { 1299 t.Errorf("got %#v, want Reason=%q, ThreadId!=1", se, "breakpoint") 1300 } 1301 1302 client.ThreadsRequest() 1303 oe := client.ExpectOutputEvent(t) 1304 if !strings.HasPrefix(oe.Body.Output, "Too many goroutines") { 1305 t.Errorf("got %#v, expected Output=\"Too many goroutines...\"\n", oe) 1306 1307 } 1308 tr := client.ExpectThreadsResponse(t) 1309 1310 if len(tr.Body.Threads) != 2 { 1311 t.Errorf("got %d threads, expected 2\n", len(tr.Body.Threads)) 1312 } 1313 1314 var selectedFound bool 1315 for _, thread := range tr.Body.Threads { 1316 if thread.Id == se.Body.ThreadId { 1317 selectedFound = true 1318 break 1319 } 1320 } 1321 if !selectedFound { 1322 t.Errorf("got %#v, want ThreadId=%d\n", tr.Body.Threads, se.Body.ThreadId) 1323 } 1324 }, 1325 disconnect: true, 1326 }}) 1327 1328 }) 1329 } 1330 1331 func TestHideSystemGoroutinesRequest(t *testing.T) { 1332 tests := []struct{ hideSystemGoroutines bool }{ 1333 {hideSystemGoroutines: true}, 1334 {hideSystemGoroutines: false}, 1335 } 1336 for _, tt := range tests { 1337 runTest(t, "goroutinestackprog", func(client *daptest.Client, fixture protest.Fixture) { 1338 runDebugSessionWithBPs(t, client, "launch", 1339 // Launch 1340 func() { 1341 client.LaunchRequestWithArgs(map[string]interface{}{ 1342 "mode": "exec", 1343 "program": fixture.Path, 1344 "hideSystemGoroutines": tt.hideSystemGoroutines, 1345 "stopOnEntry": !stopOnEntry, 1346 }) 1347 }, 1348 // Set breakpoints 1349 fixture.Source, []int{25}, 1350 []onBreakpoint{{ 1351 execute: func() { 1352 checkStop(t, client, 1, "main.main", 25) 1353 1354 client.ThreadsRequest() 1355 tr := client.ExpectThreadsResponse(t) 1356 1357 // The user process creates 10 goroutines in addition to the 1358 // main goroutine, for a total of 11 goroutines. 1359 userCount := 11 1360 if tt.hideSystemGoroutines { 1361 if len(tr.Body.Threads) != userCount { 1362 t.Errorf("got %d goroutines, expected %d\n", len(tr.Body.Threads), userCount) 1363 } 1364 } else { 1365 if len(tr.Body.Threads) <= userCount { 1366 t.Errorf("got %d goroutines, expected >%d\n", len(tr.Body.Threads), userCount) 1367 } 1368 } 1369 }, 1370 disconnect: true, 1371 }}) 1372 }) 1373 } 1374 } 1375 1376 // TestScopesAndVariablesRequests executes to a breakpoint and tests different 1377 // configurations of 'scopes' and 'variables' requests. 1378 func TestScopesAndVariablesRequests(t *testing.T) { 1379 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 1380 runDebugSessionWithBPs(t, client, "launch", 1381 // Launch 1382 func() { 1383 client.LaunchRequestWithArgs(map[string]interface{}{ 1384 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, "backend": "default", 1385 }) 1386 }, 1387 // Breakpoints are set within the program 1388 fixture.Source, []int{}, 1389 []onBreakpoint{{ 1390 // Stop at first breakpoint 1391 execute: func() { 1392 client.StackTraceRequest(1, 0, 20) 1393 stack := client.ExpectStackTraceResponse(t) 1394 1395 startLineno := 66 1396 if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) { 1397 // Go1.15 on windows inserts a NOP after the call to 1398 // runtime.Breakpoint and marks it same line as the 1399 // runtime.Breakpoint call, making this flaky, so skip the line check. 1400 startLineno = -1 1401 } 1402 1403 checkStackFramesExact(t, stack, "main.foobar", startLineno, 1000, 4, 4) 1404 1405 client.ScopesRequest(1000) 1406 scopes := client.ExpectScopesResponse(t) 1407 checkScope(t, scopes, 0, "Locals", localsScope) 1408 checkScope(t, scopes, 1, "Globals (package main)", globalsScope) 1409 1410 // Globals 1411 1412 client.VariablesRequest(globalsScope) 1413 globals := client.ExpectVariablesResponse(t) 1414 checkVarExact(t, globals, 0, "p1", "main.p1", "10", "int", noChildren) 1415 1416 // Locals 1417 1418 client.VariablesRequest(localsScope) 1419 locals := client.ExpectVariablesResponse(t) 1420 checkChildren(t, locals, "Locals", 33) 1421 checkVarExact(t, locals, 0, "baz", "baz", `"bazburzum"`, "string", noChildren) 1422 ref := checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren) 1423 if ref > 0 { 1424 client.VariablesRequest(ref) 1425 bar := client.ExpectVariablesResponse(t) 1426 checkChildren(t, bar, "bar", 2) 1427 checkVarExact(t, bar, 0, "Baz", "bar.Baz", "10", "int", noChildren) 1428 checkVarExact(t, bar, 1, "Bur", "bar.Bur", `"lorem"`, "string", noChildren) 1429 validateEvaluateName(t, client, bar, 0) 1430 validateEvaluateName(t, client, bar, 1) 1431 } 1432 1433 // reflect.Kind == Bool 1434 checkVarExact(t, locals, -1, "b1", "b1", "true", "bool", noChildren) 1435 checkVarExact(t, locals, -1, "b2", "b2", "false", "bool", noChildren) 1436 // reflect.Kind == Int 1437 checkVarExact(t, locals, -1, "a2", "a2", "6", "int", noChildren) 1438 checkVarExact(t, locals, -1, "neg", "neg", "-1", "int", noChildren) 1439 // reflect.Kind == Int8 1440 checkVarExact(t, locals, -1, "i8", "i8", "1", "int8", noChildren) 1441 // reflect.Kind == Int16 - see testvariables2 1442 // reflect.Kind == Int32 - see testvariables2 1443 // reflect.Kind == Int64 - see testvariables2 1444 // reflect.Kind == Uint 1445 // reflect.Kind == Uint8 1446 checkVarExact(t, locals, -1, "u8", "u8", "255 = 0xff", "uint8", noChildren) 1447 // reflect.Kind == Uint16 1448 checkVarExact(t, locals, -1, "u16", "u16", "65535 = 0xffff", "uint16", noChildren) 1449 // reflect.Kind == Uint32 1450 checkVarExact(t, locals, -1, "u32", "u32", "4294967295 = 0xffffffff", "uint32", noChildren) 1451 // reflect.Kind == Uint64 1452 checkVarExact(t, locals, -1, "u64", "u64", "18446744073709551615 = 0xffffffffffffffff", "uint64", noChildren) 1453 // reflect.Kind == Uintptr 1454 checkVarExact(t, locals, -1, "up", "up", "5 = 0x5", "uintptr", noChildren) 1455 // reflect.Kind == Float32 1456 checkVarExact(t, locals, -1, "f32", "f32", "1.2", "float32", noChildren) 1457 // reflect.Kind == Float64 1458 checkVarExact(t, locals, -1, "a3", "a3", "7.23", "float64", noChildren) 1459 // reflect.Kind == Complex64 1460 ref = checkVarExact(t, locals, -1, "c64", "c64", "(1 + 2i)", "complex64", hasChildren) 1461 if ref > 0 { 1462 client.VariablesRequest(ref) 1463 c64 := client.ExpectVariablesResponse(t) 1464 checkChildren(t, c64, "c64", 2) 1465 checkVarExact(t, c64, 0, "real", "", "1", "float32", noChildren) 1466 checkVarExact(t, c64, 1, "imaginary", "", "2", "float32", noChildren) 1467 } 1468 // reflect.Kind == Complex128 1469 ref = checkVarExact(t, locals, -1, "c128", "c128", "(2 + 3i)", "complex128", hasChildren) 1470 if ref > 0 { 1471 client.VariablesRequest(ref) 1472 c128 := client.ExpectVariablesResponse(t) 1473 checkChildren(t, c128, "c128", 2) 1474 checkVarExact(t, c128, 0, "real", "", "2", "float64", noChildren) 1475 checkVarExact(t, c128, 1, "imaginary", "", "3", "float64", noChildren) 1476 } 1477 // reflect.Kind == Array 1478 ref = checkVarExact(t, locals, -1, "a4", "a4", "[2]int [1,2]", "[2]int", hasChildren) 1479 if ref > 0 { 1480 client.VariablesRequest(ref) 1481 a4 := client.ExpectVariablesResponse(t) 1482 checkChildren(t, a4, "a4", 2) 1483 checkVarExact(t, a4, 0, "[0]", "a4[0]", "1", "int", noChildren) 1484 checkVarExact(t, a4, 1, "[1]", "a4[1]", "2", "int", noChildren) 1485 } 1486 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) 1487 if ref > 0 { 1488 client.VariablesRequest(ref) 1489 a11 := client.ExpectVariablesResponse(t) 1490 checkChildren(t, a11, "a11", 3) 1491 checkVarExact(t, a11, 0, "[0]", "a11[0]", `main.FooBar {Baz: 1, Bur: "a"}`, "main.FooBar", hasChildren) 1492 ref = checkVarExact(t, a11, 1, "[1]", "a11[1]", `main.FooBar {Baz: 2, Bur: "b"}`, "main.FooBar", hasChildren) 1493 if ref > 0 { 1494 client.VariablesRequest(ref) 1495 a11_1 := client.ExpectVariablesResponse(t) 1496 checkChildren(t, a11_1, "a11[1]", 2) 1497 checkVarExact(t, a11_1, 0, "Baz", "a11[1].Baz", "2", "int", noChildren) 1498 checkVarExact(t, a11_1, 1, "Bur", "a11[1].Bur", `"b"`, "string", noChildren) 1499 validateEvaluateName(t, client, a11_1, 0) 1500 validateEvaluateName(t, client, a11_1, 1) 1501 } 1502 checkVarExact(t, a11, 2, "[2]", "a11[2]", `main.FooBar {Baz: 3, Bur: "c"}`, "main.FooBar", hasChildren) 1503 } 1504 1505 // reflect.Kind == Chan - see testvariables2 1506 // reflect.Kind == Func - see testvariables2 1507 // reflect.Kind == Interface - see testvariables2 1508 // reflect.Kind == Map - see testvariables2 1509 // reflect.Kind == Ptr 1510 ref = checkVarExact(t, locals, -1, "a7", "a7", `*main.FooBar {Baz: 5, Bur: "strum"}`, "*main.FooBar", hasChildren) 1511 if ref > 0 { 1512 client.VariablesRequest(ref) 1513 a7 := client.ExpectVariablesResponse(t) 1514 checkChildren(t, a7, "a7", 1) 1515 ref = checkVarExact(t, a7, 0, "", "(*a7)", `main.FooBar {Baz: 5, Bur: "strum"}`, "main.FooBar", hasChildren) 1516 if ref > 0 { 1517 client.VariablesRequest(ref) 1518 a7val := client.ExpectVariablesResponse(t) 1519 checkChildren(t, a7val, "*a7", 2) 1520 checkVarExact(t, a7val, 0, "Baz", "(*a7).Baz", "5", "int", noChildren) 1521 checkVarExact(t, a7val, 1, "Bur", "(*a7).Bur", `"strum"`, "string", noChildren) 1522 validateEvaluateName(t, client, a7val, 0) 1523 validateEvaluateName(t, client, a7val, 1) 1524 } 1525 } 1526 // TODO(polina): how to test for "nil" (without type) and "void"? 1527 checkVarExact(t, locals, -1, "a9", "a9", "*main.FooBar nil", "*main.FooBar", noChildren) 1528 // reflect.Kind == Slice 1529 ref = checkVarExact(t, locals, -1, "a5", "a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "[]int", hasChildren) 1530 if ref > 0 { 1531 client.VariablesRequest(ref) 1532 a5 := client.ExpectVariablesResponse(t) 1533 checkChildren(t, a5, "a5", 5) 1534 checkVarExact(t, a5, 0, "[0]", "a5[0]", "1", "int", noChildren) 1535 checkVarExact(t, a5, 4, "[4]", "a5[4]", "5", "int", noChildren) 1536 validateEvaluateName(t, client, a5, 0) 1537 validateEvaluateName(t, client, a5, 1) 1538 } 1539 ref = checkVarExact(t, locals, -1, "a12", "a12", `[]main.FooBar len: 2, cap: 2, [{Baz: 4, Bur: "d"},{Baz: 5, Bur: "e"}]`, "[]main.FooBar", hasChildren) 1540 if ref > 0 { 1541 client.VariablesRequest(ref) 1542 a12 := client.ExpectVariablesResponse(t) 1543 checkChildren(t, a12, "a12", 2) 1544 checkVarExact(t, a12, 0, "[0]", "a12[0]", `main.FooBar {Baz: 4, Bur: "d"}`, "main.FooBar", hasChildren) 1545 ref = checkVarExact(t, a12, 1, "[1]", "a12[1]", `main.FooBar {Baz: 5, Bur: "e"}`, "main.FooBar", hasChildren) 1546 if ref > 0 { 1547 client.VariablesRequest(ref) 1548 a12_1 := client.ExpectVariablesResponse(t) 1549 checkChildren(t, a12_1, "a12[1]", 2) 1550 checkVarExact(t, a12_1, 0, "Baz", "a12[1].Baz", "5", "int", noChildren) 1551 checkVarExact(t, a12_1, 1, "Bur", "a12[1].Bur", `"e"`, "string", noChildren) 1552 validateEvaluateName(t, client, a12_1, 0) 1553 validateEvaluateName(t, client, a12_1, 1) 1554 } 1555 } 1556 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) 1557 if ref > 0 { 1558 client.VariablesRequest(ref) 1559 a13 := client.ExpectVariablesResponse(t) 1560 checkChildren(t, a13, "a13", 3) 1561 checkVarExact(t, a13, 0, "[0]", "a13[0]", `*main.FooBar {Baz: 6, Bur: "f"}`, "*main.FooBar", hasChildren) 1562 checkVarExact(t, a13, 1, "[1]", "a13[1]", `*main.FooBar {Baz: 7, Bur: "g"}`, "*main.FooBar", hasChildren) 1563 ref = checkVarExact(t, a13, 2, "[2]", "a13[2]", `*main.FooBar {Baz: 8, Bur: "h"}`, "*main.FooBar", hasChildren) 1564 if ref > 0 { 1565 client.VariablesRequest(ref) 1566 a13_2 := client.ExpectVariablesResponse(t) 1567 checkChildren(t, a13_2, "a13[2]", 1) 1568 ref = checkVarExact(t, a13_2, 0, "", "(*a13[2])", `main.FooBar {Baz: 8, Bur: "h"}`, "main.FooBar", hasChildren) 1569 validateEvaluateName(t, client, a13_2, 0) 1570 if ref > 0 { 1571 client.VariablesRequest(ref) 1572 val := client.ExpectVariablesResponse(t) 1573 checkChildren(t, val, "*a13[2]", 2) 1574 checkVarExact(t, val, 0, "Baz", "(*a13[2]).Baz", "8", "int", noChildren) 1575 checkVarExact(t, val, 1, "Bur", "(*a13[2]).Bur", `"h"`, "string", noChildren) 1576 validateEvaluateName(t, client, val, 0) 1577 validateEvaluateName(t, client, val, 1) 1578 } 1579 } 1580 } 1581 // reflect.Kind == String 1582 checkVarExact(t, locals, -1, "a1", "a1", `"foofoofoofoofoofoo"`, "string", noChildren) 1583 checkVarExact(t, locals, -1, "a10", "a10", `"ofo"`, "string", noChildren) 1584 // reflect.Kind == Struct 1585 ref = checkVarExact(t, locals, -1, "a6", "a6", `main.FooBar {Baz: 8, Bur: "word"}`, "main.FooBar", hasChildren) 1586 if ref > 0 { 1587 client.VariablesRequest(ref) 1588 a6 := client.ExpectVariablesResponse(t) 1589 checkChildren(t, a6, "a6", 2) 1590 checkVarExact(t, a6, 0, "Baz", "a6.Baz", "8", "int", noChildren) 1591 checkVarExact(t, a6, 1, "Bur", "a6.Bur", `"word"`, "string", noChildren) 1592 } 1593 ref = checkVarExact(t, locals, -1, "a8", "a8", `main.FooBar2 {Bur: 10, Baz: "feh"}`, "main.FooBar2", hasChildren) 1594 if ref > 0 { 1595 client.VariablesRequest(ref) 1596 a8 := client.ExpectVariablesResponse(t) 1597 checkChildren(t, a8, "a8", 2) 1598 checkVarExact(t, a8, 0, "Bur", "a8.Bur", "10", "int", noChildren) 1599 checkVarExact(t, a8, 1, "Baz", "a8.Baz", `"feh"`, "string", noChildren) 1600 } 1601 // reflect.Kind == UnsafePointer - see testvariables2 1602 }, 1603 disconnect: false, 1604 }, { 1605 // Stop at second breakpoint 1606 execute: func() { 1607 // Frame ids get reset at each breakpoint. 1608 client.StackTraceRequest(1, 0, 20) 1609 stack := client.ExpectStackTraceResponse(t) 1610 checkStackFramesExact(t, stack, "main.barfoo", 27, 1000, 5, 5) 1611 1612 client.ScopesRequest(1000) 1613 scopes := client.ExpectScopesResponse(t) 1614 checkScope(t, scopes, 0, "Locals", localsScope) 1615 checkScope(t, scopes, 1, "Globals (package main)", globalsScope) 1616 1617 client.ScopesRequest(1111) 1618 erres := client.ExpectInvisibleErrorResponse(t) 1619 if erres.Body.Error.Format != "Unable to list locals: unknown frame id 1111" { 1620 t.Errorf("\ngot %#v\nwant Format=\"Unable to list locals: unknown frame id 1111\"", erres) 1621 } 1622 1623 client.VariablesRequest(localsScope) 1624 locals := client.ExpectVariablesResponse(t) 1625 checkChildren(t, locals, "Locals", 1) 1626 checkVarExact(t, locals, -1, "a1", "a1", `"bur"`, "string", noChildren) 1627 1628 client.VariablesRequest(globalsScope) 1629 globals := client.ExpectVariablesResponse(t) 1630 checkVarExact(t, globals, 0, "p1", "main.p1", "10", "int", noChildren) 1631 1632 client.VariablesRequest(7777) 1633 erres = client.ExpectInvisibleErrorResponse(t) 1634 if erres.Body.Error.Format != "Unable to lookup variable: unknown reference 7777" { 1635 t.Errorf("\ngot %#v\nwant Format=\"Unable to lookup variable: unknown reference 7777\"", erres) 1636 } 1637 }, 1638 disconnect: false, 1639 }}) 1640 }) 1641 } 1642 1643 // TestScopesAndVariablesRequests2 executes to a breakpoint and tests different 1644 // configurations of 'scopes' and 'variables' requests. 1645 func TestScopesAndVariablesRequests2(t *testing.T) { 1646 runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) { 1647 runDebugSessionWithBPs(t, client, "launch", 1648 // Launch 1649 func() { 1650 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 1651 }, 1652 // Breakpoints are set within the program 1653 fixture.Source, []int{}, 1654 []onBreakpoint{{ 1655 execute: func() { 1656 client.StackTraceRequest(1, 0, 20) 1657 stack := client.ExpectStackTraceResponse(t) 1658 checkStackFramesExact(t, stack, "main.main", -1, 1000, 3, 3) 1659 1660 client.ScopesRequest(1000) 1661 scopes := client.ExpectScopesResponse(t) 1662 checkScope(t, scopes, 0, "Locals", localsScope) 1663 }, 1664 disconnect: false, 1665 }, { 1666 execute: func() { 1667 client.StackTraceRequest(1, 0, 20) 1668 stack := client.ExpectStackTraceResponse(t) 1669 checkStackFramesExact(t, stack, "main.main", -1, 1000, 3, 3) 1670 1671 client.ScopesRequest(1000) 1672 scopes := client.ExpectScopesResponse(t) 1673 if len(scopes.Body.Scopes) > 1 { 1674 t.Errorf("\ngot %#v\nwant len(scopes)=1 (Argumes & Locals)", scopes) 1675 } 1676 checkScope(t, scopes, 0, "Locals", localsScope) 1677 1678 // Locals 1679 client.VariablesRequest(localsScope) 1680 locals := client.ExpectVariablesResponse(t) 1681 1682 // reflect.Kind == Bool - see testvariables 1683 // reflect.Kind == Int - see testvariables 1684 // reflect.Kind == Int8 1685 checkVarExact(t, locals, -1, "ni8", "ni8", "-5", "int8", noChildren) 1686 // reflect.Kind == Int16 1687 checkVarExact(t, locals, -1, "ni16", "ni16", "-5", "int16", noChildren) 1688 // reflect.Kind == Int32 1689 checkVarExact(t, locals, -1, "ni32", "ni32", "-5", "int32", noChildren) 1690 // reflect.Kind == Int64 1691 checkVarExact(t, locals, -1, "ni64", "ni64", "-5", "int64", noChildren) 1692 // reflect.Kind == Uint 1693 // reflect.Kind == Uint8 - see testvariables 1694 // reflect.Kind == Uint16 - see testvariables 1695 // reflect.Kind == Uint32 - see testvariables 1696 // reflect.Kind == Uint64 - see testvariables 1697 // reflect.Kind == Uintptr - see testvariables 1698 // reflect.Kind == Float32 - see testvariables 1699 // reflect.Kind == Float64 1700 checkVarExact(t, locals, -1, "pinf", "pinf", "+Inf", "float64", noChildren) 1701 checkVarExact(t, locals, -1, "ninf", "ninf", "-Inf", "float64", noChildren) 1702 checkVarExact(t, locals, -1, "nan", "nan", "NaN", "float64", noChildren) 1703 // reflect.Kind == Complex64 - see testvariables 1704 // reflect.Kind == Complex128 - see testvariables 1705 // reflect.Kind == Array 1706 checkVarExact(t, locals, -1, "a0", "a0", "[0]int []", "[0]int", noChildren) 1707 // reflect.Kind == Chan 1708 ref := checkVarExact(t, locals, -1, "ch1", "ch1", "chan int 4/11", "chan int", hasChildren) 1709 if ref > 0 { 1710 client.VariablesRequest(ref) 1711 ch1 := client.ExpectVariablesResponse(t) 1712 checkChildren(t, ch1, "ch1", 11) 1713 checkVarExact(t, ch1, 0, "qcount", "ch1.qcount", "4 = 0x4", "uint", noChildren) 1714 checkVarRegex(t, ch1, 10, "lock", "ch1.lock", `runtime\.mutex {.*key: 0.*}`, `runtime\.mutex`, hasChildren) 1715 validateEvaluateName(t, client, ch1, 0) 1716 validateEvaluateName(t, client, ch1, 10) 1717 } 1718 checkVarExact(t, locals, -1, "chnil", "chnil", "chan int nil", "chan int", noChildren) 1719 // reflect.Kind == Func 1720 checkVarExact(t, locals, -1, "fn1", "fn1", "main.afunc", "main.functype", noChildren) 1721 checkVarExact(t, locals, -1, "fn2", "fn2", "nil", "main.functype", noChildren) 1722 // reflect.Kind == Interface 1723 checkVarExact(t, locals, -1, "ifacenil", "ifacenil", "interface {} nil", "interface {}", noChildren) 1724 ref = checkVarExact(t, locals, -1, "iface2", "iface2", "interface {}(string) \"test\"", "interface {}", hasChildren) 1725 if ref > 0 { 1726 client.VariablesRequest(ref) 1727 iface2 := client.ExpectVariablesResponse(t) 1728 checkChildren(t, iface2, "iface2", 1) 1729 checkVarExact(t, iface2, 0, "data", "iface2.(data)", `"test"`, "string", noChildren) 1730 validateEvaluateName(t, client, iface2, 0) 1731 } 1732 ref = checkVarExact(t, locals, -1, "iface4", "iface4", "interface {}([]go/constant.Value) [4]", "interface {}", hasChildren) 1733 if ref > 0 { 1734 client.VariablesRequest(ref) 1735 iface4 := client.ExpectVariablesResponse(t) 1736 checkChildren(t, iface4, "iface4", 1) 1737 ref = checkVarExact(t, iface4, 0, "data", "iface4.(data)", "[]go/constant.Value len: 1, cap: 1, [4]", "[]go/constant.Value", hasChildren) 1738 if ref > 0 { 1739 client.VariablesRequest(ref) 1740 iface4data := client.ExpectVariablesResponse(t) 1741 checkChildren(t, iface4data, "iface4.data", 1) 1742 ref = checkVarExact(t, iface4data, 0, "[0]", "iface4.(data)[0]", "go/constant.Value(go/constant.int64Val) 4", "go/constant.Value", hasChildren) 1743 if ref > 0 { 1744 client.VariablesRequest(ref) 1745 iface4data0 := client.ExpectVariablesResponse(t) 1746 checkChildren(t, iface4data0, "iface4.data[0]", 1) 1747 checkVarExact(t, iface4data0, 0, "data", "iface4.(data)[0].(data)", "4", "go/constant.int64Val", noChildren) 1748 validateEvaluateName(t, client, iface4data0, 0) 1749 } 1750 } 1751 } 1752 checkVarExact(t, locals, -1, "errnil", "errnil", "error nil", "error", noChildren) 1753 ref = checkVarExact(t, locals, -1, "err1", "err1", "error(*main.astruct) *{A: 1, B: 2}", "error", hasChildren) 1754 if ref > 0 { 1755 client.VariablesRequest(ref) 1756 err1 := client.ExpectVariablesResponse(t) 1757 checkChildren(t, err1, "err1", 1) 1758 checkVarExact(t, err1, 0, "data", "err1.(data)", "*main.astruct {A: 1, B: 2}", "*main.astruct", hasChildren) 1759 validateEvaluateName(t, client, err1, 0) 1760 } 1761 ref = checkVarExact(t, locals, -1, "ptrinf", "ptrinf", "*interface {}(**interface {}) **...", "*interface {}", hasChildren) 1762 if ref > 0 { 1763 client.VariablesRequest(ref) 1764 ptrinf_val := client.ExpectVariablesResponse(t) 1765 checkChildren(t, ptrinf_val, "*ptrinf", 1) 1766 ref = checkVarExact(t, ptrinf_val, 0, "", "(*ptrinf)", "interface {}(**interface {}) **...", "interface {}", hasChildren) 1767 if ref > 0 { 1768 client.VariablesRequest(ref) 1769 ptrinf_val_data := client.ExpectVariablesResponse(t) 1770 checkChildren(t, ptrinf_val_data, "(*ptrinf).data", 1) 1771 checkVarExact(t, ptrinf_val_data, 0, "data", "(*ptrinf).(data)", "**interface {}(**interface {}) ...", "**interface {}", hasChildren) 1772 validateEvaluateName(t, client, ptrinf_val_data, 0) 1773 } 1774 } 1775 // reflect.Kind == Map 1776 checkVarExact(t, locals, -1, "mnil", "mnil", "map[string]main.astruct nil", "map[string]main.astruct", noChildren) 1777 // key - scalar, value - compound 1778 ref = checkVarExact(t, locals, -1, "m2", "m2", "map[int]*main.astruct [1: *{A: 10, B: 11}, ]", "map[int]*main.astruct", hasChildren) 1779 if ref > 0 { 1780 client.VariablesRequest(ref) 1781 m2 := client.ExpectVariablesResponse(t) 1782 checkChildren(t, m2, "m2", 2) // each key-value represented by a single child 1783 checkVarExact(t, m2, 0, "len()", "len(m2)", "1", "int", noChildren) 1784 ref = checkVarExact(t, m2, 1, "1", "m2[1]", "*main.astruct {A: 10, B: 11}", "int: *main.astruct", hasChildren) 1785 if ref > 0 { 1786 client.VariablesRequest(ref) 1787 m2kv1 := client.ExpectVariablesResponse(t) 1788 checkChildren(t, m2kv1, "m2[1]", 1) 1789 ref = checkVarExact(t, m2kv1, 0, "", "(*m2[1])", "main.astruct {A: 10, B: 11}", "main.astruct", hasChildren) 1790 if ref > 0 { 1791 client.VariablesRequest(ref) 1792 m2kv1deref := client.ExpectVariablesResponse(t) 1793 checkChildren(t, m2kv1deref, "*m2[1]", 2) 1794 checkVarExact(t, m2kv1deref, 0, "A", "(*m2[1]).A", "10", "int", noChildren) 1795 checkVarExact(t, m2kv1deref, 1, "B", "(*m2[1]).B", "11", "int", noChildren) 1796 validateEvaluateName(t, client, m2kv1deref, 0) 1797 validateEvaluateName(t, client, m2kv1deref, 1) 1798 } 1799 } 1800 } 1801 // key - compound, value - scalar 1802 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) 1803 if ref > 0 { 1804 client.VariablesRequest(ref) 1805 m3 := client.ExpectVariablesResponse(t) 1806 checkChildren(t, m3, "m3", 3) // each key-value represented by a single child 1807 checkVarExact(t, m3, 0, "len()", "len(m3)", "2", "int", noChildren) 1808 ref = checkVarRegex(t, m3, 1, `main\.astruct {A: 1, B: 1}`, `m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\]`, "42", "int", hasChildren) 1809 if ref > 0 { 1810 client.VariablesRequest(ref) 1811 m3kv0 := client.ExpectVariablesResponse(t) 1812 checkChildren(t, m3kv0, "m3[0]", 2) 1813 checkVarRegex(t, m3kv0, 0, "A", `\(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A`, "1", "int", noChildren) 1814 validateEvaluateName(t, client, m3kv0, 0) 1815 } 1816 ref = checkVarRegex(t, m3, 2, `main\.astruct {A: 2, B: 2}`, `m3\[\(\*\(\*"main.astruct"\)\(0x[0-9a-f]+\)\)\]`, "43", "", hasChildren) 1817 if ref > 0 { // inspect another key from another key-value child 1818 client.VariablesRequest(ref) 1819 m3kv1 := client.ExpectVariablesResponse(t) 1820 checkChildren(t, m3kv1, "m3[1]", 2) 1821 checkVarRegex(t, m3kv1, 1, "B", `\(*\(*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B`, "2", "int", noChildren) 1822 validateEvaluateName(t, client, m3kv1, 1) 1823 } 1824 } 1825 // key - compound, value - compound 1826 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) 1827 if ref > 0 { 1828 client.VariablesRequest(ref) 1829 m4 := client.ExpectVariablesResponse(t) 1830 checkChildren(t, m4, "m4", 5) // each key and value represented by a child, so double the key-value count 1831 checkVarExact(t, m4, 0, "len()", "len(m4)", "2", "int", noChildren) 1832 checkVarRegex(t, m4, 1, `\[key 0\]`, `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)`, `main\.astruct {A: 1, B: 1}`, `main\.astruct`, hasChildren) 1833 checkVarRegex(t, m4, 2, `\[val 0\]`, `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]`, `main\.astruct {A: 11, B: 11}`, `main\.astruct`, hasChildren) 1834 ref = checkVarRegex(t, m4, 3, `\[key 1\]`, `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)`, `main\.astruct {A: 2, B: 2}`, `main\.astruct`, hasChildren) 1835 if ref > 0 { 1836 client.VariablesRequest(ref) 1837 m4Key1 := client.ExpectVariablesResponse(t) 1838 checkChildren(t, m4Key1, "m4Key1", 2) 1839 checkVarRegex(t, m4Key1, 0, "A", `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.A`, "2", "int", noChildren) 1840 checkVarRegex(t, m4Key1, 1, "B", `\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\.B`, "2", "int", noChildren) 1841 validateEvaluateName(t, client, m4Key1, 0) 1842 validateEvaluateName(t, client, m4Key1, 1) 1843 } 1844 ref = checkVarRegex(t, m4, 4, `\[val 1\]`, `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]`, `main\.astruct {A: 22, B: 22}`, "main.astruct", hasChildren) 1845 if ref > 0 { 1846 client.VariablesRequest(ref) 1847 m4Val1 := client.ExpectVariablesResponse(t) 1848 checkChildren(t, m4Val1, "m4Val1", 2) 1849 checkVarRegex(t, m4Val1, 0, "A", `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.A`, "22", "int", noChildren) 1850 checkVarRegex(t, m4Val1, 1, "B", `m4\[\(\*\(\*"main\.astruct"\)\(0x[0-9a-f]+\)\)\]\.B`, "22", "int", noChildren) 1851 validateEvaluateName(t, client, m4Val1, 0) 1852 validateEvaluateName(t, client, m4Val1, 1) 1853 } 1854 } 1855 checkVarExact(t, locals, -1, "emptymap", "emptymap", "map[string]string []", "map[string]string", noChildren) 1856 // reflect.Kind == Ptr 1857 ref = checkVarExact(t, locals, -1, "pp1", "pp1", "**1", "**int", hasChildren) 1858 if ref > 0 { 1859 client.VariablesRequest(ref) 1860 pp1val := client.ExpectVariablesResponse(t) 1861 checkChildren(t, pp1val, "*pp1", 1) 1862 ref = checkVarExact(t, pp1val, 0, "", "(*pp1)", "*1", "*int", hasChildren) 1863 if ref > 0 { 1864 client.VariablesRequest(ref) 1865 pp1valval := client.ExpectVariablesResponse(t) 1866 checkChildren(t, pp1valval, "*(*pp1)", 1) 1867 checkVarExact(t, pp1valval, 0, "", "(*(*pp1))", "1", "int", noChildren) 1868 validateEvaluateName(t, client, pp1valval, 0) 1869 } 1870 } 1871 // reflect.Kind == Slice 1872 ref = checkVarExact(t, locals, -1, "zsslice", "zsslice", "[]struct {} len: 3, cap: 3, [{},{},{}]", "[]struct {}", hasChildren) 1873 if ref > 0 { 1874 client.VariablesRequest(ref) 1875 zsslice := client.ExpectVariablesResponse(t) 1876 checkChildren(t, zsslice, "zsslice", 3) 1877 checkVarExact(t, zsslice, 2, "[2]", "zsslice[2]", "struct {} {}", "struct {}", noChildren) 1878 validateEvaluateName(t, client, zsslice, 2) 1879 } 1880 checkVarExact(t, locals, -1, "emptyslice", "emptyslice", "[]string len: 0, cap: 0, []", "[]string", noChildren) 1881 checkVarExact(t, locals, -1, "nilslice", "nilslice", "[]int len: 0, cap: 0, nil", "[]int", noChildren) 1882 // reflect.Kind == String 1883 checkVarExact(t, locals, -1, "longstr", "longstr", longstr, "string", noChildren) 1884 // reflect.Kind == Struct 1885 checkVarExact(t, locals, -1, "zsvar", "zsvar", "struct {} {}", "struct {}", noChildren) 1886 // reflect.Kind == UnsafePointer 1887 // TODO(polina): how do I test for unsafe.Pointer(nil)? 1888 checkVarRegex(t, locals, -1, "upnil", "upnil", `unsafe\.Pointer\(0x0\)`, "int", noChildren) 1889 checkVarRegex(t, locals, -1, "up1", "up1", `unsafe\.Pointer\(0x[0-9a-f]+\)`, "int", noChildren) 1890 1891 // Test unreadable variable 1892 ref = checkVarRegex(t, locals, -1, "unread", "unread", `\*\(unreadable .+\)`, "int", hasChildren) 1893 if ref > 0 { 1894 client.VariablesRequest(ref) 1895 val := client.ExpectVariablesResponse(t) 1896 checkChildren(t, val, "*unread", 1) 1897 checkVarRegex(t, val, 0, "^$", `\(\*unread\)`, `\(unreadable .+\)`, "int", noChildren) 1898 validateEvaluateName(t, client, val, 0) 1899 } 1900 }, 1901 disconnect: true, 1902 }}) 1903 }) 1904 } 1905 1906 // TestScopesRequestsOptimized executes to a breakpoint and tests different 1907 // that the name of the "Locals" scope is correctly annotated with 1908 // a warning about debugging an optimized function. 1909 func TestScopesRequestsOptimized(t *testing.T) { 1910 runTestBuildFlags(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 1911 runDebugSessionWithBPs(t, client, "launch", 1912 // Launch 1913 func() { 1914 client.LaunchRequestWithArgs(map[string]interface{}{ 1915 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, 1916 }) 1917 }, 1918 // Breakpoints are set within the program 1919 fixture.Source, []int{}, 1920 []onBreakpoint{{ 1921 // Stop at first breakpoint 1922 execute: func() { 1923 client.StackTraceRequest(1, 0, 20) 1924 stack := client.ExpectStackTraceResponse(t) 1925 1926 startLineno := 66 1927 if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) { 1928 // Go1.15 on windows inserts a NOP after the call to 1929 // runtime.Breakpoint and marks it same line as the 1930 // runtime.Breakpoint call, making this flaky, so skip the line check. 1931 startLineno = -1 1932 } 1933 1934 checkStackFramesExact(t, stack, "main.foobar", startLineno, 1000, 4, 4) 1935 1936 client.ScopesRequest(1000) 1937 scopes := client.ExpectScopesResponse(t) 1938 checkScope(t, scopes, 0, "Locals (warning: optimized function)", localsScope) 1939 checkScope(t, scopes, 1, "Globals (package main)", globalsScope) 1940 }, 1941 disconnect: false, 1942 }, { 1943 // Stop at second breakpoint 1944 execute: func() { 1945 // Frame ids get reset at each breakpoint. 1946 client.StackTraceRequest(1, 0, 20) 1947 stack := client.ExpectStackTraceResponse(t) 1948 checkStackFramesExact(t, stack, "main.barfoo", 27, 1000, 5, 5) 1949 1950 client.ScopesRequest(1000) 1951 scopes := client.ExpectScopesResponse(t) 1952 checkScope(t, scopes, 0, "Locals (warning: optimized function)", localsScope) 1953 checkScope(t, scopes, 1, "Globals (package main)", globalsScope) 1954 }, 1955 disconnect: false, 1956 }}) 1957 }, 1958 protest.EnableOptimization, false) 1959 } 1960 1961 // TestVariablesLoading exposes test cases where variables might be partially or 1962 // fully unloaded. 1963 func TestVariablesLoading(t *testing.T) { 1964 runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) { 1965 runDebugSessionWithBPs(t, client, "launch", 1966 // Launch 1967 func() { 1968 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 1969 }, 1970 // Breakpoints are set within the program 1971 fixture.Source, []int{}, 1972 []onBreakpoint{{ 1973 execute: func() {}, 1974 disconnect: false, 1975 }, { 1976 execute: func() { 1977 // Change default config values to trigger certain unloaded corner cases 1978 saveDefaultConfig := DefaultLoadConfig 1979 DefaultLoadConfig.MaxStructFields = 5 1980 DefaultLoadConfig.MaxStringLen = 64 1981 // Set the MaxArrayValues = 33 to execute a bug for map handling where 1982 // a request for 2*MaxArrayValues indexed map children would not correctly 1983 // reslice the map. See https://github.com/golang/vscode-go/issues/2351. 1984 DefaultLoadConfig.MaxArrayValues = 33 1985 defer func() { 1986 DefaultLoadConfig = saveDefaultConfig 1987 }() 1988 1989 client.StackTraceRequest(1, 0, 0) 1990 client.ExpectStackTraceResponse(t) 1991 1992 client.ScopesRequest(1000) 1993 client.ExpectScopesResponse(t) 1994 1995 client.VariablesRequest(localsScope) 1996 locals := client.ExpectVariablesResponse(t) 1997 1998 // String partially missing based on LoadConfig.MaxStringLen 1999 // See also TestVariableLoadingOfLongStrings 2000 checkVarExact(t, locals, -1, "longstr", "longstr", longstrLoaded64, "string", noChildren) 2001 2002 checkArrayChildren := func(t *testing.T, longarr *dap.VariablesResponse, parentName string, start int) { 2003 t.Helper() 2004 for i, child := range longarr.Body.Variables { 2005 idx := start + i 2006 if child.Name != fmt.Sprintf("[%d]", idx) || child.EvaluateName != fmt.Sprintf("%s[%d]", parentName, idx) { 2007 t.Errorf("Expected %s[%d] to have Name=\"[%d]\" EvaluateName=\"%s[%d]\", got %#v", parentName, idx, idx, parentName, idx, child) 2008 } 2009 } 2010 } 2011 2012 // Array not fully loaded based on LoadConfig.MaxArrayValues. 2013 // Expect to be able to load array by paging. 2014 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,...+67 more]", "[100]int", hasChildren, 100, 0) 2015 if ref > 0 { 2016 client.VariablesRequest(ref) 2017 longarr := client.ExpectVariablesResponse(t) 2018 checkChildren(t, longarr, "longarr", 33) 2019 checkArrayChildren(t, longarr, "longarr", 0) 2020 2021 client.IndexedVariablesRequest(ref, 0, 100) 2022 longarr = client.ExpectVariablesResponse(t) 2023 checkChildren(t, longarr, "longarr", 100) 2024 checkArrayChildren(t, longarr, "longarr", 0) 2025 2026 client.IndexedVariablesRequest(ref, 50, 50) 2027 longarr = client.ExpectVariablesResponse(t) 2028 checkChildren(t, longarr, "longarr", 50) 2029 checkArrayChildren(t, longarr, "longarr", 50) 2030 } 2031 2032 // Slice not fully loaded based on LoadConfig.MaxArrayValues. 2033 // Expect to be able to load slice by paging. 2034 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,...+67 more]", "[]int", hasChildren, 100, 0) 2035 if ref > 0 { 2036 client.VariablesRequest(ref) 2037 longarr := client.ExpectVariablesResponse(t) 2038 checkChildren(t, longarr, "longslice", 33) 2039 checkArrayChildren(t, longarr, "longslice", 0) 2040 2041 client.IndexedVariablesRequest(ref, 0, 100) 2042 longarr = client.ExpectVariablesResponse(t) 2043 checkChildren(t, longarr, "longslice", 100) 2044 checkArrayChildren(t, longarr, "longslice", 0) 2045 2046 client.IndexedVariablesRequest(ref, 50, 50) 2047 longarr = client.ExpectVariablesResponse(t) 2048 checkChildren(t, longarr, "longslice", 50) 2049 checkArrayChildren(t, longarr, "longslice", 50) 2050 } 2051 2052 // Map not fully loaded based on LoadConfig.MaxArrayValues 2053 // Expect to be able to load map by paging. 2054 ref = checkVarRegexIndexed(t, locals, -1, "m1", "m1", `map\[string\]main\.astruct \[.+\.\.\.`, `map\[string\]main\.astruct`, hasChildren, 66, 1) 2055 if ref > 0 { 2056 client.VariablesRequest(ref) 2057 m1 := client.ExpectVariablesResponse(t) 2058 checkChildren(t, m1, "m1", 34) 2059 2060 client.IndexedVariablesRequest(ref, 0, 66) 2061 m1 = client.ExpectVariablesResponse(t) 2062 checkChildren(t, m1, "m1", 66) 2063 2064 client.IndexedVariablesRequest(ref, 0, 33) 2065 m1part1 := client.ExpectVariablesResponse(t) 2066 checkChildren(t, m1part1, "m1", 33) 2067 2068 client.IndexedVariablesRequest(ref, 33, 33) 2069 m1part2 := client.ExpectVariablesResponse(t) 2070 checkChildren(t, m1part2, "m1", 33) 2071 2072 if len(m1part1.Body.Variables)+len(m1part2.Body.Variables) == len(m1.Body.Variables) { 2073 for i, got := range m1part1.Body.Variables { 2074 want := m1.Body.Variables[i] 2075 if got.Name != want.Name || got.Value != want.Value { 2076 t.Errorf("got %#v, want Name=%q Value=%q", got, want.Name, want.Value) 2077 } 2078 } 2079 for i, got := range m1part2.Body.Variables { 2080 want := m1.Body.Variables[i+len(m1part1.Body.Variables)] 2081 if got.Name != want.Name || got.Value != want.Value { 2082 t.Errorf("got %#v, want Name=%q Value=%q", got, want.Name, want.Value) 2083 } 2084 } 2085 } 2086 client.NamedVariablesRequest(ref) 2087 named := client.ExpectVariablesResponse(t) 2088 checkChildren(t, named, "m1", 1) 2089 checkVarExact(t, named, 0, "len()", "len(m1)", "66", "int", noChildren) 2090 } 2091 2092 // Struct partially missing based on LoadConfig.MaxStructFields 2093 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) 2094 if ref > 0 { 2095 client.VariablesRequest(ref) 2096 sd := client.ExpectVariablesResponse(t) 2097 checkChildren(t, sd, "sd", 5) 2098 } 2099 2100 // Fully missing struct auto-loaded when reaching LoadConfig.MaxVariableRecurse (also tests evaluateName corner case) 2101 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) 2102 if ref > 0 { 2103 client.VariablesRequest(ref) 2104 c1 := client.ExpectVariablesResponse(t) 2105 checkChildren(t, c1, "c1", 2) 2106 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) 2107 if ref > 0 { 2108 client.VariablesRequest(ref) 2109 c1sa := client.ExpectVariablesResponse(t) 2110 checkChildren(t, c1sa, "c1.sa", 3) 2111 ref = checkVarRegex(t, c1sa, 0, `\[0\]`, `c1\.sa\[0\]`, `\*\(\*main\.astruct\)\(0x[0-9a-f]+\)`, `\*main\.astruct`, hasChildren) 2112 if ref > 0 { 2113 // Auto-loading of fully missing struc children happens here 2114 client.VariablesRequest(ref) 2115 c1sa0 := client.ExpectVariablesResponse(t) 2116 checkChildren(t, c1sa0, "c1.sa[0]", 1) 2117 // TODO(polina): there should be children here once we support auto loading 2118 checkVarExact(t, c1sa0, 0, "", "(*c1.sa[0])", "main.astruct {A: 1, B: 2}", "main.astruct", hasChildren) 2119 } 2120 } 2121 } 2122 2123 // Fully missing struct auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluteName corner case) 2124 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) 2125 if ref > 0 { 2126 client.VariablesRequest(ref) 2127 aas := client.ExpectVariablesResponse(t) 2128 checkChildren(t, aas, "aas", 1) 2129 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) 2130 if ref > 0 { 2131 client.VariablesRequest(ref) 2132 aas0 := client.ExpectVariablesResponse(t) 2133 checkChildren(t, aas0, "aas[0]", 1) 2134 ref = checkVarRegex(t, aas0, 0, "aas", `aas\[0\]\.aas`, `\[\]main\.a len: 1, cap: 1, \[\(\*main\.a\)\(0x[0-9a-f]+\)\]`, `\[\]main\.a`, hasChildren) 2135 if ref > 0 { 2136 // Auto-loading of fully missing struct children happens here 2137 client.VariablesRequest(ref) 2138 aas0aas := client.ExpectVariablesResponse(t) 2139 checkChildren(t, aas0aas, "aas[0].aas", 1) 2140 // TODO(polina): there should be a child here once we support auto loading - test for "aas[0].aas[0].aas" 2141 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) 2142 if ref > 0 { 2143 client.VariablesRequest(ref) 2144 aas0aas0 := client.ExpectVariablesResponse(t) 2145 checkChildren(t, aas0aas, "aas[0].aas[0]", 1) 2146 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) 2147 } 2148 } 2149 } 2150 } 2151 2152 // Fully missing map auto-loaded when hitting LoadConfig.MaxVariableRecurse (also tests evaluateName corner case) 2153 ref = checkVarExact(t, locals, -1, "tm", "tm", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap", hasChildren) 2154 if ref > 0 { 2155 client.VariablesRequest(ref) 2156 tm := client.ExpectVariablesResponse(t) 2157 checkChildren(t, tm, "tm", 1) 2158 ref = checkVarExact(t, tm, 0, "v", "tm.v", "[]map[string]main.astruct len: 1, cap: 1, [[...]]", "[]map[string]main.astruct", hasChildren) 2159 if ref > 0 { 2160 // Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValuess 2161 client.VariablesRequest(ref) 2162 tmV := client.ExpectVariablesResponse(t) 2163 checkChildren(t, tmV, "tm.v", 1) 2164 ref = checkVarRegex(t, tmV, 0, `\[0\]`, `tm\.v\[0\]`, `map\[string\]main\.astruct \[.+\.\.\.`, `map\[string\]main\.astruct`, hasChildren) 2165 if ref > 0 { 2166 client.VariablesRequest(ref) 2167 tmV0 := client.ExpectVariablesResponse(t) 2168 checkChildren(t, tmV0, "tm.v[0]", 34) 2169 } 2170 } 2171 } 2172 2173 // Auto-loading works with call return variables as well 2174 protest.MustSupportFunctionCalls(t, testBackend) 2175 client.EvaluateRequest("call rettm()", 1000, "repl") 2176 got := client.ExpectEvaluateResponse(t) 2177 ref = checkEval(t, got, "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", hasChildren) 2178 if ref > 0 { 2179 client.VariablesRequest(ref) 2180 rv := client.ExpectVariablesResponse(t) 2181 checkChildren(t, rv, "rv", 1) 2182 ref = checkVarExact(t, rv, 0, "~r0", "", "main.truncatedMap {v: []map[string]main.astruct len: 1, cap: 1, [[...]]}", "main.truncatedMap", hasChildren) 2183 if ref > 0 { 2184 client.VariablesRequest(ref) 2185 tm := client.ExpectVariablesResponse(t) 2186 checkChildren(t, tm, "tm", 1) 2187 ref = checkVarExact(t, tm, 0, "v", "", "[]map[string]main.astruct len: 1, cap: 1, [[...]]", "[]map[string]main.astruct", hasChildren) 2188 if ref > 0 { 2189 // Auto-loading of fully missing map chidlren happens here, but they get trancated at MaxArrayValuess 2190 client.VariablesRequest(ref) 2191 tmV := client.ExpectVariablesResponse(t) 2192 checkChildren(t, tmV, "tm.v", 1) 2193 // TODO(polina): this evaluate name is not usable - it should be empty 2194 ref = checkVarRegex(t, tmV, 0, `\[0\]`, `\[0\]`, `map\[string\]main\.astruct \[.+\.\.\.`, `map\[string\]main\.astruct`, hasChildren) 2195 if ref > 0 { 2196 client.VariablesRequest(ref) 2197 tmV0 := client.ExpectVariablesResponse(t) 2198 checkChildren(t, tmV0, "tm.v[0]", 34) 2199 } 2200 } 2201 } 2202 } 2203 2204 // TODO(polina): need fully missing array/slice test case 2205 2206 // Zero slices, structs and maps are not treated as fully missing 2207 // See zsvar, zsslice,, emptyslice, emptymap, a0 2208 }, 2209 disconnect: true, 2210 }}) 2211 }) 2212 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 2213 runDebugSessionWithBPs(t, client, "launch", 2214 // Launch 2215 func() { 2216 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 2217 }, 2218 // Breakpoints are set within the program 2219 fixture.Source, []int{}, 2220 []onBreakpoint{{ 2221 execute: func() { 2222 DefaultLoadConfig.FollowPointers = false 2223 defer func() { DefaultLoadConfig.FollowPointers = true }() 2224 2225 client.StackTraceRequest(1, 0, 0) 2226 client.ExpectStackTraceResponse(t) 2227 2228 var loadvars = func(frame int) { 2229 client.ScopesRequest(frame) 2230 scopes := client.ExpectScopesResponse(t) 2231 localsRef := 0 2232 for _, s := range scopes.Body.Scopes { 2233 if s.Name == "Locals" { 2234 localsRef = s.VariablesReference 2235 } 2236 } 2237 2238 client.VariablesRequest(localsRef) 2239 locals := client.ExpectVariablesResponse(t) 2240 2241 // Interface auto-loaded when hitting LoadConfig.MaxVariableRecurse=1 2242 2243 ref := checkVarRegex(t, locals, -1, "ni", "ni", `\[\]interface {} len: 1, cap: 1, \[\[\]interface {} len: 1, cap: 1, \[\*\(\*interface {}\)\(0x[0-9a-f]+\)\]\]`, `\[\]interface {}`, hasChildren) 2244 if ref > 0 { 2245 client.VariablesRequest(ref) 2246 ni := client.ExpectVariablesResponse(t) 2247 ref = checkVarRegex(t, ni, 0, `\[0\]`, `ni\[0\]`, `interface \{\}\(\[\]interface \{\}\) \[\*\(\*interface \{\}\)\(0x[0-9a-f]+\)\]`, "interface {}", hasChildren) 2248 if ref > 0 { 2249 client.VariablesRequest(ref) 2250 niI1 := client.ExpectVariablesResponse(t) 2251 ref = checkVarRegex(t, niI1, 0, "data", `ni\[0\]\.\(data\)`, `\[\]interface {} len: 1, cap: 1, \[\*\(\*interface {}\)\(0x[0-9a-f]+\)`, `\[\]interface {}`, hasChildren) 2252 if ref > 0 { 2253 // Auto-loading happens here 2254 client.VariablesRequest(ref) 2255 niI1Data := client.ExpectVariablesResponse(t) 2256 ref = checkVarExact(t, niI1Data, 0, "[0]", "ni[0].(data)[0]", "interface {}(int) 123", "interface {}", hasChildren) 2257 if ref > 0 { 2258 client.VariablesRequest(ref) 2259 niI1DataI2 := client.ExpectVariablesResponse(t) 2260 checkVarExact(t, niI1DataI2, 0, "data", "ni[0].(data)[0].(data)", "123", "int", noChildren) 2261 } 2262 } 2263 } 2264 } 2265 2266 // Pointer values loaded even with LoadConfig.FollowPointers=false 2267 checkVarExact(t, locals, -1, "a7", "a7", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "*main.FooBar", hasChildren) 2268 2269 // Auto-loading works on results of evaluate expressions as well 2270 client.EvaluateRequest("a7", frame, "repl") 2271 checkEval(t, client.ExpectEvaluateResponse(t), "*main.FooBar {Baz: 5, Bur: \"strum\"}", hasChildren) 2272 2273 client.EvaluateRequest("&a7", frame, "repl") 2274 pa7 := client.ExpectEvaluateResponse(t) 2275 ref = checkEvalRegex(t, pa7, `\*\(\*main\.FooBar\)\(0x[0-9a-f]+\)`, hasChildren) 2276 if ref > 0 { 2277 client.VariablesRequest(ref) 2278 a7 := client.ExpectVariablesResponse(t) 2279 checkVarExact(t, a7, 0, "a7", "(*(&a7))", "*main.FooBar {Baz: 5, Bur: \"strum\"}", "*main.FooBar", hasChildren) 2280 } 2281 } 2282 2283 // Frame-independent loading expressions allow us to auto-load 2284 // variables in any frame, not just topmost. 2285 loadvars(1000 /*first topmost frame*/) 2286 // step into another function 2287 client.StepInRequest(1) 2288 client.ExpectStepInResponse(t) 2289 client.ExpectStoppedEvent(t) 2290 checkStop(t, client, 1, "main.barfoo", 24) 2291 loadvars(1001 /*second frame here is same as topmost above*/) 2292 }, 2293 disconnect: true, 2294 }}) 2295 }) 2296 } 2297 2298 // TestVariablesMetadata exposes test cases where variables contain metadata that 2299 // can be accessed by requesting named variables. 2300 func TestVariablesMetadata(t *testing.T) { 2301 runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) { 2302 runDebugSessionWithBPs(t, client, "launch", 2303 // Launch 2304 func() { 2305 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 2306 }, 2307 // Breakpoints are set within the program 2308 fixture.Source, []int{}, 2309 []onBreakpoint{{ 2310 execute: func() {}, 2311 disconnect: false, 2312 }, { 2313 execute: func() { 2314 checkStop(t, client, 1, "main.main", -1) 2315 2316 client.VariablesRequest(localsScope) 2317 locals := client.ExpectVariablesResponse(t) 2318 2319 checkNamedChildren := func(ref int, name, typeStr string, vals []string, evaluate bool) { 2320 // byteslice, request named variables 2321 client.NamedVariablesRequest(ref) 2322 named := client.ExpectVariablesResponse(t) 2323 checkChildren(t, named, name, 1) 2324 checkVarExact(t, named, 0, "string()", "", "\"tèst\"", "string", false) 2325 2326 client.VariablesRequest(ref) 2327 all := client.ExpectVariablesResponse(t) 2328 checkChildren(t, all, name, len(vals)+1) 2329 checkVarExact(t, all, 0, "string()", "", "\"tèst\"", "string", false) 2330 for i, v := range vals { 2331 idx := fmt.Sprintf("[%d]", i) 2332 evalName := fmt.Sprintf("%s[%d]", name, i) 2333 if evaluate { 2334 evalName = fmt.Sprintf("(%s)[%d]", name, i) 2335 } 2336 checkVarExact(t, all, i+1, idx, evalName, v, typeStr, false) 2337 } 2338 } 2339 2340 bytes := []string{"116 = 0x74", "195 = 0xc3", "168 = 0xa8", "115 = 0x73", "116 = 0x74"} 2341 runes := []string{"116", "232", "115", "116"} 2342 2343 // byteslice 2344 ref := checkVarExactIndexed(t, locals, -1, "byteslice", "byteslice", "[]uint8 len: 5, cap: 5, [116,195,168,115,116]", "[]uint8", true, 5, 1) 2345 checkNamedChildren(ref, "byteslice", "uint8", bytes, false) 2346 2347 client.EvaluateRequest("byteslice", 0, "") 2348 got := client.ExpectEvaluateResponse(t) 2349 ref = checkEvalIndexed(t, got, "[]uint8 len: 5, cap: 5, [116,195,168,115,116]", hasChildren, 5, 1) 2350 checkNamedChildren(ref, "byteslice", "uint8", bytes, true) 2351 2352 // runeslice 2353 ref = checkVarExactIndexed(t, locals, -1, "runeslice", "runeslice", "[]int32 len: 4, cap: 4, [116,232,115,116]", "[]int32", true, 4, 1) 2354 checkNamedChildren(ref, "runeslice", "int32", runes, false) 2355 2356 client.EvaluateRequest("runeslice", 0, "repl") 2357 got = client.ExpectEvaluateResponse(t) 2358 ref = checkEvalIndexed(t, got, "[]int32 len: 4, cap: 4, [116,232,115,116]", hasChildren, 4, 1) 2359 checkNamedChildren(ref, "runeslice", "int32", runes, true) 2360 2361 // bytearray 2362 ref = checkVarExactIndexed(t, locals, -1, "bytearray", "bytearray", "[5]uint8 [116,195,168,115,116]", "[5]uint8", true, 5, 1) 2363 checkNamedChildren(ref, "bytearray", "uint8", bytes, false) 2364 2365 client.EvaluateRequest("bytearray", 0, "hover") 2366 got = client.ExpectEvaluateResponse(t) 2367 ref = checkEvalIndexed(t, got, "[5]uint8 [116,195,168,115,116]", hasChildren, 5, 1) 2368 checkNamedChildren(ref, "bytearray", "uint8", bytes, true) 2369 2370 // runearray 2371 ref = checkVarExactIndexed(t, locals, -1, "runearray", "runearray", "[4]int32 [116,232,115,116]", "[4]int32", true, 4, 1) 2372 checkNamedChildren(ref, "runearray", "int32", runes, false) 2373 2374 client.EvaluateRequest("runearray", 0, "watch") 2375 got = client.ExpectEvaluateResponse(t) 2376 ref = checkEvalIndexed(t, got, "[4]int32 [116,232,115,116]", hasChildren, 4, 1) 2377 checkNamedChildren(ref, "runearray", "int32", runes, true) 2378 2379 // string feature is not available with user-defined byte types 2380 ref = checkVarExactIndexed(t, locals, -1, "bytestypeslice", "bytestypeslice", "[]main.Byte len: 5, cap: 5, [116,195,168,115,116]", "[]main.Byte", true, 5, 1) 2381 client.NamedVariablesRequest(ref) 2382 namedchildren := client.ExpectVariablesResponse(t) 2383 checkChildren(t, namedchildren, "bytestypeslice as string", 0) 2384 ref = checkVarExactIndexed(t, locals, -1, "bytetypearray", "bytetypearray", "[5]main.Byte [116,195,168,115,116]", "[5]main.Byte", true, 5, 1) 2385 client.NamedVariablesRequest(ref) 2386 namedchildren = client.ExpectVariablesResponse(t) 2387 checkChildren(t, namedchildren, "bytetypearray as string", 0) 2388 }, 2389 disconnect: true, 2390 }}) 2391 }) 2392 } 2393 2394 // TestGlobalScopeAndVariables launches the program with showGlobalVariables 2395 // arg set, executes to a breakpoint in the main package and tests that global 2396 // package main variables got loaded. It then steps into a function 2397 // in another package and tests that globals scope got updated to those vars. 2398 func TestGlobalScopeAndVariables(t *testing.T) { 2399 runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) { 2400 runDebugSessionWithBPs(t, client, "launch", 2401 // Launch 2402 func() { 2403 client.LaunchRequestWithArgs(map[string]interface{}{ 2404 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, "showRegisters": true, 2405 }) 2406 }, 2407 // Breakpoints are set within the program 2408 fixture.Source, []int{}, 2409 []onBreakpoint{{ 2410 // Stop at line 36 2411 execute: func() { 2412 if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) { 2413 client.StepInRequest(1) 2414 client.ExpectStepInResponse(t) 2415 client.ExpectStoppedEvent(t) 2416 } 2417 client.StackTraceRequest(1, 0, 20) 2418 stack := client.ExpectStackTraceResponse(t) 2419 checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3) 2420 2421 client.ScopesRequest(1000) 2422 scopes := client.ExpectScopesResponse(t) 2423 checkScope(t, scopes, 0, "Locals", localsScope) 2424 checkScope(t, scopes, 1, "Globals (package main)", globalsScope) 2425 checkScope(t, scopes, 2, "Registers", globalsScope+1) 2426 2427 client.VariablesRequest(globalsScope) 2428 client.ExpectVariablesResponse(t) 2429 // The program has no user-defined globals. 2430 // Depending on the Go version, there might 2431 // be some runtime globals (e.g. main..inittask) 2432 // so testing for the total number is too fragile. 2433 2434 // Step into pkg.AnotherMethod() 2435 client.StepInRequest(1) 2436 client.ExpectStepInResponse(t) 2437 client.ExpectStoppedEvent(t) 2438 2439 client.StackTraceRequest(1, 0, 20) 2440 stack = client.ExpectStackTraceResponse(t) 2441 checkStackFramesExact(t, stack, "", 13, 1000, 4, 4) 2442 2443 client.ScopesRequest(1000) 2444 scopes = client.ExpectScopesResponse(t) 2445 checkScope(t, scopes, 0, "Locals", localsScope) 2446 checkScope(t, scopes, 1, "Globals (package github.com/emad-elsaid/delve/_fixtures/internal/dir0/pkg)", globalsScope) 2447 2448 client.VariablesRequest(globalsScope) 2449 globals := client.ExpectVariablesResponse(t) 2450 checkChildren(t, globals, "Globals", 1) 2451 ref := checkVarExact(t, globals, 0, "SomeVar", "github.com/emad-elsaid/delve/_fixtures/internal/dir0/pkg.SomeVar", "github.com/emad-elsaid/delve/_fixtures/internal/dir0/pkg.SomeType {X: 0}", "github.com/emad-elsaid/delve/_fixtures/internal/dir0/pkg.SomeType", hasChildren) 2452 2453 if ref > 0 { 2454 client.VariablesRequest(ref) 2455 somevar := client.ExpectVariablesResponse(t) 2456 checkChildren(t, somevar, "SomeVar", 1) 2457 // TODO(polina): unlike main.p, this prefix won't work 2458 checkVarExact(t, somevar, 0, "X", "github.com/emad-elsaid/delve/_fixtures/internal/dir0/pkg.SomeVar.X", "0", "float64", noChildren) 2459 } 2460 }, 2461 disconnect: false, 2462 }}) 2463 }) 2464 } 2465 2466 // TestRegisterScopeAndVariables launches the program with showRegisters 2467 // arg set, executes to a breakpoint in the main package and tests that the registers 2468 // got loaded. It then steps into a function in another package and tests that 2469 // the registers were updated by checking PC. 2470 func TestRegistersScopeAndVariables(t *testing.T) { 2471 runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) { 2472 runDebugSessionWithBPs(t, client, "launch", 2473 // Launch 2474 func() { 2475 client.LaunchRequestWithArgs(map[string]interface{}{ 2476 "mode": "exec", "program": fixture.Path, "showRegisters": true, 2477 }) 2478 }, 2479 // Breakpoints are set within the program 2480 fixture.Source, []int{}, 2481 []onBreakpoint{{ 2482 // Stop at line 36 2483 execute: func() { 2484 if runtime.GOARCH == "386" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) { 2485 client.StepInRequest(1) 2486 client.ExpectStepInResponse(t) 2487 client.ExpectStoppedEvent(t) 2488 } 2489 2490 client.StackTraceRequest(1, 0, 20) 2491 stack := client.ExpectStackTraceResponse(t) 2492 checkStackFramesExact(t, stack, "main.main", 36, 1000, 3, 3) 2493 2494 client.ScopesRequest(1000) 2495 scopes := client.ExpectScopesResponse(t) 2496 checkScope(t, scopes, 0, "Locals", localsScope) 2497 registersScope := localsScope + 1 2498 checkScope(t, scopes, 1, "Registers", registersScope) 2499 2500 // Check that instructionPointer points to the InstructionPointerReference. 2501 pc, err := getPC(t, client, 1) 2502 if err != nil { 2503 t.Error(pc) 2504 } 2505 2506 client.VariablesRequest(registersScope) 2507 vr := client.ExpectVariablesResponse(t) 2508 if len(vr.Body.Variables) == 0 { 2509 t.Fatal("no registers returned") 2510 } 2511 idx := findPcReg(vr.Body.Variables) 2512 if idx < 0 { 2513 t.Fatalf("got %#v, want a reg with instruction pointer", vr.Body.Variables) 2514 } 2515 pcReg := vr.Body.Variables[idx] 2516 gotPc, err := strconv.ParseUint(pcReg.Value, 0, 64) 2517 if err != nil { 2518 t.Error(err) 2519 } 2520 name := strings.TrimSpace(pcReg.Name) 2521 if gotPc != pc || pcReg.EvaluateName != fmt.Sprintf("_%s", strings.ToUpper(name)) { 2522 t.Errorf("got %#v,\nwant Name=%s Value=%#x EvaluateName=%q", pcReg, name, pc, fmt.Sprintf("_%s", strings.ToUpper(name))) 2523 } 2524 2525 // The program has no user-defined globals. 2526 // Depending on the Go version, there might 2527 // be some runtime globals (e.g. main..inittask) 2528 // so testing for the total number is too fragile. 2529 2530 // Step into pkg.AnotherMethod() 2531 client.StepInRequest(1) 2532 client.ExpectStepInResponse(t) 2533 client.ExpectStoppedEvent(t) 2534 2535 client.StackTraceRequest(1, 0, 20) 2536 stack = client.ExpectStackTraceResponse(t) 2537 checkStackFramesExact(t, stack, "", 13, 1000, 4, 4) 2538 2539 client.ScopesRequest(1000) 2540 scopes = client.ExpectScopesResponse(t) 2541 checkScope(t, scopes, 0, "Locals", localsScope) 2542 checkScope(t, scopes, 1, "Registers", registersScope) 2543 2544 // Check that rip points to the InstructionPointerReference. 2545 pc, err = getPC(t, client, 1) 2546 if err != nil { 2547 t.Error(pc) 2548 } 2549 client.VariablesRequest(registersScope) 2550 vr = client.ExpectVariablesResponse(t) 2551 if len(vr.Body.Variables) == 0 { 2552 t.Fatal("no registers returned") 2553 } 2554 2555 idx = findPcReg(vr.Body.Variables) 2556 if idx < 0 { 2557 t.Fatalf("got %#v, want a reg with instruction pointer", vr.Body.Variables) 2558 } 2559 pcReg = vr.Body.Variables[idx] 2560 gotPc, err = strconv.ParseUint(pcReg.Value, 0, 64) 2561 if err != nil { 2562 t.Error(err) 2563 } 2564 2565 if gotPc != pc || pcReg.EvaluateName != fmt.Sprintf("_%s", strings.ToUpper(name)) { 2566 t.Errorf("got %#v,\nwant Name=%s Value=%#x EvaluateName=%q", pcReg, name, pc, fmt.Sprintf("_%s", strings.ToUpper(name))) 2567 } 2568 }, 2569 disconnect: false, 2570 }}) 2571 }) 2572 } 2573 2574 func findPcReg(regs []dap.Variable) int { 2575 for i, reg := range regs { 2576 if isPcReg(reg) { 2577 return i 2578 } 2579 } 2580 return -1 2581 } 2582 2583 func isPcReg(reg dap.Variable) bool { 2584 pcRegNames := []string{"rip", "pc", "eip"} 2585 for _, name := range pcRegNames { 2586 if name == strings.TrimSpace(reg.Name) { 2587 return true 2588 } 2589 } 2590 return false 2591 } 2592 2593 // TestShadowedVariables executes to a breakpoint and checks the shadowed 2594 // variable is named correctly. 2595 func TestShadowedVariables(t *testing.T) { 2596 runTest(t, "testshadow", func(client *daptest.Client, fixture protest.Fixture) { 2597 runDebugSessionWithBPs(t, client, "launch", 2598 // Launch 2599 func() { 2600 client.LaunchRequestWithArgs(map[string]interface{}{ 2601 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, 2602 }) 2603 }, 2604 // Breakpoints are set within the program 2605 fixture.Source, []int{}, 2606 []onBreakpoint{{ 2607 // Stop at line 13 2608 execute: func() { 2609 client.StackTraceRequest(1, 0, 20) 2610 stack := client.ExpectStackTraceResponse(t) 2611 checkStackFramesExact(t, stack, "main.main", 13, 1000, 3, 3) 2612 2613 client.ScopesRequest(1000) 2614 scopes := client.ExpectScopesResponse(t) 2615 checkScope(t, scopes, 0, "Locals", localsScope) 2616 checkScope(t, scopes, 1, "Globals (package main)", globalsScope) 2617 2618 client.VariablesRequest(localsScope) 2619 locals := client.ExpectVariablesResponse(t) 2620 2621 checkVarExact(t, locals, 0, "(a)", "a", "0", "int", !hasChildren) 2622 checkVarExact(t, locals, 1, "a", "a", "1", "int", !hasChildren) 2623 2624 // Check that the non-shadowed of "a" is returned from evaluate request. 2625 validateEvaluateName(t, client, locals, 1) 2626 }, 2627 disconnect: false, 2628 }}) 2629 }) 2630 } 2631 2632 // Tests that 'stackTraceDepth' from LaunchRequest is parsed and passed to 2633 // stacktrace requests handlers. 2634 func TestLaunchRequestWithStackTraceDepth(t *testing.T) { 2635 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 2636 var stResp *dap.StackTraceResponse 2637 runDebugSessionWithBPs(t, client, "launch", 2638 // Launch 2639 func() { 2640 client.LaunchRequestWithArgs(map[string]interface{}{ 2641 "mode": "exec", "program": fixture.Path, "stackTraceDepth": 1, 2642 }) 2643 }, 2644 // Set breakpoints 2645 fixture.Source, []int{8}, 2646 []onBreakpoint{{ // Stop at line 8 2647 execute: func() { 2648 client.StackTraceRequest(1, 0, 0) 2649 stResp = client.ExpectStackTraceResponse(t) 2650 checkStackFramesHasMore(t, stResp, "main.Increment", 8, 1000, 1 /*returned*/, 2 /*available*/) 2651 }, 2652 disconnect: false, 2653 }}) 2654 }) 2655 } 2656 2657 type Breakpoint struct { 2658 line int 2659 path string 2660 verified bool 2661 msgPrefix string 2662 } 2663 2664 func expectSetBreakpointsResponse(t *testing.T, client *daptest.Client, bps []Breakpoint) { 2665 t.Helper() 2666 checkSetBreakpointsResponse(t, bps, client.ExpectSetBreakpointsResponse(t)) 2667 } 2668 2669 func checkSetBreakpointsResponse(t *testing.T, bps []Breakpoint, got *dap.SetBreakpointsResponse) { 2670 t.Helper() 2671 checkBreakpoints(t, bps, got.Body.Breakpoints) 2672 } 2673 2674 func checkBreakpoints(t *testing.T, bps []Breakpoint, breakpoints []dap.Breakpoint) { 2675 t.Helper() 2676 if len(breakpoints) != len(bps) { 2677 t.Errorf("got %#v,\nwant len(Breakpoints)=%d", breakpoints, len(bps)) 2678 return 2679 } 2680 for i, bp := range breakpoints { 2681 if bps[i].line < 0 && !bps[i].verified { 2682 if bp.Verified != bps[i].verified || !stringContainsCaseInsensitive(bp.Message, bps[i].msgPrefix) { 2683 t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i]) 2684 } 2685 continue 2686 } 2687 if bp.Line != bps[i].line || bp.Verified != bps[i].verified || bp.Source.Path != bps[i].path || 2688 !strings.HasPrefix(bp.Message, bps[i].msgPrefix) { 2689 t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i]) 2690 } 2691 } 2692 } 2693 2694 // TestSetBreakpoint executes to a breakpoint and tests different 2695 // configurations of setBreakpoint requests. 2696 func TestSetBreakpoint(t *testing.T) { 2697 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 2698 runDebugSessionWithBPs(t, client, "launch", 2699 // Launch 2700 func() { 2701 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 2702 }, 2703 // Set breakpoints 2704 fixture.Source, []int{16}, // b main.main 2705 []onBreakpoint{{ 2706 execute: func() { 2707 checkStop(t, client, 1, "main.main", 16) 2708 2709 // Set two breakpoints at the next two lines in main 2710 client.SetBreakpointsRequest(fixture.Source, []int{17, 18}) 2711 expectSetBreakpointsResponse(t, client, []Breakpoint{{17, fixture.Source, true, ""}, {18, fixture.Source, true, ""}}) 2712 2713 // Clear 17, reset 18 2714 client.SetBreakpointsRequest(fixture.Source, []int{18}) 2715 expectSetBreakpointsResponse(t, client, []Breakpoint{{18, fixture.Source, true, ""}}) 2716 2717 // Skip 17, continue to 18 2718 client.ContinueRequest(1) 2719 client.ExpectContinueResponse(t) 2720 client.ExpectStoppedEvent(t) 2721 checkStop(t, client, 1, "main.main", 18) 2722 2723 // Set another breakpoint inside the loop in loop(), twice to trigger error 2724 client.SetBreakpointsRequest(fixture.Source, []int{8, 8}) 2725 expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}, {-1, "", false, "breakpoint already exists"}}) 2726 2727 // Continue into the loop 2728 client.ContinueRequest(1) 2729 client.ExpectContinueResponse(t) 2730 client.ExpectStoppedEvent(t) 2731 checkStop(t, client, 1, "main.loop", 8) 2732 client.VariablesRequest(localsScope) 2733 locals := client.ExpectVariablesResponse(t) 2734 checkVarExact(t, locals, 0, "i", "i", "0", "int", noChildren) // i == 0 2735 2736 // Edit the breakpoint to add a condition 2737 client.SetBreakpointsRequestWithArgs(fixture.Source, []int{8}, map[int]string{8: "i == 3"}, nil, nil) 2738 expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}}) 2739 2740 // Continue until condition is hit 2741 client.ContinueRequest(1) 2742 client.ExpectContinueResponse(t) 2743 client.ExpectStoppedEvent(t) 2744 checkStop(t, client, 1, "main.loop", 8) 2745 client.VariablesRequest(localsScope) 2746 locals = client.ExpectVariablesResponse(t) 2747 checkVarExact(t, locals, 0, "i", "i", "3", "int", noChildren) // i == 3 2748 2749 // Edit the breakpoint to remove a condition 2750 client.SetBreakpointsRequestWithArgs(fixture.Source, []int{8}, map[int]string{8: ""}, nil, nil) 2751 expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}}) 2752 2753 // Continue for one more loop iteration 2754 client.ContinueRequest(1) 2755 client.ExpectContinueResponse(t) 2756 client.ExpectStoppedEvent(t) 2757 checkStop(t, client, 1, "main.loop", 8) 2758 client.VariablesRequest(localsScope) 2759 locals = client.ExpectVariablesResponse(t) 2760 checkVarExact(t, locals, 0, "i", "i", "4", "int", noChildren) // i == 4 2761 2762 // Set at a line without a statement 2763 client.SetBreakpointsRequest(fixture.Source, []int{1000}) 2764 expectSetBreakpointsResponse(t, client, []Breakpoint{{-1, "", false, "could not find statement"}}) // all cleared, none set 2765 }, 2766 // The program has an infinite loop, so we must kill it by disconnecting. 2767 disconnect: true, 2768 }}) 2769 }) 2770 } 2771 2772 // TestSetInstructionBreakpoint executes to a breakpoint and tests different 2773 // configurations of setInstructionBreakpoint requests. 2774 func TestSetInstructionBreakpoint(t *testing.T) { 2775 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 2776 runDebugSessionWithBPs(t, client, "launch", 2777 // Launch 2778 func() { 2779 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 2780 }, 2781 // Set breakpoints 2782 fixture.Source, []int{16}, // b main.main 2783 []onBreakpoint{{ 2784 execute: func() { 2785 checkStop(t, client, 1, "main.main", 16) 2786 2787 // Set two breakpoints in the loop 2788 client.SetBreakpointsRequest(fixture.Source, []int{8, 9}) 2789 expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}, {9, fixture.Source, true, ""}}) 2790 2791 // Continue to the two breakpoints and get the instructionPointerReference. 2792 client.ContinueRequest(1) 2793 client.ExpectContinueResponse(t) 2794 client.ExpectStoppedEvent(t) 2795 checkStop(t, client, 1, "main.loop", 8) 2796 2797 client.StackTraceRequest(1, 0, 1) 2798 st := client.ExpectStackTraceResponse(t) 2799 if len(st.Body.StackFrames) < 1 { 2800 t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st) 2801 } 2802 pc8 := st.Body.StackFrames[0].InstructionPointerReference 2803 2804 client.ContinueRequest(1) 2805 client.ExpectContinueResponse(t) 2806 client.ExpectStoppedEvent(t) 2807 checkStop(t, client, 1, "main.loop", 9) 2808 2809 client.StackTraceRequest(1, 0, 1) 2810 st = client.ExpectStackTraceResponse(t) 2811 if len(st.Body.StackFrames) < 1 { 2812 t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st) 2813 } 2814 pc9 := st.Body.StackFrames[0].InstructionPointerReference 2815 2816 // Clear the source breakpoints. 2817 // TODO(suzmue): there is an existing issue that breakpoints with identical locations 2818 // from different setBreakpoints, setFunctionBreakpoints, setInstructionBreakpoints 2819 // requests will prevent subsequent ones from being set. 2820 client.SetBreakpointsRequest(fixture.Source, []int{}) 2821 expectSetBreakpointsResponse(t, client, []Breakpoint{}) 2822 2823 // Set the breakpoints using the instruction pointer references. 2824 client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc8}, {InstructionReference: pc9}}) 2825 bps := client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints 2826 checkBreakpoints(t, []Breakpoint{{line: 8, path: fixture.Source, verified: true}, {line: 9, path: fixture.Source, verified: true}}, bps) 2827 2828 // Continue to the two breakpoints and get the instructionPointerReference. 2829 client.ContinueRequest(1) 2830 client.ExpectContinueResponse(t) 2831 client.ExpectStoppedEvent(t) 2832 checkStop(t, client, 1, "main.loop", 8) 2833 2834 client.ContinueRequest(1) 2835 client.ExpectContinueResponse(t) 2836 client.ExpectStoppedEvent(t) 2837 checkStop(t, client, 1, "main.loop", 9) 2838 2839 // Remove the breakpoint on line 8 and continue. 2840 client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc9}}) 2841 bps = client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints 2842 checkBreakpoints(t, []Breakpoint{{line: 9, path: fixture.Source, verified: true}}, bps) 2843 2844 client.ContinueRequest(1) 2845 client.ExpectContinueResponse(t) 2846 client.ExpectStoppedEvent(t) 2847 checkStop(t, client, 1, "main.loop", 9) 2848 2849 // Set two breakpoints and expect an error on the second one. 2850 client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc8}, {InstructionReference: pc8}}) 2851 bps = client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints 2852 checkBreakpoints(t, []Breakpoint{{line: 8, path: fixture.Source, verified: true}, {line: -1, path: "", verified: false, msgPrefix: "breakpoint already exists"}}, bps) 2853 2854 // Add a condition 2855 client.SetInstructionBreakpointsRequest([]dap.InstructionBreakpoint{{InstructionReference: pc8, Condition: "i == 100"}}) 2856 bps = client.ExpectSetInstructionBreakpointsResponse(t).Body.Breakpoints 2857 checkBreakpoints(t, []Breakpoint{{line: 8, path: fixture.Source, verified: true}}, bps) 2858 2859 client.ContinueRequest(1) 2860 client.ExpectContinueResponse(t) 2861 client.ExpectStoppedEvent(t) 2862 checkStop(t, client, 1, "main.loop", 8) 2863 2864 client.VariablesRequest(localsScope) 2865 locals := client.ExpectVariablesResponse(t) 2866 checkVarExact(t, locals, 0, "i", "i", "100", "int", noChildren) // i == 100 2867 }, 2868 // The program has an infinite loop, so we must kill it by disconnecting. 2869 disconnect: true, 2870 }}) 2871 }) 2872 } 2873 2874 func TestPauseAtStop(t *testing.T) { 2875 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 2876 runDebugSessionWithBPs(t, client, "launch", 2877 // Launch 2878 func() { 2879 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 2880 }, 2881 // Set breakpoints 2882 fixture.Source, []int{16}, 2883 []onBreakpoint{{ 2884 execute: func() { 2885 checkStop(t, client, 1, "main.main", 16) 2886 2887 client.SetBreakpointsRequest(fixture.Source, []int{6, 8}) 2888 expectSetBreakpointsResponse(t, client, []Breakpoint{{6, fixture.Source, true, ""}, {8, fixture.Source, true, ""}}) 2889 2890 // Send a pause request while stopped on a cleared breakpoint. 2891 client.PauseRequest(1) 2892 client.ExpectPauseResponse(t) 2893 2894 client.ContinueRequest(1) 2895 client.ExpectContinueResponse(t) 2896 client.ExpectStoppedEvent(t) 2897 checkStop(t, client, 1, "main.loop", 6) 2898 2899 // Send a pause request while stopped on a breakpoint. 2900 client.PauseRequest(1) 2901 client.ExpectPauseResponse(t) 2902 2903 client.ContinueRequest(1) 2904 client.ExpectContinueResponse(t) 2905 se := client.ExpectStoppedEvent(t) 2906 if se.Body.Reason != "breakpoint" { 2907 t.Errorf("got %#v, expected breakpoint", se) 2908 } 2909 checkStop(t, client, 1, "main.loop", 8) 2910 2911 // Send a pause request while stopped after stepping. 2912 client.NextRequest(1) 2913 client.ExpectNextResponse(t) 2914 client.ExpectStoppedEvent(t) 2915 checkStop(t, client, 1, "main.loop", 9) 2916 2917 client.PauseRequest(1) 2918 client.ExpectPauseResponse(t) 2919 2920 client.ContinueRequest(1) 2921 client.ExpectContinueResponse(t) 2922 2923 client.ExpectStoppedEvent(t) 2924 checkStop(t, client, 1, "main.loop", 8) 2925 }, 2926 // The program has an infinite loop, so we must kill it by disconnecting. 2927 disconnect: true, 2928 }}) 2929 }) 2930 } 2931 2932 func checkHitBreakpointIds(t *testing.T, se *dap.StoppedEvent, reason string, id int) { 2933 if se.Body.ThreadId != 1 || se.Body.Reason != reason || len(se.Body.HitBreakpointIds) != 1 || se.Body.HitBreakpointIds[0] != id { 2934 t.Errorf("got %#v, want Reason=%q, ThreadId=1, HitBreakpointIds=[]int{%d}", se, reason, id) 2935 } 2936 } 2937 2938 // TestHitBreakpointIds executes to a breakpoint and tests that 2939 // the breakpoint ids in the stopped event are correct. 2940 func TestHitBreakpointIds(t *testing.T) { 2941 runTest(t, "locationsprog", func(client *daptest.Client, fixture protest.Fixture) { 2942 runDebugSessionWithBPs(t, client, "launch", 2943 // Launch 2944 func() { 2945 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 2946 }, 2947 // Set breakpoints 2948 fixture.Source, []int{30}, // b main.main 2949 []onBreakpoint{{ 2950 execute: func() { 2951 checkStop(t, client, 1, "main.main", 30) 2952 2953 // Set two source breakpoints and two function breakpoints. 2954 client.SetBreakpointsRequest(fixture.Source, []int{23, 33}) 2955 sourceBps := client.ExpectSetBreakpointsResponse(t).Body.Breakpoints 2956 checkBreakpoints(t, []Breakpoint{{line: 23, path: fixture.Source, verified: true}, {line: 33, path: fixture.Source, verified: true}}, sourceBps) 2957 2958 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 2959 {Name: "anotherFunction"}, 2960 {Name: "anotherFunction:1"}, 2961 }) 2962 functionBps := client.ExpectSetFunctionBreakpointsResponse(t).Body.Breakpoints 2963 checkBreakpoints(t, []Breakpoint{{line: 26, path: fixture.Source, verified: true}, {line: 27, path: fixture.Source, verified: true}}, functionBps) 2964 2965 client.ContinueRequest(1) 2966 client.ExpectContinueResponse(t) 2967 se := client.ExpectStoppedEvent(t) 2968 checkHitBreakpointIds(t, se, "breakpoint", sourceBps[1].Id) 2969 checkStop(t, client, 1, "main.main", 33) 2970 2971 client.ContinueRequest(1) 2972 client.ExpectContinueResponse(t) 2973 se = client.ExpectStoppedEvent(t) 2974 checkHitBreakpointIds(t, se, "breakpoint", sourceBps[0].Id) 2975 checkStop(t, client, 1, "main.(*SomeType).SomeFunction", 23) 2976 2977 client.ContinueRequest(1) 2978 client.ExpectContinueResponse(t) 2979 se = client.ExpectStoppedEvent(t) 2980 checkHitBreakpointIds(t, se, "function breakpoint", functionBps[0].Id) 2981 checkStop(t, client, 1, "main.anotherFunction", 26) 2982 2983 client.ContinueRequest(1) 2984 client.ExpectContinueResponse(t) 2985 se = client.ExpectStoppedEvent(t) 2986 2987 checkHitBreakpointIds(t, se, "function breakpoint", functionBps[1].Id) 2988 2989 checkStop(t, client, 1, "main.anotherFunction", 27) 2990 }, 2991 disconnect: true, 2992 }}) 2993 }) 2994 } 2995 2996 func stringContainsCaseInsensitive(got, want string) bool { 2997 return strings.Contains(strings.ToLower(got), strings.ToLower(want)) 2998 } 2999 3000 // TestSetFunctionBreakpoints is inspired by service/test.TestClientServer_FindLocations. 3001 func TestSetFunctionBreakpoints(t *testing.T) { 3002 runTest(t, "locationsprog", func(client *daptest.Client, fixture protest.Fixture) { 3003 runDebugSessionWithBPs(t, client, "launch", 3004 // Launch 3005 func() { 3006 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3007 }, 3008 // Set breakpoints 3009 fixture.Source, []int{30}, // b main.main 3010 []onBreakpoint{{ 3011 execute: func() { 3012 checkStop(t, client, 1, "main.main", 30) 3013 3014 type Breakpoint struct { 3015 line int 3016 sourceName string 3017 verified bool 3018 errMsg string 3019 } 3020 expectSetFunctionBreakpointsResponse := func(bps []Breakpoint) { 3021 t.Helper() 3022 got := client.ExpectSetFunctionBreakpointsResponse(t) 3023 if len(got.Body.Breakpoints) != len(bps) { 3024 t.Errorf("got %#v,\nwant len(Breakpoints)=%d", got, len(bps)) 3025 return 3026 } 3027 for i, bp := range got.Body.Breakpoints { 3028 if bps[i].line < 0 && !bps[i].verified { 3029 if bp.Verified != bps[i].verified || !stringContainsCaseInsensitive(bp.Message, bps[i].errMsg) { 3030 t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i]) 3031 } 3032 continue 3033 } 3034 // Some function breakpoints may be in packages that have been imported and we do not control, so 3035 // we do not always want to check breakpoint lines. 3036 if (bps[i].line >= 0 && bp.Line != bps[i].line) || bp.Verified != bps[i].verified || bp.Source.Name != bps[i].sourceName { 3037 t.Errorf("got breakpoints[%d] = %#v, \nwant %#v", i, bp, bps[i]) 3038 } 3039 } 3040 } 3041 3042 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3043 {Name: "anotherFunction"}, 3044 }) 3045 expectSetFunctionBreakpointsResponse([]Breakpoint{{26, filepath.Base(fixture.Source), true, ""}}) 3046 3047 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3048 {Name: "main.anotherFunction"}, 3049 }) 3050 expectSetFunctionBreakpointsResponse([]Breakpoint{{26, filepath.Base(fixture.Source), true, ""}}) 3051 3052 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3053 {Name: "SomeType.String"}, 3054 }) 3055 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}}) 3056 3057 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3058 {Name: "(*SomeType).String"}, 3059 }) 3060 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}}) 3061 3062 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3063 {Name: "main.SomeType.String"}, 3064 }) 3065 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}}) 3066 3067 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3068 {Name: "main.(*SomeType).String"}, 3069 }) 3070 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}}) 3071 3072 // Test line offsets 3073 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3074 {Name: "main.anotherFunction:1"}, 3075 }) 3076 expectSetFunctionBreakpointsResponse([]Breakpoint{{27, filepath.Base(fixture.Source), true, ""}}) 3077 3078 // Test function names in imported package. 3079 // Issue #275 3080 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3081 {Name: "io/ioutil.ReadFile"}, 3082 }) 3083 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "ioutil.go", true, ""}}) 3084 3085 // Issue #296 3086 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3087 {Name: "/io/ioutil.ReadFile"}, 3088 }) 3089 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "ioutil.go", true, ""}}) 3090 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3091 {Name: "ioutil.ReadFile"}, 3092 }) 3093 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "ioutil.go", true, ""}}) 3094 3095 // Function Breakpoint name also accepts breakpoints that are specified as file:line. 3096 // TODO(suzmue): We could return an error, but it probably is not necessary since breakpoints, 3097 // and function breakpoints come in with different requests. 3098 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3099 {Name: fmt.Sprintf("%s:14", fixture.Source)}, 3100 }) 3101 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}}) 3102 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3103 {Name: fmt.Sprintf("%s:14", filepath.Base(fixture.Source))}, 3104 }) 3105 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}}) 3106 3107 // Expect error for ambiguous function name. 3108 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3109 {Name: "String"}, 3110 }) 3111 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "Location \"String\" ambiguous"}}) 3112 3113 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3114 {Name: "main.String"}, 3115 }) 3116 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "Location \"main.String\" ambiguous"}}) 3117 3118 // Expect error for function that does not exist. 3119 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3120 {Name: "fakeFunction"}, 3121 }) 3122 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "location \"fakeFunction\" not found"}}) 3123 3124 // Expect error for negative line number. 3125 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3126 {Name: "main.anotherFunction:-1"}, 3127 }) 3128 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "line offset negative or not a number"}}) 3129 3130 // Expect error when function name is regex. 3131 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3132 {Name: `/^.*String.*$/`}, 3133 }) 3134 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, "", false, "breakpoint name \"/^.*String.*$/\" could not be parsed as a function"}}) 3135 3136 // Expect error when function name is an offset. 3137 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3138 {Name: "+1"}, 3139 }) 3140 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, "breakpoint name \"+1\" could not be parsed as a function"}}) 3141 3142 // Expect error when function name is a line number. 3143 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3144 {Name: "14"}, 3145 }) 3146 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, "breakpoint name \"14\" could not be parsed as a function"}}) 3147 3148 // Expect error when function name is an address. 3149 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3150 {Name: "*b"}, 3151 }) 3152 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, "breakpoint name \"*b\" could not be parsed as a function"}}) 3153 3154 // Expect error when function name is a relative path. 3155 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3156 {Name: fmt.Sprintf(".%s%s:14", string(filepath.Separator), filepath.Base(fixture.Source))}, 3157 }) 3158 // This relative path could also be caught by the parser, so we should not match the error message. 3159 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, ""}}) 3160 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3161 {Name: fmt.Sprintf("..%s%s:14", string(filepath.Separator), filepath.Base(fixture.Source))}, 3162 }) 3163 // This relative path could also be caught by the parser, so we should not match the error message. 3164 expectSetFunctionBreakpointsResponse([]Breakpoint{{-1, filepath.Base(fixture.Source), false, ""}}) 3165 3166 // Test multiple function breakpoints. 3167 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3168 {Name: "SomeType.String"}, {Name: "anotherFunction"}, 3169 }) 3170 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}, {26, filepath.Base(fixture.Source), true, ""}}) 3171 3172 // Test multiple breakpoints to the same location. 3173 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3174 {Name: "SomeType.String"}, {Name: "(*SomeType).String"}, 3175 }) 3176 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}, {-1, "", false, "breakpoint exists"}}) 3177 3178 // Set two breakpoints at SomeType.String and SomeType.SomeFunction. 3179 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3180 {Name: "SomeType.String"}, {Name: "SomeType.SomeFunction"}, 3181 }) 3182 expectSetFunctionBreakpointsResponse([]Breakpoint{{14, filepath.Base(fixture.Source), true, ""}, {22, filepath.Base(fixture.Source), true, ""}}) 3183 3184 // Clear SomeType.String, reset SomeType.SomeFunction (SomeType.String is called before SomeType.SomeFunction). 3185 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3186 {Name: "SomeType.SomeFunction"}, 3187 }) 3188 expectSetFunctionBreakpointsResponse([]Breakpoint{{22, filepath.Base(fixture.Source), true, ""}}) 3189 3190 // Expect the next breakpoint to be at SomeType.SomeFunction. 3191 client.ContinueRequest(1) 3192 client.ExpectContinueResponse(t) 3193 3194 if se := client.ExpectStoppedEvent(t); se.Body.Reason != "function breakpoint" || se.Body.ThreadId != 1 { 3195 t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se) 3196 } 3197 checkStop(t, client, 1, "main.(*SomeType).SomeFunction", 22) 3198 3199 // Set a breakpoint at the next line in the program. 3200 client.SetBreakpointsRequest(fixture.Source, []int{23}) 3201 got := client.ExpectSetBreakpointsResponse(t) 3202 if len(got.Body.Breakpoints) != 1 { 3203 t.Errorf("got %#v,\nwant len(Breakpoints)=%d", got, 1) 3204 return 3205 } 3206 bp := got.Body.Breakpoints[0] 3207 if bp.Line != 23 || bp.Verified != true || bp.Source.Path != fixture.Source { 3208 t.Errorf("got breakpoints[0] = %#v, \nwant Line=23 Verified=true Source.Path=%q", bp, fixture.Source) 3209 } 3210 3211 // Set a function breakpoint, this should not clear the breakpoint that was set in the previous setBreakpoints request. 3212 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{ 3213 {Name: "anotherFunction"}, 3214 }) 3215 expectSetFunctionBreakpointsResponse([]Breakpoint{{26, filepath.Base(fixture.Source), true, ""}}) 3216 3217 // Expect the next breakpoint to be at line 23. 3218 client.ContinueRequest(1) 3219 client.ExpectContinueResponse(t) 3220 3221 if se := client.ExpectStoppedEvent(t); se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 { 3222 t.Errorf("got %#v, want Reason=\"breakpoint\", ThreadId=1", se) 3223 } 3224 checkStop(t, client, 1, "main.(*SomeType).SomeFunction", 23) 3225 3226 // Set a breakpoint, this should not clear the breakpoint that was set in the previous setFunctionBreakpoints request. 3227 client.SetBreakpointsRequest(fixture.Source, []int{37}) 3228 got = client.ExpectSetBreakpointsResponse(t) 3229 if len(got.Body.Breakpoints) != 1 { 3230 t.Errorf("got %#v,\nwant len(Breakpoints)=%d", got, 1) 3231 return 3232 } 3233 bp = got.Body.Breakpoints[0] 3234 if bp.Line != 37 || bp.Verified != true || bp.Source.Path != fixture.Source { 3235 t.Errorf("got breakpoints[0] = %#v, \nwant Line=23 Verified=true Source.Path=%q", bp, fixture.Source) 3236 } 3237 3238 // Expect the next breakpoint to be at line anotherFunction. 3239 client.ContinueRequest(1) 3240 client.ExpectContinueResponse(t) 3241 3242 if se := client.ExpectStoppedEvent(t); se.Body.Reason != "function breakpoint" || se.Body.ThreadId != 1 { 3243 t.Errorf("got %#v, want Reason=\"function breakpoint\", ThreadId=1", se) 3244 } 3245 checkStop(t, client, 1, "main.anotherFunction", 26) 3246 3247 }, 3248 disconnect: true, 3249 }}) 3250 }) 3251 } 3252 3253 // TestLogPoints executes to a breakpoint and tests that log points 3254 // send OutputEvents and do not halt program execution. 3255 func TestLogPoints(t *testing.T) { 3256 runTest(t, "callme", func(client *daptest.Client, fixture protest.Fixture) { 3257 runDebugSessionWithBPs(t, client, "launch", 3258 // Launch 3259 func() { 3260 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3261 }, 3262 // Set breakpoints 3263 fixture.Source, []int{23}, 3264 []onBreakpoint{{ 3265 // Stop at line 23 3266 execute: func() { 3267 checkStop(t, client, 1, "main.main", 23) 3268 bps := []int{6, 25, 27, 16} 3269 logMessages := map[int]string{6: "{i*2}: in callme!", 16: "in callme2!"} 3270 client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages) 3271 client.ExpectSetBreakpointsResponse(t) 3272 3273 client.ContinueRequest(1) 3274 client.ExpectContinueResponse(t) 3275 3276 for i := 0; i < 5; i++ { 3277 se := client.ExpectStoppedEvent(t) 3278 if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 { 3279 t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se) 3280 } 3281 checkStop(t, client, 1, "main.main", 25) 3282 3283 client.ContinueRequest(1) 3284 client.ExpectContinueResponse(t) 3285 checkLogMessage(t, client.ExpectOutputEvent(t), 1, fmt.Sprintf("%d: in callme!", i*2), fixture.Source, 6) 3286 } 3287 se := client.ExpectStoppedEvent(t) 3288 if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 { 3289 t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se) 3290 } 3291 checkStop(t, client, 1, "main.main", 27) 3292 3293 client.NextRequest(1) 3294 client.ExpectNextResponse(t) 3295 3296 checkLogMessage(t, client.ExpectOutputEvent(t), 1, "in callme2!", fixture.Source, 16) 3297 3298 se = client.ExpectStoppedEvent(t) 3299 if se.Body.Reason != "step" || se.Body.ThreadId != 1 { 3300 t.Errorf("got stopped event = %#v, \nwant Reason=\"step\" ThreadId=1", se) 3301 } 3302 checkStop(t, client, 1, "main.main", 28) 3303 }, 3304 disconnect: true, 3305 }}) 3306 }) 3307 } 3308 3309 func checkLogMessage(t *testing.T, oe *dap.OutputEvent, goid int, text, path string, line int) { 3310 t.Helper() 3311 prefix := "> [Go " 3312 if goid >= 0 { 3313 prefix += strconv.Itoa(goid) + "]" 3314 } 3315 if oe.Body.Category != "stdout" || !strings.HasPrefix(oe.Body.Output, prefix) || !strings.HasSuffix(oe.Body.Output, text+"\n") { 3316 t.Errorf("got output event = %#v, \nwant Category=\"stdout\" Output=\"%s: %s\\n\"", oe, prefix, text) 3317 } 3318 if oe.Body.Line != line || oe.Body.Source.Path != path { 3319 t.Errorf("got output event = %#v, \nwant Line=%d Source.Path=%s", oe, line, path) 3320 } 3321 } 3322 3323 // TestHaltPreventsAutoResume tests that a pause request issued while processing 3324 // log messages will result in a real stop. 3325 func TestHaltPreventsAutoResume(t *testing.T) { 3326 runTest(t, "callme", func(client *daptest.Client, fixture protest.Fixture) { 3327 runDebugSessionWithBPs(t, client, "launch", // Launch 3328 func() { 3329 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3330 }, 3331 // Set breakpoints 3332 fixture.Source, []int{23}, 3333 []onBreakpoint{{ 3334 execute: func() { 3335 savedResumeOnce := resumeOnceAndCheckStop 3336 defer func() { 3337 resumeOnceAndCheckStop = savedResumeOnce 3338 }() 3339 checkStop(t, client, 1, "main.main", 23) 3340 bps := []int{6, 25} 3341 logMessages := map[int]string{6: "in callme!"} 3342 client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages) 3343 client.ExpectSetBreakpointsResponse(t) 3344 3345 for i := 0; i < 5; i++ { 3346 // Reset the handler to the default behavior. 3347 resumeOnceAndCheckStop = savedResumeOnce 3348 3349 // Expect a pause request while stopped not to interrupt continue. 3350 client.PauseRequest(1) 3351 client.ExpectPauseResponse(t) 3352 3353 client.ContinueRequest(1) 3354 client.ExpectContinueResponse(t) 3355 se := client.ExpectStoppedEvent(t) 3356 if se.Body.Reason != "breakpoint" || se.Body.ThreadId != 1 { 3357 t.Errorf("got stopped event = %#v, \nwant Reason=\"breakpoint\" ThreadId=1", se) 3358 } 3359 checkStop(t, client, 1, "main.main", 25) 3360 3361 pauseDoneChan := make(chan struct{}, 1) 3362 outputDoneChan := make(chan struct{}, 1) 3363 // Send a halt request when trying to resume the program after being 3364 // interrupted. This should allow the log message to be processed, 3365 // but keep the process from continuing beyond the line. 3366 resumeOnceAndCheckStop = func(s *Session, command string, allowNextStateChange chan struct{}) (*api.DebuggerState, error) { 3367 // This should trigger after the log message is sent, but before 3368 // execution is resumed. 3369 if command == api.DirectionCongruentContinue { 3370 go func() { 3371 <-outputDoneChan 3372 defer close(pauseDoneChan) 3373 client.PauseRequest(1) 3374 client.ExpectPauseResponse(t) 3375 }() 3376 // Wait for the pause to be complete. 3377 <-pauseDoneChan 3378 } 3379 return s.resumeOnceAndCheckStop(command, allowNextStateChange) 3380 } 3381 3382 client.ContinueRequest(1) 3383 client.ExpectContinueResponse(t) 3384 checkLogMessage(t, client.ExpectOutputEvent(t), 1, "in callme!", fixture.Source, 6) 3385 // Signal that the output event has been received. 3386 close(outputDoneChan) 3387 // Wait for the pause to be complete. 3388 <-pauseDoneChan 3389 se = client.ExpectStoppedEvent(t) 3390 if se.Body.Reason != "pause" { 3391 t.Errorf("got stopped event = %#v, \nwant Reason=\"pause\"", se) 3392 } 3393 checkStop(t, client, 1, "main.callme", 6) 3394 } 3395 }, 3396 disconnect: true, 3397 }}) 3398 }) 3399 } 3400 3401 // TestConcurrentBreakpointsLogPoints tests that a breakpoint set in the main 3402 // goroutine is hit the correct number of times and log points set in the 3403 // children goroutines produce the correct number of output events. 3404 func TestConcurrentBreakpointsLogPoints(t *testing.T) { 3405 if runtime.GOOS == "freebsd" { 3406 t.SkipNow() 3407 } 3408 tests := []struct { 3409 name string 3410 fixture string 3411 start int 3412 breakpoints []int 3413 }{ 3414 { 3415 name: "source breakpoints", 3416 fixture: "goroutinestackprog", 3417 breakpoints: []int{23}, 3418 }, 3419 { 3420 name: "hardcoded breakpoint", 3421 fixture: "goroutinebreak", 3422 }, 3423 } 3424 for _, tt := range tests { 3425 t.Run(tt.name, func(t *testing.T) { 3426 runTest(t, tt.fixture, func(client *daptest.Client, fixture protest.Fixture) { 3427 client.InitializeRequest() 3428 client.ExpectInitializeResponseAndCapabilities(t) 3429 3430 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3431 client.ExpectInitializedEvent(t) 3432 client.ExpectLaunchResponse(t) 3433 3434 bps := append([]int{8}, tt.breakpoints...) 3435 logMessages := map[int]string{8: "hello"} 3436 client.SetBreakpointsRequestWithArgs(fixture.Source, bps, nil, nil, logMessages) 3437 client.ExpectSetBreakpointsResponse(t) 3438 3439 client.ConfigurationDoneRequest() 3440 client.ExpectConfigurationDoneResponse(t) 3441 3442 // There may be up to 1 breakpoint and any number of log points that are 3443 // hit concurrently. We should get a stopped event everytime the breakpoint 3444 // is hit and an output event for each log point hit. 3445 var oeCount, seCount int 3446 for oeCount < 10 || seCount < 10 { 3447 switch m := client.ExpectMessage(t).(type) { 3448 case *dap.StoppedEvent: 3449 if m.Body.Reason != "breakpoint" || !m.Body.AllThreadsStopped || m.Body.ThreadId != 1 { 3450 t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", m) 3451 } 3452 seCount++ 3453 client.ContinueRequest(1) 3454 case *dap.OutputEvent: 3455 checkLogMessage(t, m, -1, "hello", fixture.Source, 8) 3456 oeCount++ 3457 case *dap.ContinueResponse: 3458 case *dap.TerminatedEvent: 3459 t.Fatalf("\nexpected 10 output events and 10 stopped events, got %d output events and %d stopped events", oeCount, seCount) 3460 default: 3461 t.Fatalf("Unexpected message type: expect StoppedEvent, OutputEvent, or ContinueResponse, got %#v", m) 3462 } 3463 } 3464 // TODO(suzmue): The dap server may identify some false 3465 // positives for hard coded breakpoints, so there may still 3466 // be more stopped events. 3467 client.DisconnectRequestWithKillOption(true) 3468 }) 3469 }) 3470 } 3471 } 3472 3473 func TestSetBreakpointWhileRunning(t *testing.T) { 3474 runTest(t, "integrationprog", func(client *daptest.Client, fixture protest.Fixture) { 3475 runDebugSessionWithBPs(t, client, "launch", 3476 // Launch 3477 func() { 3478 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3479 }, 3480 // Set breakpoints 3481 fixture.Source, []int{16}, 3482 []onBreakpoint{{ 3483 execute: func() { 3484 // The program loops 3 times over lines 14-15-8-9-10-16 3485 checkStop(t, client, 1, "main.main", 16) // Line that sleeps for 1 second 3486 3487 // We can set breakpoints while nexting 3488 client.NextRequest(1) 3489 client.ExpectNextResponse(t) 3490 client.SetBreakpointsRequest(fixture.Source, []int{15}) // [16,] => [15,] 3491 checkSetBreakpointsResponse(t, []Breakpoint{{15, fixture.Source, true, ""}}, client.ExpectSetBreakpointsResponse(t)) 3492 se := client.ExpectStoppedEvent(t) 3493 if se.Body.Reason != "step" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 { 3494 t.Errorf("\ngot %#v\nwant Reason='step' AllThreadsStopped=true ThreadId=1", se) 3495 } 3496 checkStop(t, client, 1, "main.main", 14) 3497 client.ContinueRequest(1) 3498 client.ExpectContinueResponse(t) 3499 se = client.ExpectStoppedEvent(t) 3500 if se.Body.Reason != "breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 { 3501 t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se) 3502 } 3503 checkStop(t, client, 1, "main.main", 15) 3504 3505 // We can set breakpoints while continuing 3506 client.ContinueRequest(1) 3507 client.ExpectContinueResponse(t) 3508 client.SetBreakpointsRequest(fixture.Source, []int{9}) // [15,] => [9,] 3509 checkSetBreakpointsResponse(t, []Breakpoint{{9, fixture.Source, true, ""}}, client.ExpectSetBreakpointsResponse(t)) 3510 se = client.ExpectStoppedEvent(t) 3511 if se.Body.Reason != "breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 { 3512 t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se) 3513 } 3514 checkStop(t, client, 1, "main.sayhi", 9) 3515 3516 }, 3517 disconnect: true, 3518 }}) 3519 }) 3520 } 3521 3522 func TestSetFunctionBreakpointWhileRunning(t *testing.T) { 3523 runTest(t, "integrationprog", func(client *daptest.Client, fixture protest.Fixture) { 3524 runDebugSessionWithBPs(t, client, "launch", 3525 // Launch 3526 func() { 3527 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3528 }, 3529 // Set breakpoints 3530 fixture.Source, []int{16}, 3531 []onBreakpoint{{ 3532 execute: func() { 3533 // The program loops 3 times over lines 14-15-8-9-10-16 3534 checkStop(t, client, 1, "main.main", 16) // Line that sleeps for 1 second 3535 3536 // We can set breakpoints while nexting 3537 client.NextRequest(1) 3538 client.ExpectNextResponse(t) 3539 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{{Name: "main.sayhi"}}) // [16,] => [16, 8] 3540 checkBreakpoints(t, []Breakpoint{{8, fixture.Source, true, ""}}, client.ExpectSetFunctionBreakpointsResponse(t).Body.Breakpoints) 3541 client.SetBreakpointsRequest(fixture.Source, []int{}) // [16,8] => [8] 3542 expectSetBreakpointsResponse(t, client, []Breakpoint{}) 3543 se := client.ExpectStoppedEvent(t) 3544 if se.Body.Reason != "step" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 { 3545 t.Errorf("\ngot %#v\nwant Reason='step' AllThreadsStopped=true ThreadId=1", se) 3546 } 3547 checkStop(t, client, 1, "main.main", 14) 3548 3549 // Make sure we can hit the breakpoints. 3550 client.ContinueRequest(1) 3551 client.ExpectContinueResponse(t) 3552 se = client.ExpectStoppedEvent(t) 3553 if se.Body.Reason != "function breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 { 3554 t.Errorf("\ngot %#v\nwant Reason='function breakpoint' AllThreadsStopped=true ThreadId=1", se) 3555 } 3556 checkStop(t, client, 1, "main.sayhi", 8) 3557 3558 // We can set breakpoints while continuing 3559 client.ContinueRequest(1) 3560 client.ExpectContinueResponse(t) 3561 client.SetFunctionBreakpointsRequest([]dap.FunctionBreakpoint{}) // [8,] => [] 3562 checkBreakpoints(t, []Breakpoint{}, client.ExpectSetFunctionBreakpointsResponse(t).Body.Breakpoints) 3563 client.SetBreakpointsRequest(fixture.Source, []int{16}) // [] => [16] 3564 expectSetBreakpointsResponse(t, client, []Breakpoint{{16, fixture.Source, true, ""}}) 3565 se = client.ExpectStoppedEvent(t) 3566 if se.Body.Reason != "breakpoint" || !se.Body.AllThreadsStopped || se.Body.ThreadId != 1 { 3567 t.Errorf("\ngot %#v\nwant Reason='breakpoint' AllThreadsStopped=true ThreadId=1", se) 3568 } 3569 checkStop(t, client, 1, "main.main", 16) 3570 3571 }, 3572 disconnect: true, 3573 }}) 3574 }) 3575 } 3576 3577 func TestHitConditionBreakpoints(t *testing.T) { 3578 runTest(t, "break", func(client *daptest.Client, fixture protest.Fixture) { 3579 runDebugSessionWithBPs(t, client, "launch", 3580 // Launch 3581 func() { 3582 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3583 }, 3584 // Set breakpoints 3585 fixture.Source, []int{4}, 3586 []onBreakpoint{{ 3587 execute: func() { 3588 client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "4"}, nil) 3589 expectSetBreakpointsResponse(t, client, []Breakpoint{{7, fixture.Source, true, ""}}) 3590 3591 client.ContinueRequest(1) 3592 client.ExpectContinueResponse(t) 3593 client.ExpectStoppedEvent(t) 3594 checkStop(t, client, 1, "main.main", 7) 3595 3596 // Check that we are stopped at the correct value of i. 3597 client.VariablesRequest(localsScope) 3598 locals := client.ExpectVariablesResponse(t) 3599 checkVarExact(t, locals, 0, "i", "i", "4", "int", noChildren) 3600 3601 // Change the hit condition. 3602 client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "% 2"}, nil) 3603 expectSetBreakpointsResponse(t, client, []Breakpoint{{7, fixture.Source, true, ""}}) 3604 3605 client.ContinueRequest(1) 3606 client.ExpectContinueResponse(t) 3607 client.ExpectStoppedEvent(t) 3608 checkStop(t, client, 1, "main.main", 7) 3609 3610 // Check that we are stopped at the correct value of i. 3611 client.VariablesRequest(localsScope) 3612 locals = client.ExpectVariablesResponse(t) 3613 checkVarExact(t, locals, 0, "i", "i", "6", "int", noChildren) 3614 3615 // Expect an error if an assignment is passed. 3616 client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "= 2"}, nil) 3617 expectSetBreakpointsResponse(t, client, []Breakpoint{{-1, "", false, ""}}) 3618 3619 // Change the hit condition. 3620 client.SetBreakpointsRequestWithArgs(fixture.Source, []int{7}, nil, map[int]string{7: "< 8"}, nil) 3621 expectSetBreakpointsResponse(t, client, []Breakpoint{{7, fixture.Source, true, ""}}) 3622 client.ContinueRequest(1) 3623 client.ExpectContinueResponse(t) 3624 client.ExpectStoppedEvent(t) 3625 checkStop(t, client, 1, "main.main", 7) 3626 3627 // Check that we are stopped at the correct value of i. 3628 client.VariablesRequest(localsScope) 3629 locals = client.ExpectVariablesResponse(t) 3630 checkVarExact(t, locals, 0, "i", "i", "7", "int", noChildren) 3631 3632 client.ContinueRequest(1) 3633 client.ExpectContinueResponse(t) 3634 3635 client.ExpectTerminatedEvent(t) 3636 }, 3637 disconnect: false, 3638 }}) 3639 }) 3640 } 3641 3642 // TestLaunchSubstitutePath sets a breakpoint using a path 3643 // that does not exist and expects the substitutePath attribute 3644 // in the launch configuration to take care of the mapping. 3645 func TestLaunchSubstitutePath(t *testing.T) { 3646 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 3647 substitutePathTestHelper(t, fixture, client, "launch", map[string]interface{}{"mode": "exec", "program": fixture.Path}) 3648 }) 3649 } 3650 3651 // TestAttachSubstitutePath sets a breakpoint using a path 3652 // that does not exist and expects the substitutePath attribute 3653 // in the launch configuration to take care of the mapping. 3654 func TestAttachSubstitutePath(t *testing.T) { 3655 if runtime.GOOS == "freebsd" { 3656 t.SkipNow() 3657 } 3658 if runtime.GOOS == "windows" { 3659 t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details") 3660 } 3661 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 3662 cmd := execFixture(t, fixture) 3663 3664 substitutePathTestHelper(t, fixture, client, "attach", map[string]interface{}{"mode": "local", "processId": cmd.Process.Pid}) 3665 }) 3666 } 3667 3668 func substitutePathTestHelper(t *testing.T, fixture protest.Fixture, client *daptest.Client, request string, launchAttachConfig map[string]interface{}) { 3669 t.Helper() 3670 nonexistentDir := filepath.Join(string(filepath.Separator), "path", "that", "does", "not", "exist") 3671 if runtime.GOOS == "windows" { 3672 nonexistentDir = "C:" + nonexistentDir 3673 } 3674 3675 launchAttachConfig["stopOnEntry"] = false 3676 // The rules in 'substitutePath' will be applied as follows: 3677 // - mapping paths from client to server: 3678 // The first rule["from"] to match a prefix of 'path' will be applied: 3679 // strings.Replace(path, rule["from"], rule["to"], 1) 3680 // - mapping paths from server to client: 3681 // The first rule["to"] to match a prefix of 'path' will be applied: 3682 // strings.Replace(path, rule["to"], rule["from"], 1) 3683 launchAttachConfig["substitutePath"] = []map[string]string{ 3684 {"from": nonexistentDir, "to": filepath.Dir(fixture.Source)}, 3685 // Since the path mappings are ordered, when converting from client path to 3686 // server path, this mapping will not apply, because nonexistentDir appears in 3687 // an earlier rule. 3688 {"from": nonexistentDir, "to": "this_is_a_bad_path"}, 3689 // Since the path mappings are ordered, when converting from server path to 3690 // client path, this mapping will not apply, because filepath.Dir(fixture.Source) 3691 // appears in an earlier rule. 3692 {"from": "this_is_a_bad_path", "to": filepath.Dir(fixture.Source)}, 3693 } 3694 3695 runDebugSessionWithBPs(t, client, request, 3696 func() { 3697 switch request { 3698 case "attach": 3699 client.AttachRequest(launchAttachConfig) 3700 client.ExpectCapabilitiesEventSupportTerminateDebuggee(t) 3701 case "launch": 3702 client.LaunchRequestWithArgs(launchAttachConfig) 3703 default: 3704 t.Fatalf("invalid request: %s", request) 3705 } 3706 }, 3707 // Set breakpoints 3708 filepath.Join(nonexistentDir, "loopprog.go"), []int{8}, 3709 []onBreakpoint{{ 3710 3711 execute: func() { 3712 checkStop(t, client, 1, "main.loop", 8) 3713 }, 3714 disconnect: true, 3715 }}) 3716 } 3717 3718 // execFixture runs the binary fixture.Path and hooks up stdout and stderr 3719 // to os.Stdout and os.Stderr. 3720 func execFixture(t *testing.T, fixture protest.Fixture) *exec.Cmd { 3721 t.Helper() 3722 // TODO(polina): do I need to sanity check testBackend and runtime.GOOS? 3723 cmd := exec.Command(fixture.Path) 3724 cmd.Stdout = os.Stdout 3725 cmd.Stderr = os.Stderr 3726 if err := cmd.Start(); err != nil { 3727 t.Fatal(err) 3728 } 3729 return cmd 3730 } 3731 3732 // TestWorkingDir executes to a breakpoint and tests that the specified 3733 // working directory is the one used to run the program. 3734 func TestWorkingDir(t *testing.T) { 3735 runTest(t, "workdir", func(client *daptest.Client, fixture protest.Fixture) { 3736 wd := os.TempDir() 3737 // For Darwin `os.TempDir()` returns `/tmp` which is symlink to `/private/tmp`. 3738 if runtime.GOOS == "darwin" { 3739 wd = "/private/tmp" 3740 } 3741 runDebugSessionWithBPs(t, client, "launch", 3742 // Launch 3743 func() { 3744 client.LaunchRequestWithArgs(map[string]interface{}{ 3745 "mode": "exec", 3746 "program": fixture.Path, 3747 "stopOnEntry": false, 3748 "cwd": wd, 3749 }) 3750 }, 3751 // Set breakpoints 3752 fixture.Source, []int{10}, // b main.main 3753 []onBreakpoint{{ 3754 execute: func() { 3755 checkStop(t, client, 1, "main.main", 10) 3756 client.VariablesRequest(localsScope) 3757 locals := client.ExpectVariablesResponse(t) 3758 checkChildren(t, locals, "Locals", 2) 3759 for i := range locals.Body.Variables { 3760 switch locals.Body.Variables[i].Name { 3761 case "pwd": 3762 checkVarExact(t, locals, i, "pwd", "pwd", fmt.Sprintf("%q", wd), "string", noChildren) 3763 case "err": 3764 checkVarExact(t, locals, i, "err", "err", "error nil", "error", noChildren) 3765 } 3766 } 3767 }, 3768 disconnect: false, 3769 }}) 3770 }) 3771 } 3772 3773 // checkEval is a helper for verifying the values within an EvaluateResponse. 3774 // 3775 // value - the value of the evaluated expression 3776 // hasRef - true if the evaluated expression should have children and therefore a non-0 variable reference 3777 // ref - reference to retrieve children of this evaluated expression (0 if none) 3778 func checkEval(t *testing.T, got *dap.EvaluateResponse, value string, hasRef bool) (ref int) { 3779 t.Helper() 3780 if got.Body.Result != value || (got.Body.VariablesReference > 0) != hasRef { 3781 t.Errorf("\ngot %#v\nwant Result=%q hasRef=%t", got, value, hasRef) 3782 } 3783 return got.Body.VariablesReference 3784 } 3785 3786 // checkEvalIndexed is a helper for verifying the values within an EvaluateResponse. 3787 // 3788 // value - the value of the evaluated expression 3789 // hasRef - true if the evaluated expression should have children and therefore a non-0 variable reference 3790 // ref - reference to retrieve children of this evaluated expression (0 if none) 3791 func checkEvalIndexed(t *testing.T, got *dap.EvaluateResponse, value string, hasRef bool, indexed, named int) (ref int) { 3792 t.Helper() 3793 if got.Body.Result != value || (got.Body.VariablesReference > 0) != hasRef || got.Body.IndexedVariables != indexed || got.Body.NamedVariables != named { 3794 t.Errorf("\ngot %#v\nwant Result=%q hasRef=%t IndexedVariables=%d NamedVariables=%d", got, value, hasRef, indexed, named) 3795 } 3796 return got.Body.VariablesReference 3797 } 3798 3799 func checkEvalRegex(t *testing.T, got *dap.EvaluateResponse, valueRegex string, hasRef bool) (ref int) { 3800 t.Helper() 3801 matched, _ := regexp.MatchString(valueRegex, got.Body.Result) 3802 if !matched || (got.Body.VariablesReference > 0) != hasRef { 3803 t.Errorf("\ngot %#v\nwant Result=%q hasRef=%t", got, valueRegex, hasRef) 3804 } 3805 return got.Body.VariablesReference 3806 } 3807 3808 func TestEvaluateRequest(t *testing.T) { 3809 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 3810 runDebugSessionWithBPs(t, client, "launch", 3811 // Launch 3812 func() { 3813 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3814 }, 3815 fixture.Source, []int{}, // Breakpoint set in the program 3816 []onBreakpoint{{ // Stop at first breakpoint 3817 execute: func() { 3818 checkStop(t, client, 1, "main.foobar", 66) 3819 3820 // Variable lookup 3821 client.EvaluateRequest("a2", 1000, "this context will be ignored") 3822 got := client.ExpectEvaluateResponse(t) 3823 checkEval(t, got, "6", noChildren) 3824 3825 client.EvaluateRequest("a5", 1000, "this context will be ignored") 3826 got = client.ExpectEvaluateResponse(t) 3827 ref := checkEval(t, got, "[]int len: 5, cap: 5, [1,2,3,4,5]", hasChildren) 3828 if ref > 0 { 3829 client.VariablesRequest(ref) 3830 a5 := client.ExpectVariablesResponse(t) 3831 checkChildren(t, a5, "a5", 5) 3832 checkVarExact(t, a5, 0, "[0]", "(a5)[0]", "1", "int", noChildren) 3833 checkVarExact(t, a5, 4, "[4]", "(a5)[4]", "5", "int", noChildren) 3834 validateEvaluateName(t, client, a5, 0) 3835 validateEvaluateName(t, client, a5, 4) 3836 } 3837 3838 // Variable lookup that's not fully loaded 3839 client.EvaluateRequest("ba", 1000, "this context will be ignored") 3840 got = client.ExpectEvaluateResponse(t) 3841 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) 3842 3843 // All (binary and unary) on basic types except <-, ++ and -- 3844 client.EvaluateRequest("1+1", 1000, "this context will be ignored") 3845 got = client.ExpectEvaluateResponse(t) 3846 checkEval(t, got, "2", noChildren) 3847 3848 // Comparison operators on any type 3849 client.EvaluateRequest("1<2", 1000, "this context will be ignored") 3850 got = client.ExpectEvaluateResponse(t) 3851 checkEval(t, got, "true", noChildren) 3852 3853 // Type casts between numeric types 3854 client.EvaluateRequest("int(2.3)", 1000, "this context will be ignored") 3855 got = client.ExpectEvaluateResponse(t) 3856 checkEval(t, got, "2", noChildren) 3857 3858 // Type casts of integer constants into any pointer type and vice versa 3859 client.EvaluateRequest("(*int)(2)", 1000, "this context will be ignored") 3860 got = client.ExpectEvaluateResponse(t) 3861 ref = checkEvalRegex(t, got, `\*\(unreadable .+\)`, hasChildren) 3862 if ref > 0 { 3863 client.VariablesRequest(ref) 3864 val := client.ExpectVariablesResponse(t) 3865 checkChildren(t, val, "*(*int)(2)", 1) 3866 checkVarRegex(t, val, 0, "^$", `\(\*\(\(\*int\)\(2\)\)\)`, `\(unreadable .+\)`, "int", noChildren) 3867 validateEvaluateName(t, client, val, 0) 3868 } 3869 3870 // Type casts between string, []byte and []rune 3871 client.EvaluateRequest("[]byte(\"ABC€\")", 1000, "this context will be ignored") 3872 got = client.ExpectEvaluateResponse(t) 3873 checkEvalIndexed(t, got, "[]uint8 len: 6, cap: 6, [65,66,67,226,130,172]", noChildren, 6, 0) 3874 3875 // Struct member access (i.e. somevar.memberfield) 3876 client.EvaluateRequest("ms.Nest.Level", 1000, "this context will be ignored") 3877 got = client.ExpectEvaluateResponse(t) 3878 checkEval(t, got, "1", noChildren) 3879 3880 // Slicing and indexing operators on arrays, slices and strings 3881 client.EvaluateRequest("a5[4]", 1000, "this context will be ignored") 3882 got = client.ExpectEvaluateResponse(t) 3883 checkEval(t, got, "5", noChildren) 3884 3885 // Map access 3886 client.EvaluateRequest("mp[1]", 1000, "this context will be ignored") 3887 got = client.ExpectEvaluateResponse(t) 3888 ref = checkEval(t, got, "interface {}(int) 42", hasChildren) 3889 if ref > 0 { 3890 client.VariablesRequest(ref) 3891 expr := client.ExpectVariablesResponse(t) 3892 checkChildren(t, expr, "mp[1]", 1) 3893 checkVarExact(t, expr, 0, "data", "(mp[1]).(data)", "42", "int", noChildren) 3894 validateEvaluateName(t, client, expr, 0) 3895 } 3896 3897 // Pointer dereference 3898 client.EvaluateRequest("*ms.Nest", 1000, "this context will be ignored") 3899 got = client.ExpectEvaluateResponse(t) 3900 ref = checkEvalRegex(t, got, `main\.Nest {Level: 1, Nest: \*main.Nest {Level: 2, Nest: \*\(\*main\.Nest\)\(0x[0-9a-f]+\)}}`, hasChildren) 3901 if ref > 0 { 3902 client.VariablesRequest(ref) 3903 expr := client.ExpectVariablesResponse(t) 3904 checkChildren(t, expr, "*ms.Nest", 2) 3905 checkVarExact(t, expr, 0, "Level", "(*ms.Nest).Level", "1", "int", noChildren) 3906 validateEvaluateName(t, client, expr, 0) 3907 } 3908 3909 // Calls to builtin functions: cap, len, complex, imag and real 3910 client.EvaluateRequest("len(a5)", 1000, "this context will be ignored") 3911 got = client.ExpectEvaluateResponse(t) 3912 checkEval(t, got, "5", noChildren) 3913 3914 // Type assertion on interface variables (i.e. somevar.(concretetype)) 3915 client.EvaluateRequest("mp[1].(int)", 1000, "this context will be ignored") 3916 got = client.ExpectEvaluateResponse(t) 3917 checkEval(t, got, "42", noChildren) 3918 }, 3919 disconnect: false, 3920 }, { // Stop at second breakpoint 3921 execute: func() { 3922 checkStop(t, client, 1, "main.barfoo", 27) 3923 3924 // Top-most frame 3925 client.EvaluateRequest("a1", 1000, "this context will be ignored") 3926 got := client.ExpectEvaluateResponse(t) 3927 checkEval(t, got, "\"bur\"", noChildren) 3928 // No frame defaults to top-most frame 3929 client.EvaluateRequest("a1", 0, "this context will be ignored") 3930 got = client.ExpectEvaluateResponse(t) 3931 checkEval(t, got, "\"bur\"", noChildren) 3932 // Next frame 3933 client.EvaluateRequest("a1", 1001, "this context will be ignored") 3934 got = client.ExpectEvaluateResponse(t) 3935 checkEval(t, got, "\"foofoofoofoofoofoo\"", noChildren) 3936 // Next frame 3937 client.EvaluateRequest("a1", 1002, "any context but watch") 3938 erres := client.ExpectVisibleErrorResponse(t) 3939 if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" { 3940 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres) 3941 } 3942 client.EvaluateRequest("a1", 1002, "watch") 3943 erres = client.ExpectInvisibleErrorResponse(t) 3944 if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" { 3945 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres) 3946 } 3947 client.EvaluateRequest("a1", 1002, "repl") 3948 erres = client.ExpectInvisibleErrorResponse(t) 3949 if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" { 3950 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres) 3951 } 3952 client.EvaluateRequest("a1", 1002, "hover") 3953 erres = client.ExpectInvisibleErrorResponse(t) 3954 if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" { 3955 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres) 3956 } 3957 client.EvaluateRequest("a1", 1002, "clipboard") 3958 erres = client.ExpectVisibleErrorResponse(t) 3959 if erres.Body.Error.Format != "Unable to evaluate expression: could not find symbol value for a1" { 3960 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: could not find symbol value for a1\"", erres) 3961 } 3962 }, 3963 disconnect: false, 3964 }}) 3965 }) 3966 } 3967 3968 func formatConfig(depth int, showGlobals, showRegisters bool, goroutineFilters string, hideSystemGoroutines bool, substitutePath [][2]string) string { 3969 formatStr := `stackTraceDepth %d 3970 showGlobalVariables %v 3971 showRegisters %v 3972 goroutineFilters %q 3973 hideSystemGoroutines %v 3974 substitutePath %v 3975 ` 3976 return fmt.Sprintf(formatStr, depth, showGlobals, showRegisters, goroutineFilters, hideSystemGoroutines, substitutePath) 3977 } 3978 3979 func TestEvaluateCommandRequest(t *testing.T) { 3980 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 3981 runDebugSessionWithBPs(t, client, "launch", 3982 // Launch 3983 func() { 3984 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 3985 }, 3986 fixture.Source, []int{}, // Breakpoint set in the program 3987 []onBreakpoint{{ // Stop at first breakpoint 3988 execute: func() { 3989 checkStop(t, client, 1, "main.foobar", 66) 3990 3991 // Request help. 3992 const dlvHelp = `The following commands are available: 3993 dlv help (alias: h) Prints the help message. 3994 dlv config Changes configuration parameters. 3995 dlv sources (alias: s) Print list of source files. 3996 3997 Type 'dlv help' followed by a command for full documentation. 3998 ` 3999 client.EvaluateRequest("dlv help", 1000, "repl") 4000 got := client.ExpectEvaluateResponse(t) 4001 checkEval(t, got, dlvHelp, noChildren) 4002 4003 client.EvaluateRequest("dlv help config", 1000, "repl") 4004 got = client.ExpectEvaluateResponse(t) 4005 checkEval(t, got, msgConfig, noChildren) 4006 4007 // Test config. 4008 client.EvaluateRequest("dlv config -list", 1000, "repl") 4009 got = client.ExpectEvaluateResponse(t) 4010 checkEval(t, got, formatConfig(50, false, false, "", false, [][2]string{}), noChildren) 4011 4012 // Read and modify showGlobalVariables. 4013 client.EvaluateRequest("dlv config -list showGlobalVariables", 1000, "repl") 4014 got = client.ExpectEvaluateResponse(t) 4015 checkEval(t, got, "showGlobalVariables\tfalse\n", noChildren) 4016 4017 client.ScopesRequest(1000) 4018 scopes := client.ExpectScopesResponse(t) 4019 if len(scopes.Body.Scopes) > 1 { 4020 t.Errorf("\ngot %#v\nwant len(scopes)=1 (Locals)", scopes) 4021 } 4022 checkScope(t, scopes, 0, "Locals", -1) 4023 4024 client.EvaluateRequest("dlv config showGlobalVariables true", 1000, "repl") 4025 client.ExpectInvalidatedEvent(t) 4026 got = client.ExpectEvaluateResponse(t) 4027 checkEval(t, got, "showGlobalVariables\ttrue\n\nUpdated", noChildren) 4028 4029 client.EvaluateRequest("dlv config -list", 1000, "repl") 4030 got = client.ExpectEvaluateResponse(t) 4031 checkEval(t, got, formatConfig(50, true, false, "", false, [][2]string{}), noChildren) 4032 4033 client.ScopesRequest(1000) 4034 scopes = client.ExpectScopesResponse(t) 4035 if len(scopes.Body.Scopes) < 2 { 4036 t.Errorf("\ngot %#v\nwant len(scopes)=2 (Locals & Globals)", scopes) 4037 } 4038 checkScope(t, scopes, 0, "Locals", -1) 4039 checkScope(t, scopes, 1, "Globals (package main)", -1) 4040 4041 // Read and modify substitutePath. 4042 client.EvaluateRequest("dlv config -list substitutePath", 1000, "repl") 4043 got = client.ExpectEvaluateResponse(t) 4044 checkEval(t, got, "substitutePath\t[]\n", noChildren) 4045 4046 client.EvaluateRequest(fmt.Sprintf("dlv config substitutePath %q %q", "my/client/path", "your/server/path"), 1000, "repl") 4047 got = client.ExpectEvaluateResponse(t) 4048 checkEval(t, got, "substitutePath\t[[my/client/path your/server/path]]\n\nUpdated", noChildren) 4049 4050 client.EvaluateRequest(fmt.Sprintf("dlv config substitutePath %q %q", "my/client/path", "new/your/server/path"), 1000, "repl") 4051 got = client.ExpectEvaluateResponse(t) 4052 checkEval(t, got, "substitutePath\t[[my/client/path new/your/server/path]]\n\nUpdated", noChildren) 4053 4054 client.EvaluateRequest(fmt.Sprintf("dlv config substitutePath %q", "my/client/path"), 1000, "repl") 4055 got = client.ExpectEvaluateResponse(t) 4056 checkEval(t, got, "substitutePath\t[]\n\nUpdated", noChildren) 4057 4058 // Test sources. 4059 client.EvaluateRequest("dlv sources", 1000, "repl") 4060 got = client.ExpectEvaluateResponse(t) 4061 if !strings.Contains(got.Body.Result, fixture.Source) { 4062 t.Errorf("\ngot: %#v, want sources contains %s", got, fixture.Source) 4063 } 4064 4065 client.EvaluateRequest(fmt.Sprintf("dlv sources .*%s", strings.ReplaceAll(filepath.Base(fixture.Source), ".", "\\.")), 1000, "repl") 4066 got = client.ExpectEvaluateResponse(t) 4067 if got.Body.Result != fixture.Source { 4068 t.Errorf("\ngot: %#v, want sources=%q", got, fixture.Source) 4069 } 4070 4071 client.EvaluateRequest("dlv sources nonexistentsource", 1000, "repl") 4072 got = client.ExpectEvaluateResponse(t) 4073 if got.Body.Result != "" { 4074 t.Errorf("\ngot: %#v, want sources=\"\"", got) 4075 } 4076 4077 // Test bad inputs. 4078 client.EvaluateRequest("dlv help bad", 1000, "repl") 4079 client.ExpectErrorResponse(t) 4080 4081 client.EvaluateRequest("dlv bad", 1000, "repl") 4082 client.ExpectErrorResponse(t) 4083 }, 4084 disconnect: true, 4085 }}) 4086 }) 4087 } 4088 4089 // From testvariables2 fixture 4090 const ( 4091 // As defined in the code 4092 longstr = `"very long string 0123456789a0123456789b0123456789c0123456789d0123456789e0123456789f0123456789g012345678h90123456789i0123456789j0123456789"` 4093 // Loaded with MaxStringLen=64 4094 longstrLoaded64 = `"very long string 0123456789a0123456789b0123456789c0123456789d012...+73 more"` 4095 longstrLoaded64re = `\"very long string 0123456789a0123456789b0123456789c0123456789d012\.\.\.\+73 more\"` 4096 ) 4097 4098 // TestVariableValueTruncation tests that in certain cases 4099 // we truncate the loaded variable values to make display more user-friendly. 4100 func TestVariableValueTruncation(t *testing.T) { 4101 runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) { 4102 runDebugSessionWithBPs(t, client, "launch", 4103 // Launch 4104 func() { 4105 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4106 }, 4107 // Breakpoint set within the program 4108 fixture.Source, []int{}, 4109 []onBreakpoint{{ 4110 execute: func() { 4111 checkStop(t, client, 1, "main.main", -1) 4112 4113 client.VariablesRequest(localsScope) 4114 locals := client.ExpectVariablesResponse(t) 4115 4116 // Compound variable values may be truncated 4117 m1Full := `map\[string\]main\.astruct \[(\"[A-Za-z]+\": {A: [0-9]+, B: [0-9]+}, )+,\.\.\.\+2 more\]` 4118 m1Part := `map\[string\]main\.astruct \[(\"[A-Za-z]+\": {A: [0-9]+, B: [0-9]+}, )+.+\.\.\.` 4119 4120 // In variable responses 4121 checkVarRegex(t, locals, -1, "m1", "m1", m1Part, `map\[string\]main\.astruct`, hasChildren) 4122 4123 // In evaluate responses (select contexts only) 4124 tests := []struct { 4125 context string 4126 want string 4127 }{ 4128 {"", m1Part}, 4129 {"watch", m1Part}, 4130 {"repl", m1Part}, 4131 {"hover", m1Part}, 4132 {"variables", m1Full}, // used for copy 4133 {"clipboard", m1Full}, // used for copy 4134 {"somethingelse", m1Part}, 4135 } 4136 for _, tc := range tests { 4137 t.Run(tc.context, func(t *testing.T) { 4138 client.EvaluateRequest("m1", 0, tc.context) 4139 checkEvalRegex(t, client.ExpectEvaluateResponse(t), tc.want, hasChildren) 4140 }) 4141 } 4142 4143 // Compound map keys may be truncated even further 4144 // As the keys are always inside of a map container, 4145 // this applies to variables requests only, not evalute requests. 4146 4147 // key - compound, value - scalar (inlined key:value display) => truncate key if too long 4148 ref := checkVarExact(t, locals, -1, "m5", "m5", "map[main.C]int [{s: "+longstr+"}: 1, ]", "map[main.C]int", hasChildren) 4149 if ref > 0 { 4150 client.VariablesRequest(ref) 4151 // Key format: <truncated>... @<address> 4152 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) 4153 } 4154 // key - scalar, value - scalar (inlined key:value display) => key not truncated 4155 ref = checkVarExact(t, locals, -1, "m6", "m6", "map[string]int ["+longstr+": 123, ]", "map[string]int", hasChildren) 4156 if ref > 0 { 4157 client.VariablesRequest(ref) 4158 checkVarExact(t, client.ExpectVariablesResponse(t), 1, longstr, `m6[`+longstr+`]`, "123", "string: int", noChildren) 4159 } 4160 // key - compound, value - compound (array-like display) => key not truncated 4161 ref = checkVarExact(t, locals, -1, "m7", "m7", "map[main.C]main.C [{s: "+longstr+"}: {s: \"hello\"}, ]", "map[main.C]main.C", hasChildren) 4162 if ref > 0 { 4163 client.VariablesRequest(ref) 4164 m7 := client.ExpectVariablesResponse(t) 4165 checkVarRegex(t, m7, 1, "[key 0]", `\(\*\(\*\"main\.C\"\)\(0x[0-9a-f]+\)\)`, `main\.C {s: `+longstr+`}`, `main\.C`, hasChildren) 4166 } 4167 }, 4168 disconnect: true, 4169 }}) 4170 }) 4171 } 4172 4173 // TestVariableLoadingOfLongStrings tests that different string loading limits 4174 // apply that depending on the context. 4175 func TestVariableLoadingOfLongStrings(t *testing.T) { 4176 protest.MustSupportFunctionCalls(t, testBackend) 4177 runTest(t, "longstrings", func(client *daptest.Client, fixture protest.Fixture) { 4178 runDebugSessionWithBPs(t, client, "launch", 4179 // Launch 4180 func() { 4181 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4182 }, 4183 // Breakpoint set within the program 4184 fixture.Source, []int{}, 4185 []onBreakpoint{{ 4186 execute: func() { 4187 checkStop(t, client, 1, "main.main", -1) 4188 4189 client.VariablesRequest(localsScope) 4190 locals := client.ExpectVariablesResponse(t) 4191 4192 // Limits vary for evaluate requests with different contexts 4193 tests := []struct { 4194 context string 4195 limit int 4196 }{ 4197 {"", DefaultLoadConfig.MaxStringLen}, 4198 {"watch", DefaultLoadConfig.MaxStringLen}, 4199 {"repl", maxSingleStringLen}, 4200 {"hover", maxSingleStringLen}, 4201 {"variables", maxSingleStringLen}, 4202 {"clipboard", maxSingleStringLen}, 4203 {"somethingelse", DefaultLoadConfig.MaxStringLen}, 4204 } 4205 for _, tc := range tests { 4206 t.Run(tc.context, func(t *testing.T) { 4207 // Long string by itself (limits vary) 4208 client.EvaluateRequest("s4097", 0, tc.context) 4209 want := fmt.Sprintf(`"x+\.\.\.\+%d more"`, 4097-tc.limit) 4210 checkEvalRegex(t, client.ExpectEvaluateResponse(t), want, noChildren) 4211 4212 // Evaluated container variables return values with minimally loaded 4213 // strings, which are further truncated for displaying, so we 4214 // can't test for loading limit except in contexts where an untruncated 4215 // value is returned. 4216 client.EvaluateRequest("&s4097", 0, tc.context) 4217 switch tc.context { 4218 case "variables", "clipboard": 4219 want = fmt.Sprintf(`\*"x+\.\.\.\+%d more`, 4097-DefaultLoadConfig.MaxStringLen) 4220 default: 4221 want = fmt.Sprintf(`\*"x{%d}\.\.\.`, maxVarValueLen-2) 4222 } 4223 checkEvalRegex(t, client.ExpectEvaluateResponse(t), want, hasChildren) 4224 }) 4225 } 4226 4227 // Long strings returned from calls are subject to a different limit, 4228 // same limit regardless of context 4229 for _, context := range []string{"", "watch", "repl", "variables", "hover", "clipboard", "somethingelse"} { 4230 t.Run(context, func(t *testing.T) { 4231 client.EvaluateRequest(`call buildString(4097)`, 1000, context) 4232 want := fmt.Sprintf(`"x+\.\.\.\+%d more"`, 4097-maxStringLenInCallRetVars) 4233 got := client.ExpectEvaluateResponse(t) 4234 checkEvalRegex(t, got, want, hasChildren) 4235 }) 4236 } 4237 4238 // Variables requests use the most conservative loading limit 4239 checkVarRegex(t, locals, -1, "s513", "s513", `"x{512}\.\.\.\+1 more"`, "string", noChildren) 4240 // Container variables are subject to additional stricter value truncation that drops +more part 4241 checkVarRegex(t, locals, -1, "nested", "nested", `map\[int\]string \[513: \"x+\.\.\.`, "string", hasChildren) 4242 }, 4243 disconnect: true, 4244 }}) 4245 }) 4246 } 4247 4248 func TestEvaluateCallRequest(t *testing.T) { 4249 protest.MustSupportFunctionCalls(t, testBackend) 4250 runTest(t, "fncall", func(client *daptest.Client, fixture protest.Fixture) { 4251 runDebugSessionWithBPs(t, client, "launch", 4252 // Launch 4253 func() { 4254 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4255 }, 4256 fixture.Source, []int{88}, 4257 []onBreakpoint{{ // Stop in makeclos() 4258 execute: func() { 4259 checkStop(t, client, 1, "main.makeclos", 88) 4260 4261 // Topmost frame: both types of expressions should work 4262 client.EvaluateRequest("callstacktrace", 1000, "this context will be ignored") 4263 client.ExpectEvaluateResponse(t) 4264 client.EvaluateRequest("call callstacktrace()", 1000, "this context will be ignored") 4265 client.ExpectEvaluateResponse(t) 4266 4267 // Next frame: only non-call expressions will work 4268 client.EvaluateRequest("callstacktrace", 1001, "this context will be ignored") 4269 client.ExpectEvaluateResponse(t) 4270 client.EvaluateRequest("call callstacktrace()", 1001, "not watch") 4271 erres := client.ExpectVisibleErrorResponse(t) 4272 if erres.Body.Error.Format != "Unable to evaluate expression: call is only supported with topmost stack frame" { 4273 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: call is only supported with topmost stack frame\"", erres) 4274 } 4275 4276 // A call can stop on a breakpoint 4277 client.EvaluateRequest("call callbreak()", 1000, "not watch") 4278 s := client.ExpectStoppedEvent(t) 4279 if s.Body.Reason != "hardcoded breakpoint" { 4280 t.Errorf("\ngot %#v\nwant Reason=\"hardcoded breakpoint\"", s) 4281 } 4282 erres = client.ExpectVisibleErrorResponse(t) 4283 if erres.Body.Error.Format != "Unable to evaluate expression: call stopped" { 4284 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: call stopped\"", erres) 4285 } 4286 4287 // A call during a call causes an error 4288 client.EvaluateRequest("call callstacktrace()", 1000, "not watch") 4289 erres = client.ExpectVisibleErrorResponse(t) 4290 if erres.Body.Error.Format != "Unable to evaluate expression: cannot call function while another function call is already in progress" { 4291 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: cannot call function while another function call is already in progress\"", erres) 4292 } 4293 4294 // Complete the call and get back to original breakpoint in makeclos() 4295 client.ContinueRequest(1) 4296 client.ExpectContinueResponse(t) 4297 client.ExpectStoppedEvent(t) 4298 checkStop(t, client, 1, "main.makeclos", 88) 4299 4300 // Inject a call for the same function that is stopped at breakpoint: 4301 // it might stop at the exact same breakpoint on the same goroutine, 4302 // but we should still detect that its an injected call that stopped 4303 // and not the return to the original point of injection after it 4304 // completed. 4305 client.EvaluateRequest("call makeclos(nil)", 1000, "not watch") 4306 stopped := client.ExpectStoppedEvent(t) 4307 erres = client.ExpectVisibleErrorResponse(t) 4308 if erres.Body.Error.Format != "Unable to evaluate expression: call stopped" { 4309 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: call stopped\"", erres) 4310 } 4311 checkStop(t, client, stopped.Body.ThreadId, "main.makeclos", 88) 4312 4313 // Complete the call and get back to original breakpoint in makeclos() 4314 client.ContinueRequest(1) 4315 client.ExpectContinueResponse(t) 4316 client.ExpectStoppedEvent(t) 4317 checkStop(t, client, 1, "main.makeclos", 88) 4318 }, 4319 disconnect: false, 4320 }, { // Stop at runtime breakpoint 4321 execute: func() { 4322 checkStop(t, client, 1, "main.main", -1) 4323 4324 // No return values 4325 client.EvaluateRequest("call call0(1, 2)", 1000, "this context will be ignored") 4326 got := client.ExpectEvaluateResponse(t) 4327 checkEval(t, got, "", noChildren) 4328 // One unnamed return value 4329 client.EvaluateRequest("call call1(one, two)", 1000, "this context will be ignored") 4330 got = client.ExpectEvaluateResponse(t) 4331 ref := checkEval(t, got, "3", hasChildren) 4332 if ref > 0 { 4333 client.VariablesRequest(ref) 4334 rv := client.ExpectVariablesResponse(t) 4335 checkChildren(t, rv, "rv", 1) 4336 checkVarExact(t, rv, 0, "~r2", "", "3", "int", noChildren) 4337 } 4338 // One named return value 4339 // Panic doesn't panic, but instead returns the error as a named return variable 4340 client.EvaluateRequest("call callpanic()", 1000, "this context will be ignored") 4341 got = client.ExpectEvaluateResponse(t) 4342 ref = checkEval(t, got, `interface {}(string) "callpanic panicked"`, hasChildren) 4343 if ref > 0 { 4344 client.VariablesRequest(ref) 4345 rv := client.ExpectVariablesResponse(t) 4346 checkChildren(t, rv, "rv", 1) 4347 ref = checkVarExact(t, rv, 0, "~panic", "", `interface {}(string) "callpanic panicked"`, "interface {}", hasChildren) 4348 if ref > 0 { 4349 client.VariablesRequest(ref) 4350 p := client.ExpectVariablesResponse(t) 4351 checkChildren(t, p, "~panic", 1) 4352 checkVarExact(t, p, 0, "data", "", "\"callpanic panicked\"", "string", noChildren) 4353 } 4354 } 4355 // Multiple return values 4356 client.EvaluateRequest("call call2(one, two)", 1000, "this context will be ignored") 4357 got = client.ExpectEvaluateResponse(t) 4358 ref = checkEval(t, got, "1, 2", hasChildren) 4359 if ref > 0 { 4360 client.VariablesRequest(ref) 4361 rvs := client.ExpectVariablesResponse(t) 4362 checkChildren(t, rvs, "rvs", 2) 4363 checkVarExact(t, rvs, 0, "~r2", "", "1", "int", noChildren) 4364 checkVarExact(t, rvs, 1, "~r3", "", "2", "int", noChildren) 4365 } 4366 // No frame defaults to top-most frame 4367 client.EvaluateRequest("call call1(one, two)", 0, "this context will be ignored") 4368 got = client.ExpectEvaluateResponse(t) 4369 checkEval(t, got, "3", hasChildren) 4370 // Extra spaces don't matter 4371 client.EvaluateRequest(" call call1(one, one) ", 0, "this context will be ignored") 4372 got = client.ExpectEvaluateResponse(t) 4373 checkEval(t, got, "2", hasChildren) 4374 // Just 'call', even with extra space, is treated as {expression} 4375 client.EvaluateRequest("call ", 1000, "watch") 4376 got = client.ExpectEvaluateResponse(t) 4377 checkEval(t, got, "\"this is a variable named `call`\"", noChildren) 4378 4379 // Call error 4380 client.EvaluateRequest("call call1(one)", 1000, "watch") 4381 erres := client.ExpectInvisibleErrorResponse(t) 4382 if erres.Body.Error.Format != "Unable to evaluate expression: not enough arguments" { 4383 t.Errorf("\ngot %#v\nwant Format=\"Unable to evaluate expression: not enough arguments\"", erres) 4384 } 4385 4386 // Assignment - expect no error, but no return value. 4387 client.EvaluateRequest("call one = two", 1000, "this context will be ignored") 4388 got = client.ExpectEvaluateResponse(t) 4389 checkEval(t, got, "", noChildren) 4390 // Check one=two was applied. 4391 client.EvaluateRequest("one", 1000, "repl") 4392 got = client.ExpectEvaluateResponse(t) 4393 checkEval(t, got, "2", noChildren) 4394 4395 // Call can exit. 4396 client.EvaluateRequest("call callexit()", 1000, "this context will be ignored") 4397 client.ExpectTerminatedEvent(t) 4398 if res := client.ExpectVisibleErrorResponse(t); !strings.Contains(res.Body.Error.Format, "terminated") { 4399 t.Errorf("\ngot %#v\nwant Format=.*terminated.*", res) 4400 } 4401 }, 4402 terminated: true, 4403 disconnect: true, 4404 }}) 4405 }) 4406 } 4407 4408 func TestNextAndStep(t *testing.T) { 4409 runTest(t, "testinline", func(client *daptest.Client, fixture protest.Fixture) { 4410 runDebugSessionWithBPs(t, client, "launch", 4411 // Launch 4412 func() { 4413 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4414 }, 4415 // Set breakpoints 4416 fixture.Source, []int{11}, 4417 []onBreakpoint{{ // Stop at line 11 4418 execute: func() { 4419 checkStop(t, client, 1, "main.initialize", 11) 4420 4421 expectStop := func(fun string, line int) { 4422 t.Helper() 4423 se := client.ExpectStoppedEvent(t) 4424 if se.Body.Reason != "step" || se.Body.ThreadId != 1 || !se.Body.AllThreadsStopped { 4425 t.Errorf("got %#v, want Reason=\"step\", ThreadId=1, AllThreadsStopped=true", se) 4426 } 4427 checkStop(t, client, 1, fun, line) 4428 } 4429 4430 client.StepOutRequest(1) 4431 client.ExpectStepOutResponse(t) 4432 expectStop("main.main", 18) 4433 4434 client.NextRequest(1) 4435 client.ExpectNextResponse(t) 4436 expectStop("main.main", 19) 4437 4438 client.StepInRequest(1) 4439 client.ExpectStepInResponse(t) 4440 expectStop("main.inlineThis", 5) 4441 4442 client.NextRequest(-1000) 4443 client.ExpectNextResponse(t) 4444 if se := client.ExpectStoppedEvent(t); se.Body.Reason != "error" || se.Body.Text != "unknown goroutine -1000" { 4445 t.Errorf("got %#v, want Reason=\"error\", Text=\"unknown goroutine -1000\"", se) 4446 } 4447 checkStop(t, client, 1, "main.inlineThis", 5) 4448 }, 4449 disconnect: false, 4450 }}) 4451 }) 4452 } 4453 4454 func TestHardCodedBreakpoints(t *testing.T) { 4455 runTest(t, "consts", func(client *daptest.Client, fixture protest.Fixture) { 4456 runDebugSessionWithBPs(t, client, "launch", 4457 // Launch 4458 func() { 4459 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4460 }, 4461 fixture.Source, []int{28}, 4462 []onBreakpoint{{ // Stop at line 28 4463 execute: func() { 4464 checkStop(t, client, 1, "main.main", 28) 4465 4466 client.ContinueRequest(1) 4467 client.ExpectContinueResponse(t) 4468 se := client.ExpectStoppedEvent(t) 4469 if se.Body.ThreadId != 1 || se.Body.Reason != "breakpoint" { 4470 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"breakpoint\"", se) 4471 } 4472 }, 4473 disconnect: false, 4474 }}) 4475 }) 4476 } 4477 4478 // TestStepInstruction executes to a breakpoint and tests stepping 4479 // a single instruction 4480 func TestStepInstruction(t *testing.T) { 4481 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 4482 runDebugSessionWithBPs(t, client, "launch", 4483 // Launch 4484 func() { 4485 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4486 }, 4487 // Set breakpoints 4488 fixture.Source, []int{32}, // b main.foobar 4489 []onBreakpoint{{ 4490 execute: func() { 4491 checkStop(t, client, 1, "main.foobar", 32) 4492 4493 pc, err := getPC(t, client, 1) 4494 if err != nil { 4495 t.Fatal(err) 4496 } 4497 4498 // The exact instructions may change due to compiler changes, 4499 // but we want to make sure that all of our instructions are 4500 // instantiating variables, since these should not include 4501 // jumps. 4502 verifyExpectedLocation := func() { 4503 client.StackTraceRequest(1, 0, 20) 4504 st := client.ExpectStackTraceResponse(t) 4505 if len(st.Body.StackFrames) < 1 { 4506 t.Errorf("\ngot %#v\nwant len(stackframes) => 1", st) 4507 } else { 4508 // There is a hardcoded breakpoint on line 32. All of the 4509 // steps should be completed before that line. 4510 if st.Body.StackFrames[0].Line < 32 { 4511 t.Errorf("\ngot %#v\nwant Line<32", st) 4512 } 4513 if st.Body.StackFrames[0].Name != "main.foobar" { 4514 t.Errorf("\ngot %#v\nwant Name=\"main.foobar\"", st) 4515 } 4516 } 4517 } 4518 4519 // Next instruction. 4520 client.NextInstructionRequest(1) 4521 client.ExpectNextResponse(t) 4522 se := client.ExpectStoppedEvent(t) 4523 if se.Body.ThreadId != 1 || se.Body.Reason != "step" { 4524 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"step\"", se) 4525 } 4526 verifyExpectedLocation() 4527 nextPC, err := getPC(t, client, 1) 4528 if err != nil { 4529 t.Fatal(err) 4530 } 4531 if nextPC <= pc { 4532 t.Errorf("got %#x, expected InstructionPointerReference>%#x", nextPC, pc) 4533 } 4534 4535 // StepIn instruction. 4536 pc = nextPC 4537 client.StepInInstructionRequest(1) 4538 client.ExpectStepInResponse(t) 4539 se = client.ExpectStoppedEvent(t) 4540 if se.Body.ThreadId != 1 || se.Body.Reason != "step" { 4541 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"step\"", se) 4542 } 4543 verifyExpectedLocation() 4544 nextPC, err = getPC(t, client, 1) 4545 if err != nil { 4546 t.Fatal(err) 4547 } 4548 if nextPC <= pc { 4549 t.Errorf("got %#x, expected InstructionPointerReference>%#x", nextPC, pc) 4550 } 4551 4552 // StepOut Instruction. 4553 pc = nextPC 4554 client.StepOutInstructionRequest(1) 4555 client.ExpectStepOutResponse(t) 4556 se = client.ExpectStoppedEvent(t) 4557 if se.Body.ThreadId != 1 || se.Body.Reason != "step" { 4558 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"step\"", se) 4559 } 4560 verifyExpectedLocation() 4561 nextPC, err = getPC(t, client, 1) 4562 if err != nil { 4563 t.Fatal(err) 4564 } 4565 if nextPC <= pc { 4566 t.Errorf("got %#x, expected InstructionPointerReference>%#x", nextPC, pc) 4567 } 4568 }, 4569 disconnect: true, 4570 }}) 4571 }) 4572 } 4573 4574 func getPC(t *testing.T, client *daptest.Client, threadId int) (uint64, error) { 4575 client.StackTraceRequest(threadId, 0, 1) 4576 st := client.ExpectStackTraceResponse(t) 4577 if len(st.Body.StackFrames) < 1 { 4578 t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st) 4579 } 4580 return strconv.ParseUint(st.Body.StackFrames[0].InstructionPointerReference, 0, 64) 4581 } 4582 4583 // TestNextParked tests that we can switched selected goroutine to a parked one 4584 // and perform next operation on it. 4585 func TestNextParked(t *testing.T) { 4586 if runtime.GOOS == "freebsd" { 4587 t.SkipNow() 4588 } 4589 runTest(t, "parallel_next", func(client *daptest.Client, fixture protest.Fixture) { 4590 runDebugSessionWithBPs(t, client, "launch", 4591 // Launch 4592 func() { 4593 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4594 }, 4595 // Set breakpoints 4596 fixture.Source, []int{15}, 4597 []onBreakpoint{{ // Stop at line 15 4598 execute: func() { 4599 if parkedGoid := testNextParkedHelper(t, client, fixture); parkedGoid >= 0 { 4600 client.NextRequest(parkedGoid) 4601 client.ExpectNextResponse(t) 4602 4603 se := client.ExpectStoppedEvent(t) 4604 if se.Body.ThreadId != parkedGoid { 4605 t.Fatalf("Next did not continue on the newly selected goroutine, expected %d got %d", parkedGoid, se.Body.ThreadId) 4606 } 4607 } 4608 }, 4609 // Let the test harness continue to process termination 4610 // if it hasn't gotten there already. 4611 disconnect: false, 4612 }}) 4613 }) 4614 } 4615 4616 // Finds a goroutine other than the selected one that is parked inside of main.sayhi and therefore 4617 // still has a line to execute if resumed with next. 4618 func testNextParkedHelper(t *testing.T, client *daptest.Client, fixture protest.Fixture) int { 4619 t.Helper() 4620 // Set a breakpoint at main.sayhi 4621 client.SetBreakpointsRequest(fixture.Source, []int{8}) 4622 client.ExpectSetBreakpointsResponse(t) 4623 4624 var parkedGoid = -1 4625 for parkedGoid < 0 { 4626 client.ContinueRequest(1) 4627 client.ExpectContinueResponse(t) 4628 event := client.ExpectMessage(t) 4629 switch event.(type) { 4630 case *dap.StoppedEvent: 4631 // ok 4632 case *dap.TerminatedEvent: 4633 // This is very unlikely to happen. But in theory if all sayhi 4634 // gouritines are run serially, there will never be a second parked 4635 // sayhi goroutine when another breaks and we will keep trying 4636 // until process termination. 4637 return -1 4638 } 4639 4640 se := event.(*dap.StoppedEvent) 4641 4642 client.ThreadsRequest() 4643 threads := client.ExpectThreadsResponse(t) 4644 4645 // Search for a parked goroutine that we know for sure will have to be 4646 // resumed before the program can exit. This is a parked goroutine that: 4647 // 1. is executing main.sayhi 4648 // 2. hasn't called wg.Done yet 4649 // 3. is not the currently selected goroutine 4650 for _, g := range threads.Body.Threads { 4651 if g.Id == se.Body.ThreadId { // Skip selected goroutine 4652 continue 4653 } 4654 client.StackTraceRequest(g.Id, 0, 5) 4655 frames := client.ExpectStackTraceResponse(t) 4656 for _, frame := range frames.Body.StackFrames { 4657 // line 11 is the line where wg.Done is called 4658 if frame.Name == "main.sayhi" && frame.Line < 11 { 4659 parkedGoid = g.Id 4660 break 4661 } 4662 } 4663 if parkedGoid >= 0 { 4664 break 4665 } 4666 } 4667 } 4668 4669 // Clear all breakpoints. 4670 client.SetBreakpointsRequest(fixture.Source, []int{}) 4671 client.ExpectSetBreakpointsResponse(t) 4672 return parkedGoid 4673 } 4674 4675 // TestStepOutPreservesGoroutine is inspired by proc_test.TestStepOutPreservesGoroutine 4676 // and checks that StepOut preserves the currently selected goroutine. 4677 func TestStepOutPreservesGoroutine(t *testing.T) { 4678 // Checks that StepOut preserves the currently selected goroutine. 4679 if runtime.GOOS == "freebsd" { 4680 t.SkipNow() 4681 } 4682 rand.Seed(time.Now().Unix()) 4683 runTest(t, "issue2113", func(client *daptest.Client, fixture protest.Fixture) { 4684 runDebugSessionWithBPs(t, client, "launch", 4685 // Launch 4686 func() { 4687 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4688 }, 4689 // Set breakpoints 4690 fixture.Source, []int{25}, 4691 []onBreakpoint{{ // Stop at line 25 4692 execute: func() { 4693 client.ContinueRequest(1) 4694 client.ExpectContinueResponse(t) 4695 4696 // The program contains runtime.Breakpoint() 4697 se := client.ExpectStoppedEvent(t) 4698 4699 client.ThreadsRequest() 4700 gs := client.ExpectThreadsResponse(t) 4701 4702 candg := []int{} 4703 bestg := []int{} 4704 for _, g := range gs.Body.Threads { 4705 // We do not need to check the thread that the program 4706 // is currently stopped on. 4707 if g.Id == se.Body.ThreadId { 4708 continue 4709 } 4710 4711 client.StackTraceRequest(g.Id, 0, 20) 4712 frames := client.ExpectStackTraceResponse(t) 4713 for _, frame := range frames.Body.StackFrames { 4714 if frame.Name == "main.coroutine" { 4715 candg = append(candg, g.Id) 4716 if strings.HasPrefix(frames.Body.StackFrames[0].Name, "runtime.") { 4717 bestg = append(bestg, g.Id) 4718 } 4719 break 4720 } 4721 } 4722 } 4723 var goroutineId int 4724 if len(bestg) > 0 { 4725 goroutineId = bestg[rand.Intn(len(bestg))] 4726 t.Logf("selected goroutine %d (best)\n", goroutineId) 4727 } else if len(candg) > 0 { 4728 goroutineId = candg[rand.Intn(len(candg))] 4729 t.Logf("selected goroutine %d\n", goroutineId) 4730 4731 } 4732 4733 if goroutineId != 0 { 4734 client.StepOutRequest(goroutineId) 4735 client.ExpectStepOutResponse(t) 4736 } else { 4737 client.ContinueRequest(-1) 4738 client.ExpectContinueResponse(t) 4739 } 4740 4741 switch e := client.ExpectMessage(t).(type) { 4742 case *dap.StoppedEvent: 4743 if e.Body.ThreadId != goroutineId { 4744 t.Fatalf("StepOut did not continue on the selected goroutine, expected %d got %d", goroutineId, e.Body.ThreadId) 4745 } 4746 case *dap.TerminatedEvent: 4747 t.Logf("program terminated") 4748 default: 4749 t.Fatalf("Unexpected event type: expect stopped or terminated event, got %#v", e) 4750 } 4751 }, 4752 disconnect: false, 4753 }}) 4754 }) 4755 } 4756 func checkStopOnNextWhileNextingError(t *testing.T, client *daptest.Client, threadID int) { 4757 t.Helper() 4758 oe := client.ExpectOutputEvent(t) 4759 if oe.Body.Category != "console" || oe.Body.Output != fmt.Sprintf("invalid command: %s\n", BetterNextWhileNextingError) { 4760 t.Errorf("\ngot %#v\nwant Category=\"console\" Output=\"invalid command: %s\\n\"", oe, BetterNextWhileNextingError) 4761 } 4762 se := client.ExpectStoppedEvent(t) 4763 if se.Body.ThreadId != threadID || se.Body.Reason != "exception" || se.Body.Description != "invalid command" || se.Body.Text != BetterNextWhileNextingError { 4764 t.Errorf("\ngot %#v\nwant ThreadId=%d Reason=\"exception\" Description=\"invalid command\" Text=\"%s\"", se, threadID, BetterNextWhileNextingError) 4765 } 4766 client.ExceptionInfoRequest(1) 4767 eInfo := client.ExpectExceptionInfoResponse(t) 4768 if eInfo.Body.ExceptionId != "invalid command" || eInfo.Body.Description != BetterNextWhileNextingError { 4769 t.Errorf("\ngot %#v\nwant ExceptionId=\"invalid command\" Text=\"%s\"", eInfo, BetterNextWhileNextingError) 4770 } 4771 } 4772 4773 func TestBadAccess(t *testing.T) { 4774 if runtime.GOOS != "darwin" || testBackend != "lldb" { 4775 t.Skip("not applicable") 4776 } 4777 runTest(t, "issue2078", func(client *daptest.Client, fixture protest.Fixture) { 4778 runDebugSessionWithBPs(t, client, "launch", 4779 // Launch 4780 func() { 4781 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4782 }, 4783 // Set breakpoints 4784 fixture.Source, []int{4}, 4785 []onBreakpoint{{ // Stop at line 4 4786 execute: func() { 4787 checkStop(t, client, 1, "main.main", 4) 4788 4789 expectStoppedOnError := func(errorPrefix string) { 4790 t.Helper() 4791 se := client.ExpectStoppedEvent(t) 4792 if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "runtime error" || !strings.HasPrefix(se.Body.Text, errorPrefix) { 4793 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"runtime error\" Text=\"%s\"", se, errorPrefix) 4794 } 4795 client.ExceptionInfoRequest(1) 4796 eInfo := client.ExpectExceptionInfoResponse(t) 4797 if eInfo.Body.ExceptionId != "runtime error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) { 4798 t.Errorf("\ngot %#v\nwant ExceptionId=\"runtime error\" Text=\"%s\"", eInfo, errorPrefix) 4799 } 4800 } 4801 4802 client.ContinueRequest(1) 4803 client.ExpectContinueResponse(t) 4804 expectStoppedOnError("invalid memory address or nil pointer dereference") 4805 4806 client.NextRequest(1) 4807 client.ExpectNextResponse(t) 4808 expectStoppedOnError("invalid memory address or nil pointer dereference") 4809 4810 client.NextRequest(1) 4811 client.ExpectNextResponse(t) 4812 checkStopOnNextWhileNextingError(t, client, 1) 4813 4814 client.StepInRequest(1) 4815 client.ExpectStepInResponse(t) 4816 checkStopOnNextWhileNextingError(t, client, 1) 4817 4818 client.StepOutRequest(1) 4819 client.ExpectStepOutResponse(t) 4820 checkStopOnNextWhileNextingError(t, client, 1) 4821 }, 4822 disconnect: true, 4823 }}) 4824 }) 4825 } 4826 4827 // TestNextWhileNexting is inspired by command_test.TestIssue387 and tests 4828 // that when 'next' is interrupted by a 'breakpoint', calling 'next' 4829 // again will produce an error with a helpful message, and 'continue' 4830 // will resume the program. 4831 func TestNextWhileNexting(t *testing.T) { 4832 if runtime.GOOS == "freebsd" { 4833 t.Skip("test is not valid on FreeBSD") 4834 } 4835 // a breakpoint triggering during a 'next' operation will interrupt 'next'' 4836 // Unlike the test for the terminal package, we cannot be certain 4837 // of the number of breakpoints we expect to hit, since multiple 4838 // breakpoints being hit at the same time is not supported in DAP stopped 4839 // events. 4840 runTest(t, "issue387", func(client *daptest.Client, fixture protest.Fixture) { 4841 runDebugSessionWithBPs(t, client, "launch", 4842 // Launch 4843 func() { 4844 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4845 }, 4846 // Set breakpoints 4847 fixture.Source, []int{15}, 4848 []onBreakpoint{{ // Stop at line 15 4849 execute: func() { 4850 checkStop(t, client, 1, "main.main", 15) 4851 4852 client.SetBreakpointsRequest(fixture.Source, []int{8}) 4853 client.ExpectSetBreakpointsResponse(t) 4854 4855 client.ContinueRequest(1) 4856 client.ExpectContinueResponse(t) 4857 4858 bpSe := client.ExpectStoppedEvent(t) 4859 threadID := bpSe.Body.ThreadId 4860 checkStop(t, client, threadID, "main.dostuff", 8) 4861 4862 for pos := 9; pos < 11; pos++ { 4863 client.NextRequest(threadID) 4864 client.ExpectNextResponse(t) 4865 4866 stepInProgress := true 4867 for stepInProgress { 4868 m := client.ExpectStoppedEvent(t) 4869 switch m.Body.Reason { 4870 case "step": 4871 if !m.Body.AllThreadsStopped { 4872 t.Errorf("got %#v, want Reason=\"step\", AllThreadsStopped=true", m) 4873 } 4874 checkStop(t, client, m.Body.ThreadId, "main.dostuff", pos) 4875 stepInProgress = false 4876 case "breakpoint": 4877 if !m.Body.AllThreadsStopped { 4878 t.Errorf("got %#v, want Reason=\"breakpoint\", AllThreadsStopped=true", m) 4879 } 4880 4881 if stepInProgress { 4882 // We encountered a breakpoint on a different thread. We should have to resume execution 4883 // using continue. 4884 oe := client.ExpectOutputEvent(t) 4885 if oe.Body.Category != "console" || !strings.Contains(oe.Body.Output, "Step interrupted by a breakpoint.") { 4886 t.Errorf("\ngot %#v\nwant Category=\"console\" Output=\"Step interrupted by a breakpoint.\"", oe) 4887 } 4888 client.NextRequest(m.Body.ThreadId) 4889 client.ExpectNextResponse(t) 4890 checkStopOnNextWhileNextingError(t, client, m.Body.ThreadId) 4891 // Continue since we have not finished the step request. 4892 client.ContinueRequest(threadID) 4893 client.ExpectContinueResponse(t) 4894 } else { 4895 checkStop(t, client, m.Body.ThreadId, "main.dostuff", 8) 4896 // Switch to stepping on this thread instead. 4897 pos = 8 4898 threadID = m.Body.ThreadId 4899 } 4900 default: 4901 t.Fatalf("got %#v, want StoppedEvent on step or breakpoint", m) 4902 } 4903 } 4904 } 4905 }, 4906 disconnect: true, 4907 }}) 4908 }) 4909 } 4910 4911 func TestPanicBreakpointOnContinue(t *testing.T) { 4912 runTest(t, "panic", func(client *daptest.Client, fixture protest.Fixture) { 4913 runDebugSessionWithBPs(t, client, "launch", 4914 // Launch 4915 func() { 4916 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4917 }, 4918 // Set breakpoints 4919 fixture.Source, []int{5}, 4920 []onBreakpoint{{ 4921 execute: func() { 4922 checkStop(t, client, 1, "main.main", 5) 4923 4924 client.ContinueRequest(1) 4925 client.ExpectContinueResponse(t) 4926 4927 text := "\"BOOM!\"" 4928 se := client.ExpectStoppedEvent(t) 4929 if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "panic" || se.Body.Text != text { 4930 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"panic\" Text=%q", se, text) 4931 } 4932 4933 client.ExceptionInfoRequest(1) 4934 eInfo := client.ExpectExceptionInfoResponse(t) 4935 if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != text { 4936 t.Errorf("\ngot %#v\nwant ExceptionId=\"panic\" Description=%q", eInfo, text) 4937 } 4938 4939 client.StackTraceRequest(se.Body.ThreadId, 0, 20) 4940 st := client.ExpectStackTraceResponse(t) 4941 for i, frame := range st.Body.StackFrames { 4942 if strings.HasPrefix(frame.Name, "runtime.") { 4943 if frame.PresentationHint != "subtle" { 4944 t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"subtle\"", i, frame) 4945 } 4946 } else if frame.Source.PresentationHint != "" { 4947 t.Errorf("\ngot Body.StackFrames[%d]=%#v\nwant Source.PresentationHint=\"\"", i, frame) 4948 } 4949 4950 } 4951 }, 4952 disconnect: true, 4953 }}) 4954 }) 4955 } 4956 4957 func TestPanicBreakpointOnNext(t *testing.T) { 4958 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) { 4959 // In Go 1.13, 'next' will step into the defer in the runtime 4960 // main function, instead of the next line in the main program. 4961 t.SkipNow() 4962 } 4963 4964 runTest(t, "panic", func(client *daptest.Client, fixture protest.Fixture) { 4965 runDebugSessionWithBPs(t, client, "launch", 4966 // Launch 4967 func() { 4968 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 4969 }, 4970 // Set breakpoints 4971 fixture.Source, []int{5}, 4972 []onBreakpoint{{ 4973 execute: func() { 4974 checkStop(t, client, 1, "main.main", 5) 4975 4976 client.NextRequest(1) 4977 client.ExpectNextResponse(t) 4978 4979 text := "\"BOOM!\"" 4980 se := client.ExpectStoppedEvent(t) 4981 if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "panic" || se.Body.Text != text { 4982 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"panic\" Text=%q", se, text) 4983 } 4984 4985 client.ExceptionInfoRequest(1) 4986 eInfo := client.ExpectExceptionInfoResponse(t) 4987 if eInfo.Body.ExceptionId != "panic" || eInfo.Body.Description != text { 4988 t.Errorf("\ngot %#v\nwant ExceptionId=\"panic\" Description=%q", eInfo, text) 4989 } 4990 }, 4991 disconnect: true, 4992 }}) 4993 }) 4994 } 4995 4996 func TestFatalThrowBreakpoint(t *testing.T) { 4997 runTest(t, "fatalerror", func(client *daptest.Client, fixture protest.Fixture) { 4998 runDebugSessionWithBPs(t, client, "launch", 4999 // Launch 5000 func() { 5001 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 5002 }, 5003 // Set breakpoints 5004 fixture.Source, []int{3}, 5005 []onBreakpoint{{ 5006 execute: func() { 5007 checkStop(t, client, 1, "main.main", 3) 5008 5009 client.ContinueRequest(1) 5010 client.ExpectContinueResponse(t) 5011 5012 var text string 5013 // This does not work for Go 1.16. 5014 ver, _ := goversion.Parse(runtime.Version()) 5015 if ver.Major != 1 || ver.Minor != 16 { 5016 text = "\"go of nil func value\"" 5017 } 5018 5019 se := client.ExpectStoppedEvent(t) 5020 if se.Body.ThreadId != 1 || se.Body.Reason != "exception" || se.Body.Description != "fatal error" || se.Body.Text != text { 5021 t.Errorf("\ngot %#v\nwant ThreadId=1 Reason=\"exception\" Description=\"fatal error\" Text=%q", se, text) 5022 } 5023 5024 // This does not work for Go 1.16. 5025 errorPrefix := text 5026 if errorPrefix == "" { 5027 errorPrefix = "Throw reason unavailable, see https://github.com/golang/go/issues/46425" 5028 } 5029 client.ExceptionInfoRequest(1) 5030 eInfo := client.ExpectExceptionInfoResponse(t) 5031 if eInfo.Body.ExceptionId != "fatal error" || !strings.HasPrefix(eInfo.Body.Description, errorPrefix) { 5032 t.Errorf("\ngot %#v\nwant ExceptionId=\"runtime error\" Text=%s", eInfo, errorPrefix) 5033 } 5034 5035 }, 5036 disconnect: true, 5037 }}) 5038 }) 5039 runTest(t, "testdeadlock", func(client *daptest.Client, fixture protest.Fixture) { 5040 runDebugSessionWithBPs(t, client, "launch", 5041 // Launch 5042 func() { 5043 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 5044 }, 5045 // Set breakpoints 5046 fixture.Source, []int{3}, 5047 []onBreakpoint{{ 5048 execute: func() { 5049 checkStop(t, client, 1, "main.main", 3) 5050 5051 client.ContinueRequest(1) 5052 client.ExpectContinueResponse(t) 5053 5054 // This does not work for Go 1.16 so skip by detecting versions before or after 1.16. 5055 var text string 5056 if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) || goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) { 5057 text = "\"all goroutines are asleep - deadlock!\"" 5058 } 5059 se := client.ExpectStoppedEvent(t) 5060 if se.Body.Reason != "exception" || se.Body.Description != "fatal error" || se.Body.Text != text { 5061 t.Errorf("\ngot %#v\nwant Reason=\"exception\" Description=\"fatal error\" Text=%q", se, text) 5062 } 5063 5064 // TODO(suzmue): Get the exception info for the thread and check the description 5065 // includes "all goroutines are asleep - deadlock!". 5066 // Stopped events with no selected goroutines need to be supported to test deadlock. 5067 }, 5068 disconnect: true, 5069 }}) 5070 }) 5071 } 5072 5073 // checkStop covers the standard sequence of requests issued by 5074 // a client at a breakpoint or another non-terminal stop event. 5075 // The details have been tested by other tests, 5076 // so this is just a sanity check. 5077 // Skips line check if line is -1. 5078 func checkStop(t *testing.T, client *daptest.Client, thread int, fname string, line int) { 5079 t.Helper() 5080 client.ThreadsRequest() 5081 client.ExpectThreadsResponse(t) 5082 5083 client.CheckStopLocation(t, thread, fname, line) 5084 5085 client.ScopesRequest(1000) 5086 client.ExpectScopesResponse(t) 5087 5088 client.VariablesRequest(localsScope) 5089 client.ExpectVariablesResponse(t) 5090 } 5091 5092 // onBreakpoint specifies what the test harness should simulate at 5093 // a stopped breakpoint. First execute() is to be called to test 5094 // specified editor-driven or user-driven requests. Then if 5095 // disconnect is true, the test harness will abort the program 5096 // execution. Otherwise, a continue will be issued and the 5097 // program will continue to the next breakpoint or termination. 5098 // If terminated is true, we expect requests at this breakpoint 5099 // to result in termination. 5100 type onBreakpoint struct { 5101 execute func() 5102 disconnect bool 5103 terminated bool 5104 } 5105 5106 // runDebugSessionWithBPs is a helper for executing the common init and shutdown 5107 // sequences for a program that does not stop on entry 5108 // while specifying breakpoints and unique launch/attach criteria via parameters. 5109 // 5110 // cmd - "launch" or "attach" 5111 // cmdRequest - a function that sends a launch or attach request, 5112 // so the test author has full control of its arguments. 5113 // Note that he rest of the test sequence assumes that 5114 // stopOnEntry is false. 5115 // source - source file path, needed to set breakpoints, "" if none to be set. 5116 // breakpoints - list of lines, where breakpoints are to be set 5117 // onBPs - list of test sequences to execute at each of the set breakpoints. 5118 func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, cmd string, cmdRequest func(), source string, breakpoints []int, onBPs []onBreakpoint) { 5119 client.InitializeRequest() 5120 client.ExpectInitializeResponseAndCapabilities(t) 5121 5122 cmdRequest() 5123 client.ExpectInitializedEvent(t) 5124 if cmd == "launch" { 5125 client.ExpectLaunchResponse(t) 5126 } else if cmd == "attach" { 5127 client.ExpectAttachResponse(t) 5128 } else { 5129 panic("expected launch or attach command") 5130 } 5131 5132 if source != "" { 5133 client.SetBreakpointsRequest(source, breakpoints) 5134 client.ExpectSetBreakpointsResponse(t) 5135 } 5136 5137 // Skip no-op setExceptionBreakpoints 5138 5139 client.ConfigurationDoneRequest() 5140 client.ExpectConfigurationDoneResponse(t) 5141 5142 // Program automatically continues to breakpoint or completion 5143 5144 // TODO(polina): See if we can make this more like withTestProcessArgs in proc_test: 5145 // a single function pointer gets called here and then if it wants to continue it calls 5146 // client.ContinueRequest/client.ExpectContinueResponse/client.ExpectStoppedEvent 5147 // (possibly using a helper function). 5148 for _, onBP := range onBPs { 5149 client.ExpectStoppedEvent(t) 5150 onBP.execute() 5151 if onBP.disconnect { 5152 client.DisconnectRequestWithKillOption(true) 5153 if onBP.terminated { 5154 client.ExpectOutputEventProcessExitedAnyStatus(t) 5155 client.ExpectOutputEventDetaching(t) 5156 } else { 5157 client.ExpectOutputEventDetachingKill(t) 5158 } 5159 client.ExpectDisconnectResponse(t) 5160 client.ExpectTerminatedEvent(t) 5161 return 5162 } 5163 client.ContinueRequest(1) 5164 client.ExpectContinueResponse(t) 5165 // "Continue" is triggered after the response is sent 5166 } 5167 5168 if cmd == "launch" { // Let the program run to completion 5169 client.ExpectTerminatedEvent(t) 5170 } 5171 client.DisconnectRequestWithKillOption(true) 5172 if cmd == "launch" { 5173 client.ExpectOutputEventProcessExitedAnyStatus(t) 5174 client.ExpectOutputEventDetaching(t) 5175 } else if cmd == "attach" { 5176 client.ExpectOutputEventDetachingKill(t) 5177 } 5178 client.ExpectDisconnectResponse(t) 5179 client.ExpectTerminatedEvent(t) 5180 } 5181 5182 // runDebugSession is a helper for executing the standard init and shutdown 5183 // sequences for a program that does not stop on entry 5184 // while specifying unique launch criteria via parameters. 5185 func runDebugSession(t *testing.T, client *daptest.Client, cmd string, cmdRequest func()) { 5186 runDebugSessionWithBPs(t, client, cmd, cmdRequest, "", nil, nil) 5187 } 5188 5189 func TestLaunchDebugRequest(t *testing.T) { 5190 rescueStderr := os.Stderr 5191 r, w, _ := os.Pipe() 5192 os.Stderr = w 5193 done := make(chan struct{}) 5194 5195 var err []byte 5196 5197 go func() { 5198 err, _ = ioutil.ReadAll(r) 5199 t.Log(string(err)) 5200 close(done) 5201 }() 5202 5203 tmpBin := "__tmpBin" 5204 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5205 // We reuse the harness that builds, but ignore the built binary, 5206 // only relying on the source to be built in response to LaunchRequest. 5207 runDebugSession(t, client, "launch", func() { 5208 client.LaunchRequestWithArgs(map[string]interface{}{ 5209 "mode": "debug", "program": fixture.Source, "output": tmpBin}) 5210 }) 5211 }) 5212 // Wait for the test to finish to capture all stderr 5213 time.Sleep(100 * time.Millisecond) 5214 5215 w.Close() 5216 <-done 5217 os.Stderr = rescueStderr 5218 5219 rmErrRe, _ := regexp.Compile(`could not remove .*\n`) 5220 rmErr := rmErrRe.FindString(string(err)) 5221 if rmErr != "" { 5222 // On Windows, a file in use cannot be removed, resulting in "Access is denied". 5223 // When the process exits, Delve releases the binary by calling 5224 // BinaryInfo.Close(), but it appears that it is still in use (by Windows?) 5225 // shortly after. gobuild.Remove has a delay to address this, but 5226 // to avoid any test flakiness we guard against this failure here as well. 5227 if runtime.GOOS != "windows" || !stringContainsCaseInsensitive(rmErr, "Access is denied") { 5228 t.Fatalf("Binary removal failure:\n%s\n", rmErr) 5229 } 5230 } else { 5231 tmpBin = cleanExeName(tmpBin) 5232 // We did not get a removal error, but did we even try to remove before exiting? 5233 // Confirm that the binary did get removed. 5234 if _, err := os.Stat(tmpBin); err == nil || os.IsExist(err) { 5235 t.Fatal("Failed to remove temp binary", tmpBin) 5236 } 5237 } 5238 } 5239 5240 // TestLaunchRequestDefaults tests defaults for launch attribute that are explicit in other tests. 5241 func TestLaunchRequestDefaults(t *testing.T) { 5242 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5243 runDebugSession(t, client, "launch", func() { 5244 client.LaunchRequestWithArgs(map[string]interface{}{ 5245 "mode": "" /*"debug" by default*/, "program": fixture.Source, "output": "__mybin"}) 5246 }) 5247 }) 5248 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5249 runDebugSession(t, client, "launch", func() { 5250 client.LaunchRequestWithArgs(map[string]interface{}{ 5251 /*"mode":"debug" by default*/ "program": fixture.Source, "output": "__mybin"}) 5252 }) 5253 }) 5254 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5255 runDebugSession(t, client, "launch", func() { 5256 // Use the temporary output binary. 5257 client.LaunchRequestWithArgs(map[string]interface{}{ 5258 "mode": "debug", "program": fixture.Source}) 5259 }) 5260 }) 5261 } 5262 5263 // TestLaunchRequestOutputPath verifies that relative output binary path 5264 // is mapped to server's, not target's, working directory. 5265 func TestLaunchRequestOutputPath(t *testing.T) { 5266 runTest(t, "testargs", func(client *daptest.Client, fixture protest.Fixture) { 5267 inrel := "__somebin" 5268 wd, _ := os.Getwd() 5269 outabs := cleanExeName(filepath.Join(wd, inrel)) 5270 runDebugSessionWithBPs(t, client, "launch", 5271 // Launch 5272 func() { 5273 client.LaunchRequestWithArgs(map[string]interface{}{ 5274 "mode": "debug", "program": fixture.Source, "output": inrel, 5275 "cwd": filepath.Dir(wd)}) 5276 }, 5277 // Set breakpoints 5278 fixture.Source, []int{12}, 5279 []onBreakpoint{{ 5280 execute: func() { 5281 checkStop(t, client, 1, "main.main", 12) 5282 client.EvaluateRequest("os.Args[0]", 1000, "repl") 5283 checkEval(t, client.ExpectEvaluateResponse(t), fmt.Sprintf("%q", outabs), noChildren) 5284 }, 5285 disconnect: true, 5286 }}) 5287 }) 5288 } 5289 5290 func TestExitNonZeroStatus(t *testing.T) { 5291 runTest(t, "pr1055", func(client *daptest.Client, fixture protest.Fixture) { 5292 client.InitializeRequest() 5293 client.ExpectInitializeResponseAndCapabilities(t) 5294 5295 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 5296 client.ExpectInitializedEvent(t) 5297 client.ExpectLaunchResponse(t) 5298 5299 client.ConfigurationDoneRequest() 5300 client.ExpectConfigurationDoneResponse(t) 5301 5302 client.ExpectTerminatedEvent(t) 5303 5304 client.DisconnectRequest() 5305 // Check that the process exit status is 2. 5306 oep := client.ExpectOutputEventProcessExited(t, 2) 5307 if oep.Body.Category != "console" { 5308 t.Errorf("\ngot %#v\nwant Category='console'", oep) 5309 } 5310 oed := client.ExpectOutputEventDetaching(t) 5311 if oed.Body.Category != "console" { 5312 t.Errorf("\ngot %#v\nwant Category='console'", oed) 5313 } 5314 client.ExpectDisconnectResponse(t) 5315 client.ExpectTerminatedEvent(t) 5316 }) 5317 } 5318 5319 func TestNoDebug_GoodExitStatus(t *testing.T) { 5320 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5321 runNoDebugSession(t, client, func() { 5322 client.LaunchRequestWithArgs(map[string]interface{}{ 5323 "noDebug": true, "mode": "debug", "program": fixture.Source, "output": "__mybin"}) 5324 }, 0) 5325 }) 5326 } 5327 5328 func TestNoDebug_BadExitStatus(t *testing.T) { 5329 runTest(t, "issue1101", func(client *daptest.Client, fixture protest.Fixture) { 5330 runNoDebugSession(t, client, func() { 5331 client.LaunchRequestWithArgs(map[string]interface{}{ 5332 "noDebug": true, "mode": "exec", "program": fixture.Path}) 5333 }, 2) 5334 }) 5335 } 5336 5337 // runNoDebugSession tests the session started with noDebug=true runs 5338 // to completion and logs termination status. 5339 func runNoDebugSession(t *testing.T, client *daptest.Client, launchRequest func(), exitStatus int) { 5340 client.InitializeRequest() 5341 client.ExpectInitializeResponseAndCapabilities(t) 5342 5343 launchRequest() 5344 // no initialized event. 5345 // noDebug mode applies only to "launch" requests. 5346 client.ExpectLaunchResponse(t) 5347 5348 client.ExpectOutputEventProcessExited(t, exitStatus) 5349 client.ExpectTerminatedEvent(t) 5350 client.DisconnectRequestWithKillOption(true) 5351 client.ExpectDisconnectResponse(t) 5352 client.ExpectTerminatedEvent(t) 5353 } 5354 5355 func TestNoDebug_AcceptNoRequestsButDisconnect(t *testing.T) { 5356 runTest(t, "http_server", func(client *daptest.Client, fixture protest.Fixture) { 5357 client.InitializeRequest() 5358 client.ExpectInitializeResponseAndCapabilities(t) 5359 client.LaunchRequestWithArgs(map[string]interface{}{ 5360 "noDebug": true, "mode": "exec", "program": fixture.Path}) 5361 client.ExpectLaunchResponse(t) 5362 5363 // Anything other than disconnect should get rejected 5364 var ExpectNoDebugError = func(cmd string) { 5365 er := client.ExpectErrorResponse(t) 5366 if er.Body.Error.Format != fmt.Sprintf("noDebug mode: unable to process '%s' request", cmd) { 5367 t.Errorf("\ngot %#v\nwant 'noDebug mode: unable to process '%s' request'", er, cmd) 5368 } 5369 } 5370 client.SetBreakpointsRequest(fixture.Source, []int{8}) 5371 ExpectNoDebugError("setBreakpoints") 5372 client.SetFunctionBreakpointsRequest(nil) 5373 ExpectNoDebugError("setFunctionBreakpoints") 5374 client.PauseRequest(1) 5375 ExpectNoDebugError("pause") 5376 client.RestartRequest() 5377 client.ExpectUnsupportedCommandErrorResponse(t) 5378 5379 // Disconnect request is ok 5380 client.DisconnectRequestWithKillOption(true) 5381 terminated, disconnectResp := false, false 5382 for { 5383 m, err := client.ReadMessage() 5384 if err != nil { 5385 break 5386 } 5387 switch m := m.(type) { 5388 case *dap.OutputEvent: 5389 ok := false 5390 wants := []string{`Terminating process [0-9]+\n`, fmt.Sprintf(daptest.ProcessExited, "(-1|1)")} 5391 for _, want := range wants { 5392 if matched, _ := regexp.MatchString(want, m.Body.Output); matched { 5393 ok = true 5394 break 5395 } 5396 } 5397 if !ok { 5398 t.Errorf("\ngot %#v\nwant Output=%q\n", m, wants) 5399 } 5400 case *dap.TerminatedEvent: 5401 terminated = true 5402 case *dap.DisconnectResponse: 5403 disconnectResp = true 5404 default: 5405 t.Errorf("got unexpected message %#v", m) 5406 } 5407 } 5408 if !terminated { 5409 t.Errorf("did not get TerminatedEvent") 5410 } 5411 if !disconnectResp { 5412 t.Errorf("did not get DisconnectResponse") 5413 } 5414 }) 5415 } 5416 5417 func TestLaunchRequestWithRelativeBuildPath(t *testing.T) { 5418 serverStopped := make(chan struct{}) 5419 client := startDAPServerWithClient(t, false, serverStopped) 5420 defer client.Close() 5421 5422 fixdir := protest.FindFixturesDir() 5423 if filepath.IsAbs(fixdir) { 5424 t.Fatal("this test requires relative program path") 5425 } 5426 program := filepath.Join(protest.FindFixturesDir(), "buildtest") 5427 5428 // Use different working dir for target than dlv. 5429 // Program path will be interpreted relative to dlv's. 5430 dlvwd, _ := os.Getwd() 5431 runDebugSession(t, client, "launch", func() { 5432 client.LaunchRequestWithArgs(map[string]interface{}{ 5433 "mode": "debug", "program": program, "cwd": filepath.Dir(dlvwd)}) 5434 }) 5435 <-serverStopped 5436 } 5437 5438 func TestLaunchRequestWithRelativeExecPath(t *testing.T) { 5439 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5440 symlink := "./__thisexe" 5441 err := os.Symlink(fixture.Path, symlink) 5442 defer os.Remove(symlink) 5443 if err != nil { 5444 t.Fatal("unable to create relative symlink:", err) 5445 } 5446 runDebugSession(t, client, "launch", func() { 5447 client.LaunchRequestWithArgs(map[string]interface{}{ 5448 "mode": "exec", "program": symlink}) 5449 }) 5450 }) 5451 } 5452 5453 func TestLaunchTestRequest(t *testing.T) { 5454 orgWD, _ := os.Getwd() 5455 fixtures := protest.FindFixturesDir() // relative to current working directory. 5456 absoluteFixturesDir, _ := filepath.Abs(fixtures) 5457 absolutePkgDir, _ := filepath.Abs(filepath.Join(fixtures, "buildtest")) 5458 testFile := filepath.Join(absolutePkgDir, "main_test.go") 5459 5460 for _, tc := range []struct { 5461 name string 5462 dlvWD string 5463 launchArgs map[string]interface{} 5464 wantWD string 5465 }{{ 5466 name: "default", 5467 launchArgs: map[string]interface{}{ 5468 "mode": "test", "program": absolutePkgDir, 5469 }, 5470 wantWD: absolutePkgDir, 5471 }, { 5472 name: "output", 5473 launchArgs: map[string]interface{}{ 5474 "mode": "test", "program": absolutePkgDir, "output": "test.out", 5475 }, 5476 wantWD: absolutePkgDir, 5477 }, { 5478 name: "dlvCwd", 5479 launchArgs: map[string]interface{}{ 5480 "mode": "test", "program": absolutePkgDir, "dlvCwd": ".", 5481 }, 5482 wantWD: absolutePkgDir, 5483 }, { 5484 name: "dlvCwd2", 5485 launchArgs: map[string]interface{}{ 5486 "mode": "test", "program": ".", "dlvCwd": absolutePkgDir, 5487 }, 5488 wantWD: absolutePkgDir, 5489 }, { 5490 name: "cwd", 5491 launchArgs: map[string]interface{}{ 5492 "mode": "test", "program": absolutePkgDir, "cwd": fixtures, // fixtures is relative to the current working directory. 5493 }, 5494 wantWD: absoluteFixturesDir, 5495 }, { 5496 name: "dlv runs outside of module", 5497 dlvWD: os.TempDir(), 5498 launchArgs: map[string]interface{}{ 5499 "mode": "test", "program": absolutePkgDir, "dlvCwd": absoluteFixturesDir, 5500 }, 5501 wantWD: absolutePkgDir, 5502 }, { 5503 name: "dlv builds in dlvCwd but runs in cwd", 5504 dlvWD: fixtures, 5505 launchArgs: map[string]interface{}{ 5506 "mode": "test", "program": absolutePkgDir, "dlvCwd": absolutePkgDir, "cwd": "..", // relative to dlvCwd. 5507 }, 5508 wantWD: absoluteFixturesDir, 5509 }} { 5510 t.Run(tc.name, func(t *testing.T) { 5511 // Some test cases with dlvCwd or dlvWD change process working directory. 5512 defer os.Chdir(orgWD) 5513 if tc.dlvWD != "" { 5514 os.Chdir(tc.dlvWD) 5515 defer os.Chdir(orgWD) 5516 } 5517 serverStopped := make(chan struct{}) 5518 client := startDAPServerWithClient(t, false, serverStopped) 5519 defer client.Close() 5520 5521 runDebugSessionWithBPs(t, client, "launch", 5522 func() { // Launch 5523 client.LaunchRequestWithArgs(tc.launchArgs) 5524 }, 5525 testFile, []int{14}, 5526 []onBreakpoint{{ 5527 execute: func() { 5528 checkStop(t, client, -1, "github.com/emad-elsaid/delve/_fixtures/buildtest.TestCurrentDirectory", 14) 5529 client.VariablesRequest(1001) // Locals 5530 locals := client.ExpectVariablesResponse(t) 5531 checkChildren(t, locals, "Locals", 1) 5532 for i := range locals.Body.Variables { 5533 switch locals.Body.Variables[i].Name { 5534 case "wd": // The test's working directory is the package directory by default. 5535 checkVarExact(t, locals, i, "wd", "wd", fmt.Sprintf("%q", tc.wantWD), "string", noChildren) 5536 } 5537 } 5538 }}}) 5539 5540 <-serverStopped 5541 }) 5542 } 5543 } 5544 5545 // Tests that 'args' from LaunchRequest are parsed and passed to the target 5546 // program. The target program exits without an error on success, and 5547 // panics on error, causing an unexpected StoppedEvent instead of 5548 // Terminated Event. 5549 func TestLaunchRequestWithArgs(t *testing.T) { 5550 runTest(t, "testargs", func(client *daptest.Client, fixture protest.Fixture) { 5551 runDebugSession(t, client, "launch", func() { 5552 client.LaunchRequestWithArgs(map[string]interface{}{ 5553 "mode": "exec", "program": fixture.Path, 5554 "args": []string{"test", "pass flag"}}) 5555 }) 5556 }) 5557 } 5558 5559 // Tests that 'buildFlags' from LaunchRequest are parsed and passed to the 5560 // compiler. The target program exits without an error on success, and 5561 // panics on error, causing an unexpected StoppedEvent instead of 5562 // TerminatedEvent. 5563 func TestLaunchRequestWithBuildFlags(t *testing.T) { 5564 runTest(t, "buildflagtest", func(client *daptest.Client, fixture protest.Fixture) { 5565 runDebugSession(t, client, "launch", func() { 5566 // We reuse the harness that builds, but ignore the built binary, 5567 // only relying on the source to be built in response to LaunchRequest. 5568 client.LaunchRequestWithArgs(map[string]interface{}{ 5569 "mode": "debug", "program": fixture.Source, "output": "__mybin", 5570 "buildFlags": "-ldflags '-X main.Hello=World'"}) 5571 }) 5572 }) 5573 } 5574 5575 func TestLaunchRequestWithEnv(t *testing.T) { 5576 // testenv fixture will lookup SOMEVAR with 5577 // x, y := os.Lookup("SOMEVAR") 5578 // before stopping at runtime.Breakpoint. 5579 5580 type envMap map[string]*string 5581 strVar := func(s string) *string { return &s } 5582 5583 fixtures := protest.FindFixturesDir() // relative to current working directory. 5584 testFile, _ := filepath.Abs(filepath.Join(fixtures, "testenv.go")) 5585 for _, tc := range []struct { 5586 name string 5587 initEnv envMap 5588 launchEnv envMap 5589 wantX string 5590 wantY bool 5591 }{ 5592 { 5593 name: "no env", 5594 initEnv: envMap{"SOMEVAR": strVar("baz")}, 5595 wantX: "baz", 5596 wantY: true, 5597 }, 5598 { 5599 name: "overwrite", 5600 initEnv: envMap{"SOMEVAR": strVar("baz")}, 5601 launchEnv: envMap{"SOMEVAR": strVar("bar")}, 5602 wantX: "bar", 5603 wantY: true, 5604 }, 5605 { 5606 name: "unset", 5607 initEnv: envMap{"SOMEVAR": strVar("baz")}, 5608 launchEnv: envMap{"SOMEVAR": nil}, 5609 wantX: "", 5610 wantY: false, 5611 }, 5612 { 5613 name: "empty value", 5614 initEnv: envMap{"SOMEVAR": strVar("baz")}, 5615 launchEnv: envMap{"SOMEVAR": strVar("")}, 5616 wantX: "", 5617 wantY: true, 5618 }, 5619 { 5620 name: "set", 5621 launchEnv: envMap{"SOMEVAR": strVar("foo")}, 5622 wantX: "foo", 5623 wantY: true, 5624 }, 5625 { 5626 name: "untouched", 5627 initEnv: envMap{"SOMEVAR": strVar("baz")}, 5628 launchEnv: envMap{"SOMEVAR2": nil, "SOMEVAR3": strVar("foo")}, 5629 wantX: "baz", 5630 wantY: true, 5631 }, 5632 } { 5633 5634 t.Run(tc.name, func(t *testing.T) { 5635 // cleanup 5636 defer func() { 5637 os.Unsetenv("SOMEVAR") 5638 os.Unsetenv("SOMEVAR2") 5639 os.Unsetenv("SOMEVAR3") 5640 }() 5641 5642 for k, v := range tc.initEnv { 5643 if v != nil { 5644 os.Setenv(k, *v) 5645 } 5646 } 5647 5648 serverStopped := make(chan struct{}) 5649 client := startDAPServerWithClient(t, false, serverStopped) 5650 defer client.Close() 5651 5652 runDebugSessionWithBPs(t, client, "launch", func() { // launch 5653 client.LaunchRequestWithArgs(map[string]interface{}{ 5654 "mode": "debug", 5655 "program": testFile, 5656 "env": tc.launchEnv, 5657 }) 5658 }, testFile, nil, // runtime.Breakpoint 5659 []onBreakpoint{{ 5660 execute: func() { 5661 client.EvaluateRequest("x", 1000, "whatever") 5662 gotX := client.ExpectEvaluateResponse(t) 5663 checkEval(t, gotX, fmt.Sprintf("%q", tc.wantX), false) 5664 client.EvaluateRequest("y", 1000, "whatever") 5665 gotY := client.ExpectEvaluateResponse(t) 5666 checkEval(t, gotY, fmt.Sprintf("%v", tc.wantY), false) 5667 }, 5668 disconnect: true, 5669 }}) 5670 <-serverStopped 5671 }) 5672 } 5673 } 5674 5675 func TestAttachRequest(t *testing.T) { 5676 if runtime.GOOS == "freebsd" { 5677 t.SkipNow() 5678 } 5679 if runtime.GOOS == "windows" { 5680 t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details") 5681 } 5682 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 5683 // Start the program to attach to 5684 cmd := execFixture(t, fixture) 5685 5686 runDebugSessionWithBPs(t, client, "attach", 5687 // Attach 5688 func() { 5689 client.AttachRequest(map[string]interface{}{ 5690 /*"mode": "local" by default*/ "processId": cmd.Process.Pid, "stopOnEntry": false}) 5691 client.ExpectCapabilitiesEventSupportTerminateDebuggee(t) 5692 }, 5693 // Set breakpoints 5694 fixture.Source, []int{8}, 5695 []onBreakpoint{{ 5696 // Stop at line 8 5697 execute: func() { 5698 checkStop(t, client, 1, "main.loop", 8) 5699 client.VariablesRequest(localsScope) 5700 locals := client.ExpectVariablesResponse(t) 5701 checkChildren(t, locals, "Locals", 1) 5702 checkVarRegex(t, locals, 0, "i", "i", "[0-9]+", "int", noChildren) 5703 }, 5704 disconnect: true, 5705 }}) 5706 }) 5707 } 5708 5709 // Since we are in async mode while running, we might receive thee messages after pause request 5710 // in either order. 5711 func expectPauseResponseAndStoppedEvent(t *testing.T, client *daptest.Client) { 5712 t.Helper() 5713 for i := 0; i < 2; i++ { 5714 msg := client.ExpectMessage(t) 5715 switch m := msg.(type) { 5716 case *dap.StoppedEvent: 5717 if m.Body.Reason != "pause" || m.Body.ThreadId != 0 && m.Body.ThreadId != 1 { 5718 t.Errorf("\ngot %#v\nwant ThreadId=0/1 Reason='pause'", m) 5719 } 5720 case *dap.PauseResponse: 5721 default: 5722 t.Fatalf("got %#v, want StoppedEvent or PauseResponse", m) 5723 } 5724 } 5725 } 5726 5727 func TestPauseAndContinue(t *testing.T) { 5728 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 5729 runDebugSessionWithBPs(t, client, "launch", 5730 // Launch 5731 func() { 5732 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 5733 }, 5734 // Set breakpoints 5735 fixture.Source, []int{6}, 5736 []onBreakpoint{{ 5737 execute: func() { 5738 client.CheckStopLocation(t, 1, "main.loop", 6) 5739 5740 // Continue resumes all goroutines, so thread id is ignored 5741 client.ContinueRequest(12345) 5742 client.ExpectContinueResponse(t) 5743 5744 time.Sleep(time.Second) 5745 5746 // Halt pauses all goroutines, so thread id is ignored 5747 client.PauseRequest(56789) 5748 expectPauseResponseAndStoppedEvent(t, client) 5749 5750 // Pause will be a no-op at a pause: there will be no additional stopped events 5751 client.PauseRequest(1) 5752 client.ExpectPauseResponse(t) 5753 }, 5754 // The program has an infinite loop, so we must kill it by disconnecting. 5755 disconnect: true, 5756 }}) 5757 }) 5758 } 5759 5760 func TestUnupportedCommandResponses(t *testing.T) { 5761 var got *dap.ErrorResponse 5762 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 5763 seqCnt := 1 5764 expectUnsupportedCommand := func(cmd string) { 5765 t.Helper() 5766 got = client.ExpectUnsupportedCommandErrorResponse(t) 5767 if got.RequestSeq != seqCnt || got.Command != cmd { 5768 t.Errorf("\ngot %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd) 5769 } 5770 seqCnt++ 5771 } 5772 5773 client.RestartFrameRequest() 5774 expectUnsupportedCommand("restartFrame") 5775 5776 client.GotoRequest() 5777 expectUnsupportedCommand("goto") 5778 5779 client.SourceRequest() 5780 expectUnsupportedCommand("source") 5781 5782 client.TerminateThreadsRequest() 5783 expectUnsupportedCommand("terminateThreads") 5784 5785 client.StepInTargetsRequest() 5786 expectUnsupportedCommand("stepInTargets") 5787 5788 client.GotoTargetsRequest() 5789 expectUnsupportedCommand("gotoTargets") 5790 5791 client.CompletionsRequest() 5792 expectUnsupportedCommand("completions") 5793 5794 client.DataBreakpointInfoRequest() 5795 expectUnsupportedCommand("dataBreakpointInfo") 5796 5797 client.SetDataBreakpointsRequest() 5798 expectUnsupportedCommand("setDataBreakpoints") 5799 5800 client.BreakpointLocationsRequest() 5801 expectUnsupportedCommand("breakpointLocations") 5802 5803 client.ModulesRequest() 5804 expectUnsupportedCommand("modules") 5805 5806 client.DisconnectRequest() 5807 client.ExpectDisconnectResponse(t) 5808 }) 5809 } 5810 5811 type helperForSetVariable struct { 5812 t *testing.T 5813 c *daptest.Client 5814 } 5815 5816 func (h *helperForSetVariable) expectSetVariable(ref int, name, value string) { 5817 h.t.Helper() 5818 h.expectSetVariable0(ref, name, value, false) 5819 } 5820 5821 func (h *helperForSetVariable) failSetVariable(ref int, name, value, wantErrInfo string) { 5822 h.t.Helper() 5823 h.failSetVariable0(ref, name, value, wantErrInfo, false) 5824 } 5825 5826 func (h *helperForSetVariable) failSetVariableAndStop(ref int, name, value, wantErrInfo string) { 5827 h.t.Helper() 5828 h.failSetVariable0(ref, name, value, wantErrInfo, true) 5829 } 5830 5831 func (h *helperForSetVariable) evaluate(expr, want string, hasRef bool) { 5832 h.t.Helper() 5833 h.c.EvaluateRequest(expr, 1000, "whatever") 5834 got := h.c.ExpectEvaluateResponse(h.t) 5835 checkEval(h.t, got, want, hasRef) 5836 } 5837 5838 func (h *helperForSetVariable) evaluateRegex(expr, want string, hasRef bool) { 5839 h.t.Helper() 5840 h.c.EvaluateRequest(expr, 1000, "whatever") 5841 got := h.c.ExpectEvaluateResponse(h.t) 5842 checkEvalRegex(h.t, got, want, hasRef) 5843 } 5844 5845 func (h *helperForSetVariable) expectSetVariable0(ref int, name, value string, wantStop bool) { 5846 h.t.Helper() 5847 5848 h.c.SetVariableRequest(ref, name, value) 5849 if wantStop { 5850 h.c.ExpectStoppedEvent(h.t) 5851 } 5852 if got, want := h.c.ExpectSetVariableResponse(h.t), value; got.Success != true || got.Body.Value != want { 5853 h.t.Errorf("SetVariableRequest(%v, %v)=%#v, want {Success=true, Body.Value=%q", name, value, got, want) 5854 } 5855 } 5856 5857 func (h *helperForSetVariable) failSetVariable0(ref int, name, value, wantErrInfo string, wantStop bool) { 5858 h.t.Helper() 5859 5860 h.c.SetVariableRequest(ref, name, value) 5861 if wantStop { 5862 h.c.ExpectStoppedEvent(h.t) 5863 } 5864 resp := h.c.ExpectErrorResponse(h.t) 5865 if got := resp.Body.Error.Format; !stringContainsCaseInsensitive(got, wantErrInfo) { 5866 h.t.Errorf("got %#v, want error string containing %v", got, wantErrInfo) 5867 } 5868 } 5869 5870 func (h *helperForSetVariable) variables(ref int) *dap.VariablesResponse { 5871 h.t.Helper() 5872 h.c.VariablesRequest(ref) 5873 return h.c.ExpectVariablesResponse(h.t) 5874 } 5875 5876 // TestSetVariable tests SetVariable features that do not need function call support. 5877 func TestSetVariable(t *testing.T) { 5878 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 5879 runDebugSessionWithBPs(t, client, "launch", 5880 func() { 5881 client.LaunchRequestWithArgs(map[string]interface{}{ 5882 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, 5883 }) 5884 }, 5885 fixture.Source, []int{}, // breakpoints are set within the program. 5886 []onBreakpoint{{ 5887 execute: func() { 5888 tester := &helperForSetVariable{t, client} 5889 5890 startLineno := 66 // after runtime.Breakpoint 5891 if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) { 5892 // Go1.15 on windows inserts a NOP after the call to 5893 // runtime.Breakpoint and marks it same line as the 5894 // runtime.Breakpoint call, making this flaky, so skip the line check. 5895 startLineno = -1 5896 } 5897 5898 checkStop(t, client, 1, "main.foobar", startLineno) 5899 5900 // Local variables 5901 locals := tester.variables(localsScope) 5902 5903 // Args of foobar(baz string, bar FooBar) 5904 checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren) 5905 tester.failSetVariable(localsScope, "bar", `main.FooBar {Baz: 42, Bur: "ipsum"}`, "*ast.CompositeLit not implemented") 5906 5907 // Nested field. 5908 barRef := checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren) 5909 tester.expectSetVariable(barRef, "Baz", "42") 5910 tester.evaluate("bar", `main.FooBar {Baz: 42, Bur: "lorem"}`, hasChildren) 5911 5912 tester.failSetVariable(barRef, "Baz", `"string"`, "can not convert") 5913 5914 // int 5915 checkVarExact(t, locals, -1, "a2", "a2", "6", "int", noChildren) 5916 tester.expectSetVariable(localsScope, "a2", "42") 5917 tester.evaluate("a2", "42", noChildren) 5918 5919 tester.failSetVariable(localsScope, "a2", "false", "can not convert") 5920 5921 // float 5922 checkVarExact(t, locals, -1, "a3", "a3", "7.23", "float64", noChildren) 5923 tester.expectSetVariable(localsScope, "a3", "-0.1") 5924 tester.evaluate("a3", "-0.1", noChildren) 5925 5926 // array of int 5927 a4Ref := checkVarExact(t, locals, -1, "a4", "a4", "[2]int [1,2]", "[2]int", hasChildren) 5928 tester.expectSetVariable(a4Ref, "[1]", "-7") 5929 tester.evaluate("a4", "[2]int [1,-7]", hasChildren) 5930 5931 tester.failSetVariable(localsScope, "a4", "[2]int{3, 4}", "not implemented") 5932 5933 // slice of int 5934 a5Ref := checkVarExact(t, locals, -1, "a5", "a5", "[]int len: 5, cap: 5, [1,2,3,4,5]", "[]int", hasChildren) 5935 tester.expectSetVariable(a5Ref, "[3]", "100") 5936 tester.evaluate("a5", "[]int len: 5, cap: 5, [1,2,3,100,5]", hasChildren) 5937 5938 // composite literal and its nested fields. 5939 a7Ref := checkVarExact(t, locals, -1, "a7", "a7", `*main.FooBar {Baz: 5, Bur: "strum"}`, "*main.FooBar", hasChildren) 5940 a7Val := tester.variables(a7Ref) 5941 a7ValRef := checkVarExact(t, a7Val, -1, "", "(*a7)", `main.FooBar {Baz: 5, Bur: "strum"}`, "main.FooBar", hasChildren) 5942 tester.expectSetVariable(a7ValRef, "Baz", "7") 5943 tester.evaluate("(*a7)", `main.FooBar {Baz: 7, Bur: "strum"}`, hasChildren) 5944 5945 // pointer 5946 checkVarExact(t, locals, -1, "a9", "a9", `*main.FooBar nil`, "*main.FooBar", noChildren) 5947 tester.expectSetVariable(localsScope, "a9", "&a6") 5948 tester.evaluate("a9", `*main.FooBar {Baz: 8, Bur: "word"}`, hasChildren) 5949 5950 // slice of pointers 5951 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) 5952 a13 := tester.variables(a13Ref) 5953 a13c0Ref := checkVarExact(t, a13, -1, "[0]", "a13[0]", `*main.FooBar {Baz: 6, Bur: "f"}`, "*main.FooBar", hasChildren) 5954 a13c0 := tester.variables(a13c0Ref) 5955 a13c0valRef := checkVarExact(t, a13c0, -1, "", "(*a13[0])", `main.FooBar {Baz: 6, Bur: "f"}`, "main.FooBar", hasChildren) 5956 tester.expectSetVariable(a13c0valRef, "Baz", "777") 5957 tester.evaluate("a13[0]", `*main.FooBar {Baz: 777, Bur: "f"}`, hasChildren) 5958 5959 // complex 5960 tester.evaluate("c64", `(1 + 2i)`, hasChildren) 5961 tester.expectSetVariable(localsScope, "c64", "(2 + 3i)") 5962 tester.evaluate("c64", `(2 + 3i)`, hasChildren) 5963 // note: complex's real, imaginary part can't be directly mutable. 5964 5965 // 5966 // Global variables 5967 // p1 = 10 5968 client.VariablesRequest(globalsScope) 5969 globals := client.ExpectVariablesResponse(t) 5970 5971 checkVarExact(t, globals, -1, "p1", "main.p1", "10", "int", noChildren) 5972 tester.expectSetVariable(globalsScope, "p1", "-10") 5973 tester.evaluate("p1", "-10", noChildren) 5974 tester.failSetVariable(globalsScope, "p1", "0.1", "can not convert") 5975 }, 5976 disconnect: true, 5977 }}) 5978 }) 5979 5980 runTest(t, "testvariables2", func(client *daptest.Client, fixture protest.Fixture) { 5981 runDebugSessionWithBPs(t, client, "launch", 5982 func() { 5983 client.LaunchRequestWithArgs(map[string]interface{}{ 5984 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, 5985 }) 5986 }, 5987 fixture.Source, []int{}, // breakpoints are set within the program. 5988 []onBreakpoint{{ 5989 execute: func() { 5990 tester := &helperForSetVariable{t, client} 5991 5992 checkStop(t, client, 1, "main.main", -1) 5993 locals := tester.variables(localsScope) 5994 5995 // channel 5996 tester.evaluate("chnil", "chan int nil", noChildren) 5997 tester.expectSetVariable(localsScope, "chnil", "ch1") 5998 tester.evaluate("chnil", "chan int 4/11", hasChildren) 5999 6000 // func 6001 tester.evaluate("fn2", "nil", noChildren) 6002 tester.expectSetVariable(localsScope, "fn2", "fn1") 6003 tester.evaluate("fn2", "main.afunc", noChildren) 6004 6005 // interface 6006 tester.evaluate("ifacenil", "interface {} nil", noChildren) 6007 tester.expectSetVariable(localsScope, "ifacenil", "iface1") 6008 tester.evaluate("ifacenil", "interface {}(*main.astruct) *{A: 1, B: 2}", hasChildren) 6009 6010 // interface.(data) 6011 iface1Ref := checkVarExact(t, locals, -1, "iface1", "iface1", "interface {}(*main.astruct) *{A: 1, B: 2}", "interface {}", hasChildren) 6012 iface1 := tester.variables(iface1Ref) 6013 iface1DataRef := checkVarExact(t, iface1, -1, "data", "iface1.(data)", "*main.astruct {A: 1, B: 2}", "*main.astruct", hasChildren) 6014 iface1Data := tester.variables(iface1DataRef) 6015 iface1DataValueRef := checkVarExact(t, iface1Data, -1, "", "(*iface1.(data))", "main.astruct {A: 1, B: 2}", "main.astruct", hasChildren) 6016 tester.expectSetVariable(iface1DataValueRef, "A", "2021") 6017 tester.evaluate("iface1", "interface {}(*main.astruct) *{A: 2021, B: 2}", hasChildren) 6018 6019 // map: string -> struct 6020 tester.evaluate(`m1["Malone"]`, "main.astruct {A: 2, B: 3}", hasChildren) 6021 m1Ref := checkVarRegex(t, locals, -1, "m1", "m1", `.*map\[string\]main\.astruct.*`, `map\[string\]main\.astruct`, hasChildren) 6022 m1 := tester.variables(m1Ref) 6023 elem1 := m1.Body.Variables[1] 6024 tester.expectSetVariable(elem1.VariablesReference, "A", "-9999") 6025 tester.expectSetVariable(elem1.VariablesReference, "B", "10000") 6026 tester.evaluate(elem1.EvaluateName, "main.astruct {A: -9999, B: 10000}", hasChildren) 6027 6028 // map: struct -> int 6029 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) 6030 tester.expectSetVariable(m3Ref, "main.astruct {A: 1, B: 1}", "8888") 6031 // note: updating keys is possible, but let's not promise anything. 6032 tester.evaluateRegex("m3", `.*\[\{A: 1, B: 1\}: 8888,.*`, hasChildren) 6033 6034 // map: struct -> struct 6035 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) 6036 m4 := tester.variables(m4Ref) 6037 m4Val1Ref := checkVarRegex(t, m4, -1, "[val 0]", `.*0x[0-9a-f]+.*`, `main.astruct.*`, `main\.astruct`, hasChildren) 6038 tester.expectSetVariable(m4Val1Ref, "A", "-9999") 6039 tester.evaluateRegex("m4", `.*A: -9999,.*`, hasChildren) 6040 6041 // unsigned pointer 6042 checkVarRegex(t, locals, -1, "up1", "up1", `unsafe\.Pointer\(0x[0-9a-f]+\)`, "unsafe.Pointer", noChildren) 6043 tester.expectSetVariable(localsScope, "up1", "unsafe.Pointer(0x0)") 6044 tester.evaluate("up1", "unsafe.Pointer(0x0)", noChildren) 6045 6046 // val := A{val: 1} 6047 valRef := checkVarExact(t, locals, -1, "val", "val", `main.A {val: 1}`, "main.A", hasChildren) 6048 tester.expectSetVariable(valRef, "val", "3") 6049 tester.evaluate("val", `main.A {val: 3}`, hasChildren) 6050 }, 6051 disconnect: true, 6052 }}) 6053 }) 6054 } 6055 6056 // TestSetVariableWithCall tests SetVariable features that do not depend on function calls support. 6057 func TestSetVariableWithCall(t *testing.T) { 6058 protest.MustSupportFunctionCalls(t, testBackend) 6059 6060 runTest(t, "testvariables", func(client *daptest.Client, fixture protest.Fixture) { 6061 runDebugSessionWithBPs(t, client, "launch", 6062 func() { 6063 client.LaunchRequestWithArgs(map[string]interface{}{ 6064 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, 6065 }) 6066 }, 6067 fixture.Source, []int{66, 67}, 6068 []onBreakpoint{{ 6069 execute: func() { 6070 tester := &helperForSetVariable{t, client} 6071 6072 startLineno := 66 6073 if runtime.GOOS == "windows" && goversion.VersionAfterOrEqual(runtime.Version(), 1, 15) { 6074 // Go1.15 on windows inserts a NOP after the call to 6075 // runtime.Breakpoint and marks it same line as the 6076 // runtime.Breakpoint call, making this flaky, so skip the line check. 6077 startLineno = -1 6078 } 6079 6080 checkStop(t, client, 1, "main.foobar", startLineno) 6081 6082 // Local variables 6083 locals := tester.variables(localsScope) 6084 6085 // Args of foobar(baz string, bar FooBar) 6086 checkVarExact(t, locals, 0, "baz", "baz", `"bazburzum"`, "string", noChildren) 6087 tester.expectSetVariable(localsScope, "baz", `"BazBurZum"`) 6088 tester.evaluate("baz", `"BazBurZum"`, noChildren) 6089 6090 barRef := checkVarExact(t, locals, 1, "bar", "bar", `main.FooBar {Baz: 10, Bur: "lorem"}`, "main.FooBar", hasChildren) 6091 tester.expectSetVariable(barRef, "Bur", `"ipsum"`) 6092 tester.evaluate("bar", `main.FooBar {Baz: 10, Bur: "ipsum"}`, hasChildren) 6093 6094 checkVarExact(t, locals, -1, "a1", "a1", `"foofoofoofoofoofoo"`, "string", noChildren) 6095 tester.expectSetVariable(localsScope, "a1", `"barbarbar"`) 6096 tester.evaluate("a1", `"barbarbar"`, noChildren) 6097 6098 a6Ref := checkVarExact(t, locals, -1, "a6", "a6", `main.FooBar {Baz: 8, Bur: "word"}`, "main.FooBar", hasChildren) 6099 tester.failSetVariable(a6Ref, "Bur", "false", "can not convert") 6100 6101 tester.expectSetVariable(a6Ref, "Bur", `"sentence"`) 6102 tester.evaluate("a6", `main.FooBar {Baz: 8, Bur: "sentence"}`, hasChildren) 6103 }, 6104 }, { 6105 // Stop at second breakpoint and set a1. 6106 execute: func() { 6107 tester := &helperForSetVariable{t, client} 6108 6109 checkStop(t, client, 1, "main.barfoo", -1) 6110 // Test: set string 'a1' in main.barfoo. 6111 // This shouldn't affect 'a1' in main.foobar - we will check that in the next breakpoint. 6112 locals := tester.variables(localsScope) 6113 checkVarExact(t, locals, -1, "a1", "a1", `"bur"`, "string", noChildren) 6114 tester.expectSetVariable(localsScope, "a1", `"fur"`) 6115 tester.evaluate("a1", `"fur"`, noChildren) 6116 // We will check a1 in main.foobar isn't affected from the next breakpoint. 6117 6118 client.StackTraceRequest(1, 1, 20) 6119 res := client.ExpectStackTraceResponse(t) 6120 if len(res.Body.StackFrames) < 1 { 6121 t.Fatalf("stack trace response = %#v, wanted at least one stack frame", res) 6122 } 6123 outerFrame := res.Body.StackFrames[0].Id 6124 client.EvaluateRequest("a1", outerFrame, "whatever_context") 6125 evalRes := client.ExpectEvaluateResponse(t) 6126 checkEval(t, evalRes, `"barbarbar"`, noChildren) 6127 }, 6128 disconnect: true, 6129 }}) 6130 }) 6131 6132 runTest(t, "fncall", func(client *daptest.Client, fixture protest.Fixture) { 6133 runDebugSessionWithBPs(t, client, "launch", 6134 func() { 6135 client.LaunchRequestWithArgs(map[string]interface{}{ 6136 "mode": "exec", "program": fixture.Path, "showGlobalVariables": true, 6137 }) 6138 }, 6139 fixture.Source, []int{}, // breakpoints are set within the program. 6140 []onBreakpoint{{ 6141 // Stop at second breakpoint and set a1. 6142 execute: func() { 6143 tester := &helperForSetVariable{t, client} 6144 6145 checkStop(t, client, 1, "main.main", -1) 6146 6147 _ = tester.variables(localsScope) 6148 6149 // successful variable set using a function call. 6150 tester.expectSetVariable(localsScope, "str", `callstacktrace()`) 6151 tester.evaluateRegex("str", `.*in main.callstacktrace at.*`, noChildren) 6152 6153 tester.failSetVariableAndStop(localsScope, "str", `callpanic()`, `callpanic panicked`) 6154 checkStop(t, client, 1, "main.main", -1) 6155 6156 // breakpoint during a function call. 6157 tester.failSetVariableAndStop(localsScope, "str", `callbreak()`, "call stopped") 6158 6159 // TODO(hyangah): continue after this causes runtime error while resuming 6160 // unfinished injected call. 6161 // runtime error: can not convert %!s(<nil>) constant to string 6162 // This can be reproducible with dlv cli. (`call str = callbreak(); continue`) 6163 }, 6164 disconnect: true, 6165 }}) 6166 }) 6167 } 6168 6169 func TestOptionalNotYetImplementedResponses(t *testing.T) { 6170 var got *dap.ErrorResponse 6171 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 6172 seqCnt := 1 6173 expectNotYetImplemented := func(cmd string) { 6174 t.Helper() 6175 got = client.ExpectNotYetImplementedErrorResponse(t) 6176 if got.RequestSeq != seqCnt || got.Command != cmd { 6177 t.Errorf("\ngot %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd) 6178 } 6179 seqCnt++ 6180 } 6181 6182 client.TerminateRequest() 6183 expectNotYetImplemented("terminate") 6184 6185 client.RestartRequest() 6186 expectNotYetImplemented("restart") 6187 6188 client.SetExpressionRequest() 6189 expectNotYetImplemented("setExpression") 6190 6191 client.LoadedSourcesRequest() 6192 expectNotYetImplemented("loadedSources") 6193 6194 client.ReadMemoryRequest() 6195 expectNotYetImplemented("readMemory") 6196 6197 client.CancelRequest() 6198 expectNotYetImplemented("cancel") 6199 6200 client.DisconnectRequest() 6201 client.ExpectDisconnectResponse(t) 6202 }) 6203 } 6204 6205 func TestBadLaunchRequests(t *testing.T) { 6206 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 6207 seqCnt := 1 6208 checkFailedToLaunch := func(response *dap.ErrorResponse) { 6209 t.Helper() 6210 if response.RequestSeq != seqCnt { 6211 t.Errorf("RequestSeq got %d, want %d", seqCnt, response.RequestSeq) 6212 } 6213 if response.Command != "launch" { 6214 t.Errorf("Command got %q, want \"launch\"", response.Command) 6215 } 6216 if response.Message != "Failed to launch" { 6217 t.Errorf("Message got %q, want \"Failed to launch\"", response.Message) 6218 } 6219 if response.Body.Error.Id != FailedToLaunch { 6220 t.Errorf("Id got %d, want %d", response.Body.Error.Id, FailedToLaunch) 6221 } 6222 seqCnt++ 6223 } 6224 6225 checkFailedToLaunchWithMessage := func(response *dap.ErrorResponse, errmsg string) { 6226 t.Helper() 6227 checkFailedToLaunch(response) 6228 if response.Body.Error.Format != errmsg { 6229 t.Errorf("\ngot %q\nwant %q", response.Body.Error.Format, errmsg) 6230 } 6231 } 6232 6233 // Test for the DAP-specific detailed error message. 6234 client.LaunchRequest("exec", "", stopOnEntry) 6235 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6236 "Failed to launch: The program attribute is missing in debug configuration.") 6237 6238 // Bad "program" 6239 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": 12345}) 6240 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6241 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"program\" of type string") 6242 6243 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": nil}) 6244 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6245 "Failed to launch: The program attribute is missing in debug configuration.") 6246 6247 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug"}) 6248 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6249 "Failed to launch: The program attribute is missing in debug configuration.") 6250 6251 // Bad "mode" 6252 client.LaunchRequest("remote", fixture.Path, stopOnEntry) 6253 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6254 "Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"remote\"") 6255 6256 client.LaunchRequest("notamode", fixture.Path, stopOnEntry) 6257 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6258 "Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"notamode\"") 6259 6260 client.LaunchRequestWithArgs(map[string]interface{}{"mode": 12345, "program": fixture.Path}) 6261 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6262 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"mode\" of type string") 6263 6264 client.LaunchRequestWithArgs(map[string]interface{}{"mode": ""}) // empty mode defaults to "debug" (not an error) 6265 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6266 "Failed to launch: The program attribute is missing in debug configuration.") 6267 6268 client.LaunchRequestWithArgs(map[string]interface{}{}) // missing mode defaults to "debug" (not an error) 6269 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6270 "Failed to launch: The program attribute is missing in debug configuration.") 6271 6272 // Bad "args" 6273 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": "foobar"}) 6274 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6275 "Failed to launch: invalid debug configuration - cannot unmarshal string into \"args\" of type []string") 6276 6277 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": 12345}) 6278 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6279 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"args\" of type []string") 6280 6281 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "exec", "program": fixture.Path, "args": []int{1, 2}}) 6282 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6283 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"args\" of type string") 6284 6285 // Bad "buildFlags" 6286 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": 123}) 6287 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6288 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"buildFlags\" of type string") 6289 6290 // Bad "backend" 6291 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "backend": 123}) 6292 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6293 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"backend\" of type string") 6294 6295 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "backend": "foo"}) 6296 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6297 "Failed to launch: could not launch process: unknown backend \"foo\"") 6298 6299 // Bad "substitutePath" 6300 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": 123}) 6301 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6302 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"substitutePath\" of type {\"from\":string, \"to\":string}") 6303 6304 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{123}}) 6305 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6306 "Failed to launch: invalid debug configuration - cannot use 123 as 'substitutePath' of type {\"from\":string, \"to\":string}") 6307 6308 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"to": "path2"}}}) 6309 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6310 "Failed to launch: invalid debug configuration - 'substitutePath' requires both 'from' and 'to' entries") 6311 6312 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "substitutePath": []interface{}{map[string]interface{}{"from": "path1", "to": 123}}}) 6313 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6314 "Failed to launch: invalid debug configuration - cannot use {\"from\":\"path1\",\"to\":123} as 'substitutePath' of type {\"from\":string, \"to\":string}") 6315 // Bad "cwd" 6316 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "cwd": 123}) 6317 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6318 "Failed to launch: invalid debug configuration - cannot unmarshal number into \"cwd\" of type string") 6319 6320 // Skip detailed message checks for potentially different OS-specific errors. 6321 client.LaunchRequest("exec", fixture.Path+"_does_not_exist", stopOnEntry) 6322 checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // No such file or directory 6323 6324 client.LaunchRequest("debug", fixture.Path+"_does_not_exist", stopOnEntry) 6325 oe := client.ExpectOutputEvent(t) 6326 if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" { 6327 t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe) 6328 } 6329 checkFailedToLaunch(client.ExpectInvisibleErrorResponse(t)) 6330 6331 client.LaunchRequestWithArgs(map[string]interface{}{ 6332 "request": "launch", 6333 /* mode: debug by default*/ 6334 "program": fixture.Path + "_does_not_exist", 6335 "stopOnEntry": stopOnEntry, 6336 }) 6337 oe = client.ExpectOutputEvent(t) 6338 if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" { 6339 t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe) 6340 } 6341 checkFailedToLaunch(client.ExpectInvisibleErrorResponse(t)) 6342 6343 client.LaunchRequest("exec", fixture.Source, stopOnEntry) 6344 checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // Not an executable 6345 6346 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "buildFlags": "-bad -flags"}) 6347 oe = client.ExpectOutputEvent(t) 6348 if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" { 6349 t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe) 6350 } 6351 checkFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t), "Failed to launch: Build error: Check the debug console for details.") 6352 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "buildFlags": "-bad -flags"}) 6353 oe = client.ExpectOutputEvent(t) 6354 if !strings.HasPrefix(oe.Body.Output, "Build Error: ") || oe.Body.Category != "stderr" { 6355 t.Errorf("got %#v, want Category=\"stderr\" Output=\"Build Error: ...\"", oe) 6356 } 6357 checkFailedToLaunchWithMessage(client.ExpectInvisibleErrorResponse(t), "Failed to launch: Build error: Check the debug console for details.") 6358 6359 // Bad "cwd" 6360 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": false, "cwd": "dir/invalid"}) 6361 checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // invalid directory, the error message is system-dependent. 6362 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": true, "cwd": "dir/invalid"}) 6363 checkFailedToLaunch(client.ExpectVisibleErrorResponse(t)) // invalid directory, the error message is system-dependent. 6364 6365 // Bad "noDebug" 6366 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "debug", "program": fixture.Source, "noDebug": "true"}) 6367 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), "Failed to launch: invalid debug configuration - cannot unmarshal string into \"noDebug\" of type bool") 6368 6369 // Bad "replay" parameters 6370 // These errors come from dap layer 6371 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "replay", "traceDirPath": ""}) 6372 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6373 "Failed to launch: The 'traceDirPath' attribute is missing in debug configuration.") 6374 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "replay", "program": fixture.Source, "traceDirPath": ""}) 6375 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6376 "Failed to launch: The 'traceDirPath' attribute is missing in debug configuration.") 6377 // These errors come from debugger layer 6378 if _, err := exec.LookPath("rr"); err != nil { 6379 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "replay", "backend": "ignored", "traceDirPath": ".."}) 6380 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6381 "Failed to launch: backend unavailable") 6382 } 6383 6384 // Bad "core" parameters 6385 // These errors come from dap layer 6386 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "core", "coreFilePath": ""}) 6387 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6388 "Failed to launch: The program attribute is missing in debug configuration.") 6389 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "core", "program": fixture.Source, "coreFilePath": ""}) 6390 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6391 "Failed to launch: The 'coreFilePath' attribute is missing in debug configuration.") 6392 // These errors come from debugger layer 6393 client.LaunchRequestWithArgs(map[string]interface{}{"mode": "core", "backend": "ignored", "program": fixture.Source, "coreFilePath": fixture.Source}) 6394 checkFailedToLaunchWithMessage(client.ExpectVisibleErrorResponse(t), 6395 "Failed to launch: unrecognized core format") 6396 6397 // We failed to launch the program. Make sure shutdown still works. 6398 client.DisconnectRequest() 6399 dresp := client.ExpectDisconnectResponse(t) 6400 if dresp.RequestSeq != seqCnt { 6401 t.Errorf("got %#v, want RequestSeq=%d", dresp, seqCnt) 6402 } 6403 }) 6404 } 6405 6406 func TestBadAttachRequest(t *testing.T) { 6407 runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) { 6408 seqCnt := 1 6409 checkFailedToAttach := func(response *dap.ErrorResponse) { 6410 t.Helper() 6411 if response.RequestSeq != seqCnt { 6412 t.Errorf("RequestSeq got %d, want %d", seqCnt, response.RequestSeq) 6413 } 6414 if response.Command != "attach" { 6415 t.Errorf("Command got %q, want \"attach\"", response.Command) 6416 } 6417 if response.Message != "Failed to attach" { 6418 t.Errorf("Message got %q, want \"Failed to attach\"", response.Message) 6419 } 6420 if response.Body.Error.Id != FailedToAttach { 6421 t.Errorf("Id got %d, want %d", response.Body.Error.Id, FailedToAttach) 6422 } 6423 seqCnt++ 6424 } 6425 6426 checkFailedToAttachWithMessage := func(response *dap.ErrorResponse, errmsg string) { 6427 t.Helper() 6428 checkFailedToAttach(response) 6429 if response.Body.Error.Format != errmsg { 6430 t.Errorf("\ngot %q\nwant %q", response.Body.Error.Format, errmsg) 6431 } 6432 } 6433 6434 // Bad "mode" 6435 client.AttachRequest(map[string]interface{}{"mode": "blah blah blah"}) 6436 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6437 "Failed to attach: invalid debug configuration - unsupported 'mode' attribute \"blah blah blah\"") 6438 6439 client.AttachRequest(map[string]interface{}{"mode": 123}) 6440 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6441 "Failed to attach: invalid debug configuration - cannot unmarshal number into \"mode\" of type string") 6442 6443 client.AttachRequest(map[string]interface{}{"mode": ""}) // empty mode defaults to "local" (not an error) 6444 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6445 "Failed to attach: The 'processId' attribute is missing in debug configuration") 6446 6447 client.AttachRequest(map[string]interface{}{}) // no mode defaults to "local" (not an error) 6448 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6449 "Failed to attach: The 'processId' attribute is missing in debug configuration") 6450 6451 // Bad "processId" 6452 client.AttachRequest(map[string]interface{}{"mode": "local"}) 6453 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6454 "Failed to attach: The 'processId' attribute is missing in debug configuration") 6455 6456 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": nil}) 6457 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6458 "Failed to attach: The 'processId' attribute is missing in debug configuration") 6459 6460 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 0}) 6461 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6462 "Failed to attach: The 'processId' attribute is missing in debug configuration") 6463 6464 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": "1"}) 6465 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6466 "Failed to attach: invalid debug configuration - cannot unmarshal string into \"processId\" of type int") 6467 6468 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1}) 6469 // The exact message varies on different systems, so skip that check 6470 checkFailedToAttach(client.ExpectVisibleErrorResponse(t)) // could not attach to pid 1 6471 6472 // This will make debugger.(*Debugger) panic, which we will catch as an internal error. 6473 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": -1}) 6474 er := client.ExpectInvisibleErrorResponse(t) 6475 if er.RequestSeq != seqCnt { 6476 t.Errorf("RequestSeq got %d, want %d", seqCnt, er.RequestSeq) 6477 } 6478 seqCnt++ 6479 if er.Command != "" { 6480 t.Errorf("Command got %q, want \"attach\"", er.Command) 6481 } 6482 if er.Body.Error.Format != "Internal Error: runtime error: index out of range [0] with length 0" { 6483 t.Errorf("Message got %q, want \"Internal Error: runtime error: index out of range [0] with length 0\"", er.Message) 6484 } 6485 if er.Body.Error.Id != InternalError { 6486 t.Errorf("Id got %d, want %d", er.Body.Error.Id, InternalError) 6487 } 6488 6489 // Bad "backend" 6490 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "backend": 123}) 6491 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6492 "Failed to attach: invalid debug configuration - cannot unmarshal number into \"backend\" of type string") 6493 6494 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 1, "backend": "foo"}) 6495 checkFailedToAttachWithMessage(client.ExpectVisibleErrorResponse(t), 6496 "Failed to attach: could not attach to pid 1: unknown backend \"foo\"") 6497 6498 // We failed to attach to the program. Make sure shutdown still works. 6499 client.DisconnectRequest() 6500 dresp := client.ExpectDisconnectResponse(t) 6501 if dresp.RequestSeq != seqCnt { 6502 t.Errorf("got %#v, want RequestSeq=%d", dresp, seqCnt) 6503 } 6504 }) 6505 } 6506 6507 func launchDebuggerWithTargetRunning(t *testing.T, fixture string) (*protest.Fixture, *debugger.Debugger) { 6508 t.Helper() 6509 fixbin, dbg := launchDebuggerWithTargetHalted(t, fixture) 6510 running := make(chan struct{}) 6511 var err error 6512 go func() { 6513 t.Helper() 6514 _, err = dbg.Command(&api.DebuggerCommand{Name: api.Continue}, running) 6515 select { 6516 case <-running: 6517 default: 6518 close(running) 6519 } 6520 }() 6521 <-running 6522 if err != nil { 6523 t.Fatal("failed to continue on launch", err) 6524 } 6525 return fixbin, dbg 6526 } 6527 6528 func launchDebuggerWithTargetHalted(t *testing.T, fixture string) (*protest.Fixture, *debugger.Debugger) { 6529 t.Helper() 6530 fixbin := protest.BuildFixture(fixture, protest.AllNonOptimized) 6531 cfg := service.Config{ 6532 ProcessArgs: []string{fixbin.Path}, 6533 Debugger: debugger.Config{Backend: "default"}, 6534 } 6535 dbg, err := debugger.New(&cfg.Debugger, cfg.ProcessArgs) // debugger halts process on entry 6536 if err != nil { 6537 t.Fatal("failed to start debugger:", err) 6538 } 6539 return &fixbin, dbg 6540 } 6541 6542 func attachDebuggerWithTargetHalted(t *testing.T, fixture string) (*exec.Cmd, *debugger.Debugger) { 6543 t.Helper() 6544 fixbin := protest.BuildFixture(fixture, protest.AllNonOptimized) 6545 cmd := execFixture(t, fixbin) 6546 cfg := service.Config{Debugger: debugger.Config{Backend: "default", AttachPid: cmd.Process.Pid}} 6547 dbg, err := debugger.New(&cfg.Debugger, nil) // debugger halts process on entry 6548 if err != nil { 6549 t.Fatal("failed to start debugger:", err) 6550 } 6551 return cmd, dbg 6552 } 6553 6554 // runTestWithDebugger starts the server and sets its debugger, initializes a debug session, 6555 // runs test, then disconnects. Expects no running async handler at the end of test() (either 6556 // process is halted or debug session never launched.) 6557 func runTestWithDebugger(t *testing.T, dbg *debugger.Debugger, test func(c *daptest.Client)) { 6558 serverStopped := make(chan struct{}) 6559 server, _ := startDAPServer(t, false, serverStopped) 6560 client := daptest.NewClient(server.listener.Addr().String()) 6561 time.Sleep(100 * time.Millisecond) // Give time for connection to be set as dap.Session 6562 server.sessionMu.Lock() 6563 if server.session == nil { 6564 t.Fatal("DAP session is not ready") 6565 } 6566 // Mock dap.NewSession arguments, so 6567 // this dap.Server can be used as a proxy for 6568 // rpccommon.Server running dap.Session. 6569 server.session.config.Debugger.AttachPid = dbg.AttachPid() 6570 server.session.debugger = dbg 6571 server.sessionMu.Unlock() 6572 defer client.Close() 6573 client.InitializeRequest() 6574 client.ExpectInitializeResponseAndCapabilities(t) 6575 6576 test(client) 6577 6578 client.DisconnectRequest() 6579 if dbg.AttachPid() == 0 { // launched target 6580 client.ExpectOutputEventDetachingKill(t) 6581 } else { // attached to target 6582 client.ExpectOutputEventDetachingNoKill(t) 6583 } 6584 client.ExpectDisconnectResponse(t) 6585 client.ExpectTerminatedEvent(t) 6586 6587 <-serverStopped 6588 } 6589 6590 func TestAttachRemoteToDlvLaunchHaltedStopOnEntry(t *testing.T) { 6591 // Halted + stop on entry 6592 _, dbg := launchDebuggerWithTargetHalted(t, "increment") 6593 runTestWithDebugger(t, dbg, func(client *daptest.Client) { 6594 client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true}) 6595 client.ExpectInitializedEvent(t) 6596 client.ExpectAttachResponse(t) 6597 client.ConfigurationDoneRequest() 6598 client.ExpectStoppedEvent(t) 6599 client.ExpectConfigurationDoneResponse(t) 6600 }) 6601 } 6602 6603 func TestAttachRemoteToDlvAttachHaltedStopOnEntry(t *testing.T) { 6604 if runtime.GOOS == "freebsd" || runtime.GOOS == "windows" { 6605 t.SkipNow() 6606 } 6607 cmd, dbg := attachDebuggerWithTargetHalted(t, "http_server") 6608 runTestWithDebugger(t, dbg, func(client *daptest.Client) { 6609 client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true}) 6610 client.ExpectCapabilitiesEventSupportTerminateDebuggee(t) 6611 client.ExpectInitializedEvent(t) 6612 client.ExpectAttachResponse(t) 6613 client.ConfigurationDoneRequest() 6614 client.ExpectStoppedEvent(t) 6615 client.ExpectConfigurationDoneResponse(t) 6616 }) 6617 cmd.Process.Kill() 6618 } 6619 6620 func TestAttachRemoteToHaltedTargetContinueOnEntry(t *testing.T) { 6621 // Halted + continue on entry 6622 _, dbg := launchDebuggerWithTargetHalted(t, "http_server") 6623 runTestWithDebugger(t, dbg, func(client *daptest.Client) { 6624 client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": false}) 6625 client.ExpectInitializedEvent(t) 6626 client.ExpectAttachResponse(t) 6627 client.ConfigurationDoneRequest() 6628 client.ExpectConfigurationDoneResponse(t) 6629 // Continuing 6630 time.Sleep(time.Second) 6631 // Halt to make the disconnect sequence more predictable. 6632 client.PauseRequest(1) 6633 expectPauseResponseAndStoppedEvent(t, client) 6634 }) 6635 } 6636 6637 func TestAttachRemoteToRunningTargetStopOnEntry(t *testing.T) { 6638 fixture, dbg := launchDebuggerWithTargetRunning(t, "loopprog") 6639 runTestWithDebugger(t, dbg, func(client *daptest.Client) { 6640 client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true}) 6641 client.ExpectInitializedEvent(t) 6642 client.ExpectAttachResponse(t) 6643 // Target is halted here 6644 client.SetBreakpointsRequest(fixture.Source, []int{8}) 6645 expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}}) 6646 client.ConfigurationDoneRequest() 6647 client.ExpectStoppedEvent(t) 6648 client.ExpectConfigurationDoneResponse(t) 6649 client.ContinueRequest(1) 6650 client.ExpectContinueResponse(t) 6651 client.ExpectStoppedEvent(t) 6652 checkStop(t, client, 1, "main.loop", 8) 6653 }) 6654 } 6655 6656 func TestAttachRemoteToRunningTargetContinueOnEntry(t *testing.T) { 6657 fixture, dbg := launchDebuggerWithTargetRunning(t, "loopprog") 6658 runTestWithDebugger(t, dbg, func(client *daptest.Client) { 6659 client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": false}) 6660 client.ExpectInitializedEvent(t) 6661 client.ExpectAttachResponse(t) 6662 // Target is halted here 6663 client.SetBreakpointsRequest(fixture.Source, []int{8}) 6664 expectSetBreakpointsResponse(t, client, []Breakpoint{{8, fixture.Source, true, ""}}) 6665 client.ConfigurationDoneRequest() 6666 // Target is restarted here 6667 client.ExpectConfigurationDoneResponse(t) 6668 client.ExpectStoppedEvent(t) 6669 checkStop(t, client, 1, "main.loop", 8) 6670 }) 6671 } 6672 6673 // MultiClientCloseServerMock mocks the rpccommon.Server using a dap.Server to exercise 6674 // the shutdown logic in dap.Session where it does NOT take down the server on close 6675 // in multi-client mode. (The dap mode of the rpccommon.Server is tested in dlv_test). 6676 // The dap.Server is a single-use server. Once its one and only session is closed, 6677 // the server and the target must be taken down manually for the test not to leak. 6678 type MultiClientCloseServerMock struct { 6679 impl *Server 6680 debugger *debugger.Debugger 6681 forceStop chan struct{} 6682 stopped chan struct{} 6683 } 6684 6685 func NewMultiClientCloseServerMock(t *testing.T, fixture string) *MultiClientCloseServerMock { 6686 var s MultiClientCloseServerMock 6687 s.stopped = make(chan struct{}) 6688 s.impl, s.forceStop = startDAPServer(t, false, s.stopped) 6689 _, s.debugger = launchDebuggerWithTargetHalted(t, "http_server") 6690 return &s 6691 } 6692 6693 func (s *MultiClientCloseServerMock) acceptNewClient(t *testing.T) *daptest.Client { 6694 client := daptest.NewClient(s.impl.listener.Addr().String()) 6695 time.Sleep(100 * time.Millisecond) // Give time for connection to be set as dap.Session 6696 s.impl.sessionMu.Lock() 6697 if s.impl.session == nil { 6698 t.Fatal("dap session is not ready") 6699 } 6700 // A dap.Server doesn't support accept-multiclient, but we can use this 6701 // hack to test the inner connection logic that is used by a server that does. 6702 s.impl.session.config.AcceptMulti = true 6703 s.impl.session.debugger = s.debugger 6704 s.impl.sessionMu.Unlock() 6705 return client 6706 } 6707 6708 func (s *MultiClientCloseServerMock) stop(t *testing.T) { 6709 close(s.forceStop) 6710 // If the server doesn't have an active session, 6711 // closing it would leak the debbuger with the target because 6712 // they are part of dap.Session. 6713 // We must take it down manually as if we are in rpccommon::ServerImpl::Stop. 6714 if s.debugger.IsRunning() { 6715 s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil) 6716 } 6717 s.debugger.Detach(true) 6718 } 6719 6720 func (s *MultiClientCloseServerMock) verifyStopped(t *testing.T) { 6721 if state, err := s.debugger.State(true /*nowait*/); err != proc.ErrProcessDetached && !processExited(state, err) { 6722 t.Errorf("target leak") 6723 } 6724 verifyServerStopped(t, s.impl) 6725 } 6726 6727 // TestAttachRemoteMultiClientDisconnect tests that that remote attach doesn't take down 6728 // the server in multi-client mode unless terminateDebuggee is explicitely set. 6729 func TestAttachRemoteMultiClientDisconnect(t *testing.T) { 6730 closingClientSessionOnly := fmt.Sprintf(daptest.ClosingClient, "halted") 6731 detachingAndTerminating := "Detaching and terminating target process" 6732 tests := []struct { 6733 name string 6734 disconnectRequest func(client *daptest.Client) 6735 expect string 6736 }{ 6737 {"default", func(c *daptest.Client) { c.DisconnectRequest() }, closingClientSessionOnly}, 6738 {"terminate=true", func(c *daptest.Client) { c.DisconnectRequestWithKillOption(true) }, detachingAndTerminating}, 6739 {"terminate=false", func(c *daptest.Client) { c.DisconnectRequestWithKillOption(false) }, closingClientSessionOnly}, 6740 } 6741 for _, tc := range tests { 6742 t.Run(tc.name, func(t *testing.T) { 6743 server := NewMultiClientCloseServerMock(t, "increment") 6744 client := server.acceptNewClient(t) 6745 defer client.Close() 6746 6747 client.InitializeRequest() 6748 client.ExpectInitializeResponseAndCapabilities(t) 6749 6750 client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true}) 6751 client.ExpectCapabilitiesEventSupportTerminateDebuggee(t) 6752 client.ExpectInitializedEvent(t) 6753 client.ExpectAttachResponse(t) 6754 client.ConfigurationDoneRequest() 6755 client.ExpectStoppedEvent(t) 6756 client.ExpectConfigurationDoneResponse(t) 6757 6758 tc.disconnectRequest(client) 6759 e := client.ExpectOutputEvent(t) 6760 if matched, _ := regexp.MatchString(tc.expect, e.Body.Output); !matched { 6761 t.Errorf("\ngot %#v\nwant Output=%q", e, tc.expect) 6762 } 6763 client.ExpectDisconnectResponse(t) 6764 client.ExpectTerminatedEvent(t) 6765 time.Sleep(10 * time.Millisecond) // give time for things to shut down 6766 6767 if tc.expect == closingClientSessionOnly { 6768 // At this point a multi-client server is still running. but session should be done. 6769 verifySessionStopped(t, server.impl.session) 6770 // Verify target's running state. 6771 if server.debugger.IsRunning() { 6772 t.Errorf("\ngot running=true, want false") 6773 } 6774 server.stop(t) 6775 } 6776 <-server.stopped 6777 server.verifyStopped(t) 6778 }) 6779 } 6780 } 6781 6782 func TestLaunchAttachErrorWhenDebugInProgress(t *testing.T) { 6783 tests := []struct { 6784 name string 6785 dbg func() *debugger.Debugger 6786 }{ 6787 {"halted", func() *debugger.Debugger { _, dbg := launchDebuggerWithTargetHalted(t, "increment"); return dbg }}, 6788 {"running", func() *debugger.Debugger { _, dbg := launchDebuggerWithTargetRunning(t, "loopprog"); return dbg }}, 6789 } 6790 for _, tc := range tests { 6791 t.Run(tc.name, func(t *testing.T) { 6792 runTestWithDebugger(t, tc.dbg(), func(client *daptest.Client) { 6793 client.EvaluateRequest("1==1", 0 /*no frame specified*/, "repl") 6794 if tc.name == "running" { 6795 client.ExpectInvisibleErrorResponse(t) 6796 } else { 6797 client.ExpectEvaluateResponse(t) 6798 } 6799 6800 // Both launch and attach requests should go through for additional error checking 6801 client.AttachRequest(map[string]interface{}{"mode": "local", "processId": 100}) 6802 er := client.ExpectVisibleErrorResponse(t) 6803 msgRe, _ := regexp.Compile("Failed to attach: debug session already in progress at [0-9]+:[0-9]+ - use remote mode to connect to a server with an active debug session") 6804 if er.Body.Error.Id != FailedToAttach || msgRe.MatchString(er.Body.Error.Format) { 6805 t.Errorf("got %#v, want Id=%d Format=%q", er, FailedToAttach, msgRe) 6806 } 6807 tests := []string{"debug", "test", "exec", "replay", "core"} 6808 for _, mode := range tests { 6809 t.Run(mode, func(t *testing.T) { 6810 client.LaunchRequestWithArgs(map[string]interface{}{"mode": mode}) 6811 er := client.ExpectVisibleErrorResponse(t) 6812 msgRe, _ := regexp.Compile("Failed to launch: debug session already in progress at [0-9]+:[0-9]+ - use remote attach mode to connect to a server with an active debug session") 6813 if er.Body.Error.Id != FailedToLaunch || msgRe.MatchString(er.Body.Error.Format) { 6814 t.Errorf("got %#v, want Id=%d Format=%q", er, FailedToLaunch, msgRe) 6815 } 6816 }) 6817 } 6818 }) 6819 }) 6820 } 6821 } 6822 6823 func TestBadInitializeRequest(t *testing.T) { 6824 runInitializeTest := func(args dap.InitializeRequestArguments, err string) { 6825 t.Helper() 6826 // Only one initialize request is allowed, so use a new server 6827 // for each test. 6828 serverStopped := make(chan struct{}) 6829 client := startDAPServerWithClient(t, false, serverStopped) 6830 defer client.Close() 6831 6832 client.InitializeRequestWithArgs(args) 6833 response := client.ExpectErrorResponse(t) 6834 if response.Command != "initialize" { 6835 t.Errorf("Command got %q, want \"launch\"", response.Command) 6836 } 6837 if response.Message != "Failed to initialize" { 6838 t.Errorf("Message got %q, want \"Failed to launch\"", response.Message) 6839 } 6840 if response.Body.Error.Id != FailedToInitialize { 6841 t.Errorf("Id got %d, want %d", response.Body.Error.Id, FailedToInitialize) 6842 } 6843 if response.Body.Error.Format != err { 6844 t.Errorf("\ngot %q\nwant %q", response.Body.Error.Format, err) 6845 } 6846 6847 client.DisconnectRequest() 6848 client.ExpectDisconnectResponse(t) 6849 <-serverStopped 6850 } 6851 6852 // Bad path format. 6853 runInitializeTest(dap.InitializeRequestArguments{ 6854 AdapterID: "go", 6855 PathFormat: "url", // unsupported 'pathFormat' 6856 LinesStartAt1: true, 6857 ColumnsStartAt1: true, 6858 Locale: "en-us", 6859 }, 6860 "Failed to initialize: Unsupported 'pathFormat' value 'url'.", 6861 ) 6862 6863 // LinesStartAt1 must be true. 6864 runInitializeTest(dap.InitializeRequestArguments{ 6865 AdapterID: "go", 6866 PathFormat: "path", 6867 LinesStartAt1: false, // only 1-based line numbers are supported 6868 ColumnsStartAt1: true, 6869 Locale: "en-us", 6870 }, 6871 "Failed to initialize: Only 1-based line numbers are supported.", 6872 ) 6873 6874 // ColumnsStartAt1 must be true. 6875 runInitializeTest(dap.InitializeRequestArguments{ 6876 AdapterID: "go", 6877 PathFormat: "path", 6878 LinesStartAt1: true, 6879 ColumnsStartAt1: false, // only 1-based column numbers are supported 6880 Locale: "en-us", 6881 }, 6882 "Failed to initialize: Only 1-based column numbers are supported.", 6883 ) 6884 } 6885 6886 func TestBadlyFormattedMessageToServer(t *testing.T) { 6887 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 6888 // Send a badly formatted message to the server, and expect it to close the 6889 // connection. 6890 client.BadRequest() 6891 time.Sleep(100 * time.Millisecond) 6892 6893 _, err := client.ReadMessage() 6894 6895 if err != io.EOF { 6896 t.Errorf("got err=%v, want io.EOF", err) 6897 } 6898 }) 6899 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 6900 // Send an unknown request message to the server, and expect it to send 6901 // an error response. 6902 client.UnknownRequest() 6903 err := client.ExpectErrorResponse(t) 6904 if err.Body.Error.Format != "Internal Error: Request command 'unknown' is not supported (seq: 1)" || err.RequestSeq != 1 { 6905 t.Errorf("got %v, want RequestSeq=1 Error=\"Internal Error: Request command 'unknown' is not supported (seq: 1)\"", err) 6906 } 6907 6908 // Make sure that the unknown request did not kill the server. 6909 client.InitializeRequest() 6910 client.ExpectInitializeResponse(t) 6911 6912 client.DisconnectRequest() 6913 client.ExpectDisconnectResponse(t) 6914 }) 6915 } 6916 6917 func TestParseLogPoint(t *testing.T) { 6918 tests := []struct { 6919 name string 6920 msg string 6921 wantTracepoint bool 6922 wantFormat string 6923 wantArgs []string 6924 wantErr bool 6925 }{ 6926 // Test simple log messages. 6927 {name: "simple string", msg: "hello, world!", wantTracepoint: true, wantFormat: "hello, world!"}, 6928 {name: "empty string", msg: "", wantTracepoint: false, wantErr: false}, 6929 // Test parse eval expressions. 6930 { 6931 name: "simple eval", 6932 msg: "{x}", 6933 wantTracepoint: true, 6934 wantFormat: "%s", 6935 wantArgs: []string{"x"}, 6936 }, 6937 { 6938 name: "type cast", 6939 msg: "hello {string(x)}", 6940 wantTracepoint: true, 6941 wantFormat: "hello %s", 6942 wantArgs: []string{"string(x)"}, 6943 }, 6944 { 6945 name: "multiple eval", 6946 msg: "{x} {y} {z}", 6947 wantTracepoint: true, 6948 wantFormat: "%s %s %s", 6949 wantArgs: []string{"x", "y", "z"}, 6950 }, 6951 { 6952 name: "eval expressions contain braces", 6953 msg: "{interface{}(x)} {myType{y}} {[]myType{{z}}}", 6954 wantTracepoint: true, 6955 wantFormat: "%s %s %s", 6956 wantArgs: []string{"interface{}(x)", "myType{y}", "[]myType{{z}}"}, 6957 }, 6958 // Test parse errors. 6959 {name: "empty evaluation", msg: "{}", wantErr: true}, 6960 {name: "empty space evaluation", msg: "{ \n}", wantErr: true}, 6961 {name: "open brace missing closed", msg: "{", wantErr: true}, 6962 {name: "closed brace missing open", msg: "}", wantErr: true}, 6963 {name: "open brace in expression", msg: `{m["{"]}`, wantErr: true}, 6964 {name: "closed brace in expression", msg: `{m["}"]}`, wantErr: true}, 6965 } 6966 for _, tt := range tests { 6967 t.Run(tt.name, func(t *testing.T) { 6968 gotTracepoint, gotLogMessage, err := parseLogPoint(tt.msg) 6969 if gotTracepoint != tt.wantTracepoint { 6970 t.Errorf("parseLogPoint() tracepoint = %v, wantTracepoint %v", gotTracepoint, tt.wantTracepoint) 6971 return 6972 } 6973 if (err != nil) != tt.wantErr { 6974 t.Errorf("parseLogPoint() error = %v, wantErr %v", err, tt.wantErr) 6975 return 6976 } 6977 if !tt.wantTracepoint { 6978 return 6979 } 6980 if gotLogMessage == nil { 6981 t.Errorf("parseLogPoint() gotLogMessage = nil, want log message") 6982 return 6983 } 6984 if gotLogMessage.format != tt.wantFormat { 6985 t.Errorf("parseLogPoint() gotFormat = %v, want %v", gotLogMessage.format, tt.wantFormat) 6986 } 6987 if !reflect.DeepEqual(gotLogMessage.args, tt.wantArgs) { 6988 t.Errorf("parseLogPoint() gotArgs = %v, want %v", gotLogMessage.args, tt.wantArgs) 6989 } 6990 }) 6991 } 6992 } 6993 6994 func TestDisassemble(t *testing.T) { 6995 runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) { 6996 runDebugSessionWithBPs(t, client, "launch", 6997 // Launch 6998 func() { 6999 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 7000 }, 7001 // Set breakpoints 7002 fixture.Source, []int{17}, 7003 []onBreakpoint{{ 7004 // Stop at line 17 7005 execute: func() { 7006 checkStop(t, client, 1, "main.main", 17) 7007 7008 client.StackTraceRequest(1, 0, 1) 7009 st := client.ExpectStackTraceResponse(t) 7010 if len(st.Body.StackFrames) < 1 { 7011 t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st) 7012 } 7013 // Request the single instruction that the program is stopped at. 7014 pc := st.Body.StackFrames[0].InstructionPointerReference 7015 client.DisassembleRequest(pc, 0, 1) 7016 dr := client.ExpectDisassembleResponse(t) 7017 if len(dr.Body.Instructions) != 1 { 7018 t.Errorf("\ngot %#v\nwant len(instructions) = 1", dr) 7019 } else if dr.Body.Instructions[0].Address != pc { 7020 t.Errorf("\ngot %#v\nwant instructions[0].Address = %s", dr, pc) 7021 } 7022 7023 // Request the instruction that the program is stopped at, and the two 7024 // surrounding it. 7025 client.DisassembleRequest(pc, -1, 3) 7026 dr = client.ExpectDisassembleResponse(t) 7027 if len(dr.Body.Instructions) != 3 { 7028 t.Errorf("\ngot %#v\nwant len(instructions) = 3", dr) 7029 } else if dr.Body.Instructions[1].Address != pc { 7030 t.Errorf("\ngot %#v\nwant instructions[1].Address = %s", dr, pc) 7031 } 7032 7033 // Request zero instrutions. 7034 client.DisassembleRequest(pc, 0, 0) 7035 dr = client.ExpectDisassembleResponse(t) 7036 if len(dr.Body.Instructions) != 0 { 7037 t.Errorf("\ngot %#v\nwant len(instructions) = 0", dr) 7038 } 7039 7040 // Request invalid instructions. 7041 var checkInvalidInstruction = func(instructions []dap.DisassembledInstruction, count int, address uint64) { 7042 if len(instructions) != count { 7043 t.Errorf("\ngot %#v\nwant len(instructions) = %d", dr, count) 7044 } 7045 for i, got := range instructions { 7046 if got.Instruction != invalidInstruction.Instruction { 7047 t.Errorf("\ngot [%d].Instruction=%q\nwant = %#v", i, got.Instruction, invalidInstruction.Address) 7048 } 7049 addr, err := strconv.ParseUint(got.Address, 0, 64) 7050 if err != nil { 7051 t.Error(err) 7052 continue 7053 } 7054 if addr != address { 7055 t.Errorf("\ngot [%d].Address=%s\nwant = %#x", i, got.Address, address) 7056 } 7057 } 7058 } 7059 client.DisassembleRequest("0x0", 0, 10) 7060 checkInvalidInstruction(client.ExpectDisassembleResponse(t).Body.Instructions, 10, 0) 7061 7062 client.DisassembleRequest(fmt.Sprintf("%#x", uint64(math.MaxUint64)), 0, 10) 7063 checkInvalidInstruction(client.ExpectDisassembleResponse(t).Body.Instructions, 10, uint64(math.MaxUint64)) 7064 7065 // Bad request, not a number. 7066 client.DisassembleRequest("hello, world!", 0, 1) 7067 client.ExpectErrorResponse(t) 7068 7069 // Bad request, not an address in program. 7070 client.DisassembleRequest("0x5", 0, 100) 7071 client.ExpectErrorResponse(t) 7072 }, 7073 disconnect: true, 7074 }}, 7075 ) 7076 }) 7077 } 7078 7079 func TestAlignPCs(t *testing.T) { 7080 NUM_FUNCS := 10 7081 // Create fake functions to test align PCs. 7082 funcs := make([]proc.Function, NUM_FUNCS) 7083 for i := 0; i < len(funcs); i++ { 7084 funcs[i] = proc.Function{ 7085 Entry: uint64(100 + i*10), 7086 End: uint64(100 + i*10 + 5), 7087 } 7088 } 7089 bi := &proc.BinaryInfo{ 7090 Functions: funcs, 7091 } 7092 type args struct { 7093 start uint64 7094 end uint64 7095 } 7096 tests := []struct { 7097 name string 7098 args args 7099 wantStart uint64 7100 wantEnd uint64 7101 }{ 7102 { 7103 name: "out of bounds", 7104 args: args{ 7105 start: funcs[0].Entry - 5, 7106 end: funcs[NUM_FUNCS-1].End + 5, 7107 }, 7108 wantStart: funcs[0].Entry, // start of first function 7109 wantEnd: funcs[NUM_FUNCS-1].End, // end of last function 7110 }, 7111 { 7112 name: "same function", 7113 args: args{ 7114 start: funcs[1].Entry + 1, 7115 end: funcs[1].Entry + 2, 7116 }, 7117 wantStart: funcs[1].Entry, // start of containing function 7118 wantEnd: funcs[1].End, // end of containing function 7119 }, 7120 { 7121 name: "between functions", 7122 args: args{ 7123 start: funcs[1].End + 1, 7124 end: funcs[1].End + 2, 7125 }, 7126 wantStart: funcs[1].Entry, // start of function before 7127 wantEnd: funcs[2].Entry, // start of function after 7128 }, 7129 { 7130 name: "start of function", 7131 args: args{ 7132 start: funcs[2].Entry, 7133 end: funcs[5].Entry, 7134 }, 7135 wantStart: funcs[2].Entry, // start of current function 7136 wantEnd: funcs[5].End, // end of current function 7137 }, 7138 { 7139 name: "end of function", 7140 args: args{ 7141 start: funcs[4].End, 7142 end: funcs[8].End, 7143 }, 7144 wantStart: funcs[4].Entry, // start of current function 7145 wantEnd: funcs[9].Entry, // start of next function 7146 }, 7147 } 7148 for _, tt := range tests { 7149 t.Run(tt.name, func(t *testing.T) { 7150 gotStart, gotEnd := alignPCs(bi, tt.args.start, tt.args.end) 7151 if gotStart != tt.wantStart { 7152 t.Errorf("alignPCs() got start = %v, want %v", gotStart, tt.wantStart) 7153 } 7154 if gotEnd != tt.wantEnd { 7155 t.Errorf("alignPCs() got end = %v, want %v", gotEnd, tt.wantEnd) 7156 } 7157 }) 7158 } 7159 } 7160 7161 func TestFindInstructions(t *testing.T) { 7162 numInstructions := 100 7163 startPC := 0x1000 7164 procInstructions := make([]proc.AsmInstruction, numInstructions) 7165 for i := 0; i < len(procInstructions); i++ { 7166 procInstructions[i] = proc.AsmInstruction{ 7167 Loc: proc.Location{ 7168 PC: uint64(startPC + 2*i), 7169 }, 7170 } 7171 } 7172 type args struct { 7173 addr uint64 7174 offset int 7175 count int 7176 } 7177 tests := []struct { 7178 name string 7179 args args 7180 wantInstructions []proc.AsmInstruction 7181 wantOffset int 7182 wantErr bool 7183 }{ 7184 { 7185 name: "request all", 7186 args: args{ 7187 addr: uint64(startPC), 7188 offset: 0, 7189 count: 100, 7190 }, 7191 wantInstructions: procInstructions, 7192 wantOffset: 0, 7193 wantErr: false, 7194 }, 7195 { 7196 name: "request all (with offset)", 7197 args: args{ 7198 addr: uint64(startPC + numInstructions), // the instruction addr at numInstructions/2 7199 offset: -numInstructions / 2, 7200 count: numInstructions, 7201 }, 7202 wantInstructions: procInstructions, 7203 wantOffset: 0, 7204 wantErr: false, 7205 }, 7206 { 7207 name: "request half (with offset)", 7208 args: args{ 7209 addr: uint64(startPC), 7210 offset: 0, 7211 count: numInstructions / 2, 7212 }, 7213 wantInstructions: procInstructions[:numInstructions/2], 7214 wantOffset: 0, 7215 wantErr: false, 7216 }, 7217 { 7218 name: "request half (with offset)", 7219 args: args{ 7220 addr: uint64(startPC), 7221 offset: numInstructions / 2, 7222 count: numInstructions / 2, 7223 }, 7224 wantInstructions: procInstructions[numInstructions/2:], 7225 wantOffset: 0, 7226 wantErr: false, 7227 }, 7228 { 7229 name: "request too many", 7230 args: args{ 7231 addr: uint64(startPC), 7232 offset: 0, 7233 count: numInstructions * 2, 7234 }, 7235 wantInstructions: procInstructions, 7236 wantOffset: 0, 7237 wantErr: false, 7238 }, 7239 { 7240 name: "request too many with offset", 7241 args: args{ 7242 addr: uint64(startPC), 7243 offset: -numInstructions, 7244 count: numInstructions * 2, 7245 }, 7246 wantInstructions: procInstructions, 7247 wantOffset: numInstructions, 7248 wantErr: false, 7249 }, 7250 { 7251 name: "request out of bounds", 7252 args: args{ 7253 addr: uint64(startPC), 7254 offset: -numInstructions, 7255 count: numInstructions, 7256 }, 7257 wantInstructions: []proc.AsmInstruction{}, 7258 wantOffset: 0, 7259 wantErr: false, 7260 }, 7261 { 7262 name: "request out of bounds", 7263 args: args{ 7264 addr: uint64(uint64(startPC + 2*(numInstructions-1))), 7265 offset: 1, 7266 count: numInstructions, 7267 }, 7268 wantInstructions: []proc.AsmInstruction{}, 7269 wantOffset: 0, 7270 wantErr: false, 7271 }, 7272 { 7273 name: "addr out of bounds (low)", 7274 args: args{ 7275 addr: 0, 7276 offset: 0, 7277 count: 100, 7278 }, 7279 wantInstructions: nil, 7280 wantOffset: -1, 7281 wantErr: true, 7282 }, 7283 { 7284 name: "addr out of bounds (high)", 7285 args: args{ 7286 addr: uint64(startPC + 2*(numInstructions+1)), 7287 offset: -10, 7288 count: 20, 7289 }, 7290 wantInstructions: nil, 7291 wantOffset: -1, 7292 wantErr: true, 7293 }, 7294 { 7295 name: "addr not aligned", 7296 args: args{ 7297 addr: uint64(startPC + 1), 7298 offset: 0, 7299 count: 20, 7300 }, 7301 wantInstructions: nil, 7302 wantOffset: -1, 7303 wantErr: true, 7304 }, 7305 } 7306 for _, tt := range tests { 7307 t.Run(tt.name, func(t *testing.T) { 7308 gotInstructions, gotOffset, err := findInstructions(procInstructions, tt.args.addr, tt.args.offset, tt.args.count) 7309 if (err != nil) != tt.wantErr { 7310 t.Errorf("findInstructions() error = %v, wantErr %v", err, tt.wantErr) 7311 return 7312 } 7313 if !reflect.DeepEqual(gotInstructions, tt.wantInstructions) { 7314 t.Errorf("findInstructions() got instructions = %v, want %v", gotInstructions, tt.wantInstructions) 7315 } 7316 if gotOffset != tt.wantOffset { 7317 t.Errorf("findInstructions() got offset = %v, want %v", gotOffset, tt.wantOffset) 7318 } 7319 }) 7320 } 7321 } 7322 7323 func TestDisassembleCgo(t *testing.T) { 7324 // Test that disassembling a program containing cgo code does not create problems. 7325 // See issue #3040 7326 runTestBuildFlags(t, "cgodisass", func(client *daptest.Client, fixture protest.Fixture) { 7327 runDebugSessionWithBPs(t, client, "launch", 7328 // Launch 7329 func() { 7330 client.LaunchRequest("exec", fixture.Path, !stopOnEntry) 7331 }, 7332 // Set breakpoints 7333 fixture.Source, []int{11}, 7334 []onBreakpoint{{ 7335 execute: func() { 7336 checkStop(t, client, 1, "main.main", 11) 7337 7338 client.StackTraceRequest(1, 0, 1) 7339 st := client.ExpectStackTraceResponse(t) 7340 if len(st.Body.StackFrames) < 1 { 7341 t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st) 7342 } 7343 7344 // Request the single instruction that the program is stopped at. 7345 pc := st.Body.StackFrames[0].InstructionPointerReference 7346 7347 client.DisassembleRequest(pc, -200, 400) 7348 client.ExpectDisassembleResponse(t) 7349 }, 7350 disconnect: true, 7351 }}, 7352 ) 7353 }, 7354 protest.AllNonOptimized, true) 7355 }