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