github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/service/dap/server.go (about) 1 // Package dap implements VSCode's Debug Adaptor Protocol (DAP). 2 // This allows delve to communicate with frontends using DAP 3 // without a separate adaptor. The frontend will run the debugger 4 // (which now doubles as an adaptor) in server mode listening on 5 // a port and communicating over TCP. This is work in progress, 6 // so for now Delve in dap mode only supports synchronous 7 // request-response communication, blocking while processing each request. 8 // For DAP details see https://microsoft.github.io/debug-adapter-protocol. 9 package dap 10 11 import ( 12 "bufio" 13 "bytes" 14 "encoding/json" 15 "errors" 16 "fmt" 17 "go/constant" 18 "go/parser" 19 "io" 20 "math" 21 "net" 22 "os" 23 "os/exec" 24 "path/filepath" 25 "reflect" 26 "regexp" 27 "runtime" 28 "runtime/debug" 29 "sort" 30 "strconv" 31 "strings" 32 "sync" 33 "time" 34 35 "github.com/go-delve/delve/pkg/gobuild" 36 "github.com/go-delve/delve/pkg/goversion" 37 "github.com/go-delve/delve/pkg/locspec" 38 "github.com/go-delve/delve/pkg/logflags" 39 "github.com/go-delve/delve/pkg/proc" 40 41 "github.com/go-delve/delve/service" 42 "github.com/go-delve/delve/service/api" 43 "github.com/go-delve/delve/service/debugger" 44 "github.com/go-delve/delve/service/internal/sameuser" 45 "github.com/google/go-dap" 46 ) 47 48 // Server implements a DAP server that can accept a single client for 49 // a single debug session (for now). It does not yet support restarting. 50 // That means that in addition to explicit shutdown requests, 51 // program termination and failed or closed client connection 52 // would also result in stopping this single-use server. 53 // 54 // The DAP server operates via the following goroutines: 55 // 56 // (1) Main goroutine where the server is created via NewServer(), 57 // started via Run() and stopped via Stop(). Once the server is 58 // started, this goroutine blocks until it receives a stop-server 59 // signal that can come from an OS interrupt (such as Ctrl-C) or 60 // config.DisconnectChan (passed to NewServer()) as a result of 61 // client connection failure or closure or a DAP disconnect request. 62 // 63 // (2) Run goroutine started from Run() that serves as both 64 // a listener and a client goroutine. It accepts a client connection, 65 // reads, decodes and dispatches each request from the client. 66 // For synchronous requests, it issues commands to the 67 // underlying debugger and sends back events and responses. 68 // These requests block while the debuggee is running, so, 69 // where applicable, the handlers need to check if debugging 70 // state is running, so there is a need for a halt request or 71 // a dummy/error response to avoid blocking. 72 // 73 // This is the only goroutine that sends a stop-server signal 74 // via config.DisconnectChan when encountering a client connection 75 // error or responding to a (synchronous) DAP disconnect request. 76 // Once stop is triggered, the goroutine exits. 77 // 78 // Unlike rpccommon, there is not another layer of per-client 79 // goroutines here because the dap server does not support 80 // multiple clients. 81 // 82 // (3) Per-request goroutine is started for each asynchronous request 83 // that resumes execution. We check if target is running already, so 84 // there should be no more than one pending asynchronous request at 85 // a time. This goroutine issues commands to the underlying debugger 86 // and sends back events and responses. It takes a setup-done channel 87 // as an argument and temporarily blocks the request loop until setup 88 // for asynchronous execution is complete and targe is running. 89 // Once done, it unblocks processing of parallel requests unblocks 90 // (e.g. disconnecting while the program is running). 91 // 92 // These per-request goroutines never send a stop-server signal. 93 // They block on running debugger commands that are interrupted 94 // when halt is issued while stopping. At that point these goroutines 95 // wrap-up and exit. 96 type Server struct { 97 // config is all the information necessary to start the debugger and server. 98 config *Config 99 // listener is used to accept the client connection. 100 // When working with a predetermined client, this is nil. 101 listener net.Listener 102 // session is the debug session that comes with an client connection. 103 session *Session 104 sessionMu sync.Mutex 105 } 106 107 // Session is an abstraction for serving and shutting down 108 // a DAP debug session with a pre-connected client. 109 // TODO(polina): move this to a different file/package 110 type Session struct { 111 config *Config 112 113 id int 114 115 // stackFrameHandles maps frames of each goroutine to unique ids across all goroutines. 116 // Reset at every stop. 117 stackFrameHandles *handlesMap 118 // variableHandles maps compound variables to unique references within their stack frame. 119 // Reset at every stop. 120 // See also comment for convertVariable. 121 variableHandles *variablesHandlesMap 122 // args tracks special settings for handling debug session requests. 123 args launchAttachArgs 124 // exceptionErr tracks the runtime error that last occurred. 125 exceptionErr error 126 // clientCapabilities tracks special settings for handling debug session requests. 127 clientCapabilities dapClientCapabilities 128 129 // mu synchronizes access to objects set on start-up (from run goroutine) 130 // and stopped on teardown (from main goroutine) 131 mu sync.Mutex 132 133 // conn is the accepted client connection. 134 conn *connection 135 // debugger is the underlying debugger service. 136 debugger *debugger.Debugger 137 // binaryToRemove is the temp compiled binary to be removed on disconnect (if any). 138 binaryToRemove string 139 // noDebugProcess is set for the noDebug launch process. 140 noDebugProcess *process 141 142 // sendingMu synchronizes writing to conn 143 // to ensure that messages do not get interleaved 144 sendingMu sync.Mutex 145 146 // runningCmd tracks whether the server is running an asynchronous 147 // command that resumes execution, which may not correspond to the actual 148 // running state of the process (e.g. if a command is temporarily interrupted). 149 runningCmd bool 150 runningMu sync.Mutex 151 152 // haltRequested tracks whether a halt of the program has been requested, which may 153 // not correspond to whether a Halt Request has been sent to the target. 154 haltRequested bool 155 haltMu sync.Mutex 156 157 // changeStateMu must be held for a request to protect itself from another goroutine 158 // changing the state of the running process at the same time. 159 changeStateMu sync.Mutex 160 161 // stdoutReader the programs's stdout. 162 stdoutReader io.ReadCloser 163 164 // stderrReader the program's stderr. 165 stderrReader io.ReadCloser 166 167 // preTerminatedWG the WaitGroup that needs to wait before sending a terminated event. 168 preTerminatedWG sync.WaitGroup 169 } 170 171 // Config is all the information needed to start the debugger, handle 172 // DAP connection traffic and signal to the server when it is time to stop. 173 type Config struct { 174 *service.Config 175 176 // log is used for structured logging. 177 log logflags.Logger 178 // StopTriggered is closed when the server is Stop()-ed. 179 // Can be used to safeguard against duplicate shutdown sequences. 180 StopTriggered chan struct{} 181 } 182 183 type connection struct { 184 io.ReadWriteCloser 185 closed chan struct{} 186 } 187 188 func (c *connection) Close() error { 189 select { 190 case <-c.closed: 191 default: 192 close(c.closed) 193 } 194 return c.ReadWriteCloser.Close() 195 } 196 197 func (c *connection) isClosed() bool { 198 select { 199 case <-c.closed: 200 return true 201 default: 202 return false 203 } 204 } 205 206 type process struct { 207 *exec.Cmd 208 exited chan struct{} 209 } 210 211 // launchAttachArgs captures arguments from launch/attach request that 212 // impact handling of subsequent requests. 213 // The fields with cfgName tag can be updated through an evaluation request. 214 type launchAttachArgs struct { 215 // stopOnEntry is set to automatically stop the debuggee after start. 216 stopOnEntry bool 217 // StackTraceDepth is the maximum length of the returned list of stack frames. 218 StackTraceDepth int `cfgName:"stackTraceDepth"` 219 // ShowGlobalVariables indicates if global package variables should be loaded. 220 ShowGlobalVariables bool `cfgName:"showGlobalVariables"` 221 // ShowRegisters indicates if register values should be loaded. 222 ShowRegisters bool `cfgName:"showRegisters"` 223 // GoroutineFilters are the filters used when loading goroutines. 224 GoroutineFilters string `cfgName:"goroutineFilters"` 225 // HideSystemGoroutines indicates if system goroutines should be removed from threads 226 // responses. 227 HideSystemGoroutines bool `cfgName:"hideSystemGoroutines"` 228 // substitutePathClientToServer indicates rules for converting file paths between client and debugger. 229 substitutePathClientToServer [][2]string `cfgName:"substitutePath"` 230 // substitutePathServerToClient indicates rules for converting file paths between debugger and client. 231 substitutePathServerToClient [][2]string 232 } 233 234 // defaultArgs borrows the defaults for the arguments from the original vscode-go adapter. 235 // TODO(polinasok): clean up this and its reference (Server.args) 236 // in favor of default*Config variables defined in types.go. 237 var defaultArgs = launchAttachArgs{ 238 stopOnEntry: false, 239 StackTraceDepth: 50, 240 ShowGlobalVariables: false, 241 HideSystemGoroutines: false, 242 ShowRegisters: false, 243 GoroutineFilters: "", 244 substitutePathClientToServer: [][2]string{}, 245 substitutePathServerToClient: [][2]string{}, 246 } 247 248 // dapClientCapabilities captures arguments from initialize request that 249 // impact handling of subsequent requests. 250 type dapClientCapabilities struct { 251 supportsVariableType bool 252 supportsVariablePaging bool 253 supportsRunInTerminalRequest bool 254 supportsMemoryReferences bool 255 supportsProgressReporting bool 256 } 257 258 // DefaultLoadConfig controls how variables are loaded from the target's memory. 259 // These limits are conservative to minimize performance overhead for bulk loading. 260 // With dlv-dap, users do not have a way to adjust these. 261 // Instead we are focusing in interactive loading with nested reloads, array/map 262 // paging and context-specific string limits. 263 var DefaultLoadConfig = proc.LoadConfig{ 264 FollowPointers: true, 265 MaxVariableRecurse: 1, 266 // TODO(polina): consider 1024 limit instead: 267 // - vscode+C appears to use 1024 as the load limit 268 // - vscode viewlet hover truncates at 1023 characters 269 MaxStringLen: 512, 270 MaxArrayValues: 64, 271 MaxStructFields: -1, 272 } 273 274 const ( 275 // When a user examines a single string, we can relax the loading limit. 276 maxSingleStringLen = 4 << 10 // 4096 277 // Results of a call are single-use and transient. We need to maximize 278 // what is presented. A common use case of a call injection is to 279 // stringify complex data conveniently. 280 maxStringLenInCallRetVars = 1 << 10 // 1024 281 ) 282 283 var ( 284 // Max number of goroutines that we will return. 285 // This is a var for testing 286 maxGoroutines = 1 << 10 287 ) 288 289 // NewServer creates a new DAP Server. It takes an opened Listener 290 // via config and assumes its ownership. config.DisconnectChan has to be set; 291 // it will be closed by the server when the client fails to connect, 292 // disconnects or requests shutdown. Once config.DisconnectChan is closed, 293 // Server.Stop() must be called to shutdown this single-user server. 294 // 295 // NewServer can be used to create a special DAP Server that works 296 // only with a predetermined client. In that case, config.Listener is 297 // nil and its RunWithClient must be used instead of Run. 298 func NewServer(config *service.Config) *Server { 299 logger := logflags.DAPLogger() 300 if config.Listener != nil { 301 logflags.WriteDAPListeningMessage(config.Listener.Addr()) 302 } else { 303 logger.Debug("DAP server for a predetermined client") 304 } 305 logger.Debug("DAP server pid = ", os.Getpid()) 306 if config.AcceptMulti { 307 logger.Warn("DAP server does not support accept-multiclient mode") 308 config.AcceptMulti = false 309 } 310 return &Server{ 311 config: &Config{ 312 Config: config, 313 log: logger, 314 StopTriggered: make(chan struct{}), 315 }, 316 listener: config.Listener, 317 } 318 } 319 320 var sessionCount = 0 321 322 // NewSession creates a new client session that can handle DAP traffic. 323 // It takes an open connection and provides a Close() method to shut it 324 // down when the DAP session disconnects or a connection error occurs. 325 func NewSession(conn io.ReadWriteCloser, config *Config, debugger *debugger.Debugger) *Session { 326 sessionCount++ 327 if config.log == nil { 328 config.log = logflags.DAPLogger() 329 } 330 config.log.Debugf("DAP connection %d started", sessionCount) 331 if config.StopTriggered == nil { 332 config.log.Error("Session must be configured with StopTriggered") 333 os.Exit(1) 334 } 335 return &Session{ 336 config: config, 337 id: sessionCount, 338 conn: &connection{conn, make(chan struct{})}, 339 stackFrameHandles: newHandlesMap(), 340 variableHandles: newVariablesHandlesMap(), 341 args: defaultArgs, 342 exceptionErr: nil, 343 debugger: debugger, 344 } 345 } 346 347 // If user-specified options are provided via Launch/AttachRequest, 348 // we override the defaults for optional args. 349 func (s *Session) setLaunchAttachArgs(args LaunchAttachCommonConfig) { 350 s.args.stopOnEntry = args.StopOnEntry 351 if depth := args.StackTraceDepth; depth > 0 { 352 s.args.StackTraceDepth = depth 353 } 354 s.args.ShowGlobalVariables = args.ShowGlobalVariables 355 s.args.ShowRegisters = args.ShowRegisters 356 s.args.HideSystemGoroutines = args.HideSystemGoroutines 357 s.args.GoroutineFilters = args.GoroutineFilters 358 if paths := args.SubstitutePath; len(paths) > 0 { 359 clientToServer := make([][2]string, 0, len(paths)) 360 serverToClient := make([][2]string, 0, len(paths)) 361 for _, p := range paths { 362 clientToServer = append(clientToServer, [2]string{p.From, p.To}) 363 serverToClient = append(serverToClient, [2]string{p.To, p.From}) 364 } 365 s.args.substitutePathClientToServer = clientToServer 366 s.args.substitutePathServerToClient = serverToClient 367 } 368 } 369 370 // Stop stops the DAP debugger service, closes the listener and the client 371 // connection. It shuts down the underlying debugger and kills the target 372 // process if it was launched by it or stops the noDebug process. 373 // This method mustn't be called more than once. 374 // StopTriggered notifies other goroutines that stop is in progress. 375 func (s *Server) Stop() { 376 s.config.log.Debug("DAP server stopping...") 377 defer s.config.log.Debug("DAP server stopped") 378 close(s.config.StopTriggered) 379 380 if s.listener != nil { 381 // If run goroutine is blocked on accept, this will unblock it. 382 _ = s.listener.Close() 383 } 384 385 s.sessionMu.Lock() 386 defer s.sessionMu.Unlock() 387 if s.session == nil { 388 return 389 } 390 // If run goroutine is blocked on read, this will unblock it. 391 s.session.Close() 392 } 393 394 // Close closes the underlying debugger/process and connection. 395 // May be called more than once. 396 func (s *Session) Close() { 397 s.mu.Lock() 398 defer s.mu.Unlock() 399 400 if s.debugger != nil { 401 killProcess := s.debugger.AttachPid() == 0 402 s.stopDebugSession(killProcess) 403 } else if s.noDebugProcess != nil { 404 s.stopNoDebugProcess() 405 } 406 // The binary is no longer in use by the debugger. It is safe to remove it. 407 if s.binaryToRemove != "" { 408 gobuild.Remove(s.binaryToRemove) 409 s.binaryToRemove = "" // avoid error printed on duplicate removal 410 } 411 // Close client connection last, so other shutdown stages 412 // can send client notifications. 413 // Unless Stop() was called after read loop in ServeDAPCodec() 414 // returned, this will result in a closed connection error 415 // on next read, breaking out the read loop and 416 // allowing the run goroutines to exit. 417 // This connection is closed here and in serveDAPCodec(). 418 // If this was a forced shutdown, external stop logic can close this first. 419 // If this was a client loop exit (on error or disconnect), serveDAPCodec() 420 // will be first. 421 // Duplicate close calls return an error, but are not fatal. 422 _ = s.conn.Close() 423 } 424 425 // triggerServerStop closes DisconnectChan if not nil, which 426 // signals that client sent a disconnect request or there was connection 427 // failure or closure. Since the server currently services only one 428 // client, this is used as a signal to stop the entire server. 429 // The function safeguards against closing the channel more 430 // than once and can be called multiple times. It is not thread-safe 431 // and is currently only called from the run goroutine. 432 func (c *Config) triggerServerStop() { 433 // Avoid accidentally closing the channel twice and causing a panic, when 434 // this function is called more than once because stop was triggered 435 // by multiple conditions simultaneously. 436 if c.DisconnectChan != nil { 437 close(c.DisconnectChan) 438 c.DisconnectChan = nil 439 } 440 // There should be no logic here after the stop-server 441 // signal that might cause everything to shutdown before this 442 // logic gets executed. 443 } 444 445 // Run launches a new goroutine where it accepts a client connection 446 // and starts processing requests from it. Use Stop() to close connection. 447 // The server does not support multiple clients, serially or in parallel. 448 // The server should be restarted for every new debug session. 449 // The debugger won't be started until launch/attach request is received. 450 // TODO(polina): allow new client connections for new debug sessions, 451 // so the editor needs to launch dap server only once? Note that some requests 452 // may change the server's environment (e.g. see dlvCwd of launch configuration). 453 // So if we want to reuse this server for multiple independent debugging sessions 454 // we need to take that into consideration. 455 func (s *Server) Run() { 456 if s.listener == nil { 457 s.config.log.Error("Misconfigured server: no Listener is configured.") 458 os.Exit(1) 459 } 460 461 go func() { 462 conn, err := s.listener.Accept() // listener is closed in Stop() 463 if err != nil { 464 select { 465 case <-s.config.StopTriggered: 466 default: 467 s.config.log.Errorf("Error accepting client connection: %s\n", err) 468 s.config.triggerServerStop() 469 } 470 return 471 } 472 if s.config.CheckLocalConnUser { 473 if !sameuser.CanAccept(s.listener.Addr(), conn.LocalAddr(), conn.RemoteAddr()) { 474 s.config.log.Error("Error accepting client connection: Only connections from the same user that started this instance of Delve are allowed to connect. See --only-same-user.") 475 s.config.triggerServerStop() 476 return 477 } 478 } 479 s.runSession(conn) 480 }() 481 } 482 483 func (s *Server) runSession(conn io.ReadWriteCloser) { 484 s.sessionMu.Lock() 485 s.session = NewSession(conn, s.config, nil) // closed in Stop() 486 s.sessionMu.Unlock() 487 s.session.ServeDAPCodec() 488 } 489 490 // RunWithClient is similar to Run but works only with an already established 491 // connection instead of waiting on the listener to accept a new client. 492 // RunWithClient takes ownership of conn. Debugger won't be started 493 // until a launch/attach request is received over the connection. 494 func (s *Server) RunWithClient(conn net.Conn) { 495 if s.listener != nil { 496 s.config.log.Error("RunWithClient must not be used when the Server is configured with a Listener") 497 os.Exit(1) 498 } 499 s.config.log.Debugf("Connected to the client at %s", conn.RemoteAddr()) 500 go s.runSession(conn) 501 } 502 503 func (s *Session) address() string { 504 if s.config.Listener != nil { 505 return s.config.Listener.Addr().String() 506 } 507 if netconn, ok := s.conn.ReadWriteCloser.(net.Conn); ok { 508 return netconn.LocalAddr().String() 509 } 510 return "" 511 } 512 513 // ServeDAPCodec reads and decodes requests from the client 514 // until it encounters an error or EOF, when it sends 515 // a disconnect signal and returns. 516 func (s *Session) ServeDAPCodec() { 517 // Close conn, but not the debugger in case we are in AcceptMulti mode. 518 // If not, debugger will be shut down in Stop(). 519 defer s.conn.Close() 520 reader := bufio.NewReader(s.conn) 521 for { 522 request, err := dap.ReadProtocolMessage(reader) 523 // Handle dap.DecodeProtocolMessageFieldError errors gracefully by responding with an ErrorResponse. 524 // For example: 525 // -- "Request command 'foo' is not supported" means we 526 // potentially got some new DAP request that we do not yet have 527 // decoding support for, so we can respond with an ErrorResponse. 528 // 529 // Other errors, such as unmarshaling errors, will log the error and cause the server to trigger 530 // a stop. 531 if err != nil { 532 s.config.log.Debug("DAP error: ", err) 533 select { 534 case <-s.config.StopTriggered: 535 default: 536 if !s.config.AcceptMulti { 537 defer s.config.triggerServerStop() 538 } 539 if err != io.EOF { // EOF means client closed connection 540 if decodeErr, ok := err.(*dap.DecodeProtocolMessageFieldError); ok { 541 // Send an error response to the users if we were unable to process the message. 542 s.sendInternalErrorResponse(decodeErr.Seq, err.Error()) 543 continue 544 } 545 s.config.log.Error("DAP error: ", err) 546 } 547 } 548 return 549 } 550 s.handleRequest(request) 551 552 if _, ok := request.(*dap.DisconnectRequest); ok { 553 // disconnect already shut things down and triggered stopping 554 return 555 } 556 } 557 } 558 559 // In case a handler panics, we catch the panic to avoid crashing both 560 // the server and the target. We send an error response back, but 561 // in case its a dup and ignored by the client, we also log the error. 562 func (s *Session) recoverPanic(request dap.Message) { 563 if ierr := recover(); ierr != nil { 564 s.config.log.Errorf("recovered panic: %s\n%s\n", ierr, debug.Stack()) 565 s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("%v", ierr)) 566 } 567 } 568 569 func (s *Session) handleRequest(request dap.Message) { 570 defer s.recoverPanic(request) 571 jsonmsg, _ := json.Marshal(request) 572 s.config.log.Debug("[<- from client]", string(jsonmsg)) 573 574 if _, ok := request.(dap.RequestMessage); !ok { 575 s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("Unable to process non-request %#v\n", request)) 576 return 577 } 578 579 if s.isNoDebug() { 580 switch request := request.(type) { 581 case *dap.DisconnectRequest: 582 s.onDisconnectRequest(request) 583 case *dap.RestartRequest: 584 s.sendUnsupportedErrorResponse(request.Request) 585 default: 586 r := request.(dap.RequestMessage).GetRequest() 587 s.sendErrorResponse(*r, NoDebugIsRunning, "noDebug mode", fmt.Sprintf("unable to process '%s' request", r.Command)) 588 } 589 return 590 } 591 592 // These requests, can be handled regardless of whether the target is running 593 switch request := request.(type) { 594 case *dap.InitializeRequest: // Required 595 s.onInitializeRequest(request) 596 return 597 case *dap.LaunchRequest: // Required 598 s.onLaunchRequest(request) 599 return 600 case *dap.AttachRequest: // Required 601 s.onAttachRequest(request) 602 return 603 case *dap.DisconnectRequest: // Required 604 s.onDisconnectRequest(request) 605 return 606 case *dap.PauseRequest: // Required 607 s.onPauseRequest(request) 608 return 609 case *dap.TerminateRequest: // Optional (capability ‘supportsTerminateRequest‘) 610 /*TODO*/ s.onTerminateRequest(request) // not yet implemented 611 return 612 case *dap.RestartRequest: // Optional (capability ‘supportsRestartRequest’) 613 /*TODO*/ s.onRestartRequest(request) // not yet implemented 614 return 615 } 616 617 // Most requests cannot be processed while the debuggee is running. 618 // We have a couple of options for handling these without blocking 619 // the request loop indefinitely when we are in running state. 620 // --1-- Return a dummy response or an error right away. 621 // --2-- Halt execution, process the request, maybe resume execution. 622 // --3-- Handle such requests asynchronously and let them block until 623 // the process stops or terminates (e.g. using a channel and a single 624 // goroutine to preserve the order). This might not be appropriate 625 // for requests such as continue or step because they would skip 626 // the stop, resuming execution right away. Other requests 627 // might not be relevant anymore when the stop is finally reached, and 628 // state changed from the previous snapshot. The user might want to 629 // resume execution before the backlog of buffered requests is cleared, 630 // so we would have to either cancel them or delay processing until 631 // the next stop. In addition, the editor itself might block waiting 632 // for these requests to return. We are not aware of any requests 633 // that would benefit from this approach at this time. 634 if s.debugger != nil && s.debugger.IsRunning() || s.isRunningCmd() { 635 switch request := request.(type) { 636 case *dap.ThreadsRequest: // Required 637 // On start-up, the client requests the baseline of currently existing threads 638 // right away as there are a number of DAP requests that require a thread id 639 // (pause, continue, stacktrace, etc). This can happen after the program 640 // continues on entry, preventing the client from handling any pause requests 641 // from the user. We remedy this by sending back a placeholder thread id 642 // for the current goroutine. 643 response := &dap.ThreadsResponse{ 644 Response: *newResponse(request.Request), 645 Body: dap.ThreadsResponseBody{Threads: []dap.Thread{{Id: -1, Name: "Current"}}}, 646 } 647 s.send(response) 648 case *dap.SetBreakpointsRequest: // Required 649 s.changeStateMu.Lock() 650 defer s.changeStateMu.Unlock() 651 s.config.log.Debug("halting execution to set breakpoints") 652 _, err := s.halt() 653 if err != nil { 654 s.sendErrorResponse(request.Request, UnableToSetBreakpoints, "Unable to set or clear breakpoints", err.Error()) 655 return 656 } 657 s.onSetBreakpointsRequest(request) 658 case *dap.SetFunctionBreakpointsRequest: // Optional (capability ‘supportsFunctionBreakpoints’) 659 s.changeStateMu.Lock() 660 defer s.changeStateMu.Unlock() 661 s.config.log.Debug("halting execution to set breakpoints") 662 _, err := s.halt() 663 if err != nil { 664 s.sendErrorResponse(request.Request, UnableToSetBreakpoints, "Unable to set or clear breakpoints", err.Error()) 665 return 666 } 667 s.onSetFunctionBreakpointsRequest(request) 668 default: 669 r := request.(dap.RequestMessage).GetRequest() 670 s.sendErrorResponse(*r, DebuggeeIsRunning, fmt.Sprintf("Unable to process `%s`", r.Command), "debuggee is running") 671 } 672 return 673 } 674 675 // Requests below can only be handled while target is stopped. 676 // Some of them are blocking and will be handled synchronously 677 // on this goroutine while non-blocking requests will be dispatched 678 // to another goroutine. Please note that because of the running 679 // check above, there should be no more than one pending asynchronous 680 // request at a time. 681 682 // Non-blocking request handlers will signal when they are ready 683 // setting up for async execution, so more requests can be processed. 684 resumeRequestLoop := make(chan struct{}) 685 686 switch request := request.(type) { 687 //--- Asynchronous requests --- 688 case *dap.ConfigurationDoneRequest: // Optional (capability ‘supportsConfigurationDoneRequest’) 689 go func() { 690 defer s.recoverPanic(request) 691 s.onConfigurationDoneRequest(request, resumeRequestLoop) 692 }() 693 <-resumeRequestLoop 694 case *dap.ContinueRequest: // Required 695 go func() { 696 defer s.recoverPanic(request) 697 s.onContinueRequest(request, resumeRequestLoop) 698 }() 699 <-resumeRequestLoop 700 case *dap.NextRequest: // Required 701 go func() { 702 defer s.recoverPanic(request) 703 s.onNextRequest(request, resumeRequestLoop) 704 }() 705 <-resumeRequestLoop 706 case *dap.StepInRequest: // Required 707 go func() { 708 defer s.recoverPanic(request) 709 s.onStepInRequest(request, resumeRequestLoop) 710 }() 711 <-resumeRequestLoop 712 case *dap.StepOutRequest: // Required 713 go func() { 714 defer s.recoverPanic(request) 715 s.onStepOutRequest(request, resumeRequestLoop) 716 }() 717 <-resumeRequestLoop 718 case *dap.StepBackRequest: // Optional (capability ‘supportsStepBack’) 719 go func() { 720 defer s.recoverPanic(request) 721 s.onStepBackRequest(request, resumeRequestLoop) 722 }() 723 <-resumeRequestLoop 724 case *dap.ReverseContinueRequest: // Optional (capability ‘supportsStepBack’) 725 go func() { 726 defer s.recoverPanic(request) 727 s.onReverseContinueRequest(request, resumeRequestLoop) 728 }() 729 <-resumeRequestLoop 730 //--- Synchronous requests --- 731 case *dap.SetBreakpointsRequest: // Required 732 s.onSetBreakpointsRequest(request) 733 case *dap.SetFunctionBreakpointsRequest: // Optional (capability ‘supportsFunctionBreakpoints’) 734 s.onSetFunctionBreakpointsRequest(request) 735 case *dap.SetInstructionBreakpointsRequest: // Optional (capability 'supportsInstructionBreakpoints') 736 s.onSetInstructionBreakpointsRequest(request) 737 case *dap.SetExceptionBreakpointsRequest: // Optional (capability ‘exceptionBreakpointFilters’) 738 s.onSetExceptionBreakpointsRequest(request) 739 case *dap.ThreadsRequest: // Required 740 s.onThreadsRequest(request) 741 case *dap.StackTraceRequest: // Required 742 s.onStackTraceRequest(request) 743 case *dap.ScopesRequest: // Required 744 s.onScopesRequest(request) 745 case *dap.VariablesRequest: // Required 746 s.onVariablesRequest(request) 747 case *dap.EvaluateRequest: // Required 748 s.onEvaluateRequest(request) 749 case *dap.SetVariableRequest: // Optional (capability ‘supportsSetVariable’) 750 s.onSetVariableRequest(request) 751 case *dap.ExceptionInfoRequest: // Optional (capability ‘supportsExceptionInfoRequest’) 752 s.onExceptionInfoRequest(request) 753 case *dap.DisassembleRequest: // Optional (capability ‘supportsDisassembleRequest’) 754 s.onDisassembleRequest(request) 755 //--- Requests that we may want to support --- 756 case *dap.SourceRequest: // Required 757 /*TODO*/ s.sendUnsupportedErrorResponse(request.Request) // https://github.com/go-delve/delve/issues/2851 758 case *dap.SetExpressionRequest: // Optional (capability ‘supportsSetExpression’) 759 /*TODO*/ s.onSetExpressionRequest(request) // Not yet implemented 760 case *dap.LoadedSourcesRequest: // Optional (capability ‘supportsLoadedSourcesRequest’) 761 /*TODO*/ s.onLoadedSourcesRequest(request) // Not yet implemented 762 case *dap.ReadMemoryRequest: // Optional (capability ‘supportsReadMemoryRequest‘) 763 /*TODO*/ s.onReadMemoryRequest(request) // Not yet implemented 764 case *dap.CancelRequest: // Optional (capability ‘supportsCancelRequest’) 765 /*TODO*/ s.onCancelRequest(request) // Not yet implemented (does this make sense?) 766 case *dap.ModulesRequest: // Optional (capability ‘supportsModulesRequest’) 767 /*TODO*/ s.sendUnsupportedErrorResponse(request.Request) // Not yet implemented (does this make sense?) 768 //--- Requests that we do not plan to support --- 769 case *dap.RestartFrameRequest: // Optional (capability ’supportsRestartFrame’) 770 s.sendUnsupportedErrorResponse(request.Request) 771 case *dap.GotoRequest: // Optional (capability ‘supportsGotoTargetsRequest’) 772 s.sendUnsupportedErrorResponse(request.Request) 773 case *dap.TerminateThreadsRequest: // Optional (capability ‘supportsTerminateThreadsRequest’) 774 s.sendUnsupportedErrorResponse(request.Request) 775 case *dap.StepInTargetsRequest: // Optional (capability ‘supportsStepInTargetsRequest’) 776 s.sendUnsupportedErrorResponse(request.Request) 777 case *dap.GotoTargetsRequest: // Optional (capability ‘supportsGotoTargetsRequest’) 778 s.sendUnsupportedErrorResponse(request.Request) 779 case *dap.CompletionsRequest: // Optional (capability ‘supportsCompletionsRequest’) 780 s.sendUnsupportedErrorResponse(request.Request) 781 case *dap.DataBreakpointInfoRequest: // Optional (capability ‘supportsDataBreakpoints’) 782 s.sendUnsupportedErrorResponse(request.Request) 783 case *dap.SetDataBreakpointsRequest: // Optional (capability ‘supportsDataBreakpoints’) 784 s.sendUnsupportedErrorResponse(request.Request) 785 case *dap.BreakpointLocationsRequest: // Optional (capability ‘supportsBreakpointLocationsRequest’) 786 s.sendUnsupportedErrorResponse(request.Request) 787 default: 788 // This is a DAP message that go-dap has a struct for, so 789 // decoding succeeded, but this function does not know how 790 // to handle. 791 s.sendInternalErrorResponse(request.GetSeq(), fmt.Sprintf("Unable to process %#v\n", request)) 792 } 793 } 794 795 func (s *Session) send(message dap.Message) { 796 jsonmsg, _ := json.Marshal(message) 797 s.config.log.Debug("[-> to client]", string(jsonmsg)) 798 // TODO(polina): consider using a channel for all the sends and to have a dedicated 799 // goroutine that reads from that channel and sends over the connection. 800 // This will avoid blocking on slow network sends. 801 s.sendingMu.Lock() 802 defer s.sendingMu.Unlock() 803 err := dap.WriteProtocolMessage(s.conn, message) 804 if err != nil { 805 s.config.log.Debug(err) 806 } 807 } 808 809 func (s *Session) logToConsole(msg string) { 810 s.send(&dap.OutputEvent{ 811 Event: *newEvent("output"), 812 Body: dap.OutputEventBody{ 813 Output: msg + "\n", 814 Category: "console", 815 }}) 816 } 817 818 func (s *Session) onInitializeRequest(request *dap.InitializeRequest) { 819 s.setClientCapabilities(request.Arguments) 820 if request.Arguments.PathFormat != "path" { 821 s.sendErrorResponse(request.Request, FailedToInitialize, "Failed to initialize", 822 fmt.Sprintf("Unsupported 'pathFormat' value '%s'.", request.Arguments.PathFormat)) 823 return 824 } 825 if !request.Arguments.LinesStartAt1 { 826 s.sendErrorResponse(request.Request, FailedToInitialize, "Failed to initialize", 827 "Only 1-based line numbers are supported.") 828 return 829 } 830 if !request.Arguments.ColumnsStartAt1 { 831 s.sendErrorResponse(request.Request, FailedToInitialize, "Failed to initialize", 832 "Only 1-based column numbers are supported.") 833 return 834 } 835 836 // TODO(polina): Respond with an error if debug session started 837 // with an initialize request is in progress? 838 response := &dap.InitializeResponse{Response: *newResponse(request.Request)} 839 response.Body.SupportsConfigurationDoneRequest = true 840 response.Body.SupportsConditionalBreakpoints = true 841 response.Body.SupportsDelayedStackTraceLoading = true 842 response.Body.SupportsFunctionBreakpoints = true 843 response.Body.SupportsInstructionBreakpoints = true 844 response.Body.SupportsExceptionInfoRequest = true 845 response.Body.SupportsSetVariable = true 846 response.Body.SupportsEvaluateForHovers = true 847 response.Body.SupportsClipboardContext = true 848 response.Body.SupportsSteppingGranularity = true 849 response.Body.SupportsLogPoints = true 850 response.Body.SupportsDisassembleRequest = true 851 // To be enabled by CapabilitiesEvent based on launch configuration 852 response.Body.SupportsStepBack = false 853 response.Body.SupportTerminateDebuggee = false 854 // TODO(polina): support these requests in addition to vscode-go feature parity 855 response.Body.SupportsTerminateRequest = false 856 response.Body.SupportsRestartRequest = false 857 response.Body.SupportsSetExpression = false 858 response.Body.SupportsLoadedSourcesRequest = false 859 response.Body.SupportsReadMemoryRequest = false 860 response.Body.SupportsCancelRequest = false 861 s.send(response) 862 } 863 864 func (s *Session) setClientCapabilities(args dap.InitializeRequestArguments) { 865 s.clientCapabilities.supportsMemoryReferences = args.SupportsMemoryReferences 866 s.clientCapabilities.supportsProgressReporting = args.SupportsProgressReporting 867 s.clientCapabilities.supportsRunInTerminalRequest = args.SupportsRunInTerminalRequest 868 s.clientCapabilities.supportsVariablePaging = args.SupportsVariablePaging 869 s.clientCapabilities.supportsVariableType = args.SupportsVariableType 870 } 871 872 func cleanExeName(name string) string { 873 if runtime.GOOS == "windows" && filepath.Ext(name) != ".exe" { 874 return name + ".exe" 875 } 876 return name 877 } 878 879 func (s *Session) onLaunchRequest(request *dap.LaunchRequest) { 880 var err error 881 if s.debugger != nil { 882 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 883 fmt.Sprintf("debug session already in progress at %s - use remote attach mode to connect to a server with an active debug session", s.address())) 884 return 885 } 886 887 var args = defaultLaunchConfig // narrow copy for initializing non-zero default values 888 if err := unmarshalLaunchAttachArgs(request.Arguments, &args); err != nil { 889 s.sendShowUserErrorResponse(request.Request, 890 FailedToLaunch, "Failed to launch", fmt.Sprintf("invalid debug configuration - %v", err)) 891 return 892 } 893 s.config.log.Debug("parsed launch config: ", prettyPrint(args)) 894 895 if args.DlvCwd != "" { 896 if err := os.Chdir(args.DlvCwd); err != nil { 897 s.sendShowUserErrorResponse(request.Request, 898 FailedToLaunch, "Failed to launch", fmt.Sprintf("failed to chdir to %q - %v", args.DlvCwd, err)) 899 return 900 } 901 } 902 903 for k, v := range args.Env { 904 if v != nil { 905 if err := os.Setenv(k, *v); err != nil { 906 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", fmt.Sprintf("failed to setenv(%v) - %v", k, err)) 907 return 908 } 909 } else { 910 if err := os.Unsetenv(k); err != nil { 911 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", fmt.Sprintf("failed to unsetenv(%v) - %v", k, err)) 912 return 913 } 914 } 915 } 916 917 if args.Mode == "" { 918 args.Mode = "debug" 919 } 920 if !isValidLaunchMode(args.Mode) { 921 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 922 fmt.Sprintf("invalid debug configuration - unsupported 'mode' attribute %q", args.Mode)) 923 return 924 } 925 926 if args.Program == "" && args.Mode != "replay" { // Only fail on modes requiring a program 927 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 928 "The program attribute is missing in debug configuration.") 929 return 930 } 931 932 if args.Backend == "" { 933 args.Backend = "default" 934 } 935 936 if args.Mode == "replay" { 937 // Validate trace directory 938 if args.TraceDirPath == "" { 939 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 940 "The 'traceDirPath' attribute is missing in debug configuration.") 941 return 942 } 943 944 // Assign the rr trace directory path to debugger configuration 945 s.config.Debugger.CoreFile = args.TraceDirPath 946 args.Backend = "rr" 947 } 948 if args.Mode == "core" { 949 // Validate core dump path 950 if args.CoreFilePath == "" { 951 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 952 "The 'coreFilePath' attribute is missing in debug configuration.") 953 return 954 } 955 // Assign the non-empty core file path to debugger configuration. This will 956 // trigger a native core file replay instead of an rr trace replay 957 s.config.Debugger.CoreFile = args.CoreFilePath 958 args.Backend = "core" 959 } 960 961 s.config.Debugger.Backend = args.Backend 962 963 // Prepare the debug executable filename, building it if necessary 964 debugbinary := args.Program 965 if args.Mode == "debug" || args.Mode == "test" { 966 deleteOnError := false 967 if args.Output == "" { 968 deleteOnError = true 969 args.Output = gobuild.DefaultDebugBinaryPath("__debug_bin") 970 } else { 971 args.Output = cleanExeName(args.Output) 972 } 973 args.Output, err = filepath.Abs(args.Output) 974 if err != nil { 975 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", err.Error()) 976 return 977 } 978 debugbinary = args.Output 979 980 var cmd string 981 var out []byte 982 983 switch args.Mode { 984 case "debug": 985 cmd, out, err = gobuild.GoBuildCombinedOutput(args.Output, []string{args.Program}, args.BuildFlags) 986 case "test": 987 cmd, out, err = gobuild.GoTestBuildCombinedOutput(args.Output, []string{args.Program}, args.BuildFlags) 988 } 989 args.DlvCwd, _ = filepath.Abs(args.DlvCwd) 990 s.config.log.Debugf("building from %q: [%s]", args.DlvCwd, cmd) 991 if err != nil { 992 if deleteOnError { 993 gobuild.Remove(args.Output) 994 } 995 s.send(&dap.OutputEvent{ 996 Event: *newEvent("output"), 997 Body: dap.OutputEventBody{ 998 Output: fmt.Sprintf("Build Error: %s\n%s (%s)\n", cmd, strings.TrimSpace(string(out)), err.Error()), 999 Category: "stderr", 1000 }}) 1001 // Users are used to checking the Debug Console for build errors. 1002 // No need to bother them with a visible pop-up. 1003 s.sendErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 1004 "Build error: Check the debug console for details.") 1005 return 1006 } 1007 s.mu.Lock() 1008 s.binaryToRemove = args.Output 1009 s.mu.Unlock() 1010 } 1011 s.config.ProcessArgs = append([]string{debugbinary}, args.Args...) 1012 1013 s.setLaunchAttachArgs(args.LaunchAttachCommonConfig) 1014 1015 if args.Cwd == "" { 1016 if args.Mode == "test" { 1017 // In test mode, run the test binary from the package directory 1018 // like in `go test` and `dlv test` by default. 1019 args.Cwd = s.getPackageDir(args.Program) 1020 } else { 1021 args.Cwd = "." 1022 } 1023 } 1024 s.config.Debugger.WorkingDir = args.Cwd 1025 1026 // Backend layers will interpret paths relative to server's working directory: 1027 // reflect that before logging. 1028 argsToLog := args 1029 argsToLog.Program, _ = filepath.Abs(args.Program) 1030 argsToLog.Cwd, _ = filepath.Abs(args.Cwd) 1031 s.config.log.Debugf("launching binary '%s' with config: %s", debugbinary, prettyPrint(argsToLog)) 1032 1033 var redirected = false 1034 switch args.OutputMode { 1035 case "remote": 1036 redirected = true 1037 case "local", "": 1038 // noting 1039 default: 1040 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", 1041 fmt.Sprintf("invalid debug configuration - unsupported 'outputMode' attribute %q", args.OutputMode)) 1042 return 1043 } 1044 1045 redirectedFunc := func(stdoutReader io.ReadCloser, stderrReader io.ReadCloser) { 1046 runReadFunc := func(reader io.ReadCloser, category string) { 1047 defer s.preTerminatedWG.Done() 1048 defer reader.Close() 1049 // Read output from `reader` and send to client 1050 var out [1024]byte 1051 for { 1052 n, err := reader.Read(out[:]) 1053 if err != nil { 1054 if err == io.EOF { 1055 return 1056 } 1057 s.config.log.Errorf("failed read by %s - %v ", category, err) 1058 return 1059 } 1060 outs := string(out[:n]) 1061 s.send(&dap.OutputEvent{ 1062 Event: *newEvent("output"), 1063 Body: dap.OutputEventBody{ 1064 Output: outs, 1065 Category: category, 1066 }}) 1067 } 1068 } 1069 1070 s.preTerminatedWG.Add(2) 1071 go runReadFunc(stdoutReader, "stdout") 1072 go runReadFunc(stderrReader, "stderr") 1073 } 1074 1075 if args.NoDebug { 1076 s.mu.Lock() 1077 cmd, err := s.newNoDebugProcess(debugbinary, args.Args, s.config.Debugger.WorkingDir, redirected) 1078 s.mu.Unlock() 1079 if err != nil { 1080 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", err.Error()) 1081 return 1082 } 1083 // Skip 'initialized' event, which will prevent the client from sending 1084 // debug-related requests. 1085 s.send(&dap.LaunchResponse{Response: *newResponse(request.Request)}) 1086 1087 // Start the program on a different goroutine, so we can listen for disconnect request. 1088 go func() { 1089 if redirected { 1090 redirectedFunc(s.stdoutReader, s.stderrReader) 1091 } 1092 1093 if err := cmd.Wait(); err != nil { 1094 s.config.log.Debugf("program exited with error: %v", err) 1095 } 1096 1097 close(s.noDebugProcess.exited) 1098 s.logToConsole(proc.ErrProcessExited{Pid: cmd.ProcessState.Pid(), Status: cmd.ProcessState.ExitCode()}.Error()) 1099 s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")}) 1100 }() 1101 return 1102 } 1103 1104 var clear func() 1105 if redirected { 1106 var ( 1107 readers [2]io.ReadCloser 1108 outputRedirects [2]proc.OutputRedirect 1109 ) 1110 1111 for i := 0; i < 2; i++ { 1112 readers[i], outputRedirects[i], err = proc.Redirector() 1113 if err != nil { 1114 s.sendShowUserErrorResponse(request.Request, InternalError, "Internal Error", 1115 fmt.Sprintf("failed to generate stdio pipes - %v", err)) 1116 return 1117 } 1118 } 1119 1120 s.config.Debugger.Stdout = outputRedirects[0] 1121 s.config.Debugger.Stderr = outputRedirects[1] 1122 1123 redirectedFunc(readers[0], readers[1]) 1124 clear = func() { 1125 for index := range readers { 1126 if closeErr := readers[index].Close(); closeErr != nil { 1127 s.config.log.Warnf("failed to clear redirects - %v", closeErr) 1128 } 1129 } 1130 } 1131 } 1132 1133 func() { 1134 s.mu.Lock() 1135 defer s.mu.Unlock() // Make sure to unlock in case of panic that will become internal error 1136 s.debugger, err = debugger.New(&s.config.Debugger, s.config.ProcessArgs) 1137 }() 1138 if err != nil { 1139 if s.binaryToRemove != "" { 1140 gobuild.Remove(s.binaryToRemove) 1141 } 1142 s.sendShowUserErrorResponse(request.Request, FailedToLaunch, "Failed to launch", err.Error()) 1143 if redirected { 1144 clear() 1145 } 1146 return 1147 } 1148 // Enable StepBack controls on supported backends 1149 if s.config.Debugger.Backend == "rr" { 1150 s.send(&dap.CapabilitiesEvent{Event: *newEvent("capabilities"), Body: dap.CapabilitiesEventBody{Capabilities: dap.Capabilities{SupportsStepBack: true}}}) 1151 } 1152 1153 // Notify the client that the debugger is ready to start accepting 1154 // configuration requests for setting breakpoints, etc. The client 1155 // will end the configuration sequence with 'configurationDone'. 1156 s.send(&dap.InitializedEvent{Event: *newEvent("initialized")}) 1157 s.send(&dap.LaunchResponse{Response: *newResponse(request.Request)}) 1158 } 1159 1160 func (s *Session) getPackageDir(pkg string) string { 1161 cmd := exec.Command("go", "list", "-f", "{{.Dir}}", pkg) 1162 out, err := cmd.Output() 1163 if err != nil { 1164 s.config.log.Debugf("failed to determine package directory for %v: %v\n%s", pkg, err, out) 1165 return "." 1166 } 1167 return string(bytes.TrimSpace(out)) 1168 } 1169 1170 // newNoDebugProcess is called from onLaunchRequest (run goroutine) and 1171 // requires holding mu lock. It prepares process exec.Cmd to be started. 1172 func (s *Session) newNoDebugProcess(program string, targetArgs []string, wd string, redirected bool) (cmd *exec.Cmd, err error) { 1173 if s.noDebugProcess != nil { 1174 return nil, fmt.Errorf("another launch request is in progress") 1175 } 1176 1177 cmd = exec.Command(program, targetArgs...) 1178 cmd.Stdin, cmd.Dir = os.Stdin, wd 1179 1180 if redirected { 1181 if s.stderrReader, err = cmd.StderrPipe(); err != nil { 1182 return nil, err 1183 } 1184 1185 if s.stdoutReader, err = cmd.StdoutPipe(); err != nil { 1186 return nil, err 1187 } 1188 } else { 1189 cmd.Stdout, cmd.Stderr = os.Stdin, os.Stderr 1190 } 1191 1192 if err = cmd.Start(); err != nil { 1193 return nil, err 1194 } 1195 1196 s.noDebugProcess = &process{Cmd: cmd, exited: make(chan struct{})} 1197 return cmd, nil 1198 } 1199 1200 // stopNoDebugProcess is called from Stop (main goroutine) and 1201 // onDisconnectRequest (run goroutine) and requires holding mu lock. 1202 func (s *Session) stopNoDebugProcess() { 1203 if s.noDebugProcess == nil { 1204 // We already handled termination or there was never a process 1205 return 1206 } 1207 select { 1208 case <-s.noDebugProcess.exited: 1209 s.noDebugProcess = nil 1210 return 1211 default: 1212 } 1213 1214 // TODO(hyangah): gracefully terminate the process and its children processes. 1215 s.logToConsole(fmt.Sprintf("Terminating process %d", s.noDebugProcess.Process.Pid)) 1216 s.noDebugProcess.Process.Kill() // Don't check error. Process killing and self-termination may race. 1217 1218 // Wait for kill to complete or time out 1219 select { 1220 case <-time.After(5 * time.Second): 1221 s.config.log.Debug("noDebug process kill timed out") 1222 case <-s.noDebugProcess.exited: 1223 s.config.log.Debug("noDebug process killed") 1224 s.noDebugProcess = nil 1225 } 1226 } 1227 1228 // onDisconnectRequest handles the DisconnectRequest. Per the DAP spec, 1229 // it disconnects the debuggee and signals that the debug adaptor 1230 // (in our case this TCP server) can be terminated. 1231 func (s *Session) onDisconnectRequest(request *dap.DisconnectRequest) { 1232 s.mu.Lock() 1233 defer s.mu.Unlock() 1234 1235 if s.debugger != nil && s.config.AcceptMulti && (request.Arguments == nil || !request.Arguments.TerminateDebuggee) { 1236 // This is a multi-use server/debugger, so a disconnect request that doesn't 1237 // terminate the debuggee should clean up only the client connection and pointer to debugger, 1238 // but not the entire server. 1239 status := "halted" 1240 if s.isRunningCmd() { 1241 status = "running" 1242 } else if state, err := s.debugger.State(false); processExited(state, err) { 1243 status = "exited" 1244 s.preTerminatedWG.Wait() 1245 } 1246 1247 s.logToConsole(fmt.Sprintf("Closing client session, but leaving multi-client DAP server at %s with debuggee %s", s.config.Listener.Addr().String(), status)) 1248 s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)}) 1249 s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")}) 1250 s.conn.Close() 1251 s.debugger = nil 1252 // The target is left in whatever state it is already in - halted or running. 1253 // The users therefore have the flexibility to choose the appropriate state 1254 // for their case before disconnecting. This is also desirable in case of 1255 // the client connection fails unexpectedly and the user needs to reconnect. 1256 // TODO(polina): should we always issue a continue here if it is not running 1257 // like is done in vscode-go legacy adapter? 1258 // Ideally we want to use bool suspendDebuggee flag, but it is not yet 1259 // available in vscode: https://github.com/microsoft/vscode/issues/134412 1260 return 1261 } 1262 1263 defer s.config.triggerServerStop() 1264 var err error 1265 if s.debugger != nil { 1266 // We always kill launched programs. 1267 // In case of attach, we leave the program 1268 // running by default, which can be 1269 // overridden by an explicit request to terminate. 1270 killProcess := s.debugger.AttachPid() == 0 || (request.Arguments != nil && request.Arguments.TerminateDebuggee) 1271 err = s.stopDebugSession(killProcess) 1272 } else if s.noDebugProcess != nil { 1273 s.stopNoDebugProcess() 1274 } 1275 if err != nil { 1276 s.sendErrorResponse(request.Request, DisconnectError, "Error while disconnecting", err.Error()) 1277 } else { 1278 s.send(&dap.DisconnectResponse{Response: *newResponse(request.Request)}) 1279 } 1280 s.preTerminatedWG.Wait() 1281 // The debugging session has ended, so we send a terminated event. 1282 s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")}) 1283 } 1284 1285 // stopDebugSession is called from Stop (main goroutine) and 1286 // onDisconnectRequest (run goroutine) and requires holding mu lock. 1287 // Returns any detach error other than proc.ErrProcessExited. 1288 func (s *Session) stopDebugSession(killProcess bool) error { 1289 s.changeStateMu.Lock() 1290 defer func() { 1291 // Avoid running stop sequence twice. 1292 // It's not fatal, but will result in duplicate logging. 1293 s.debugger = nil 1294 s.changeStateMu.Unlock() 1295 }() 1296 if s.debugger == nil { 1297 return nil 1298 } 1299 var err error 1300 var exited error 1301 // Halting will stop any debugger command that's pending on another 1302 // per-request goroutine. Tell auto-resumer not to resume, so the 1303 // goroutine can wrap-up and exit. 1304 s.setHaltRequested(true) 1305 state, err := s.halt() 1306 if err == proc.ErrProcessDetached { 1307 s.config.log.Debug("halt returned error: ", err) 1308 return nil 1309 } 1310 if err != nil { 1311 switch err.(type) { 1312 case proc.ErrProcessExited: 1313 exited = err 1314 default: 1315 s.config.log.Error("halt returned error: ", err) 1316 if err.Error() == "no such process" { 1317 exited = err 1318 } 1319 } 1320 } else if state.Exited { 1321 exited = proc.ErrProcessExited{Pid: s.debugger.ProcessPid(), Status: state.ExitStatus} 1322 s.config.log.Debug("halt returned state: ", exited) 1323 } 1324 if exited != nil { 1325 // TODO(suzmue): log exited error when the process exits, which may have been before 1326 // halt was called. 1327 s.logToConsole(exited.Error()) 1328 s.logToConsole("Detaching") 1329 } else if killProcess { 1330 s.logToConsole("Detaching and terminating target process") 1331 } else { 1332 s.logToConsole("Detaching without terminating target process") 1333 } 1334 err = s.debugger.Detach(killProcess) 1335 if err != nil { 1336 switch err.(type) { 1337 case proc.ErrProcessExited: 1338 s.config.log.Debug(err) 1339 s.logToConsole(exited.Error()) 1340 err = nil 1341 default: 1342 s.config.log.Error("detach returned error: ", err) 1343 } 1344 } 1345 return err 1346 } 1347 1348 // halt sends a halt request if the debuggee is running. 1349 // changeStateMu should be held when calling (*Server).halt. 1350 func (s *Session) halt() (*api.DebuggerState, error) { 1351 s.config.log.Debug("halting") 1352 // Only send a halt request if the debuggee is running. 1353 if s.debugger.IsRunning() { 1354 return s.debugger.Command(&api.DebuggerCommand{Name: api.Halt}, nil) 1355 } 1356 s.config.log.Debug("process not running") 1357 return s.debugger.State(false) 1358 } 1359 1360 func (s *Session) isNoDebug() bool { 1361 s.mu.Lock() 1362 defer s.mu.Unlock() 1363 return s.noDebugProcess != nil 1364 } 1365 1366 func (s *Session) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) { 1367 if request.Arguments.Source.Path == "" { 1368 s.sendErrorResponse(request.Request, UnableToSetBreakpoints, "Unable to set or clear breakpoints", "empty file path") 1369 return 1370 } 1371 1372 clientPath := request.Arguments.Source.Path 1373 serverPath := s.toServerPath(clientPath) 1374 1375 // Get all existing breakpoints that match for this source. 1376 sourceRequestPrefix := fmt.Sprintf("sourceBp Path=%q ", request.Arguments.Source.Path) 1377 1378 breakpoints := s.setBreakpoints(sourceRequestPrefix, len(request.Arguments.Breakpoints), func(i int) *bpMetadata { 1379 want := request.Arguments.Breakpoints[i] 1380 return &bpMetadata{ 1381 name: fmt.Sprintf("%s Line=%d Column=%d", sourceRequestPrefix, want.Line, want.Column), 1382 condition: want.Condition, 1383 hitCondition: want.HitCondition, 1384 logMessage: want.LogMessage, 1385 } 1386 }, func(i int) (*bpLocation, error) { 1387 want := request.Arguments.Breakpoints[i] 1388 return &bpLocation{ 1389 file: serverPath, 1390 line: want.Line, 1391 }, nil 1392 }) 1393 1394 response := &dap.SetBreakpointsResponse{Response: *newResponse(request.Request)} 1395 response.Body.Breakpoints = breakpoints 1396 1397 s.send(response) 1398 } 1399 1400 type bpMetadata struct { 1401 name string 1402 condition string 1403 hitCondition string 1404 logMessage string 1405 } 1406 1407 type bpLocation struct { 1408 file string 1409 line int 1410 addr uint64 1411 addrs []uint64 1412 } 1413 1414 // setBreakpoints is a helper function for setting source, function and instruction 1415 // breakpoints. It takes the prefix of the name for all breakpoints that should be 1416 // included, the total number of breakpoints, and functions for computing the metadata 1417 // and the location. The location is computed separately because this may be more 1418 // expensive to compute and may not always be necessary. 1419 func (s *Session) setBreakpoints(prefix string, totalBps int, metadataFunc func(i int) *bpMetadata, locFunc func(i int) (*bpLocation, error)) []dap.Breakpoint { 1420 // If a breakpoint: 1421 // -- exists and not in request => ClearBreakpoint 1422 // -- exists and in request => AmendBreakpoint 1423 // -- doesn't exist and in request => SetBreakpoint 1424 1425 // Get all existing breakpoints matching the prefix. 1426 existingBps := s.getMatchingBreakpoints(prefix) 1427 1428 // createdBps is a set of breakpoint names that have been added 1429 // during this request. This is used to catch duplicate set 1430 // breakpoints requests and to track which breakpoints need to 1431 // be deleted. 1432 createdBps := make(map[string]struct{}, len(existingBps)) 1433 1434 breakpoints := make([]dap.Breakpoint, totalBps) 1435 // Amend existing breakpoints. 1436 for i := 0; i < totalBps; i++ { 1437 want := metadataFunc(i) 1438 got, ok := existingBps[want.name] 1439 if got == nil || !ok { 1440 // Skip if the breakpoint does not already exist. 1441 continue 1442 } 1443 1444 var err error 1445 if _, ok := createdBps[want.name]; ok { 1446 err = fmt.Errorf("breakpoint already exists") 1447 } else { 1448 got.Disabled = false 1449 got.Cond = want.condition 1450 got.HitCond = want.hitCondition 1451 err = setLogMessage(got, want.logMessage) 1452 if err == nil { 1453 err = s.debugger.AmendBreakpoint(got) 1454 } 1455 } 1456 createdBps[want.name] = struct{}{} 1457 s.updateBreakpointsResponse(breakpoints, i, err, got) 1458 } 1459 1460 // Clear breakpoints. 1461 // Any breakpoint that existed before this request but was not amended must be deleted. 1462 s.clearBreakpoints(existingBps, createdBps) 1463 1464 // Add new breakpoints. 1465 for i := 0; i < totalBps; i++ { 1466 want := metadataFunc(i) 1467 if _, ok := existingBps[want.name]; ok { 1468 continue 1469 } 1470 1471 var got *api.Breakpoint 1472 wantLoc, err := locFunc(i) 1473 if err == nil { 1474 if _, ok := createdBps[want.name]; ok { 1475 err = fmt.Errorf("breakpoint already exists") 1476 } else { 1477 bp := &api.Breakpoint{ 1478 Name: want.name, 1479 File: wantLoc.file, 1480 Line: wantLoc.line, 1481 Addr: wantLoc.addr, 1482 Addrs: wantLoc.addrs, 1483 Cond: want.condition, 1484 HitCond: want.hitCondition, 1485 } 1486 err = setLogMessage(bp, want.logMessage) 1487 if err == nil { 1488 // Create new breakpoints. 1489 got, err = s.debugger.CreateBreakpoint(bp, "", nil, false) 1490 } 1491 } 1492 } 1493 createdBps[want.name] = struct{}{} 1494 s.updateBreakpointsResponse(breakpoints, i, err, got) 1495 } 1496 return breakpoints 1497 } 1498 1499 func setLogMessage(bp *api.Breakpoint, msg string) error { 1500 tracepoint, userdata, err := parseLogPoint(msg) 1501 if err != nil { 1502 return err 1503 } 1504 bp.Tracepoint = tracepoint 1505 if userdata != nil { 1506 bp.UserData = *userdata 1507 } 1508 return nil 1509 } 1510 1511 func (s *Session) updateBreakpointsResponse(breakpoints []dap.Breakpoint, i int, err error, got *api.Breakpoint) { 1512 breakpoints[i].Verified = (err == nil) 1513 if err != nil { 1514 breakpoints[i].Message = err.Error() 1515 } else { 1516 path := s.toClientPath(got.File) 1517 breakpoints[i].Id = got.ID 1518 breakpoints[i].Line = got.Line 1519 breakpoints[i].Source = &dap.Source{Name: filepath.Base(path), Path: path} 1520 } 1521 } 1522 1523 // functionBpPrefix is the prefix of bp.Name for every breakpoint bp set 1524 // in this request. 1525 const functionBpPrefix = "functionBreakpoint" 1526 1527 func (s *Session) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest) { 1528 breakpoints := s.setBreakpoints(functionBpPrefix, len(request.Arguments.Breakpoints), func(i int) *bpMetadata { 1529 want := request.Arguments.Breakpoints[i] 1530 return &bpMetadata{ 1531 name: fmt.Sprintf("%s Name=%s", functionBpPrefix, want.Name), 1532 condition: want.Condition, 1533 hitCondition: want.HitCondition, 1534 logMessage: "", 1535 } 1536 }, func(i int) (*bpLocation, error) { 1537 want := request.Arguments.Breakpoints[i] 1538 // Set the function breakpoint 1539 spec, err := locspec.Parse(want.Name) 1540 if err != nil { 1541 return nil, err 1542 } 1543 if loc, ok := spec.(*locspec.NormalLocationSpec); !ok || loc.FuncBase == nil { 1544 // Other locations do not make sense in the context of function breakpoints. 1545 // Regex locations are likely to resolve to multiple places and offset locations 1546 // are only meaningful at the time the breakpoint was created. 1547 return nil, fmt.Errorf("breakpoint name %q could not be parsed as a function. name must be in the format 'funcName', 'funcName:line' or 'fileName:line'", want.Name) 1548 } 1549 1550 if want.Name[0] == '.' { 1551 return nil, fmt.Errorf("breakpoint names that are relative paths are not supported") 1552 } 1553 // Find the location of the function name. CreateBreakpoint requires the name to include the base 1554 // (e.g. main.functionName is supported but not functionName). 1555 // We first find the location of the function, and then set breakpoints for that location. 1556 var locs []api.Location 1557 locs, err = s.debugger.FindLocationSpec(-1, 0, 0, want.Name, spec, true, s.args.substitutePathClientToServer) 1558 if err != nil { 1559 return nil, err 1560 } 1561 if len(locs) == 0 { 1562 return nil, err 1563 } 1564 if len(locs) > 0 { 1565 s.config.log.Debugf("multiple locations found for %s", want.Name) 1566 } 1567 1568 // Set breakpoint using the PCs that were found. 1569 loc := locs[0] 1570 return &bpLocation{addr: loc.PC, addrs: loc.PCs}, nil 1571 }) 1572 1573 response := &dap.SetFunctionBreakpointsResponse{Response: *newResponse(request.Request)} 1574 response.Body.Breakpoints = breakpoints 1575 1576 s.send(response) 1577 } 1578 1579 const instructionBpPrefix = "instructionBreakpoint" 1580 1581 func (s *Session) onSetInstructionBreakpointsRequest(request *dap.SetInstructionBreakpointsRequest) { 1582 breakpoints := s.setBreakpoints(instructionBpPrefix, len(request.Arguments.Breakpoints), func(i int) *bpMetadata { 1583 want := request.Arguments.Breakpoints[i] 1584 return &bpMetadata{ 1585 name: fmt.Sprintf("%s PC=%s", instructionBpPrefix, want.InstructionReference), 1586 condition: want.Condition, 1587 hitCondition: want.HitCondition, 1588 logMessage: "", 1589 } 1590 }, func(i int) (*bpLocation, error) { 1591 want := request.Arguments.Breakpoints[i] 1592 addr, err := strconv.ParseInt(want.InstructionReference, 0, 64) 1593 if err != nil { 1594 return nil, err 1595 } 1596 return &bpLocation{addr: uint64(addr)}, nil 1597 }) 1598 1599 response := &dap.SetInstructionBreakpointsResponse{Response: *newResponse(request.Request)} 1600 response.Body.Breakpoints = breakpoints 1601 s.send(response) 1602 } 1603 1604 func (s *Session) clearBreakpoints(existingBps map[string]*api.Breakpoint, amendedBps map[string]struct{}) error { 1605 for req, bp := range existingBps { 1606 if _, ok := amendedBps[req]; ok { 1607 continue 1608 } 1609 _, err := s.debugger.ClearBreakpoint(bp) 1610 if err != nil { 1611 return err 1612 } 1613 } 1614 return nil 1615 } 1616 1617 func (s *Session) getMatchingBreakpoints(prefix string) map[string]*api.Breakpoint { 1618 existing := s.debugger.Breakpoints(false) 1619 matchingBps := make(map[string]*api.Breakpoint, len(existing)) 1620 for _, bp := range existing { 1621 // Skip special breakpoints such as for panic. 1622 if bp.ID < 0 { 1623 continue 1624 } 1625 // Skip breakpoints that do not meet the condition. 1626 if !strings.HasPrefix(bp.Name, prefix) { 1627 continue 1628 } 1629 matchingBps[bp.Name] = bp 1630 } 1631 return matchingBps 1632 } 1633 1634 func (s *Session) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest) { 1635 // Unlike what DAP documentation claims, this request is always sent 1636 // even though we specified no filters at initialization. Handle as no-op. 1637 s.send(&dap.SetExceptionBreakpointsResponse{Response: *newResponse(request.Request)}) 1638 } 1639 1640 func closeIfOpen(ch chan struct{}) { 1641 if ch != nil { 1642 select { 1643 case <-ch: 1644 // already closed 1645 default: 1646 close(ch) 1647 } 1648 } 1649 } 1650 1651 // onConfigurationDoneRequest handles 'configurationDone' request. 1652 // This is an optional request enabled by capability ‘supportsConfigurationDoneRequest’. 1653 // It gets triggered after all the debug requests that follow initialized event, 1654 // so the s.debugger is guaranteed to be set. Expects the target to be halted. 1655 func (s *Session) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest, allowNextStateChange chan struct{}) { 1656 defer closeIfOpen(allowNextStateChange) 1657 if s.args.stopOnEntry { 1658 e := &dap.StoppedEvent{ 1659 Event: *newEvent("stopped"), 1660 Body: dap.StoppedEventBody{Reason: "entry", ThreadId: 1, AllThreadsStopped: true}, 1661 } 1662 s.send(e) 1663 } 1664 s.debugger.TargetGroup().KeepSteppingBreakpoints = proc.HaltKeepsSteppingBreakpoints | proc.TracepointKeepsSteppingBreakpoints 1665 1666 s.logToConsole("Type 'dlv help' for list of commands.") 1667 s.send(&dap.ConfigurationDoneResponse{Response: *newResponse(request.Request)}) 1668 1669 if !s.args.stopOnEntry { 1670 s.runUntilStopAndNotify(api.Continue, allowNextStateChange) 1671 } 1672 } 1673 1674 // onContinueRequest handles 'continue' request. 1675 // This is a mandatory request to support. 1676 func (s *Session) onContinueRequest(request *dap.ContinueRequest, allowNextStateChange chan struct{}) { 1677 s.send(&dap.ContinueResponse{ 1678 Response: *newResponse(request.Request), 1679 Body: dap.ContinueResponseBody{AllThreadsContinued: true}}) 1680 s.runUntilStopAndNotify(api.Continue, allowNextStateChange) 1681 } 1682 1683 func fnName(loc *proc.Location) string { 1684 if loc.Fn == nil { 1685 return "???" 1686 } 1687 return loc.Fn.Name 1688 } 1689 1690 func fnPackageName(loc *proc.Location) string { 1691 if loc.Fn == nil { 1692 // attribute unknown functions to the runtime 1693 return "runtime" 1694 } 1695 return loc.Fn.PackageName() 1696 } 1697 1698 // onThreadsRequest handles 'threads' request. 1699 // This is a mandatory request to support. 1700 // It is sent in response to configurationDone response and stopped events. 1701 // Depending on the debug session stage, goroutines information 1702 // might not be available. However, the DAP spec states that 1703 // "even if a debug adapter does not support multiple threads, 1704 // it must implement the threads request and return a single 1705 // (dummy) thread". Therefore, this handler never returns 1706 // an error response. If the dummy thread is returned in its place, 1707 // the next waterfall request for its stackTrace will return the error. 1708 func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) { 1709 var err error 1710 var gs []*proc.G 1711 var next int 1712 if s.debugger != nil { 1713 gs, next, err = s.debugger.Goroutines(0, maxGoroutines) 1714 if err == nil { 1715 // Parse the goroutine arguments. 1716 filters, _, _, _, _, _, _, parseErr := api.ParseGoroutineArgs(s.args.GoroutineFilters) 1717 if parseErr != nil { 1718 s.logToConsole(parseErr.Error()) 1719 } 1720 if s.args.HideSystemGoroutines { 1721 filters = append(filters, api.ListGoroutinesFilter{ 1722 Kind: api.GoroutineUser, 1723 Negated: false, 1724 }) 1725 } 1726 gs = s.debugger.FilterGoroutines(gs, filters) 1727 } 1728 } 1729 1730 var threads []dap.Thread 1731 if err != nil { 1732 switch err.(type) { 1733 case proc.ErrProcessExited: 1734 // If the program exits very quickly, the initial threads request will complete after it has exited. 1735 // A TerminatedEvent has already been sent. Ignore the err returned in this case. 1736 s.config.log.Debug(err) 1737 default: 1738 s.send(&dap.OutputEvent{ 1739 Event: *newEvent("output"), 1740 Body: dap.OutputEventBody{ 1741 Output: fmt.Sprintf("Unable to retrieve goroutines: %s\n", err.Error()), 1742 Category: "stderr", 1743 }}) 1744 } 1745 threads = []dap.Thread{{Id: 1, Name: "Dummy"}} 1746 } else if len(gs) == 0 { 1747 threads = []dap.Thread{{Id: 1, Name: "Dummy"}} 1748 } else { 1749 state, err := s.debugger.State( /*nowait*/ true) 1750 if err != nil { 1751 s.config.log.Debug("Unable to get debugger state: ", err) 1752 } 1753 1754 if next >= 0 { 1755 s.logToConsole(fmt.Sprintf("Too many goroutines, only loaded %d", len(gs))) 1756 1757 // Make sure the selected goroutine is included in the list of threads 1758 // to return. 1759 if state != nil && state.SelectedGoroutine != nil { 1760 var selectedFound bool 1761 for _, g := range gs { 1762 if g.ID == state.SelectedGoroutine.ID { 1763 selectedFound = true 1764 break 1765 } 1766 } 1767 if !selectedFound { 1768 g, err := s.debugger.FindGoroutine(state.SelectedGoroutine.ID) 1769 if err != nil { 1770 s.config.log.Debug("Error getting selected goroutine: ", err) 1771 } else { 1772 // TODO(suzmue): Consider putting the selected goroutine at the top. 1773 // To be consistent we may want to do this for all threads requests. 1774 gs = append(gs, g) 1775 } 1776 } 1777 } 1778 } 1779 1780 threads = make([]dap.Thread, len(gs)) 1781 s.debugger.LockTarget() 1782 defer s.debugger.UnlockTarget() 1783 1784 for i, g := range gs { 1785 selected := "" 1786 if state != nil && state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID { 1787 selected = "* " 1788 } 1789 thread := "" 1790 if g.Thread != nil && g.Thread.ThreadID() != 0 { 1791 thread = fmt.Sprintf(" (Thread %d)", g.Thread.ThreadID()) 1792 } 1793 // File name and line number are communicated via `stackTrace` 1794 // so no need to include them here. 1795 loc := g.UserCurrent() 1796 threads[i].Name = fmt.Sprintf("%s[Go %d] %s%s", selected, g.ID, fnName(&loc), thread) 1797 threads[i].Id = int(g.ID) 1798 } 1799 } 1800 1801 response := &dap.ThreadsResponse{ 1802 Response: *newResponse(request.Request), 1803 Body: dap.ThreadsResponseBody{Threads: threads}, 1804 } 1805 s.send(response) 1806 } 1807 1808 // onAttachRequest handles 'attach' request. 1809 // This is a mandatory request to support. 1810 // Attach debug sessions support the following modes: 1811 // 1812 // - [DEFAULT] "local" -- attaches debugger to a local running process. 1813 // Required args: processID 1814 // - "remote" -- attaches client to a debugger already attached to a process. 1815 // Required args: none (host/port are used externally to connect) 1816 func (s *Session) onAttachRequest(request *dap.AttachRequest) { 1817 var args AttachConfig = defaultAttachConfig // narrow copy for initializing non-zero default values 1818 if err := unmarshalLaunchAttachArgs(request.Arguments, &args); err != nil { 1819 s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", fmt.Sprintf("invalid debug configuration - %v", err)) 1820 return 1821 } 1822 s.config.log.Debug("parsed launch config: ", prettyPrint(args)) 1823 1824 switch args.Mode { 1825 case "": 1826 args.Mode = "local" 1827 fallthrough 1828 case "local": 1829 if s.debugger != nil { 1830 s.sendShowUserErrorResponse( 1831 request.Request, FailedToAttach, 1832 "Failed to attach", 1833 fmt.Sprintf("debug session already in progress at %s - use remote mode to connect to a server with an active debug session", s.address())) 1834 return 1835 } 1836 if args.ProcessID == 0 { 1837 s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", 1838 "The 'processId' attribute is missing in debug configuration") 1839 return 1840 } 1841 s.config.Debugger.AttachPid = args.ProcessID 1842 if args.Backend == "" { 1843 args.Backend = "default" 1844 } 1845 s.config.Debugger.Backend = args.Backend 1846 s.config.log.Debugf("attaching to pid %d", args.ProcessID) 1847 var err error 1848 func() { 1849 s.mu.Lock() 1850 defer s.mu.Unlock() // Make sure to unlock in case of panic that will become internal error 1851 s.debugger, err = debugger.New(&s.config.Debugger, nil) 1852 }() 1853 if err != nil { 1854 s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", err.Error()) 1855 return 1856 } 1857 // Give the user an option to terminate debuggee when client disconnects (default is to leave it) 1858 s.send(&dap.CapabilitiesEvent{Event: *newEvent("capabilities"), Body: dap.CapabilitiesEventBody{Capabilities: dap.Capabilities{SupportTerminateDebuggee: true}}}) 1859 case "remote": 1860 if s.debugger == nil { 1861 s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", "no debugger found") 1862 return 1863 } 1864 s.config.log.Debug("debugger already started") 1865 // Halt for configuration sequence. onConfigurationDone will restart 1866 // execution if user requested !stopOnEntry. 1867 s.changeStateMu.Lock() 1868 defer s.changeStateMu.Unlock() 1869 if _, err := s.halt(); err != nil { 1870 s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", err.Error()) 1871 return 1872 } 1873 // Enable StepBack controls on supported backends 1874 if s.config.Debugger.Backend == "rr" { 1875 s.send(&dap.CapabilitiesEvent{Event: *newEvent("capabilities"), Body: dap.CapabilitiesEventBody{Capabilities: dap.Capabilities{SupportsStepBack: true}}}) 1876 } 1877 // Customize termination options for debugger and debuggee 1878 if s.config.AcceptMulti { 1879 // User can stop debugger with process or leave it running 1880 s.send(&dap.CapabilitiesEvent{Event: *newEvent("capabilities"), Body: dap.CapabilitiesEventBody{Capabilities: dap.Capabilities{SupportTerminateDebuggee: true}}}) 1881 // TODO(polina): support SupportSuspendDebuggee when available 1882 } else if s.config.Debugger.AttachPid > 0 { 1883 // User can stop debugger with process or leave the process running 1884 s.send(&dap.CapabilitiesEvent{Event: *newEvent("capabilities"), Body: dap.CapabilitiesEventBody{Capabilities: dap.Capabilities{SupportTerminateDebuggee: true}}}) 1885 } // else program was launched and the only option will be to stop both 1886 default: 1887 s.sendShowUserErrorResponse(request.Request, FailedToAttach, "Failed to attach", 1888 fmt.Sprintf("invalid debug configuration - unsupported 'mode' attribute %q", args.Mode)) 1889 return 1890 } 1891 1892 s.setLaunchAttachArgs(args.LaunchAttachCommonConfig) 1893 1894 // Notify the client that the debugger is ready to start accepting 1895 // configuration requests for setting breakpoints, etc. The client 1896 // will end the configuration sequence with 'configurationDone'. 1897 s.send(&dap.InitializedEvent{Event: *newEvent("initialized")}) 1898 s.send(&dap.AttachResponse{Response: *newResponse(request.Request)}) 1899 } 1900 1901 // onNextRequest handles 'next' request. 1902 // This is a mandatory request to support. 1903 func (s *Session) onNextRequest(request *dap.NextRequest, allowNextStateChange chan struct{}) { 1904 s.sendStepResponse(request.Arguments.ThreadId, &dap.NextResponse{Response: *newResponse(request.Request)}) 1905 s.stepUntilStopAndNotify(api.Next, request.Arguments.ThreadId, request.Arguments.Granularity, allowNextStateChange) 1906 } 1907 1908 // onStepInRequest handles 'stepIn' request 1909 // This is a mandatory request to support. 1910 func (s *Session) onStepInRequest(request *dap.StepInRequest, allowNextStateChange chan struct{}) { 1911 s.sendStepResponse(request.Arguments.ThreadId, &dap.StepInResponse{Response: *newResponse(request.Request)}) 1912 s.stepUntilStopAndNotify(api.Step, request.Arguments.ThreadId, request.Arguments.Granularity, allowNextStateChange) 1913 } 1914 1915 // onStepOutRequest handles 'stepOut' request 1916 // This is a mandatory request to support. 1917 func (s *Session) onStepOutRequest(request *dap.StepOutRequest, allowNextStateChange chan struct{}) { 1918 s.sendStepResponse(request.Arguments.ThreadId, &dap.StepOutResponse{Response: *newResponse(request.Request)}) 1919 s.stepUntilStopAndNotify(api.StepOut, request.Arguments.ThreadId, request.Arguments.Granularity, allowNextStateChange) 1920 } 1921 1922 func (s *Session) sendStepResponse(threadId int, message dap.Message) { 1923 // All of the threads will be continued by this request, so we need to send 1924 // a continued event so the UI can properly reflect the current state. 1925 s.send(&dap.ContinuedEvent{ 1926 Event: *newEvent("continued"), 1927 Body: dap.ContinuedEventBody{ 1928 ThreadId: threadId, 1929 AllThreadsContinued: true, 1930 }, 1931 }) 1932 s.send(message) 1933 } 1934 1935 func stoppedGoroutineID(state *api.DebuggerState) (id int64) { 1936 if state.SelectedGoroutine != nil { 1937 id = state.SelectedGoroutine.ID 1938 } else if state.CurrentThread != nil { 1939 id = state.CurrentThread.GoroutineID 1940 } 1941 return id 1942 } 1943 1944 // stoppedOnBreakpointGoroutineID gets the goroutine id of the first goroutine 1945 // that is stopped on a real breakpoint, starting with the selected goroutine. 1946 func (s *Session) stoppedOnBreakpointGoroutineID(state *api.DebuggerState) (int64, *api.Breakpoint) { 1947 // Use the first goroutine that is stopped on a breakpoint. 1948 gs := s.stoppedGs(state) 1949 if len(gs) == 0 { 1950 return 0, nil 1951 } 1952 goid := gs[0] 1953 if goid == 0 { 1954 return goid, state.CurrentThread.Breakpoint 1955 } 1956 g, _ := s.debugger.FindGoroutine(goid) 1957 if g == nil || g.Thread == nil { 1958 return goid, nil 1959 } 1960 bp := g.Thread.Breakpoint() 1961 if bp == nil || bp.Breakpoint == nil || bp.Breakpoint.Logical == nil { 1962 return goid, nil 1963 } 1964 abp := api.ConvertLogicalBreakpoint(bp.Breakpoint.Logical) 1965 api.ConvertPhysicalBreakpoints(abp, bp.Breakpoint.Logical, []int{0}, []*proc.Breakpoint{bp.Breakpoint}) 1966 return goid, abp 1967 } 1968 1969 // stepUntilStopAndNotify is a wrapper around runUntilStopAndNotify that 1970 // first switches selected goroutine. allowNextStateChange is 1971 // a channel that will be closed to signal that an 1972 // asynchronous command has completed setup or was interrupted 1973 // due to an error, so the server is ready to receive new requests. 1974 func (s *Session) stepUntilStopAndNotify(command string, threadId int, granularity dap.SteppingGranularity, allowNextStateChange chan struct{}) { 1975 defer closeIfOpen(allowNextStateChange) 1976 _, err := s.debugger.Command(&api.DebuggerCommand{Name: api.SwitchGoroutine, GoroutineID: int64(threadId)}, nil) 1977 if err != nil { 1978 s.config.log.Errorf("Error switching goroutines while stepping: %v", err) 1979 // If we encounter an error, we will have to send a stopped event 1980 // since we already sent the step response. 1981 stopped := &dap.StoppedEvent{Event: *newEvent("stopped")} 1982 stopped.Body.AllThreadsStopped = true 1983 if state, err := s.debugger.State(false); err != nil { 1984 s.config.log.Errorf("Error retrieving state: %e", err) 1985 } else { 1986 stopped.Body.ThreadId = int(stoppedGoroutineID(state)) 1987 } 1988 stopped.Body.Reason = "error" 1989 stopped.Body.Text = err.Error() 1990 s.send(stopped) 1991 return 1992 } 1993 1994 if granularity == "instruction" { 1995 switch command { 1996 case api.ReverseNext: 1997 command = api.ReverseStepInstruction 1998 default: 1999 // TODO(suzmue): consider differentiating between next, step in, and step out. 2000 // For example, next could step over call requests. 2001 command = api.StepInstruction 2002 } 2003 } 2004 s.runUntilStopAndNotify(command, allowNextStateChange) 2005 } 2006 2007 // onPauseRequest handles 'pause' request. 2008 // This is a mandatory request to support. 2009 func (s *Session) onPauseRequest(request *dap.PauseRequest) { 2010 s.changeStateMu.Lock() 2011 defer s.changeStateMu.Unlock() 2012 s.setHaltRequested(true) 2013 _, err := s.halt() 2014 if err != nil { 2015 s.sendErrorResponse(request.Request, UnableToHalt, "Unable to halt execution", err.Error()) 2016 return 2017 } 2018 s.send(&dap.PauseResponse{Response: *newResponse(request.Request)}) 2019 // No need to send any event here. 2020 // If we received this request while stopped, there already was an event for the stop. 2021 // If we received this while running, then doCommand will unblock and trigger the right 2022 // event, using debugger.StopReason because manual stop reason always wins even if we 2023 // simultaneously receive a manual stop request and hit a breakpoint. 2024 } 2025 2026 // stackFrame represents the index of a frame within 2027 // the context of a stack of a specific goroutine. 2028 type stackFrame struct { 2029 goroutineID int 2030 frameIndex int 2031 } 2032 2033 // onStackTraceRequest handles ‘stackTrace’ requests. 2034 // This is a mandatory request to support. 2035 // As per DAP spec, this request only gets triggered as a follow-up 2036 // to a successful threads request as part of the "request waterfall". 2037 func (s *Session) onStackTraceRequest(request *dap.StackTraceRequest) { 2038 if s.debugger == nil { 2039 s.sendErrorResponse(request.Request, UnableToProduceStackTrace, "Unable to produce stack trace", "debugger is nil") 2040 return 2041 } 2042 2043 goroutineID := request.Arguments.ThreadId 2044 start := request.Arguments.StartFrame 2045 if start < 0 { 2046 start = 0 2047 } 2048 levels := s.args.StackTraceDepth 2049 if request.Arguments.Levels > 0 { 2050 levels = request.Arguments.Levels 2051 } 2052 2053 // Since the backend doesn't support paging, we load all frames up to 2054 // the requested depth and then slice them here per 2055 // `supportsDelayedStackTraceLoading` capability. 2056 frames, err := s.debugger.Stacktrace(int64(goroutineID), start+levels-1, 0) 2057 if err != nil { 2058 s.sendErrorResponse(request.Request, UnableToProduceStackTrace, "Unable to produce stack trace", err.Error()) 2059 return 2060 } 2061 2062 // Determine if the goroutine is a system goroutine. 2063 isSystemGoroutine := true 2064 if g, _ := s.debugger.FindGoroutine(int64(goroutineID)); g != nil { 2065 isSystemGoroutine = g.System(s.debugger.Target()) 2066 } 2067 2068 stackFrames := []dap.StackFrame{} // initialize to empty, since nil is not an accepted response. 2069 for i := 0; i < levels && i+start < len(frames); i++ { 2070 frame := frames[start+i] 2071 loc := &frame.Call 2072 uniqueStackFrameID := s.stackFrameHandles.create(stackFrame{goroutineID, start + i}) 2073 stackFrame := dap.StackFrame{Id: uniqueStackFrameID, Line: loc.Line, Name: fnName(loc), InstructionPointerReference: fmt.Sprintf("%#x", loc.PC)} 2074 if loc.File != "<autogenerated>" { 2075 clientPath := s.toClientPath(loc.File) 2076 stackFrame.Source = &dap.Source{Name: filepath.Base(clientPath), Path: clientPath} 2077 } 2078 stackFrame.Column = 0 2079 2080 packageName := fnPackageName(loc) 2081 if !isSystemGoroutine && packageName == "runtime" { 2082 stackFrame.PresentationHint = "subtle" 2083 } 2084 stackFrames = append(stackFrames, stackFrame) 2085 } 2086 2087 totalFrames := len(frames) 2088 if len(frames) >= start+levels && !frames[len(frames)-1].Bottom { 2089 // We don't know the exact number of available stack frames, so 2090 // add an arbitrary number so the client knows to request additional 2091 // frames. 2092 totalFrames += s.args.StackTraceDepth 2093 } 2094 response := &dap.StackTraceResponse{ 2095 Response: *newResponse(request.Request), 2096 Body: dap.StackTraceResponseBody{StackFrames: stackFrames, TotalFrames: totalFrames}, 2097 } 2098 s.send(response) 2099 } 2100 2101 // onScopesRequest handles 'scopes' requests. 2102 // This is a mandatory request to support. 2103 // It is automatically sent as part of the threads > stacktrace > scopes > variables 2104 // "waterfall" to highlight the topmost frame at stops, after an evaluate request 2105 // for the selected scope or when a user selects different scopes in the UI. 2106 func (s *Session) onScopesRequest(request *dap.ScopesRequest) { 2107 sf, ok := s.stackFrameHandles.get(request.Arguments.FrameId) 2108 if !ok { 2109 s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list locals", fmt.Sprintf("unknown frame id %d", request.Arguments.FrameId)) 2110 return 2111 } 2112 2113 goid := sf.(stackFrame).goroutineID 2114 frame := sf.(stackFrame).frameIndex 2115 2116 // Check if the function is optimized. 2117 fn, err := s.debugger.Function(int64(goid), frame, 0, DefaultLoadConfig) 2118 if fn == nil || err != nil { 2119 var details string 2120 if err != nil { 2121 details = err.Error() 2122 } 2123 s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to find enclosing function", details) 2124 return 2125 } 2126 suffix := "" 2127 if fn.Optimized() { 2128 suffix = " (warning: optimized function)" 2129 } 2130 // Retrieve arguments 2131 args, err := s.debugger.FunctionArguments(int64(goid), frame, 0, DefaultLoadConfig) 2132 if err != nil { 2133 s.sendErrorResponse(request.Request, UnableToListArgs, "Unable to list args", err.Error()) 2134 return 2135 } 2136 2137 // Retrieve local variables 2138 locals, err := s.debugger.LocalVariables(int64(goid), frame, 0, DefaultLoadConfig) 2139 if err != nil { 2140 s.sendErrorResponse(request.Request, UnableToListLocals, "Unable to list locals", err.Error()) 2141 return 2142 } 2143 locScope := &fullyQualifiedVariable{&proc.Variable{Name: fmt.Sprintf("Locals%s", suffix), Children: slicePtrVarToSliceVar(append(args, locals...))}, "", true, 0} 2144 scopeLocals := dap.Scope{Name: locScope.Name, VariablesReference: s.variableHandles.create(locScope)} 2145 scopes := []dap.Scope{scopeLocals} 2146 2147 if s.args.ShowGlobalVariables { 2148 // Limit what global variables we will return to the current package only. 2149 // TODO(polina): This is how vscode-go currently does it to make 2150 // the amount of the returned data manageable. In fact, this is 2151 // considered so expensive even with the package filter, that 2152 // the default for showGlobalVariables was recently flipped to 2153 // not showing. If we delay loading of the globals until the corresponding 2154 // scope is expanded, generating an explicit variable request, 2155 // should we consider making all globals accessible with a scope per package? 2156 // Or users can just rely on watch variables. 2157 currPkg, err := s.debugger.CurrentPackage() 2158 if err != nil { 2159 s.sendErrorResponse(request.Request, UnableToListGlobals, "Unable to list globals", err.Error()) 2160 return 2161 } 2162 currPkgFilter := fmt.Sprintf("^%s\\.", currPkg) 2163 globals, err := s.debugger.PackageVariables(currPkgFilter, DefaultLoadConfig) 2164 if err != nil { 2165 s.sendErrorResponse(request.Request, UnableToListGlobals, "Unable to list globals", err.Error()) 2166 return 2167 } 2168 // Remove package prefix from the fully-qualified variable names. 2169 // We will include the package info once in the name of the scope instead. 2170 for i, g := range globals { 2171 globals[i].Name = strings.TrimPrefix(g.Name, currPkg+".") 2172 } 2173 2174 globScope := &fullyQualifiedVariable{&proc.Variable{ 2175 Name: fmt.Sprintf("Globals (package %s)", currPkg), 2176 Children: slicePtrVarToSliceVar(globals), 2177 }, currPkg, true, 0} 2178 scopeGlobals := dap.Scope{Name: globScope.Name, VariablesReference: s.variableHandles.create(globScope)} 2179 scopes = append(scopes, scopeGlobals) 2180 } 2181 2182 if s.args.ShowRegisters { 2183 // Retrieve registers 2184 regs, err := s.debugger.ScopeRegisters(int64(goid), frame, 0, false) 2185 if err != nil { 2186 s.sendErrorResponse(request.Request, UnableToListRegisters, "Unable to list registers", err.Error()) 2187 return 2188 } 2189 outRegs := api.ConvertRegisters(regs, s.debugger.DwarfRegisterToString, false) 2190 regsVar := make([]proc.Variable, len(outRegs)) 2191 for i, r := range outRegs { 2192 regsVar[i] = proc.Variable{ 2193 Name: r.Name, 2194 Value: constant.MakeString(r.Value), 2195 Kind: reflect.Kind(proc.VariableConstant), 2196 } 2197 } 2198 regsScope := &fullyQualifiedVariable{&proc.Variable{Name: "Registers", Children: regsVar}, "", true, 0} 2199 scopeRegisters := dap.Scope{Name: regsScope.Name, VariablesReference: s.variableHandles.create(regsScope)} 2200 scopes = append(scopes, scopeRegisters) 2201 } 2202 response := &dap.ScopesResponse{ 2203 Response: *newResponse(request.Request), 2204 Body: dap.ScopesResponseBody{Scopes: scopes}, 2205 } 2206 s.send(response) 2207 } 2208 2209 func slicePtrVarToSliceVar(vars []*proc.Variable) []proc.Variable { 2210 r := make([]proc.Variable, len(vars)) 2211 for i := range vars { 2212 r[i] = *vars[i] 2213 } 2214 return r 2215 } 2216 2217 // onVariablesRequest handles 'variables' requests. 2218 // This is a mandatory request to support. 2219 func (s *Session) onVariablesRequest(request *dap.VariablesRequest) { 2220 ref := request.Arguments.VariablesReference 2221 v, ok := s.variableHandles.get(ref) 2222 if !ok { 2223 s.sendErrorResponse(request.Request, UnableToLookupVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", ref)) 2224 return 2225 } 2226 2227 // If there is a filter applied, we will need to create a new variable that includes 2228 // the values actually needed to load. This cannot be done when loading the parent 2229 // node, since it is unknown at that point which children will need to be loaded. 2230 if request.Arguments.Filter == "indexed" { 2231 var err error 2232 v, err = s.maybeLoadResliced(v, request.Arguments.Start, request.Arguments.Count) 2233 if err != nil { 2234 s.sendErrorResponse(request.Request, UnableToLookupVariable, "Unable to lookup variable", err.Error()) 2235 return 2236 } 2237 } 2238 2239 children := []dap.Variable{} // must return empty array, not null, if no children 2240 if request.Arguments.Filter == "named" || request.Arguments.Filter == "" { 2241 named, err := s.metadataToDAPVariables(v) 2242 if err != nil { 2243 s.sendErrorResponse(request.Request, UnableToLookupVariable, "Unable to lookup variable", err.Error()) 2244 return 2245 } 2246 children = append(children, named...) 2247 } 2248 if request.Arguments.Filter == "indexed" || request.Arguments.Filter == "" { 2249 indexed := s.childrenToDAPVariables(v) 2250 children = append(children, indexed...) 2251 } 2252 response := &dap.VariablesResponse{ 2253 Response: *newResponse(request.Request), 2254 Body: dap.VariablesResponseBody{Variables: children}, 2255 } 2256 s.send(response) 2257 } 2258 2259 func (s *Session) maybeLoadResliced(v *fullyQualifiedVariable, start, count int) (*fullyQualifiedVariable, error) { 2260 if start == 0 { 2261 want := count 2262 if v.Kind == reflect.Map { 2263 // For maps, we need to have 2*count children since each key-value pair is two variables. 2264 want *= 2 2265 } 2266 if want == len(v.Children) { 2267 // If we have already loaded the correct children, 2268 // just return the variable. 2269 return v, nil 2270 } 2271 } 2272 indexedLoadConfig := DefaultLoadConfig 2273 indexedLoadConfig.MaxArrayValues = count 2274 newV, err := s.debugger.LoadResliced(v.Variable, start, indexedLoadConfig) 2275 if err != nil { 2276 return nil, err 2277 } 2278 return &fullyQualifiedVariable{newV, v.fullyQualifiedNameOrExpr, false, start}, nil 2279 } 2280 2281 // getIndexedVariableCount returns the number of indexed variables 2282 // for a DAP variable. For maps this may be less than the actual 2283 // number of children returned, since a key-value pair may be split 2284 // into two separate children. 2285 func getIndexedVariableCount(v *proc.Variable) int { 2286 indexedVars := 0 2287 switch v.Kind { 2288 case reflect.Array, reflect.Slice, reflect.Map: 2289 indexedVars = int(v.Len) 2290 } 2291 return indexedVars 2292 } 2293 2294 // childrenToDAPVariables returns the DAP presentation of the referenced variable's children. 2295 func (s *Session) childrenToDAPVariables(v *fullyQualifiedVariable) []dap.Variable { 2296 // TODO(polina): consider convertVariableToString instead of convertVariable 2297 // and avoid unnecessary creation of variable handles when this is called to 2298 // compute evaluate names when this is called from onSetVariableRequest. 2299 children := []dap.Variable{} // must return empty array, not null, if no children 2300 2301 switch v.Kind { 2302 case reflect.Map: 2303 for i := 0; i < len(v.Children); i += 2 { 2304 // A map will have twice as many children as there are key-value elements. 2305 kvIndex := i / 2 2306 // Process children in pairs: even indices are map keys, odd indices are values. 2307 keyv, valv := &v.Children[i], &v.Children[i+1] 2308 keyexpr := fmt.Sprintf("(*(*%q)(%#x))", keyv.TypeString(), keyv.Addr) 2309 valexpr := fmt.Sprintf("%s[%s]", v.fullyQualifiedNameOrExpr, keyexpr) 2310 switch keyv.Kind { 2311 // For value expression, use the key value, not the corresponding expression if the key is a scalar. 2312 case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128, 2313 reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 2314 reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2315 valexpr = fmt.Sprintf("%s[%s]", v.fullyQualifiedNameOrExpr, api.VariableValueAsString(keyv)) 2316 case reflect.String: 2317 if key := constant.StringVal(keyv.Value); keyv.Len == int64(len(key)) { // fully loaded 2318 valexpr = fmt.Sprintf("%s[%q]", v.fullyQualifiedNameOrExpr, key) 2319 } 2320 } 2321 key, keyref := s.convertVariable(keyv, keyexpr) 2322 val, valref := s.convertVariable(valv, valexpr) 2323 keyType := s.getTypeIfSupported(keyv) 2324 valType := s.getTypeIfSupported(valv) 2325 // If key or value or both are scalars, we can use 2326 // a single variable to represent key:value format. 2327 // Otherwise, we must return separate variables for both. 2328 if keyref > 0 && valref > 0 { // Both are not scalars 2329 keyvar := dap.Variable{ 2330 Name: fmt.Sprintf("[key %d]", v.startIndex+kvIndex), 2331 EvaluateName: keyexpr, 2332 Type: keyType, 2333 Value: key, 2334 VariablesReference: keyref, 2335 IndexedVariables: getIndexedVariableCount(keyv), 2336 NamedVariables: getNamedVariableCount(keyv), 2337 } 2338 valvar := dap.Variable{ 2339 Name: fmt.Sprintf("[val %d]", v.startIndex+kvIndex), 2340 EvaluateName: valexpr, 2341 Type: valType, 2342 Value: val, 2343 VariablesReference: valref, 2344 IndexedVariables: getIndexedVariableCount(valv), 2345 NamedVariables: getNamedVariableCount(valv), 2346 } 2347 children = append(children, keyvar, valvar) 2348 } else { // At least one is a scalar 2349 keyValType := valType 2350 if len(keyType) > 0 && len(valType) > 0 { 2351 keyValType = fmt.Sprintf("%s: %s", keyType, valType) 2352 } 2353 kvvar := dap.Variable{ 2354 Name: key, 2355 EvaluateName: valexpr, 2356 Type: keyValType, 2357 Value: val, 2358 } 2359 if keyref != 0 { // key is a type to be expanded 2360 if len(key) > maxMapKeyValueLen { 2361 // Truncate and make unique 2362 kvvar.Name = fmt.Sprintf("%s... @ %#x", key[0:maxMapKeyValueLen], keyv.Addr) 2363 } 2364 kvvar.VariablesReference = keyref 2365 kvvar.IndexedVariables = getIndexedVariableCount(keyv) 2366 kvvar.NamedVariables = getNamedVariableCount(keyv) 2367 } else if valref != 0 { // val is a type to be expanded 2368 kvvar.VariablesReference = valref 2369 kvvar.IndexedVariables = getIndexedVariableCount(valv) 2370 kvvar.NamedVariables = getNamedVariableCount(valv) 2371 } 2372 children = append(children, kvvar) 2373 } 2374 } 2375 case reflect.Slice, reflect.Array: 2376 children = make([]dap.Variable, len(v.Children)) 2377 for i := range v.Children { 2378 idx := v.startIndex + i 2379 cfqname := fmt.Sprintf("%s[%d]", v.fullyQualifiedNameOrExpr, idx) 2380 cvalue, cvarref := s.convertVariable(&v.Children[i], cfqname) 2381 children[i] = dap.Variable{ 2382 Name: fmt.Sprintf("[%d]", idx), 2383 EvaluateName: cfqname, 2384 Type: s.getTypeIfSupported(&v.Children[i]), 2385 Value: cvalue, 2386 VariablesReference: cvarref, 2387 IndexedVariables: getIndexedVariableCount(&v.Children[i]), 2388 NamedVariables: getNamedVariableCount(&v.Children[i]), 2389 } 2390 } 2391 default: 2392 children = make([]dap.Variable, len(v.Children)) 2393 for i := range v.Children { 2394 c := &v.Children[i] 2395 cfqname := fmt.Sprintf("%s.%s", v.fullyQualifiedNameOrExpr, c.Name) 2396 2397 if strings.HasPrefix(c.Name, "~") || strings.HasPrefix(c.Name, ".") { 2398 cfqname = "" 2399 } else if v.isScope && v.fullyQualifiedNameOrExpr == "" { 2400 cfqname = c.Name 2401 } else if v.fullyQualifiedNameOrExpr == "" { 2402 cfqname = "" 2403 } else if v.Kind == reflect.Interface { 2404 cfqname = fmt.Sprintf("%s.(%s)", v.fullyQualifiedNameOrExpr, c.Name) // c is data 2405 } else if v.Kind == reflect.Ptr { 2406 cfqname = fmt.Sprintf("(*%v)", v.fullyQualifiedNameOrExpr) // c is the nameless pointer value 2407 } else if v.Kind == reflect.Complex64 || v.Kind == reflect.Complex128 { 2408 cfqname = "" // complex children are not struct fields and can't be accessed directly 2409 } 2410 cvalue, cvarref := s.convertVariable(c, cfqname) 2411 2412 // Annotate any shadowed variables to "(name)" in order 2413 // to distinguish from non-shadowed variables. 2414 // TODO(suzmue): should we support a special evaluateName syntax that 2415 // can access shadowed variables? 2416 name := c.Name 2417 if c.Flags&proc.VariableShadowed == proc.VariableShadowed { 2418 name = fmt.Sprintf("(%s)", name) 2419 } 2420 2421 if v.isScope && v.Name == "Registers" { 2422 // Align all of the register names. 2423 name = fmt.Sprintf("%6s", strings.ToLower(c.Name)) 2424 // Set the correct evaluate name for the register. 2425 cfqname = fmt.Sprintf("_%s", strings.ToUpper(c.Name)) 2426 // Unquote the value 2427 if ucvalue, err := strconv.Unquote(cvalue); err == nil { 2428 cvalue = ucvalue 2429 } 2430 } 2431 2432 children[i] = dap.Variable{ 2433 Name: name, 2434 EvaluateName: cfqname, 2435 Type: s.getTypeIfSupported(c), 2436 Value: cvalue, 2437 VariablesReference: cvarref, 2438 IndexedVariables: getIndexedVariableCount(c), 2439 NamedVariables: getNamedVariableCount(c), 2440 } 2441 } 2442 } 2443 return children 2444 } 2445 2446 func getNamedVariableCount(v *proc.Variable) int { 2447 namedVars := 0 2448 if v.Kind == reflect.Map && v.Len > 0 { 2449 // len 2450 namedVars += 1 2451 } 2452 if isListOfBytesOrRunes(v) { 2453 // string value of array/slice of bytes and runes. 2454 namedVars += 1 2455 } 2456 2457 return namedVars 2458 } 2459 2460 // metadataToDAPVariables returns the DAP presentation of the referenced variable's metadata. 2461 // These are included as named variables 2462 func (s *Session) metadataToDAPVariables(v *fullyQualifiedVariable) ([]dap.Variable, error) { 2463 children := []dap.Variable{} // must return empty array, not null, if no children 2464 2465 if v.Kind == reflect.Map && v.Len > 0 { 2466 children = append(children, dap.Variable{ 2467 Name: "len()", 2468 Value: fmt.Sprintf("%d", v.Len), 2469 Type: "int", 2470 EvaluateName: fmt.Sprintf("len(%s)", v.fullyQualifiedNameOrExpr), 2471 }) 2472 } 2473 2474 if isListOfBytesOrRunes(v.Variable) { 2475 // Return the string value of []byte or []rune. 2476 typeName := api.PrettyTypeName(v.DwarfType) 2477 loadExpr := fmt.Sprintf("string(*(*%q)(%#x))", typeName, v.Addr) 2478 2479 s.config.log.Debugf("loading %s (type %s) with %s", v.fullyQualifiedNameOrExpr, typeName, loadExpr) 2480 // We know that this is an array/slice of Uint8 or Int32, so we will load up to MaxStringLen. 2481 config := DefaultLoadConfig 2482 config.MaxArrayValues = config.MaxStringLen 2483 vLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, loadExpr, config) 2484 if err == nil { 2485 val := s.convertVariableToString(vLoaded) 2486 // TODO(suzmue): Add evaluate name. Using string(name) will not get the same result because the 2487 // MaxArrayValues is not auto adjusted in evaluate requests like MaxStringLen is adjusted. 2488 children = append(children, dap.Variable{ 2489 Name: "string()", 2490 Value: val, 2491 Type: "string", 2492 }) 2493 } else { 2494 s.config.log.Debugf("failed to load %q: %v", v.fullyQualifiedNameOrExpr, err) 2495 } 2496 } 2497 return children, nil 2498 } 2499 2500 func isListOfBytesOrRunes(v *proc.Variable) bool { 2501 if len(v.Children) > 0 && (v.Kind == reflect.Array || v.Kind == reflect.Slice) { 2502 childKind := v.Children[0].RealType.Common().ReflectKind 2503 return childKind == reflect.Uint8 || childKind == reflect.Int32 2504 } 2505 return false 2506 } 2507 2508 func (s *Session) getTypeIfSupported(v *proc.Variable) string { 2509 if !s.clientCapabilities.supportsVariableType { 2510 return "" 2511 } 2512 return v.TypeString() 2513 } 2514 2515 // convertVariable converts proc.Variable to dap.Variable value and reference 2516 // while keeping track of the full qualified name or load expression. 2517 // Variable reference is used to keep track of the children associated with each 2518 // variable. It is shared with the host via scopes or evaluate response and is an index 2519 // into the s.variableHandles map, used to look up variables and their children on 2520 // subsequent variables requests. A positive reference signals the host that another 2521 // variables request can be issued to get the elements of the compound variable. As a 2522 // custom, a zero reference, reminiscent of a zero pointer, is used to indicate that 2523 // a scalar variable cannot be "dereferenced" to get its elements (as there are none). 2524 func (s *Session) convertVariable(v *proc.Variable, qualifiedNameOrExpr string) (value string, variablesReference int) { 2525 return s.convertVariableWithOpts(v, qualifiedNameOrExpr, 0) 2526 } 2527 2528 func (s *Session) convertVariableToString(v *proc.Variable) string { 2529 val, _ := s.convertVariableWithOpts(v, "", skipRef) 2530 return val 2531 } 2532 2533 const ( 2534 // Limit the length of a string representation of a compound or reference type variable. 2535 maxVarValueLen = 1 << 8 // 256 2536 // Limit the length of an inlined map key. 2537 maxMapKeyValueLen = 64 2538 ) 2539 2540 // Flags for convertVariableWithOpts option. 2541 type convertVariableFlags uint8 2542 2543 const ( 2544 skipRef convertVariableFlags = 1 << iota 2545 showFullValue 2546 ) 2547 2548 // convertVariableWithOpts allows to skip reference generation in case all we need is 2549 // a string representation of the variable. When the variable is a compound or reference 2550 // type variable and its full string representation can be larger than defaultMaxValueLen, 2551 // this returns a truncated value unless showFull option flag is set. 2552 func (s *Session) convertVariableWithOpts(v *proc.Variable, qualifiedNameOrExpr string, opts convertVariableFlags) (value string, variablesReference int) { 2553 canHaveRef := false 2554 maybeCreateVariableHandle := func(v *proc.Variable) int { 2555 canHaveRef = true 2556 if opts&skipRef != 0 { 2557 return 0 2558 } 2559 return s.variableHandles.create(&fullyQualifiedVariable{v, qualifiedNameOrExpr, false /*not a scope*/, 0}) 2560 } 2561 value = api.ConvertVar(v).SinglelineString() 2562 if v.Unreadable != nil { 2563 return value, 0 2564 } 2565 2566 // Some of the types might be fully or partially not loaded based on LoadConfig. 2567 // Those that are fully missing (e.g. due to hitting MaxVariableRecurse), can be reloaded in place. 2568 var reloadVariable = func(v *proc.Variable, qualifiedNameOrExpr string) (value string) { 2569 // We might be loading variables from the frame that's not topmost, so use 2570 // frame-independent address-based expression, not fully-qualified name as per 2571 // https://github.com/go-delve/delve/blob/master/Documentation/api/ClientHowto.md#looking-into-variables. 2572 // TODO(polina): Get *proc.Variable object from debugger instead. Export a function to set v.loaded to false 2573 // and call v.loadValue gain with a different load config. It's more efficient, and it's guaranteed to keep 2574 // working with generics. 2575 value = api.ConvertVar(v).SinglelineString() 2576 typeName := api.PrettyTypeName(v.DwarfType) 2577 loadExpr := fmt.Sprintf("*(*%q)(%#x)", typeName, v.Addr) 2578 s.config.log.Debugf("loading %s (type %s) with %s", qualifiedNameOrExpr, typeName, loadExpr) 2579 // Make sure we can load the pointers directly, not by updating just the child 2580 // This is not really necessary now because users have no way of setting FollowPointers to false. 2581 config := DefaultLoadConfig 2582 config.FollowPointers = true 2583 vLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, loadExpr, config) 2584 if err != nil { 2585 value += fmt.Sprintf(" - FAILED TO LOAD: %s", err) 2586 } else { 2587 v.Children = vLoaded.Children 2588 v.Value = vLoaded.Value 2589 value = api.ConvertVar(v).SinglelineString() 2590 } 2591 return value 2592 } 2593 2594 switch v.Kind { 2595 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 2596 n, _ := strconv.ParseUint(api.ExtractIntValue(api.ConvertVar(v).Value), 10, 64) 2597 value = fmt.Sprintf("%s = %#x", value, n) 2598 case reflect.UnsafePointer: 2599 // Skip child reference 2600 case reflect.Ptr: 2601 if v.DwarfType != nil && len(v.Children) > 0 && v.Children[0].Addr != 0 && v.Children[0].Kind != reflect.Invalid { 2602 if v.Children[0].OnlyAddr { // Not loaded 2603 if v.Addr == 0 { 2604 // This is equivalent to the following with the cli: 2605 // (dlv) p &a7 2606 // (**main.FooBar)(0xc0000a3918) 2607 // 2608 // TODO(polina): what is more appropriate? 2609 // Option 1: leave it unloaded because it is a special case 2610 // Option 2: load it, but then we have to load the child, not the parent, unlike all others 2611 // TODO(polina): see if reloadVariable can be reused here 2612 cTypeName := api.PrettyTypeName(v.Children[0].DwarfType) 2613 cLoadExpr := fmt.Sprintf("*(*%q)(%#x)", cTypeName, v.Children[0].Addr) 2614 s.config.log.Debugf("loading *(%s) (type %s) with %s", qualifiedNameOrExpr, cTypeName, cLoadExpr) 2615 cLoaded, err := s.debugger.EvalVariableInScope(-1, 0, 0, cLoadExpr, DefaultLoadConfig) 2616 if err != nil { 2617 value += fmt.Sprintf(" - FAILED TO LOAD: %s", err) 2618 } else { 2619 cLoaded.Name = v.Children[0].Name // otherwise, this will be the pointer expression 2620 v.Children = []proc.Variable{*cLoaded} 2621 value = api.ConvertVar(v).SinglelineString() 2622 } 2623 } else { 2624 value = reloadVariable(v, qualifiedNameOrExpr) 2625 } 2626 } 2627 if !v.Children[0].OnlyAddr { 2628 variablesReference = maybeCreateVariableHandle(v) 2629 } 2630 } 2631 case reflect.Slice, reflect.Array: 2632 if v.Len > int64(len(v.Children)) { // Not fully loaded 2633 if v.Base != 0 && len(v.Children) == 0 { // Fully missing 2634 value = reloadVariable(v, qualifiedNameOrExpr) 2635 } else if !s.clientCapabilities.supportsVariablePaging { 2636 value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children), v.Len) + value 2637 } 2638 } 2639 if v.Base != 0 && len(v.Children) > 0 { 2640 variablesReference = maybeCreateVariableHandle(v) 2641 } 2642 case reflect.Map: 2643 if v.Len > int64(len(v.Children)/2) { // Not fully loaded 2644 if len(v.Children) == 0 { // Fully missing 2645 value = reloadVariable(v, qualifiedNameOrExpr) 2646 } else if !s.clientCapabilities.supportsVariablePaging { 2647 value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children)/2, v.Len) + value 2648 } 2649 } 2650 if v.Base != 0 && len(v.Children) > 0 { 2651 variablesReference = maybeCreateVariableHandle(v) 2652 } 2653 case reflect.String: 2654 // TODO(polina): implement auto-loading here. 2655 case reflect.Interface: 2656 if v.Addr != 0 && len(v.Children) > 0 && v.Children[0].Kind != reflect.Invalid && v.Children[0].Addr != 0 { 2657 if v.Children[0].OnlyAddr { // Not loaded 2658 value = reloadVariable(v, qualifiedNameOrExpr) 2659 } 2660 if !v.Children[0].OnlyAddr { 2661 variablesReference = maybeCreateVariableHandle(v) 2662 } 2663 } 2664 case reflect.Struct: 2665 if v.Len > int64(len(v.Children)) { // Not fully loaded 2666 if len(v.Children) == 0 { // Fully missing 2667 value = reloadVariable(v, qualifiedNameOrExpr) 2668 } else { // Partially missing (TODO) 2669 value = fmt.Sprintf("(loaded %d/%d) ", len(v.Children), v.Len) + value 2670 } 2671 } 2672 if len(v.Children) > 0 { 2673 variablesReference = maybeCreateVariableHandle(v) 2674 } 2675 case reflect.Complex64, reflect.Complex128: 2676 v.Children = make([]proc.Variable, 2) 2677 v.Children[0].Name = "real" 2678 v.Children[0].Value = constant.Real(v.Value) 2679 v.Children[1].Name = "imaginary" 2680 v.Children[1].Value = constant.Imag(v.Value) 2681 if v.Kind == reflect.Complex64 { 2682 v.Children[0].Kind = reflect.Float32 2683 v.Children[1].Kind = reflect.Float32 2684 } else { 2685 v.Children[0].Kind = reflect.Float64 2686 v.Children[1].Kind = reflect.Float64 2687 } 2688 fallthrough 2689 default: // Complex, Scalar, Chan, Func 2690 if len(v.Children) > 0 { 2691 variablesReference = maybeCreateVariableHandle(v) 2692 } 2693 } 2694 2695 // By default, only values of variables that have children can be truncated. 2696 // If showFullValue is set, then all value strings are not truncated. 2697 canTruncateValue := showFullValue&opts == 0 2698 if len(value) > maxVarValueLen && canTruncateValue && canHaveRef { 2699 value = value[:maxVarValueLen] + "..." 2700 } 2701 return value, variablesReference 2702 } 2703 2704 // onEvaluateRequest handles 'evaluate' requests. 2705 // This is a mandatory request to support. 2706 // Support the following expressions: 2707 // 2708 // - {expression} - evaluates the expression and returns the result as a variable 2709 // - call {function} - injects a function call and returns the result as a variable 2710 // - config {expression} - updates configuration parameters 2711 // 2712 // TODO(polina): users have complained about having to click to expand multi-level 2713 // variables, so consider also adding the following: 2714 // 2715 // - print {expression} - return the result as a string like from dlv cli 2716 func (s *Session) onEvaluateRequest(request *dap.EvaluateRequest) { 2717 showErrorToUser := request.Arguments.Context != "watch" && request.Arguments.Context != "repl" && request.Arguments.Context != "hover" 2718 if s.debugger == nil { 2719 s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", "debugger is nil", showErrorToUser) 2720 return 2721 } 2722 2723 // Default to the topmost stack frame of the current goroutine in case 2724 // no frame is specified (e.g. when stopped on entry or no call stack frame is expanded) 2725 goid, frame := -1, 0 2726 if sf, ok := s.stackFrameHandles.get(request.Arguments.FrameId); ok { 2727 goid = sf.(stackFrame).goroutineID 2728 frame = sf.(stackFrame).frameIndex 2729 } 2730 2731 response := &dap.EvaluateResponse{Response: *newResponse(request.Request)} 2732 expr := request.Arguments.Expression 2733 2734 if isConfig, err := regexp.MatchString(`^\s*dlv\s+\S+`, expr); err == nil && isConfig { // dlv {command} 2735 expr := strings.Replace(expr, "dlv ", "", 1) 2736 result, err := s.delveCmd(goid, frame, expr) 2737 if err != nil { 2738 s.sendErrorResponseWithOpts(request.Request, UnableToRunDlvCommand, "Unable to run dlv command", err.Error(), showErrorToUser) 2739 return 2740 } 2741 response.Body = dap.EvaluateResponseBody{ 2742 Result: result, 2743 } 2744 } else if isCall, err := regexp.MatchString(`^\s*call\s+\S+`, expr); err == nil && isCall { // call {expression} 2745 expr := strings.Replace(expr, "call ", "", 1) 2746 _, retVars, err := s.doCall(goid, frame, expr) 2747 if err != nil { 2748 s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", err.Error(), showErrorToUser) 2749 return 2750 } 2751 // The call completed and we can reply with its return values (if any) 2752 if len(retVars) > 0 { 2753 // Package one or more return values in a single scope-like nameless variable 2754 // that preserves their names. 2755 retVarsAsVar := &proc.Variable{Children: slicePtrVarToSliceVar(retVars)} 2756 // As a shortcut also express the return values as a single string. 2757 retVarsAsStr := "" 2758 for _, v := range retVars { 2759 retVarsAsStr += s.convertVariableToString(v) + ", " 2760 } 2761 response.Body = dap.EvaluateResponseBody{ 2762 Result: strings.TrimRight(retVarsAsStr, ", "), 2763 VariablesReference: s.variableHandles.create(&fullyQualifiedVariable{retVarsAsVar, "", false /*not a scope*/, 0}), 2764 } 2765 } 2766 } else { // {expression} 2767 exprVar, err := s.debugger.EvalVariableInScope(int64(goid), frame, 0, expr, DefaultLoadConfig) 2768 if err != nil { 2769 s.sendErrorResponseWithOpts(request.Request, UnableToEvaluateExpression, "Unable to evaluate expression", err.Error(), showErrorToUser) 2770 return 2771 } 2772 2773 ctxt := request.Arguments.Context 2774 switch ctxt { 2775 case "repl", "variables", "hover", "clipboard": 2776 if exprVar.Kind == reflect.String { 2777 if strVal := constant.StringVal(exprVar.Value); exprVar.Len > int64(len(strVal)) { 2778 // Reload the string value with a bigger limit. 2779 loadCfg := DefaultLoadConfig 2780 loadCfg.MaxStringLen = maxSingleStringLen 2781 if v, err := s.debugger.EvalVariableInScope(int64(goid), frame, 0, request.Arguments.Expression, loadCfg); err != nil { 2782 s.config.log.Debugf("Failed to load more for %v: %v", request.Arguments.Expression, err) 2783 } else { 2784 exprVar = v 2785 } 2786 } 2787 } 2788 } 2789 var opts convertVariableFlags 2790 // Send the full value when the context is "clipboard" or "variables" since 2791 // these contexts are used to copy the value. 2792 if ctxt == "clipboard" || ctxt == "variables" { 2793 opts |= showFullValue 2794 } 2795 exprVal, exprRef := s.convertVariableWithOpts(exprVar, fmt.Sprintf("(%s)", request.Arguments.Expression), opts) 2796 response.Body = dap.EvaluateResponseBody{Result: exprVal, Type: s.getTypeIfSupported(exprVar), VariablesReference: exprRef, IndexedVariables: getIndexedVariableCount(exprVar), NamedVariables: getNamedVariableCount(exprVar)} 2797 } 2798 s.send(response) 2799 } 2800 2801 func (s *Session) doCall(goid, frame int, expr string) (*api.DebuggerState, []*proc.Variable, error) { 2802 // This call might be evaluated in the context of the frame that is not topmost 2803 // if the editor is set to view the variables for one of the parent frames. 2804 // If the call expression refers to any of these variables, unlike regular 2805 // expressions, it will evaluate them in the context of the topmost frame, 2806 // and the user will get an unexpected result or an unexpected symbol error. 2807 // We prevent this but disallowing any frames other than topmost. 2808 if frame > 0 { 2809 return nil, nil, fmt.Errorf("call is only supported with topmost stack frame") 2810 } 2811 stateBeforeCall, err := s.debugger.State( /*nowait*/ true) 2812 if err != nil { 2813 return nil, nil, err 2814 } 2815 // The return values of injected function calls are volatile. 2816 // Load as much useful data as possible. 2817 // TODO: investigate whether we need to increase other limits. For example, 2818 // the return value is a pointer to a temporary object, which can become 2819 // invalid by other injected function calls. Do we care about such use cases? 2820 loadCfg := DefaultLoadConfig 2821 loadCfg.MaxStringLen = maxStringLenInCallRetVars 2822 2823 // TODO(polina): since call will resume execution of all goroutines, 2824 // we should do this asynchronously and send a continued event to the 2825 // editor, followed by a stop event when the call completes. 2826 state, err := s.debugger.Command(&api.DebuggerCommand{ 2827 Name: api.Call, 2828 ReturnInfoLoadConfig: api.LoadConfigFromProc(&loadCfg), 2829 Expr: expr, 2830 UnsafeCall: false, 2831 GoroutineID: int64(goid), 2832 }, nil) 2833 if processExited(state, err) { 2834 s.preTerminatedWG.Wait() 2835 e := &dap.TerminatedEvent{Event: *newEvent("terminated")} 2836 s.send(e) 2837 return nil, nil, errors.New("terminated") 2838 } 2839 if err != nil { 2840 return nil, nil, err 2841 } 2842 2843 // After the call is done, the goroutine where we injected the call should 2844 // return to the original stopped line with return values. However, 2845 // it is not guaranteed to be selected due to the possibility of the 2846 // of simultaneous breakpoints. Therefore, we check all threads. 2847 var retVars []*proc.Variable 2848 found := false 2849 for _, t := range state.Threads { 2850 if t.GoroutineID == stateBeforeCall.SelectedGoroutine.ID && 2851 t.Line == stateBeforeCall.SelectedGoroutine.CurrentLoc.Line && t.CallReturn { 2852 found = true 2853 // The call completed. Get the return values. 2854 retVars, err = s.debugger.FindThreadReturnValues(t.ID, loadCfg) 2855 if err != nil { 2856 return nil, nil, err 2857 } 2858 break 2859 } 2860 } 2861 // Normal function calls expect return values, but call commands 2862 // used for variable assignments do not return a value when they succeed. 2863 // In go '=' is not an operator. Check if go/parser complains. 2864 // If the above Call command passed but the expression is not a valid 2865 // go expression, we just handled a variable assignment request. 2866 isAssignment := false 2867 if _, err := parser.ParseExpr(expr); err != nil { 2868 isAssignment = true 2869 } 2870 2871 // note: as described in https://github.com/golang/go/issues/25578, function call injection 2872 // causes to resume the entire Go process. Due to this limitation, there is no guarantee 2873 // that the process is in the same state even after the injected call returns normally 2874 // without any surprises such as breakpoints or panic. To handle this correctly we need 2875 // to reset all the handles (both variables and stack frames). 2876 // 2877 // We considered sending a stopped event after each call unconditionally, but a stopped 2878 // event can be expensive and can interact badly with the client-side optimization 2879 // to refresh information. For example, VS Code reissues scopes/evaluate (for watch) after 2880 // completing a setVariable or evaluate request for repl context. Thus, for now, we 2881 // do not trigger a stopped event and hope editors to refetch the updated state as soon 2882 // as the user resumes debugging. 2883 2884 if !found || !isAssignment && retVars == nil { 2885 // The call got interrupted by a stop (e.g. breakpoint in injected 2886 // function call or in another goroutine). 2887 s.resetHandlesForStoppedEvent() 2888 s.sendStoppedEvent(state) 2889 2890 // TODO(polina): once this is asynchronous, we could wait to reply until the user 2891 // continues, call ends, original stop point is hit and return values are available 2892 // instead of returning an error 'call stopped' here. 2893 return nil, nil, errors.New("call stopped") 2894 } 2895 return state, retVars, nil 2896 } 2897 2898 func (s *Session) sendStoppedEvent(state *api.DebuggerState) { 2899 stopped := &dap.StoppedEvent{Event: *newEvent("stopped")} 2900 stopped.Body.AllThreadsStopped = true 2901 stopped.Body.ThreadId = int(stoppedGoroutineID(state)) 2902 stopped.Body.Reason = s.debugger.StopReason().String() 2903 s.send(stopped) 2904 } 2905 2906 // onTerminateRequest sends a not-yet-implemented error response. 2907 // Capability 'supportsTerminateRequest' is not set in 'initialize' response. 2908 func (s *Session) onTerminateRequest(request *dap.TerminateRequest) { 2909 s.sendNotYetImplementedErrorResponse(request.Request) 2910 } 2911 2912 // onRestartRequest sends a not-yet-implemented error response 2913 // Capability 'supportsRestartRequest' is not set in 'initialize' response. 2914 func (s *Session) onRestartRequest(request *dap.RestartRequest) { 2915 s.sendNotYetImplementedErrorResponse(request.Request) 2916 } 2917 2918 // onStepBackRequest handles 'stepBack' request. 2919 // This is an optional request enabled by capability ‘supportsStepBackRequest’. 2920 func (s *Session) onStepBackRequest(request *dap.StepBackRequest, allowNextStateChange chan struct{}) { 2921 s.sendStepResponse(request.Arguments.ThreadId, &dap.StepBackResponse{Response: *newResponse(request.Request)}) 2922 s.stepUntilStopAndNotify(api.ReverseNext, request.Arguments.ThreadId, request.Arguments.Granularity, allowNextStateChange) 2923 } 2924 2925 // onReverseContinueRequest performs a rewind command call up to the previous 2926 // breakpoint or the start of the process 2927 // This is an optional request enabled by capability ‘supportsStepBackRequest’. 2928 func (s *Session) onReverseContinueRequest(request *dap.ReverseContinueRequest, allowNextStateChange chan struct{}) { 2929 s.send(&dap.ReverseContinueResponse{ 2930 Response: *newResponse(request.Request), 2931 }) 2932 s.runUntilStopAndNotify(api.Rewind, allowNextStateChange) 2933 } 2934 2935 // computeEvaluateName finds the named child, and computes its evaluate name. 2936 func (s *Session) computeEvaluateName(v *fullyQualifiedVariable, cname string) (string, error) { 2937 children := s.childrenToDAPVariables(v) 2938 for _, c := range children { 2939 if c.Name == cname { 2940 if c.EvaluateName != "" { 2941 return c.EvaluateName, nil 2942 } 2943 return "", errors.New("cannot set the variable without evaluate name") 2944 } 2945 } 2946 return "", errors.New("failed to find the named variable") 2947 } 2948 2949 // onSetVariableRequest handles 'setVariable' requests. 2950 func (s *Session) onSetVariableRequest(request *dap.SetVariableRequest) { 2951 arg := request.Arguments 2952 2953 v, ok := s.variableHandles.get(arg.VariablesReference) 2954 if !ok { 2955 s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", fmt.Sprintf("unknown reference %d", arg.VariablesReference)) 2956 return 2957 } 2958 // We need to translate the arg.Name to its evaluateName if the name 2959 // refers to a field or element of a variable. 2960 // https://github.com/microsoft/vscode/issues/120774 2961 evaluateName, err := s.computeEvaluateName(v, arg.Name) 2962 if err != nil { 2963 s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error()) 2964 return 2965 } 2966 2967 // By running EvalVariableInScope, we get the type info of the variable 2968 // that can be accessed with the evaluateName, and ensure the variable we are 2969 // trying to update is valid and accessible from the top most frame & the 2970 // current goroutine. 2971 goid, frame := -1, 0 2972 evaluated, err := s.debugger.EvalVariableInScope(int64(goid), frame, 0, evaluateName, DefaultLoadConfig) 2973 if err != nil { 2974 s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to lookup variable", err.Error()) 2975 return 2976 } 2977 2978 useFnCall := false 2979 switch evaluated.Kind { 2980 case reflect.String: 2981 useFnCall = true 2982 default: 2983 // TODO(hyangah): it's possible to set a non-string variable using (`call i = fn()`) 2984 // and we don't support it through the Set Variable request yet. 2985 // If we want to support it for non-string types, we need to parse arg.Value. 2986 } 2987 2988 if useFnCall { 2989 // TODO(hyangah): function call injection currently allows to assign return values of 2990 // a function call to variables. So, curious users would find set variable 2991 // on string would accept expression like `fn()`. 2992 if state, retVals, err := s.doCall(goid, frame, fmt.Sprintf("%v=%v", evaluateName, arg.Value)); err != nil { 2993 s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error()) 2994 return 2995 } else if retVals != nil { 2996 // The assignment expression isn't supposed to return values, but we got them. 2997 // That indicates something went wrong (e.g. panic). 2998 // TODO: isn't it simpler to do this in s.doCall? 2999 s.resetHandlesForStoppedEvent() 3000 s.sendStoppedEvent(state) 3001 3002 var r []string 3003 for _, v := range retVals { 3004 r = append(r, s.convertVariableToString(v)) 3005 } 3006 msg := "interrupted" 3007 if len(r) > 0 { 3008 msg = "interrupted:" + strings.Join(r, ", ") 3009 } 3010 3011 s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", msg) 3012 return 3013 } 3014 } else { 3015 if err := s.debugger.SetVariableInScope(int64(goid), frame, 0, evaluateName, arg.Value); err != nil { 3016 s.sendErrorResponse(request.Request, UnableToSetVariable, "Unable to set variable", err.Error()) 3017 return 3018 } 3019 } 3020 // * Note on inconsistent state after set variable: 3021 // 3022 // The variable handles may be in inconsistent state - for example, 3023 // let's assume there are two aliased variables pointing to the same 3024 // memory and both are already loaded and cached in the variable handle. 3025 // VSCode tries to locally update the UI when the set variable 3026 // request succeeds, and may issue additional scopes or evaluate requests 3027 // to update the variable/watch sections if necessary. 3028 // 3029 // More complicated situation is when the set variable involves call 3030 // injection - after the injected call is completed, the debuggee can 3031 // be in a completely different state (see the note in doCall) due to 3032 // how the call injection is implemented. Ideally, we need to also refresh 3033 // the stack frames but that is complicated. For now we don't try to actively 3034 // invalidate this state hoping that the editors will refetch the state 3035 // as soon as the user resumes debugging. 3036 3037 response := &dap.SetVariableResponse{Response: *newResponse(request.Request)} 3038 response.Body.Value = arg.Value 3039 // TODO(hyangah): instead of arg.Value, reload the variable and return 3040 // the presentation of the new value. 3041 s.send(response) 3042 } 3043 3044 // onSetExpression sends a not-yet-implemented error response. 3045 // Capability 'supportsSetExpression' is not set 'initialize' response. 3046 func (s *Session) onSetExpressionRequest(request *dap.SetExpressionRequest) { 3047 s.sendNotYetImplementedErrorResponse(request.Request) 3048 } 3049 3050 // onLoadedSourcesRequest sends a not-yet-implemented error response. 3051 // Capability 'supportsLoadedSourcesRequest' is not set 'initialize' response. 3052 func (s *Session) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest) { 3053 s.sendNotYetImplementedErrorResponse(request.Request) 3054 } 3055 3056 // onReadMemoryRequest sends a not-yet-implemented error response. 3057 // Capability 'supportsReadMemoryRequest' is not set 'initialize' response. 3058 func (s *Session) onReadMemoryRequest(request *dap.ReadMemoryRequest) { 3059 s.sendNotYetImplementedErrorResponse(request.Request) 3060 } 3061 3062 var invalidInstruction = dap.DisassembledInstruction{ 3063 Instruction: "invalid instruction", 3064 } 3065 3066 // onDisassembleRequest handles 'disassemble' requests. 3067 // Capability 'supportsDisassembleRequest' is set in 'initialize' response. 3068 func (s *Session) onDisassembleRequest(request *dap.DisassembleRequest) { 3069 // TODO(suzmue): microsoft/vscode#129655 is discussing the difference between 3070 // memory reference and instructionPointerReference, which are currently 3071 // being used interchangeably by vscode. 3072 addr, err := strconv.ParseUint(request.Arguments.MemoryReference, 0, 64) 3073 if err != nil { 3074 s.sendErrorResponse(request.Request, UnableToDisassemble, "Unable to disassemble", err.Error()) 3075 return 3076 } 3077 3078 // If the requested memory address is an invalid location, return all invalid instructions. 3079 // TODO(suzmue): consider adding fake addresses that would allow us to receive out of bounds 3080 // requests that include valid instructions designated by InstructionOffset or InstructionCount. 3081 if addr == 0 || addr == uint64(math.MaxUint64) { 3082 instructions := make([]dap.DisassembledInstruction, request.Arguments.InstructionCount) 3083 for i := range instructions { 3084 instructions[i] = invalidInstruction 3085 instructions[i].Address = request.Arguments.MemoryReference 3086 } 3087 response := &dap.DisassembleResponse{ 3088 Response: *newResponse(request.Request), 3089 Body: dap.DisassembleResponseBody{ 3090 Instructions: instructions, 3091 }, 3092 } 3093 s.send(response) 3094 return 3095 } 3096 3097 start := uint64(addr) 3098 maxInstructionLength := s.debugger.Target().BinInfo().Arch.MaxInstructionLength() 3099 byteOffset := request.Arguments.InstructionOffset * maxInstructionLength 3100 // Adjust the offset to include instructions before the requested address. 3101 if byteOffset < 0 { 3102 start = uint64(int(addr) + byteOffset) 3103 } 3104 // Adjust the number of instructions to include enough instructions after 3105 // the requested address. 3106 count := request.Arguments.InstructionCount 3107 if byteOffset > 0 { 3108 count += byteOffset 3109 } 3110 end := uint64(int(addr) + count*maxInstructionLength) 3111 3112 // Make sure the PCs are lined up with instructions. 3113 start, end = alignPCs(s.debugger.Target().BinInfo(), start, end) 3114 3115 // Disassemble the instructions 3116 procInstructions, err := s.debugger.Disassemble(-1, start, end) 3117 if err != nil { 3118 s.sendErrorResponse(request.Request, UnableToDisassemble, "Unable to disassemble", err.Error()) 3119 return 3120 } 3121 3122 // Find the section of instructions that were requested. 3123 procInstructions, offset, err := findInstructions(procInstructions, addr, request.Arguments.InstructionOffset, request.Arguments.InstructionCount) 3124 if err != nil { 3125 s.sendErrorResponse(request.Request, UnableToDisassemble, "Unable to disassemble", err.Error()) 3126 return 3127 } 3128 3129 // Turn the given range of instructions into dap instructions. 3130 instructions := make([]dap.DisassembledInstruction, request.Arguments.InstructionCount) 3131 lastFile, lastLine := "", -1 3132 for i := range instructions { 3133 // i is not in a valid range, use an address that is just before or after 3134 // the range. This ensures that it can still be parsed as an int. 3135 if i < offset { 3136 // i is not in a valid range. 3137 instructions[i] = invalidInstruction 3138 instructions[i].Address = "0x0" 3139 continue 3140 } 3141 if (i - offset) >= len(procInstructions) { 3142 // i is not in a valid range. 3143 instructions[i] = invalidInstruction 3144 instructions[i].Address = fmt.Sprintf("%#x", uint64(math.MaxUint64)) 3145 continue 3146 3147 } 3148 instruction := api.ConvertAsmInstruction(procInstructions[i-offset], s.debugger.AsmInstructionText(&procInstructions[i-offset], proc.GoFlavour)) 3149 instructions[i] = dap.DisassembledInstruction{ 3150 Address: fmt.Sprintf("%#x", instruction.Loc.PC), 3151 InstructionBytes: fmt.Sprintf("%x", instruction.Bytes), 3152 Instruction: instruction.Text, 3153 } 3154 // Only set the location on the first instruction for a given line. 3155 if instruction.Loc.File != lastFile || instruction.Loc.Line != lastLine { 3156 instructions[i].Location = &dap.Source{Path: instruction.Loc.File} 3157 instructions[i].Line = instruction.Loc.Line 3158 lastFile, lastLine = instruction.Loc.File, instruction.Loc.Line 3159 } 3160 } 3161 3162 response := &dap.DisassembleResponse{ 3163 Response: *newResponse(request.Request), 3164 Body: dap.DisassembleResponseBody{ 3165 Instructions: instructions, 3166 }, 3167 } 3168 s.send(response) 3169 } 3170 3171 func findInstructions(procInstructions []proc.AsmInstruction, addr uint64, instructionOffset, count int) ([]proc.AsmInstruction, int, error) { 3172 ref := sort.Search(len(procInstructions), func(i int) bool { 3173 return procInstructions[i].Loc.PC >= addr 3174 }) 3175 if ref == len(procInstructions) || procInstructions[ref].Loc.PC != uint64(addr) { 3176 return nil, -1, fmt.Errorf("could not find memory reference") 3177 } 3178 // offset is the number of instructions that should appear before the first instruction 3179 // returned by findInstructions. 3180 offset := 0 3181 if ref+instructionOffset < 0 { 3182 offset = -(ref + instructionOffset) 3183 } 3184 // Figure out the index to slice at. 3185 startIdx := ref + instructionOffset 3186 endIdx := ref + instructionOffset + count 3187 if endIdx <= 0 || startIdx >= len(procInstructions) { 3188 return []proc.AsmInstruction{}, 0, nil 3189 } 3190 // Adjust start and end to be inbounds. 3191 if startIdx < 0 { 3192 offset = -startIdx 3193 startIdx = 0 3194 } 3195 if endIdx > len(procInstructions) { 3196 endIdx = len(procInstructions) 3197 } 3198 return procInstructions[startIdx:endIdx], offset, nil 3199 } 3200 3201 func getValidRange(bi *proc.BinaryInfo) (uint64, uint64) { 3202 return bi.Functions[0].Entry, bi.Functions[len(bi.Functions)-1].End 3203 } 3204 3205 func alignPCs(bi *proc.BinaryInfo, start, end uint64) (uint64, uint64) { 3206 // We want to find the function locations position that would enclose 3207 // the range from start to end. 3208 // 3209 // Example: 3210 // 3211 // 0x0000 instruction (func1) 3212 // 0x0004 instruction (func1) 3213 // 0x0008 instruction (func1) 3214 // 0x000c nop 3215 // 0x000e nop 3216 // 0x0000 nop 3217 // 0x0002 nop 3218 // 0x0004 instruction (func2) 3219 // 0x0008 instruction (func2) 3220 // 0x000c instruction (func2) 3221 // 3222 // start values: 3223 // < 0x0000 at func1.Entry = 0x0000 3224 // 0x0000-0x000b at func1.Entry = 0x0000 3225 // 0x000c-0x0003 at func1.End = 0x000c 3226 // 0x0004-0x000f at func2.Entry = 0x0004 3227 // > 0x000f at func2.End = 0x0010 3228 // 3229 // end values: 3230 // < 0x0000 at func1.Entry = 0x0000 3231 // 0x0000-0x000b at func1.End = 0x0000 3232 // 0x000c-0x0003 at func2.Entry = 0x000c 3233 // 0x0004-0x000f at func2.End = 0x0004 3234 // > 0x000f at func2.End = 0x0004 3235 // Handle start values: 3236 fn := bi.PCToFunc(start) 3237 if fn != nil { 3238 // start is in a function. 3239 start = fn.Entry 3240 } else if b, pc := checkOutOfAddressSpace(start, bi); b { 3241 start = pc 3242 } else { 3243 // Otherwise it must come after some function. 3244 i := sort.Search(len(bi.Functions), func(i int) bool { 3245 fn := bi.Functions[len(bi.Functions)-(i+1)] 3246 return start >= fn.End 3247 }) 3248 start = bi.Functions[len(bi.Functions)-(i+1)].Entry 3249 } 3250 3251 // Handle end values: 3252 if fn := bi.PCToFunc(end); fn != nil { 3253 // end is in a function. 3254 end = fn.End 3255 } else if b, pc := checkOutOfAddressSpace(end, bi); b { 3256 end = pc 3257 } else { 3258 // Otherwise it must come before some function. 3259 i := sort.Search(len(bi.Functions), func(i int) bool { 3260 fn := bi.Functions[i] 3261 return end < fn.Entry 3262 }) 3263 end = bi.Functions[i].Entry 3264 const limit = 10 * 1024 3265 if end-start > limit { 3266 end = start + limit 3267 } 3268 } 3269 3270 return start, end 3271 } 3272 3273 func checkOutOfAddressSpace(pc uint64, bi *proc.BinaryInfo) (bool, uint64) { 3274 entry, end := getValidRange(bi) 3275 if pc < entry { 3276 return true, entry 3277 } 3278 if pc >= end { 3279 return true, end 3280 } 3281 return false, pc 3282 } 3283 3284 // onCancelRequest sends a not-yet-implemented error response. 3285 // Capability 'supportsCancelRequest' is not set 'initialize' response. 3286 func (s *Session) onCancelRequest(request *dap.CancelRequest) { 3287 s.sendNotYetImplementedErrorResponse(request.Request) 3288 } 3289 3290 // onExceptionInfoRequest handles 'exceptionInfo' requests. 3291 // Capability 'supportsExceptionInfoRequest' is set in 'initialize' response. 3292 func (s *Session) onExceptionInfoRequest(request *dap.ExceptionInfoRequest) { 3293 goroutineID := int64(request.Arguments.ThreadId) 3294 var body dap.ExceptionInfoResponseBody 3295 // Get the goroutine and the current state. 3296 g, err := s.debugger.FindGoroutine(goroutineID) 3297 if err != nil { 3298 s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", err.Error()) 3299 return 3300 } 3301 if g == nil { 3302 s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", fmt.Sprintf("could not find goroutine %d", goroutineID)) 3303 return 3304 } 3305 var bpState *proc.BreakpointState 3306 if g.Thread != nil { 3307 bpState = g.Thread.Breakpoint() 3308 } 3309 // Check if this goroutine ID is stopped at a breakpoint. 3310 includeStackTrace := true 3311 if bpState != nil && bpState.Breakpoint != nil && bpState.Breakpoint.Logical != nil && (bpState.Breakpoint.Logical.Name == proc.FatalThrow || bpState.Breakpoint.Logical.Name == proc.UnrecoveredPanic) { 3312 switch bpState.Breakpoint.Logical.Name { 3313 case proc.FatalThrow: 3314 body.ExceptionId = "fatal error" 3315 body.Description, err = s.throwReason(goroutineID) 3316 if err != nil { 3317 body.Description = fmt.Sprintf("Error getting throw reason: %s", err.Error()) 3318 // This is not currently working for Go 1.16. 3319 ver := goversion.ParseProducer(s.debugger.TargetGoVersion()) 3320 if ver.Major == 1 && ver.Minor == 16 { 3321 body.Description = "Throw reason unavailable, see https://github.com/golang/go/issues/46425" 3322 } 3323 } 3324 case proc.UnrecoveredPanic: 3325 body.ExceptionId = "panic" 3326 // Attempt to get the value of the panic message. 3327 body.Description, err = s.panicReason(goroutineID) 3328 if err != nil { 3329 body.Description = fmt.Sprintf("Error getting panic message: %s", err.Error()) 3330 } 3331 } 3332 } else { 3333 // If this thread is not stopped on a breakpoint, then a runtime error must have occurred. 3334 // If we do not have any error saved, or if this thread is not current thread, 3335 // return an error. 3336 if s.exceptionErr == nil { 3337 s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", "no runtime error found") 3338 return 3339 } 3340 3341 state, err := s.debugger.State( /*nowait*/ true) 3342 if err != nil { 3343 s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", err.Error()) 3344 return 3345 } 3346 if s.exceptionErr.Error() != "next while nexting" && (state == nil || state.CurrentThread == nil || g.Thread == nil || state.CurrentThread.ID != g.Thread.ThreadID()) { 3347 s.sendErrorResponse(request.Request, UnableToGetExceptionInfo, "Unable to get exception info", fmt.Sprintf("no exception found for goroutine %d", goroutineID)) 3348 return 3349 } 3350 body.ExceptionId = "runtime error" 3351 body.Description = s.exceptionErr.Error() 3352 if body.Description == "bad access" { 3353 body.Description = BetterBadAccessError 3354 } 3355 if body.Description == "next while nexting" { 3356 body.ExceptionId = "invalid command" 3357 body.Description = BetterNextWhileNextingError 3358 includeStackTrace = false 3359 } 3360 } 3361 3362 if includeStackTrace { 3363 body.Details = &dap.ExceptionDetails{} 3364 frames, err := s.stacktrace(goroutineID, g) 3365 if err != nil { 3366 body.Details.StackTrace = fmt.Sprintf("Error getting stack trace: %s", err.Error()) 3367 } else { 3368 body.Details.StackTrace = frames 3369 } 3370 } 3371 response := &dap.ExceptionInfoResponse{ 3372 Response: *newResponse(request.Request), 3373 Body: body, 3374 } 3375 s.send(response) 3376 } 3377 3378 func (s *Session) stacktrace(goroutineID int64, g *proc.G) (string, error) { 3379 frames, err := s.debugger.Stacktrace(goroutineID, s.args.StackTraceDepth, 0) 3380 if err != nil { 3381 return "", err 3382 } 3383 apiFrames, err := s.debugger.ConvertStacktrace(frames, nil) 3384 if err != nil { 3385 return "", err 3386 } 3387 3388 var buf bytes.Buffer 3389 fmt.Fprintln(&buf, "Stack:") 3390 userLoc := g.UserCurrent() 3391 userFuncPkg := fnPackageName(&userLoc) 3392 api.PrintStack(s.toClientPath, &buf, apiFrames, "\t", false, func(s api.Stackframe) bool { 3393 // Include all stack frames if the stack trace is for a system goroutine, 3394 // otherwise, skip runtime stack frames. 3395 if userFuncPkg == "runtime" { 3396 return true 3397 } 3398 return s.Location.Function != nil && !strings.HasPrefix(s.Location.Function.Name(), "runtime.") 3399 }) 3400 return buf.String(), nil 3401 } 3402 3403 func (s *Session) throwReason(goroutineID int64) (string, error) { 3404 return s.getExprString("s", goroutineID, 0) 3405 } 3406 3407 func (s *Session) panicReason(goroutineID int64) (string, error) { 3408 return s.getExprString("(*msgs).arg.(data)", goroutineID, 0) 3409 } 3410 3411 func (s *Session) getExprString(expr string, goroutineID int64, frame int) (string, error) { 3412 exprVar, err := s.debugger.EvalVariableInScope(goroutineID, frame, 0, expr, DefaultLoadConfig) 3413 if err != nil { 3414 return "", err 3415 } 3416 if exprVar.Value == nil { 3417 return "", exprVar.Unreadable 3418 } 3419 return exprVar.Value.String(), nil 3420 } 3421 3422 // sendErrorResponseWithOpts offers configuration options. 3423 // 3424 // showUser - if true, the error will be shown to the user (e.g. via a visible pop-up) 3425 func (s *Session) sendErrorResponseWithOpts(request dap.Request, id int, summary, details string, showUser bool) { 3426 er := &dap.ErrorResponse{} 3427 er.Type = "response" 3428 er.Command = request.Command 3429 er.RequestSeq = request.Seq 3430 er.Success = false 3431 er.Message = summary 3432 er.Body.Error = &dap.ErrorMessage{ 3433 Id: id, 3434 Format: fmt.Sprintf("%s: %s", summary, details), 3435 ShowUser: showUser, 3436 } 3437 s.config.log.Debug(er.Body.Error.Format) 3438 s.send(er) 3439 } 3440 3441 // sendErrorResponse sends an error response with showUser disabled (default). 3442 func (s *Session) sendErrorResponse(request dap.Request, id int, summary, details string) { 3443 s.sendErrorResponseWithOpts(request, id, summary, details, false /*showUser*/) 3444 } 3445 3446 // sendShowUserErrorResponse sends an error response with showUser enabled. 3447 func (s *Session) sendShowUserErrorResponse(request dap.Request, id int, summary, details string) { 3448 s.sendErrorResponseWithOpts(request, id, summary, details, true /*showUser*/) 3449 } 3450 3451 // sendInternalErrorResponse sends an "internal error" response back to the client. 3452 // We only take a seq here because we don't want to make assumptions about the 3453 // kind of message received by the server that this error is a reply to. 3454 func (s *Session) sendInternalErrorResponse(seq int, details string) { 3455 er := &dap.ErrorResponse{} 3456 er.Type = "response" 3457 er.RequestSeq = seq 3458 er.Success = false 3459 er.Message = "Internal Error" 3460 er.Body.Error = &dap.ErrorMessage{ 3461 Id: InternalError, 3462 Format: fmt.Sprintf("%s: %s", er.Message, details), 3463 } 3464 s.config.log.Debug(er.Body.Error.Format) 3465 s.send(er) 3466 } 3467 3468 func (s *Session) sendUnsupportedErrorResponse(request dap.Request) { 3469 s.sendErrorResponse(request, UnsupportedCommand, "Unsupported command", 3470 fmt.Sprintf("cannot process %q request", request.Command)) 3471 } 3472 3473 func (s *Session) sendNotYetImplementedErrorResponse(request dap.Request) { 3474 s.sendErrorResponse(request, NotYetImplemented, "Not yet implemented", 3475 fmt.Sprintf("cannot process %q request", request.Command)) 3476 } 3477 3478 func newResponse(request dap.Request) *dap.Response { 3479 return &dap.Response{ 3480 ProtocolMessage: dap.ProtocolMessage{ 3481 Seq: 0, 3482 Type: "response", 3483 }, 3484 Command: request.Command, 3485 RequestSeq: request.Seq, 3486 Success: true, 3487 } 3488 } 3489 3490 func newEvent(event string) *dap.Event { 3491 return &dap.Event{ 3492 ProtocolMessage: dap.ProtocolMessage{ 3493 Seq: 0, 3494 Type: "event", 3495 }, 3496 Event: event, 3497 } 3498 } 3499 3500 const BetterBadAccessError = `invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation] 3501 Unable to propagate EXC_BAD_ACCESS signal to target process and panic (see https://github.com/go-delve/delve/issues/852)` 3502 const BetterNextWhileNextingError = `Unable to step while the previous step is interrupted by a breakpoint. 3503 Use 'Continue' to resume the original step command.` 3504 3505 func (s *Session) resetHandlesForStoppedEvent() { 3506 s.stackFrameHandles.reset() 3507 s.variableHandles.reset() 3508 s.exceptionErr = nil 3509 } 3510 3511 func processExited(state *api.DebuggerState, err error) bool { 3512 _, isexited := err.(proc.ErrProcessExited) 3513 return isexited || err == nil && state.Exited 3514 } 3515 3516 func (s *Session) setRunningCmd(running bool) { 3517 s.runningMu.Lock() 3518 defer s.runningMu.Unlock() 3519 s.runningCmd = running 3520 } 3521 3522 func (s *Session) isRunningCmd() bool { 3523 s.runningMu.Lock() 3524 defer s.runningMu.Unlock() 3525 return s.runningCmd 3526 } 3527 3528 func (s *Session) setHaltRequested(requested bool) { 3529 s.haltMu.Lock() 3530 defer s.haltMu.Unlock() 3531 s.haltRequested = requested 3532 } 3533 3534 func (s *Session) checkHaltRequested() bool { 3535 s.haltMu.Lock() 3536 defer s.haltMu.Unlock() 3537 return s.haltRequested 3538 } 3539 3540 // resumeOnce is a helper function to resume the execution 3541 // of the target when the program is halted. 3542 func (s *Session) resumeOnce(command string, allowNextStateChange chan struct{}) (bool, *api.DebuggerState, error) { 3543 // No other goroutines should be able to try to resume 3544 // or halt execution while this goroutine is resuming 3545 // execution, so we do not miss those events. 3546 asyncSetupDone := make(chan struct{}, 1) 3547 defer closeIfOpen(asyncSetupDone) 3548 s.changeStateMu.Lock() 3549 go func() { 3550 defer s.changeStateMu.Unlock() 3551 defer closeIfOpen(allowNextStateChange) 3552 <-asyncSetupDone 3553 }() 3554 3555 // There may have been a manual halt while the program was 3556 // stopped. If this happened, do not resume execution of 3557 // the program. 3558 if s.checkHaltRequested() { 3559 state, err := s.debugger.State(false) 3560 return false, state, err 3561 } 3562 state, err := s.debugger.Command(&api.DebuggerCommand{Name: command}, asyncSetupDone) 3563 return true, state, err 3564 } 3565 3566 // runUntilStopAndNotify runs a debugger command until it stops on 3567 // termination, error, breakpoint, etc, when an appropriate 3568 // event needs to be sent to the client. allowNextStateChange is 3569 // a channel that will be closed to signal that an 3570 // asynchronous command has completed setup or was interrupted 3571 // due to an error, so the server is ready to receive new requests. 3572 func (s *Session) runUntilStopAndNotify(command string, allowNextStateChange chan struct{}) { 3573 state, err := s.runUntilStop(command, allowNextStateChange) 3574 3575 if s.conn.isClosed() { 3576 s.config.log.Debugf("connection %d closed - stopping %q command", s.id, command) 3577 return 3578 } 3579 3580 if processExited(state, err) { 3581 s.preTerminatedWG.Wait() 3582 s.send(&dap.TerminatedEvent{Event: *newEvent("terminated")}) 3583 return 3584 } 3585 3586 stopReason := s.debugger.StopReason() 3587 file, line := "?", -1 3588 if state != nil && state.CurrentThread != nil { 3589 file, line = state.CurrentThread.File, state.CurrentThread.Line 3590 } 3591 s.config.log.Debugf("%q command stopped - reason %q, location %s:%d", command, stopReason, file, line) 3592 3593 s.resetHandlesForStoppedEvent() 3594 stopped := &dap.StoppedEvent{Event: *newEvent("stopped")} 3595 stopped.Body.AllThreadsStopped = true 3596 3597 if err == nil { 3598 if stopReason == proc.StopManual { 3599 if err := s.debugger.CancelNext(); err != nil { 3600 s.config.log.Error(err) 3601 } else { 3602 state.NextInProgress = false 3603 } 3604 } 3605 stopped.Body.ThreadId = int(stoppedGoroutineID(state)) 3606 3607 switch stopReason { 3608 case proc.StopNextFinished: 3609 stopped.Body.Reason = "step" 3610 case proc.StopManual: // triggered by halt 3611 stopped.Body.Reason = "pause" 3612 case proc.StopUnknown: // can happen while terminating 3613 stopped.Body.Reason = "unknown" 3614 case proc.StopWatchpoint: 3615 stopped.Body.Reason = "data breakpoint" 3616 default: 3617 stopped.Body.Reason = "breakpoint" 3618 goid, bp := s.stoppedOnBreakpointGoroutineID(state) 3619 stopped.Body.ThreadId = int(goid) 3620 if bp != nil { 3621 switch bp.Name { 3622 case proc.FatalThrow: 3623 stopped.Body.Reason = "exception" 3624 stopped.Body.Description = "fatal error" 3625 stopped.Body.Text, _ = s.throwReason(int64(stopped.Body.ThreadId)) 3626 case proc.UnrecoveredPanic: 3627 stopped.Body.Reason = "exception" 3628 stopped.Body.Description = "panic" 3629 stopped.Body.Text, _ = s.panicReason(int64(stopped.Body.ThreadId)) 3630 } 3631 if strings.HasPrefix(bp.Name, functionBpPrefix) { 3632 stopped.Body.Reason = "function breakpoint" 3633 } 3634 if strings.HasPrefix(bp.Name, instructionBpPrefix) { 3635 stopped.Body.Reason = "instruction breakpoint" 3636 } 3637 stopped.Body.HitBreakpointIds = []int{bp.ID} 3638 } 3639 } 3640 3641 // Override the stop reason if there was a manual stop request. 3642 // TODO(suzmue): move this logic into the runUntilStop command 3643 // so that the stop reason is determined by that function which 3644 // has all the context. 3645 if stopped.Body.Reason != "exception" && s.checkHaltRequested() { 3646 s.config.log.Debugf("manual halt requested, stop reason %q converted to \"pause\"", stopped.Body.Reason) 3647 stopped.Body.Reason = "pause" 3648 stopped.Body.HitBreakpointIds = []int{} 3649 } 3650 3651 } else { 3652 s.exceptionErr = err 3653 s.config.log.Error("runtime error: ", err) 3654 stopped.Body.Reason = "exception" 3655 stopped.Body.Description = "runtime error" 3656 stopped.Body.Text = err.Error() 3657 // Special case in the spirit of https://github.com/microsoft/vscode-go/issues/1903 3658 if stopped.Body.Text == "bad access" { 3659 stopped.Body.Text = BetterBadAccessError 3660 } 3661 if stopped.Body.Text == "next while nexting" { 3662 stopped.Body.Description = "invalid command" 3663 stopped.Body.Text = BetterNextWhileNextingError 3664 s.logToConsole(fmt.Sprintf("%s: %s", stopped.Body.Description, stopped.Body.Text)) 3665 } 3666 3667 state, err := s.debugger.State( /*nowait*/ true) 3668 if err == nil { 3669 stopped.Body.ThreadId = int(stoppedGoroutineID(state)) 3670 } 3671 } 3672 3673 // NOTE: If we happen to be responding to another request with an is-running 3674 // error while this one completes, it is possible that the error response 3675 // will arrive after this stopped event. 3676 s.send(stopped) 3677 3678 // Send an output event with more information if next is in progress. 3679 if state != nil && state.NextInProgress { 3680 s.logToConsole("Step interrupted by a breakpoint. Use 'Continue' to resume the original step command.") 3681 } 3682 } 3683 3684 func (s *Session) runUntilStop(command string, allowNextStateChange chan struct{}) (*api.DebuggerState, error) { 3685 // Clear any manual stop requests that came in before we started running. 3686 s.setHaltRequested(false) 3687 3688 s.setRunningCmd(true) 3689 defer s.setRunningCmd(false) 3690 3691 var state *api.DebuggerState 3692 var err error 3693 for s.isRunningCmd() { 3694 state, err = resumeOnceAndCheckStop(s, command, allowNextStateChange) 3695 command = api.DirectionCongruentContinue 3696 } 3697 return state, err 3698 } 3699 3700 // Make this a var so it can be stubbed in testing. 3701 var resumeOnceAndCheckStop = func(s *Session, command string, allowNextStateChange chan struct{}) (*api.DebuggerState, error) { 3702 return s.resumeOnceAndCheckStop(command, allowNextStateChange) 3703 } 3704 3705 func (s *Session) resumeOnceAndCheckStop(command string, allowNextStateChange chan struct{}) (*api.DebuggerState, error) { 3706 resumed, state, err := s.resumeOnce(command, allowNextStateChange) 3707 // We should not try to process the log points if the program was not 3708 // resumed or there was an error. 3709 if !resumed || processExited(state, err) || state == nil || err != nil || s.conn.isClosed() { 3710 s.setRunningCmd(false) 3711 return state, err 3712 } 3713 3714 s.handleLogPoints(state) 3715 gsOnBp := s.stoppedGs(state) 3716 3717 switch s.debugger.StopReason() { 3718 case proc.StopBreakpoint, proc.StopManual: 3719 // Make sure a real manual stop was requested or a real breakpoint was hit. 3720 if len(gsOnBp) > 0 || s.checkHaltRequested() { 3721 s.setRunningCmd(false) 3722 } 3723 default: 3724 s.setRunningCmd(false) 3725 } 3726 3727 // Stepping a single instruction will never require continuing again. 3728 if command == api.StepInstruction || command == api.ReverseStepInstruction { 3729 s.setRunningCmd(false) 3730 } 3731 3732 return state, err 3733 } 3734 3735 func (s *Session) handleLogPoints(state *api.DebuggerState) { 3736 for _, th := range state.Threads { 3737 if bp := th.Breakpoint; bp != nil { 3738 s.logBreakpointMessage(bp, th.GoroutineID) 3739 } 3740 } 3741 } 3742 3743 func (s *Session) stoppedGs(state *api.DebuggerState) (gs []int64) { 3744 // Check the current thread first. There may be no selected goroutine. 3745 if state.CurrentThread.Breakpoint != nil && !state.CurrentThread.Breakpoint.Tracepoint { 3746 gs = append(gs, state.CurrentThread.GoroutineID) 3747 } 3748 if s.debugger.StopReason() == proc.StopHardcodedBreakpoint { 3749 gs = append(gs, stoppedGoroutineID(state)) 3750 } 3751 for _, th := range state.Threads { 3752 // Some threads may be stopped on a hardcoded breakpoint. 3753 // TODO(suzmue): This is a workaround for detecting hard coded breakpoints, 3754 // though this check is likely not sufficient. It would be better to resolve 3755 // this in the debugger layer instead. 3756 if th.Function.Name() == "runtime.breakpoint" { 3757 gs = append(gs, th.GoroutineID) 3758 continue 3759 } 3760 // We already added the current thread if it had a breakpoint. 3761 if th.ID == state.CurrentThread.ID { 3762 continue 3763 } 3764 if bp := th.Breakpoint; bp != nil { 3765 if !th.Breakpoint.Tracepoint { 3766 gs = append(gs, th.GoroutineID) 3767 } 3768 } 3769 } 3770 return gs 3771 } 3772 3773 func (s *Session) logBreakpointMessage(bp *api.Breakpoint, goid int64) bool { 3774 if !bp.Tracepoint { 3775 return false 3776 } 3777 if lMsg, ok := bp.UserData.(logMessage); ok { 3778 msg := lMsg.evaluate(s, goid) 3779 s.send(&dap.OutputEvent{ 3780 Event: *newEvent("output"), 3781 Body: dap.OutputEventBody{ 3782 Category: "stdout", 3783 Output: fmt.Sprintf("> [Go %d]: %s\n", goid, msg), 3784 Source: &dap.Source{ 3785 Path: s.toClientPath(bp.File), 3786 }, 3787 Line: bp.Line, 3788 }, 3789 }) 3790 } 3791 return true 3792 } 3793 3794 func (msg *logMessage) evaluate(s *Session, goid int64) string { 3795 evaluated := make([]interface{}, len(msg.args)) 3796 for i := range msg.args { 3797 exprVar, err := s.debugger.EvalVariableInScope(goid, 0, 0, msg.args[i], DefaultLoadConfig) 3798 if err != nil { 3799 evaluated[i] = fmt.Sprintf("{eval err: %e}", err) 3800 continue 3801 } 3802 evaluated[i], _ = s.convertVariableWithOpts(exprVar, "", skipRef|showFullValue) 3803 } 3804 return fmt.Sprintf(msg.format, evaluated...) 3805 } 3806 3807 func (s *Session) toClientPath(path string) string { 3808 if len(s.args.substitutePathServerToClient) == 0 { 3809 return path 3810 } 3811 clientPath := locspec.SubstitutePath(path, s.args.substitutePathServerToClient) 3812 if clientPath != path { 3813 s.config.log.Debugf("server path=%s converted to client path=%s\n", path, clientPath) 3814 } 3815 return clientPath 3816 } 3817 3818 func (s *Session) toServerPath(path string) string { 3819 if len(s.args.substitutePathClientToServer) == 0 { 3820 return path 3821 } 3822 serverPath := locspec.SubstitutePath(path, s.args.substitutePathClientToServer) 3823 if serverPath != path { 3824 s.config.log.Debugf("client path=%s converted to server path=%s\n", path, serverPath) 3825 } 3826 return serverPath 3827 } 3828 3829 type logMessage struct { 3830 format string 3831 args []string 3832 } 3833 3834 // parseLogPoint parses a log message according to the DAP spec: 3835 // 3836 // "Expressions within {} are interpolated." 3837 func parseLogPoint(msg string) (bool, *logMessage, error) { 3838 // Note: All braces *must* come in pairs, even those within an 3839 // expression to be interpolated. 3840 // TODO(suzmue): support individual braces in string values in 3841 // eval expressions. 3842 var args []string 3843 3844 var isArg bool 3845 var formatSlice, argSlice []rune 3846 braceCount := 0 3847 for _, r := range msg { 3848 if isArg { 3849 switch r { 3850 case '}': 3851 if braceCount--; braceCount == 0 { 3852 argStr := strings.TrimSpace(string(argSlice)) 3853 if len(argStr) == 0 { 3854 return false, nil, fmt.Errorf("empty evaluation string") 3855 } 3856 args = append(args, argStr) 3857 formatSlice = append(formatSlice, '%', 's') 3858 isArg = false 3859 continue 3860 } 3861 case '{': 3862 braceCount += 1 3863 } 3864 argSlice = append(argSlice, r) 3865 continue 3866 } 3867 3868 switch r { 3869 case '}': 3870 return false, nil, fmt.Errorf("invalid log point format, unexpected '}'") 3871 case '{': 3872 if braceCount++; braceCount == 1 { 3873 isArg, argSlice = true, []rune{} 3874 continue 3875 } 3876 } 3877 formatSlice = append(formatSlice, r) 3878 } 3879 if isArg { 3880 return false, nil, fmt.Errorf("invalid log point format") 3881 } 3882 if len(formatSlice) == 0 { 3883 return false, nil, nil 3884 } 3885 return true, &logMessage{ 3886 format: string(formatSlice), 3887 args: args, 3888 }, nil 3889 }