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