Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support delve/dlv dap-mode directly #318

Closed
apmattil opened this issue Jul 31, 2020 · 38 comments · Fixed by #603
Closed

support delve/dlv dap-mode directly #318

apmattil opened this issue Jul 31, 2020 · 38 comments · Fixed by #603
Labels
enhancement New feature or request

Comments

@apmattil
Copy link
Contributor

Instead of using vs-code adapter we could use dlv dap-mode directly.. this would eliminate need to install nodejs, vs-code adapter.

now dlv dap-mode is ready : go-delve/delve#1515
vs-code adapter itself is starting to use it, as well vim work has started.

@yyoncho yyoncho added the enhancement New feature or request label Jul 31, 2020
@magandrez
Copy link

Has there been any developments with regards to this feature request? I would be interested as well.

@hyangah
Copy link

hyangah commented Jan 10, 2022

vscode-go's node.js adapter is no longer actively maintained, so it would be great if emacs dap mode directly uses dlv dap.

Delve's DAP documentation: https://github.com/go-delve/delve/blob/master/Documentation/api/dap/README.md

note: dlv dap outputs stdout/stderr to its tty instead of forwarding them as OutputEvents, so it's desirable to launch the process from a terminal.

@wsw0108
Copy link

wsw0108 commented Apr 10, 2022

  (setq dap-go-debug-program (executable-find "dlv"))

  (defun dap-go--populate-dap-args (conf)
    (setq conf (pcase (plist-get conf :mode)
                 ("auto" (dap-go--populate-auto-args conf))
                 ("debug" (dap--put-if-absent conf :program (f-dirname (buffer-file-name))))
                 ("test" (dap--put-if-absent conf :program (buffer-file-name)))
                 ("exec" (dap--put-if-absent conf :program (read-file-name "enter full path to executable without tilde:")))
                 ("local"
                  (dap--put-if-absent conf :cwd (f-dirname (buffer-file-name)))
                  (dap--put-if-absent conf :processId (string-to-number (read-string "Enter pid: " "2345"))))
                 ))

    (let ((debug-port (dap--find-available-port)))
      (plist-put conf :host "localhost")
      (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s" dap-go-debug-program debug-port))
      (plist-put conf :debugServer debug-port))

    (if (stringp (plist-get conf :args)) (plist-put conf :args (split-string (plist-get conf :args))) ())

    (-> conf
        (dap--put-if-absent :type "go")
        (dap--put-if-absent :name "Go Debug")))

  (dap-register-debug-provider "go" 'dap-go--populate-dap-args)

That is, remove the line contains :dap-server-path, add below:

    (let ((debug-port (dap--find-available-port)))
      (plist-put conf :host "localhost")
      (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s" dap-go-debug-program debug-port))
      (plist-put conf :debugServer debug-port))

@apmattil
Copy link
Contributor Author

I tried above patch, but I got some error with args passed.

Sending: 
{
  "command": "launch",
  "arguments": {
    "type": "go",
    "request": "launch",
    "name": "Launch Executable",
    "mode": "exec",
    "program": "/home/apmattil/src/ari/ari",
    "args": null,
    "env": null,
    "envFile": null,
    "host": "localhost",
    "debugServer": 38995
  },
  "type": "request",
  "seq": 2
}
Received:
{
  "body": {
    "error": {
      "format": "Failed to launch: 'args' attribute '<nil>' in debug configuration is not an array.",
      "id": 3000
    }
  },
  "message": "Failed to launch",
  "command": "launch",
  "success": null,
  "request_seq": 2,
  "type": "response",
  "seq": 0
}
Debug session process exited with status: deleted

Debug Adapter finished
Mark set

I'll try to find out why it failed, but if anybody has guess let me know.

@sata-form3
Copy link

sata-form3 commented Apr 11, 2022

Thanks @wsw0108! Really, really appreciate it

I don't think the snippets were for the latest version.. This worked for me to debug a golang unit test at least. Which is what I'm after, I think it should work with other profiles as well.

(require 'dap-mode)
(require 'dap-utils)

(setq dap-go-debug-program (executable-find "dlv"))

(defcustom dap-go-delve-path (or (executable-find "dlv")
                                 (expand-file-name "dlv" (expand-file-name "bin" (getenv "GOPATH"))))
  "The path to the delve command."
  :group 'dap-go
  :type 'string)

(defun dap-go--populate-default-args (conf)
  "Populate CONF with the default arguments."
  (setq conf (pcase (plist-get conf :mode)
               ("auto" (dap-go--populate-auto-args conf))
               ("debug" (dap--put-if-absent conf :program (f-dirname (buffer-file-name))))
               ("exec" (dap--put-if-absent conf :program (read-file-name "enter full path to executable without tilde:")))
               ("remote" (dap--put-if-absent conf :program (f-dirname (buffer-file-name)))
		            (dap--put-if-absent conf :host (read-string "enter host:" "127.0.0.1"))
		            (dap--put-if-absent conf :port (string-to-number (read-string "Enter port: " "2345"))))
               ("local"
                (dap--put-if-absent conf :cwd (f-dirname (buffer-file-name)))
                (dap--put-if-absent conf :processId (string-to-number (read-string "Enter pid: " "2345"))))
	             ))

  (let ((debug-port (dap--find-available-port)))
    (plist-put conf :host "localhost")
    (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s" dap-go-debug-program debug-port))
    (plist-put conf :debugServer debug-port))  


  (if (stringp (plist-get conf :args)) (plist-put conf :args (split-string (plist-get conf :args))) ())
  
  (-> conf

      (dap--put-if-absent :dlvToolPath dap-go-delve-path)
      (dap--put-if-absent :packagePathToGoModPathMap
                          (ht<-alist `((,(f-dirname (buffer-file-name))  . ,(lsp-find-session-folder (lsp-session) (buffer-file-name))))))
      
      (dap--put-if-absent :type "go")
      (dap--put-if-absent :name "Go Debug")))

(defun dap-go--populate-auto-args (conf)
  "Populate auto arguments."
  (dap--put-if-absent conf :program (buffer-file-name))

  (if (string-suffix-p "_test.go" (buffer-file-name))
      (plist-put conf :mode "test")
    (plist-put conf :mode "debug")))

(dap-register-debug-provider "go" 'dap-go--populate-default-args)

(dap-register-debug-template "Go Launch File Configuration"
                             (list :type "go"
                                   :request "launch"
                                   :name "Launch File"
                                   :mode "auto"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))
(provide 'dap-go)
;;; dap-go.el ends here

@s-kostyaev
Copy link
Collaborator

Is it possible to debug remotely with this solution?

@apmattil
Copy link
Contributor Author

I had old dlv, solution did not work with it.
but it does work with dlv 1.8.2

@s-kostyaev
Copy link
Collaborator

I have latest dlv, but can't create working debug template for remote debug.

@sata
Copy link

sata commented Apr 15, 2022

I have latest dlv, but can't create working debug template for remote debug.

Yeah, I'm having some trouble being able to run tests properly if the implementation is elsewhere. I think it comes down to the fact that I don't know enough about how Delve works.

This should hopefully help you as well:

(setq dap-print-io t)

You will see the DAP messages going over the wire with the above configuration

EDIT:

I think in my case, it seems to do with packagePathToGoModPathMap not being populated correctly in one of my projects.

(dap--put-if-absent :packagePathToGoModPathMap
                          (ht<-alist `((,(f-dirname (buffer-file-name))  . ,(lsp-find-session-folder (lsp-session) (buffer-file-name))))))

@sata
Copy link

sata commented Apr 15, 2022

I have latest dlv, but can't create working debug template for remote debug.

I'm not sure It's not supported over DAP interface.

Sending: 
{
  "command": "launch",
  "arguments": {
    "type": "go",
    "request": "launch",
    "name": "Connect to Remote dlv",
    "mode": "remote",
    "program": "/home/s/sources/go-git-http-backend/pkg/server",
    "args": null,
    "env": null,
    "envFile": null,
    "host": "localhost",
    "port": 39967,
    "debugServer": 33705,
    "dlvToolPath": "/home/s/go/bin/dlv",
    "packagePathToGoModPathMap": {
      "/home/s/sources/go-git-http-backend/pkg/server": "/home/s/sources/go-git-http-backend"
    }
  },
  "type": "request",
  "seq": 2
}
Received:
{
  "seq": 0,
  "type": "response",
  "request_seq": 2,
  "success": null,
  "command": "launch",
  "message": "Failed to launch",
  "body": {
    "error": {
      "id": 3000,
      "format": "Failed to launch: invalid debug configuration - unsupported 'mode' attribute \"remote\"",
      "showUser": true
    }
  }
}
Debug session process exited with status: deleted

Again, I haven't dug deep enough for understanding if I'm doing it right or if I'm assuming too much.

I'm curious to know about your use case, would you mind sharing some details on how you're setting it up?

@s-kostyaev
Copy link
Collaborator

I'm curious to know about your use case, would you mind sharing some details on how you're setting it up?

Sure.

(dap-register-debug-template "Go Dlv Remote Debug"
                             (list :type "go"
                                   :request "attach"
                                   :name "Dlv Remote Debug"
                                   :mode "remote"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))

@hyangah
Copy link

hyangah commented Apr 15, 2022

Did you start the dlv headless server outside the editor (remote) and attempt to connect to it? (e.g. dlv debug --headless ...) Then, the editor plugin (dap-mode) needs to connect to the remote headless dlv server directly. (I don't know what change is necessary to bypass dlv dap process launch).

FYI this is how vscode-go plugin utilizes dlv-dap for remote/attach mode - https://github.com/golang/vscode-go/blob/master/docs/debugging.md#connecting-to-headless-delve-with-target-specified-at-server-start-up

@s-kostyaev
Copy link
Collaborator

s-kostyaev commented Apr 15, 2022

Did you start the dlv headless server outside the editor (remote) and attempt to connect to it? (e.g. dlv debug --headless ...)

Yes. And it works for other team members with Goland and doesn't work for me with emacs and dap-mode

@apmattil
Copy link
Contributor Author

I think there is two options:

  • old json-rcp connection
  • new dap connection

and two modes how server operates:

  • manual starting the debugged app
  • client pushes config+app

this ticket:
go-delve/delve#2328
points to here:
https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging

@apmattil
Copy link
Contributor Author

with this patch to dap-go.el above I can get remote template to work with local dap-server:

20c20
<                           (dap--put-if-absent conf :port (string-to-number (read-string "Enter port: " "2345"))))
---
>                           (dap--put-if-absent conf :port (string-to-number (read-string "Enter port: " "12345"))))
28c28
<     (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s" dap-go-debug-program debug-port))
---
>     (plist-put conf :program-to-start (format "%s dap --listen :%s --log --log-output=dap,rpc" dap-go-debug-program debug-port))
66c66
<                                    :request "attach"
---
>                                    :request "launch"
68c68
<                                    :mode "remote"
---
>                                    :mode "debug"

my main consern is that they might depricate the rpc version since already dap is used as default in vs-code when debuging locally.

@hyangah
Copy link

hyangah commented Apr 19, 2022

VS Code Go extension dev team stopped supporting the legacy debug adapter long ago in favor of the native DAP protocol support from delve and plans to remove the code sometime this year.

@polinasok made the traditional dlv commands (dlv debug --headless, dlv exec --headless, etc) all support DAP communication, so dap-mode needs to adjust its behavior to directly establish a TCP connection to the user-specified host/port if the launch configuration specifies remote+attach mode.

@apmattil
Copy link
Contributor Author

apmattil commented Apr 19, 2022

I know this would work in remote debug if I could get the if statement to work (I really can not understand elisp logic):

(require 'dap-mode)
(require 'dap-utils)

(setq dap-go-debug-program (executable-find "dlv"))

(defcustom dap-go-delve-path (or (executable-find "dlv")
                                 (expand-file-name "dlv" (expand-file-name "bin" (getenv "GOPATH"))))
  "The path to the delve command."
  :group 'dap-go
  :type 'string)

(defun dap-go--populate-default-args (conf)
  "Populate CONF with the default arguments."
  
  (setq conf (pcase (plist-get conf :mode)
               ("auto" (dap-go--populate-auto-args conf))
               ("debug" (dap--put-if-absent conf :program (f-dirname (buffer-file-name))))
               ("exec" (dap--put-if-absent conf :program (read-file-name "enter full path to executable without tilde:")))
               ("remote" (dap--put-if-absent conf :program (f-dirname (buffer-file-name)))
		            (dap--put-if-absent conf :host (read-string "enter host:" "127.0.0.1"))
		            (dap--put-if-absent conf :debugServer (string-to-number (read-string "Enter port: " "2345")))
                            ) ;;    !!!!  remove the mode from conf here (dap--plist-delete conf: mode))
               ("local"
                (dap--put-if-absent conf :cwd (f-dirname (buffer-file-name)))
                (dap--put-if-absent conf :processId (string-to-number (read-string "Enter pid: " "2345"))))
	             ))

  (message "ARI test mode:%s\n" mode)
  
  ;; !! ----------- below if statement fails --------------- !!!

  (if (equal mode "remote")
      (dap--plist-delete conf: mode) ;; !!!!! or remove the mode from conf here
    (let ((debug-port (dap--find-available-port)))
      (plist-put conf :host "localhost")
       (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s" dap-go-debug-program debug-port))
       (plist-put conf :debugServer debug-port)))
    

  (if (stringp (plist-get conf :args)) (plist-put conf :args (split-string (plist-get conf :args))) ())
  
  (-> conf

      (dap--put-if-absent :dlvToolPath dap-go-delve-path)
      (dap--put-if-absent :packagePathToGoModPathMap
                          (ht<-alist `((,(f-dirname (buffer-file-name))  . ,(lsp-find-session-folder (lsp-session) (buffer-file-name))))))
      
      (dap--put-if-absent :type "go")
      (dap--put-if-absent :name "Go Debug")))

(defun dap-go--populate-auto-args (conf)
  "Populate auto arguments."
  (dap--put-if-absent conf :program (buffer-file-name))

  (if (string-suffix-p "_test.go" (buffer-file-name))
      (plist-put conf :mode "test")
    (plist-put conf :mode "debug")))

(dap-register-debug-provider "go" 'dap-go--populate-default-args)

(dap-register-debug-template "Go Launch File Configuration"
                             (list :type "go"
                                   :request "launch"
                                   :name "Launch File"
                                   :mode "auto"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))

(dap-register-debug-template "Go Dlv Remote Debug"
                             (list :type "go"
                                   :request "launch"
                                   :name "Dlv Remote Debug"
                                   :debugAdapter "dlv-dap"
                                   :port "12345"
                                   :host "192.168.121.6"
                                   :mode "remote"
                                   :program "/home/vagrant/src/ari/ari"
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))
(provide 'dap-go)
;;; dap-go.el ends here

variable I try to make is just some kind of reference and once it is deleted inside the if (remote) check execution fails.
anyway the point is to set debugServer in remote mode, not port.
also we can not pass mode=remote to server in dap mode since it does not support it.

start the server as 'dlv dap --headless --listen=:12345 --log --log-output=rpc,dap,debugger'

@s-kostyaev
Copy link
Collaborator

@apmattil try string= instead of equal

@apmattil
Copy link
Contributor Author

comparison in if statement is not the problem, I get error (about mode missing from conf ?):
dap-go--populate-default-args: Symbol’s value as variable is void: mode

@s-kostyaev
Copy link
Collaborator

(plist-get conf :mode) instead of mode?

@polinasok
Copy link

"command": "launch",
"arguments": {
"type": "go",
"request": "launch",
"name": "Connect to Remote dlv",
"mode": "remote",
"message": "Failed to launch",
"body": {
"error": {
"id": 3000,
"format": "Failed to launch: invalid debug configuration - unsupported 'mode' attribute "remote"",

@sata You must use "request":"attach", not "launch". Launch+remote is not a valid combination. Launch requests tell the debugger to launch the process to debug for you, which is not the case here.

@polinasok
Copy link

polinasok commented Apr 21, 2022

As described in https://github.com/go-delve/delve/blob/master/Documentation/api/dap/README.md and https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging, you have two options for working with a remote debugger:

Option 1
Remotely start the server:

dlv dap --listen=xxx:yyy ### not associated with any target yet, waiting for a client to specify what to debug

Locally start the client and tell the server what to debug at the remote location:

:request "launch" ### tell the server to launch the target
:port "xxx" ### don't start new server, but instead connect to the one at host:port
:host "yyy"
:mode "exec" ### or "debug" or "test"
:program "/remote/location/program"

or

:request "attach" ### we want to attach to something that's already running
:port "xxx" ### don't start new server, but instead connect to the one at host:port
:host "yyy"
:mode "local" ### the thing that's running is a process that is local to the server
:processId "..." ### and this is its PID

Option 2
Remotely start the server and associate it with the target:

dlv debug/exec <program> --listen=xxx:yyy --headless  ### launches the program and attaches to it

or

dlv attach <pid> --listen=xxx:yyy --headless  ### attaches to existing process

Locally start the client to attach (aka connect) to an existing remote session:

:request "attach" ### we want to attach to something that's already running
:port "xxx" ### don't start new server, but instead connect to the one at host:port
:host "yyy"
:mode "remote" ### the thing running is a remote debugger session already in progress, no need to specify program or pid

Also, the DAP server is perfectly happy to take a subset of relevant optional attributes like buildFlags and provide defaults (blank or otherwise) for those that are not specified.

@polinasok
Copy link

polinasok commented Apr 21, 2022

@apmattil

           ("remote" (dap--put-if-absent conf :program (f-dirname (buffer-file-name)))

No program in remote mode. To use the remote mode, you should already have a dlv server that specified the program on the command line when it was started.

(dap-register-debug-template "Go Dlv Remote Debug"
(list :type "go"
:request "launch"

should be "attach" if used with "remote" mode below

                               :name "Dlv Remote Debug"
                               :debugAdapter "dlv-dap"
                               :port "12345"
                               :host "192.168.121.6"
                               :mode "remote"
                               :program "/home/vagrant/src/ari/ari"

Don't need program in remote attach mode as the remote server should already be tracing a target program. This attribute will be ignored.

anyway the point is to set debugServer in remote mode, not port.

If this matches vscode-go, then debugServer is a way to specify local port only and is generally advertised for development and testing. host/port is what is advertised to users when connecting to remote or external debug server. If host is omitted, localhost is implied.

also we can not pass mode=remote to server in dap mode since it does not support it.

Yes. dlv dap only supports launch+debug, launch+test, launch+exec and attach+local. dlv debug/exec/attach <target> --headless supports attach+remote.

(The mode names are not ideal and we considered revising them, but then decided to stick with the familiar legacy names, so peoples configurations could be used as-is or with minimal changes as we switched adapters behind the scenes.)

start the server as 'dlv dap --headless --listen=:12345 --log --log-output=rpc,dap,debugger'

dlv dap is headless by default, so --headless is redundant.
dlv debug/exec/attach start a headless server and a terminal client connected to it. This command needs '--headless' if you want to skip the default terminal client and connect with a different client (like emacs or vscode) instead.

@sata-form3
Copy link

"command": "launch",
"arguments": {
"type": "go",
"request": "launch",
"name": "Connect to Remote dlv",
"mode": "remote",
"message": "Failed to launch",
"body": {
"error": {
"id": 3000,
"format": "Failed to launch: invalid debug configuration - unsupported 'mode' attribute "remote"",

@sata You must use "request":"attach", not "launch". Launch+remote is not a valid combination. Launch requests tell the debugger to launch the process to debug for you, which is not the case here.

Thanks for correcting me!

I spent some time in the weekend looking at the delve documentation and also debugging delve to understand it better, got it working in the end :)

@polinasok
Copy link

polinasok commented Apr 21, 2022

I spent some time in the weekend looking at the delve documentation and also debugging delve to understand it better, got it working in the end :)

sata-form3 That's great to hear! I am adding more info to the documentation, so hopefully it will be more clear now. But please don't hesitate to let us know if there is more that can be clarified to make onboarding easier.

@s-kostyaev
Copy link
Collaborator

s-kostyaev commented Apr 21, 2022

My current setup:

(require 'dap-mode)
(require 'dap-utils)

(setq dap-dlv-go-debug-program (executable-find "dlv"))

(defcustom dap-dlv-go-delve-path (or (executable-find "dlv")
                                     (expand-file-name "dlv" (expand-file-name "bin" (getenv "GOPATH"))))
  "The path to the delve command."
  :group 'dap-dlv-go
  :type 'string)

(defun dap-dlv-go--populate-default-args (conf)
  "Populate CONF with the default arguments."
  (setq conf (pcase (plist-get conf :mode)
               ("auto" (dap-dlv-go--populate-auto-args conf))
               ("debug" (dap--put-if-absent conf :program (f-dirname (buffer-file-name))))
               ("exec" (dap--put-if-absent conf :program (read-file-name "enter full path to executable without tilde:")))
               ("remote" (dap--put-if-absent conf :program (f-dirname (buffer-file-name)))
		(dap--put-if-absent conf :host (read-string "enter host:" "127.0.0.1"))
		(dap--put-if-absent conf :debugPort (string-to-number (read-string "Enter port: " "2345"))))
               ("local"
                (dap--put-if-absent conf :cwd (f-dirname (buffer-file-name)))
                (dap--put-if-absent conf :processId (string-to-number (read-string "Enter pid: " "2345"))))))

  (let ((debug-port (if (string= (plist-get conf :mode)
				 "remote")
			(plist-get conf :debugPort)
		      (dap--find-available-port))))
    (dap--put-if-absent conf :host "localhost")
    (when (not (string= "remote" (plist-get conf :mode)))
      (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s --log" dap-dlv-go-debug-program debug-port)))
    (plist-put conf :debugServer debug-port))


  (if (stringp (plist-get conf :args)) (plist-put conf :args (split-string (plist-get conf :args))) ())
  
  (-> conf

      (dap--put-if-absent :dlvToolPath dap-dlv-go-delve-path)
      (dap--put-if-absent :packagePathToGoModPathMap
                          (ht<-alist `((,(f-dirname (buffer-file-name))  . ,(lsp-find-session-folder (lsp-session) (buffer-file-name))))))
      
      (dap--put-if-absent :type "go")
      (dap--put-if-absent :name "Go Debug")))

(defun dap-dlv-go--populate-auto-args (conf)
  "Populate auto arguments."
  (dap--put-if-absent conf :program (buffer-file-name))

  (if (string-suffix-p "_test.go" (buffer-file-name))
      (plist-put conf :mode "test")
    (plist-put conf :mode "debug")))

(dap-register-debug-provider "go" 'dap-dlv-go--populate-default-args)

(dap-register-debug-template "Go Dlv Launch File Configuration"
                             (list :type "go"
                                   :request "launch"
                                   :name "Launch File"
                                   :mode "auto"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))

(dap-register-debug-template "Go Dlv Remote Debug"
                             (list :type "go"
                                   :request "attach"
                                   :name "Dlv Remote Debug"
                                   :mode "remote"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))
(provide 'dap-dlv-go)

Then start application on remote machine and start delve on remote machine:

dlv --headless --accept-multiclient attach 1 -l :10080 --log --log-output rpc,dap,debugger

Then connect to debugger from emacs by remote debug template and can't debug. In logs I can see:

2022-04-21T13:38:24Z debug layer=dap [<- from client]{"seq":11,"type":"request","command":"setBreakpoints","arguments":{"source":{"name":"main.go","path":"/home/user/projects/tribonacci/cmd/tribonacci-web/main.go"},"breakpoints":[{"line":95}],"lines":[95]}}
2022-04-21T13:38:24Z debug layer=dap [-> to client]{"seq":0,"type":"response","request_seq":11,"success":true,"command":"setBreakpoints","body":{"breakpoints":[{"verified":false,"message":"could not find file /home/user/projects/tribonacci/cmd/tribonacci-web/main.go","source":{}}]}}

Looks like I have missed something.

UPD. Maybe it fails because binary that I try to debug has been build inside podman container and has other source paths. But I'm not sure.

UPD 2. Bingo! When I build binary locally and put it into container all works fine.

@s-kostyaev
Copy link
Collaborator

@polinasok Thank you! Without your explanation I can't make it work.

@s-kostyaev
Copy link
Collaborator

Should we use delve directly as only option or should we provide both options? I can provide PR, but we should make the choice. As soon as other option is deprecated maybe we should chose first option, but this code is not tested enough yet.

@apmattil
Copy link
Contributor Author

apmattil commented Apr 21, 2022

I vote for option 1.
but perhaps we should check if host is not localhost instead of port when selecting is this remote debugin case ?

@polinasok
Copy link

polinasok commented Apr 21, 2022

UPD. Maybe it fails because binary that I try to debug has been build inside podman container and has other source paths. But I'm not sure.

UPD 2. Bingo! When I build binary locally and put it into container all works fine.

Your binary is built with some set of paths. With remote debugging (or when using symlinks or when building with trimipath) your client might to have a different set of paths for the source code. So when you set breakpoints it will ask the server using the client-side version, which won't match the server-side version embedded in the binary. You can specify the directory mappings using substitutePath, so client-side path can be translated to the server-side version for breakpoints, traces, etc.

From https://github.com/golang/vscode-go/blob/master/docs/debugging.md#remote-debugging

    "substitutePath": [
      { "from": "/path/to/local/workspace", "to": "/path/to/remote/workspace" },
      ...
  ]

@apmattil
Copy link
Contributor Author

is the '${workspaceFolder}' vscode variable ?

@polinasok
Copy link

polinasok commented Apr 21, 2022

About the remote options described in #318 (comment).

Option 1 is new and inspired by the DAP model. It makes it easy for the user to switch back and forth between letting the client launch the debugger for them or connecting to their own (redirecting output, issuing signals in the terminal, etc) with just a change of the port field. This also means that a client can ask the remote server to debug any arbitrary code (potential security concern). This is also not available in multi-client mode - e.g. if you want to keep reconnecting to your session.

Option 2 is the more familiar traditional option that delve users have been used to for a while. It's a power option of sorts. It requires more familiarity with the dlv command-line args. It makes it possible to connect other clients to the server (terminal, GoLand, etc.)

Vscode users found use cases for both.

@polinasok
Copy link

is the '${workspaceFolder}' vscode variable ?

Yes, sorry, it's just the client-side root. I will make an edit to the example.
(FYI keep the questions coming. This is giving me more ideas how to improve general dlv documentation.)

@polinasok
Copy link

Oh and one more note. Host can very be localhost. Some users use port forwarding. Others might want this option to use with a local, but external server, so they get access to the terminal where it was launched to enter stdin, issue signals, redirect output.

@s-kostyaev
Copy link
Collaborator

s-kostyaev commented Apr 21, 2022

By options I mean

  1. Replace current solution that uses VS code debug adapter under the hood with new one that uses delve itself without any wrappers.
  2. Let old solution continue existing and put second one nearby. And user can pick any of these.

Both solutions can be used locally or remotely, with exec or attach etc.

@polinasok do you mean the same?

@apmattil
Copy link
Contributor Author

option one has options sub options @polinasok mentioned.

@apmattil
Copy link
Contributor Author

@polinasok yes you are right.. just checking host option might not be so great idea, but checking port is just confusing (IMOH).
if users use it for port forwarding then there would anyway be dlv server listening.
of course there might be other uses cases.

host check version here anyway:

(require 'dap-mode)
(require 'dap-utils)

(setq dap-dlv-go-debug-program (executable-find "dlv"))

(defcustom dap-dlv-go-delve-path (or (executable-find "dlv")
                                     (expand-file-name "dlv" (expand-file-name "bin" (getenv "GOPATH"))))
  "The path to the delve command."
  :group 'dap-dlv-go
  :type 'string)

(defun dap-dlv-go--populate-default-args (conf)
  "Populate CONF with the default arguments."
  (setq conf (pcase (plist-get conf :mode)
               ("auto" (dap-dlv-go--populate-auto-args conf))
               ("debug" (dap--put-if-absent conf :program (f-dirname (buffer-file-name))))
               ("exec" (dap--put-if-absent conf :program (read-file-name "enter full path to executable without tilde:")))
               ("remote" (dap--put-if-absent conf :program (f-dirname (buffer-file-name)))
		(dap--put-if-absent conf :host (read-string "enter host:" "127.0.0.1"))
		(dap--put-if-absent conf :debugPort (string-to-number (read-string "Enter port: " "2345"))))
               ("local"
                (dap--put-if-absent conf :cwd (f-dirname (buffer-file-name)))
               (dap--put-if-absent conf :processId (string-to-number (read-string "Enter pid: " "2345"))))))

  (if (plist-member conf :host)
       (dap--put-if-absent conf :debugPort (string-to-number (read-string "Enter server port: " "12345"))))
  
  (let ((debug-port (if (plist-member conf :host)
			(plist-get conf :debugPort)
		      (dap--find-available-port))))
    (when (not (plist-member conf :host))
      (dap--put-if-absent conf :host "localhost")
      (plist-put conf :program-to-start (format "%s dap --listen 127.0.0.1:%s --log" dap-dlv-go-debug-program debug-port)))
    (plist-put conf :debugServer debug-port))


  (if (stringp (plist-get conf :args)) (plist-put conf :args (split-string (plist-get conf :args))) ())
  
  (-> conf

      (dap--put-if-absent :dlvToolPath dap-dlv-go-delve-path)
      (dap--put-if-absent :packagePathToGoModPathMap
                          (ht<-alist `((,(f-dirname (buffer-file-name))  . ,(lsp-find-session-folder (lsp-session) (buffer-file-name))))))
      
      (dap--put-if-absent :type "go")
      (dap--put-if-absent :name "Go Debug")))

(defun dap-dlv-go--populate-auto-args (conf)
  "Populate auto arguments."
  (dap--put-if-absent conf :program (buffer-file-name))

  (if (string-suffix-p "_test.go" (buffer-file-name))
      (plist-put conf :mode "test")
    (plist-put conf :mode "debug")))

(dap-register-debug-provider "go" 'dap-dlv-go--populate-default-args)

(dap-register-debug-template "Go Dlv Launch File Configuration"
                             (list :type "go"
                                   :request "launch"
                                   :name "Launch File"
                                   :mode "auto"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))

(dap-register-debug-template "Go Dlv Remote Debug"
                             (list :type "go"
                                   :request "launch"
                                   :name "Dlv Remote Debug"
                                   :mode "exec"
                                   :host "192.168.121.6"
                                   :program nil
                                   :buildFlags nil
                                   :args nil
                                   :env nil
                                   :envFile nil))
(provide 'dap-dlv-go)

@polinasok
Copy link

polinasok commented Apr 22, 2022

By options I mean

  1. Replace current solution that uses VS code debug adapter under the hood with new one that uses delve itself without any wrappers.
  2. Let old solution continue existing and put second one nearby. And user can pick any of these.

Both solutions can be used locally or remotely, with exec or attach etc.

@polinasok do you mean the same?

No, I misunderstood. I thought you were referring to my [earlier comment](#318 (comment) with two options for remote.

In vscode we replaced old with new under the hood back in summer 2021 for everything that uses dlv dap, so those modes (all launch and local attach with or without host:port) are well tested by now. The remote attach is a newer addition. It is still opt-in and by default vscode-go still uses the old adapter. We do plan to change that soon (golang/vscode-go#2205), but in the meantime appreciate every bit of feedback that can help us work out any kinks.

s-kostyaev pushed a commit to s-kostyaev/dap-mode that referenced this issue Apr 22, 2022
s-kostyaev pushed a commit to s-kostyaev/dap-mode that referenced this issue Apr 27, 2022
s-kostyaev pushed a commit to s-kostyaev/dap-mode that referenced this issue Apr 27, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants