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