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