Repository: go-delve/delve
Pull request #2797 (2021-11-17 20:01:11)

service/dap: expose sources command in evaluate request
This change exposes the list sources feature in the debug console in order to facilitate easier substitute path mappings. When user's have symlinks, are debugging on a remote host, or use trimpath to build, the source paths may not line up with the source files sent from the dap client. By allowing users to see the source names that the program was actually built with, this will allow users to more easily set up a correct configuration of substitutePath to debug their programs.

Updates #2795 


Patch:
diff --git a/service/dap/command.go b/service/dap/command.go
index 0b08c569..a9690d2f 100644
--- a/service/dap/command.go
+++ b/service/dap/command.go
@@ -4,6 +4,7 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
+	"sort"
 	"strings"
 
 	"github.com/go-delve/delve/pkg/config"
@@ -37,24 +38,29 @@ type command struct {
 const (
 	msgHelp = `Prints the help message.
 	
-help [command]
+dlv help [command]
 
 Type "help" followed by the name of a command for more information about it.`
 
 	msgConfig = `Changes configuration parameters.
 	
-	config -list
+	dlv config -list
 	
 	Show all configuration parameters.
 	
-	config <parameter> <value>
+	dlv config <parameter> <value>
 	
 	Changes the value of a configuration parameter.
 	
-	config substitutePath <from> <to>
-	config substitutePath <from>
+	dlv config substitutePath <from> <to>
+	dlv config substitutePath <from>
 	
 	Adds or removes a path substitution rule.`
+	msgSources = `Print list of source files.
+
+	dlv sources [<regex>]
+
+If regex is specified only the source files matching it will be returned.`
 )
 
 // debugCommands returns a list of commands with default commands defined.
@@ -62,6 +68,7 @@ func debugCommands(s *Session) []command {
 	return []command{
 		{aliases: []string{"help", "h"}, cmdFn: s.helpMessage, helpMsg: msgHelp},
 		{aliases: []string{"config"}, cmdFn: s.evaluateConfig, helpMsg: msgConfig},
+		{aliases: []string{"sources", "s"}, cmdFn: s.sources, helpMsg: msgSources},
 	}
 }
 
@@ -88,14 +95,14 @@ func (s *Session) helpMessage(_, _ int, args string) (string, error) {
 			h = h[:idx]
 		}
 		if len(cmd.aliases) > 1 {
-			fmt.Fprintf(&buf, "    %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), h)
+			fmt.Fprintf(&buf, "    dlv %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), h)
 		} else {
-			fmt.Fprintf(&buf, "    %s \t %s\n", cmd.aliases[0], h)
+			fmt.Fprintf(&buf, "    dlv %s \t %s\n", cmd.aliases[0], h)
 		}
 	}
 
 	fmt.Fprintln(&buf)
-	fmt.Fprintln(&buf, "Type help followed by a command for full documentation.")
+	fmt.Fprintln(&buf, "Type 'dlv help' followed by a command for full documentation.")
 	return buf.String(), nil
 }
 
@@ -113,3 +120,12 @@ func (s *Session) evaluateConfig(_, _ int, expr string) (string, error) {
 		return res, nil
 	}
 }
+
+func (s *Session) sources(_, _ int, filter string) (string, error) {
+	sources, err := s.debugger.Sources(filter)
+	if err != nil {
+		return "", err
+	}
+	sort.Strings(sources)
+	return strings.Join(sources, "\n"), nil
+}


Test patch:
diff --git a/_scripts/latestver.py b/_scripts/latestver.py
index 886d476e..b8483009 100644
--- a/_scripts/latestver.py
+++ b/_scripts/latestver.py
@@ -19,7 +19,7 @@ def splitver(x):
 	return v
 
 ver = sys.argv[1]
-d = json.loads(urllib.urlopen('https://golang.org/dl/?mode=json&include=all').read())
+d = json.loads(urllib.urlopen('https://go.dev/dl/?mode=json&include=all').read())
 ds = sorted(d, reverse=True, key=lambda it: splitver(it['version'][2:]))
 for x in ds:
 	if x['version'][:len(ver)] == ver:
