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