diff --git a/_scripts/test_linux.sh b/_scripts/test_linux.sh
index 5bd251f8..3fe0c1fd 100755
--- a/_scripts/test_linux.sh
+++ b/_scripts/test_linux.sh
@@ -26,7 +26,7 @@ if [ "$version" = "gotip" ]; then
 		exit 0
 	fi
 	echo Building Go from tip
-	getgo $(curl https://golang.org/VERSION?m=text)
+	getgo $(curl https://go.dev/VERSION?m=text)
 	export GOROOT_BOOTSTRAP=$GOROOT
 	export GOROOT=/usr/local/go/go-tip
 	git clone https://go.googlesource.com/go /usr/local/go/go-tip
@@ -35,7 +35,7 @@ if [ "$version" = "gotip" ]; then
 	cd -
 else
 	echo Finding latest patch version for $version
-	version=$(curl 'https://golang.org/dl/?mode=json&include=all' | jq '.[].version' --raw-output | egrep ^$version'($|\.|beta|rc)' | sort -rV | head -1)
+	version=$(curl 'https://go.dev/dl/?mode=json&include=all' | jq '.[].version' --raw-output | egrep ^$version'($|\.|beta|rc)' | sort -rV | head -1)
 	echo "Go $version on $arch"
 	getgo $version
 fi
diff --git a/_scripts/test_mac.sh b/_scripts/test_mac.sh
index f4395a84..e48cbf0e 100644
--- a/_scripts/test_mac.sh
+++ b/_scripts/test_mac.sh
@@ -12,7 +12,7 @@ if [ "$GOVERSION" = "gotip" ]; then
     if [ "$arch" != "amd64" ]; then
         exit 0
     fi
-    bootstrapver=$(curl https://golang.org/VERSION?m=text)
+    bootstrapver=$(curl https://go.dev/VERSION?m=text)
     cd $TMPDIR
     curl -sSL "https://storage.googleapis.com/golang/$bootstrapver.darwin-$ARCH.tar.gz" | tar -xz
     cd -
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index cbd75b3a..24e1591c 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -3846,10 +3846,11 @@ func TestEvaluateCommandRequest(t *testing.T) {
 
 					// Request help.
 					const dlvHelp = `The following commands are available:
-    help (alias: h) 	 Prints the help message.
-    config 	 Changes configuration parameters.
+    dlv help (alias: h) 	 Prints the help message.
+    dlv config 	 Changes configuration parameters.
+    dlv sources (alias: s) 	 Print list of source files.
 
-Type help followed by a command for full documentation.
+Type 'dlv help' followed by a command for full documentation.
 `
 					client.EvaluateRequest("dlv help", 1000, "repl")
 					got := client.ExpectEvaluateResponse(t)
@@ -3909,6 +3910,25 @@ Type help followed by a command for full documentation.
 					got = client.ExpectEvaluateResponse(t)
 					checkEval(t, got, "substitutePath\t[]\n\nUpdated", noChildren)
 
+					// Test sources.
+					client.EvaluateRequest("dlv sources", 1000, "repl")
+					got = client.ExpectEvaluateResponse(t)
+					if !strings.Contains(got.Body.Result, fixture.Source) {
+						t.Errorf("\ngot: %#v, want sources contains %s", got, fixture.Source)
+					}
+
+					client.EvaluateRequest(fmt.Sprintf("dlv sources .*%s", strings.ReplaceAll(filepath.Base(fixture.Source), ".", "\\.")), 1000, "repl")
+					got = client.ExpectEvaluateResponse(t)
+					if got.Body.Result != fixture.Source {
+						t.Errorf("\ngot: %#v, want sources=%q", got, fixture.Source)
+					}
+
+					client.EvaluateRequest("dlv sources nonexistentsource", 1000, "repl")
+					got = client.ExpectEvaluateResponse(t)
+					if got.Body.Result != "" {
+						t.Errorf("\ngot: %#v, want sources=\"\"", got)
+					}
+
 					// Test bad inputs.
 					client.EvaluateRequest("dlv help bad", 1000, "repl")
 					client.ExpectErrorResponse(t)