pkg/terminal: add 'packages' command (#3499)

This command lists the packages included in the debugee.
The implementation utilizes "ListPackagesBuildInfo" RPC.

In order to support server-side filtering like `sources` and other
commands, expanded the ListPackagesBuildInfo RPC to take an optional
filter field.
This commit is contained in:
Hyang-Ah Hana Kim 2023-09-18 11:59:58 -04:00 committed by GitHub
parent 382bb0fde6
commit e072a64903
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 87 additions and 4 deletions

@ -89,6 +89,7 @@ Command | Description
[help](#help) | Prints the help message.
[libraries](#libraries) | List loaded dynamic libraries
[list](#list) | Show source code.
[packages](#packages) | Print list of packages.
[source](#source) | Executes a file containing a list of delve commands
[sources](#sources) | Print list of source files.
[target](#target) | Manages child process debugging.
@ -539,6 +540,14 @@ The command 'on <bp> cond <cond-arguments>' is equivalent to 'cond <bp> <cond-ar
The command 'on x -edit' can be used to edit the list of commands executed when the breakpoint is hit.
## packages
Print list of packages.
packages [<regex>]
If regex is specified only the packages matching it will be returned.
## print
Evaluate an expression.

@ -54,7 +54,7 @@ functions(Filter) | Equivalent to API call [ListFunctions](https://godoc.org/git
goroutines(Start, Count, Filters, GoroutineGroupingOptions, EvalScope) | Equivalent to API call [ListGoroutines](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListGoroutines)
local_vars(Scope, Cfg) | Equivalent to API call [ListLocalVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListLocalVars)
package_vars(Filter, Cfg) | Equivalent to API call [ListPackageVars](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackageVars)
packages_build_info(IncludeFiles) | Equivalent to API call [ListPackagesBuildInfo](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
packages_build_info(IncludeFiles, Filter) | Equivalent to API call [ListPackagesBuildInfo](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListPackagesBuildInfo)
registers(ThreadID, IncludeFp, Scope) | Equivalent to API call [ListRegisters](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListRegisters)
sources(Filter) | Equivalent to API call [ListSources](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListSources)
targets() | Equivalent to API call [ListTargets](https://godoc.org/github.com/go-delve/delve/service/rpc2#RPCServer.ListTargets)

@ -356,6 +356,11 @@ If regex is specified only the functions matching it will be returned.`},
types [<regex>]
If regex is specified only the types matching it will be returned.`},
{aliases: []string{"packages"}, cmdFn: packages, helpMsg: `Print list of packages.
packages [<regex>]
If regex is specified only the packages matching it will be returned.`},
{aliases: []string{"args"}, allowedPrefixes: onPrefix | deferredPrefix, group: dataCmds, cmdFn: args, helpMsg: `Print function arguments.
[goroutine <n>] [frame <m>] args [-v] [<regex>]
@ -2224,6 +2229,18 @@ func sources(t *Term, ctx callContext, args string) error {
return t.printSortedStrings(t.client.ListSources(args))
}
func packages(t *Term, ctx callContext, args string) error {
info, err := t.client.ListPackagesBuildInfo(args, false)
if err != nil {
return err
}
pkgs := make([]string, 0, len(info))
for _, i := range info {
pkgs = append(pkgs, i.ImportPath)
}
return t.printSortedStrings(pkgs, nil)
}
func funcs(t *Term, ctx callContext, args string) error {
return t.printSortedStrings(t.client.ListFunctions(args))
}

@ -1445,3 +1445,31 @@ func TestRestartBreakpoints(t *testing.T) {
}
})
}
func TestListPackages(t *testing.T) {
test.AllowRecording(t)
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
out := term.MustExec("packages")
t.Logf("> packages\n%s", out)
seen := map[string]bool{}
for _, p := range strings.Split(strings.TrimSpace(out), "\n") {
seen[p] = true
}
if !seen["main"] || !seen["runtime"] {
t.Error("output omits 'main' and 'runtime'")
}
out = term.MustExec("packages runtime")
t.Logf("> packages runtime\n%s", out)
for _, p := range strings.Split(strings.TrimSpace(out), "\n") {
if !strings.Contains(p, "runtime") {
t.Errorf("output includes unexpected %q", p)
}
seen[p] = true
}
if !seen["runtime"] {
t.Error("output omits 'runtime'")
}
})
}

@ -1275,11 +1275,19 @@ func (env *Env) starlarkPredeclare() (starlark.StringDict, map[string]string) {
return starlark.None, decorateError(thread, err)
}
}
if len(args) > 1 && args[1] != starlark.None {
err := unmarshalStarlarkValue(args[1], &rpcArgs.Filter, "Filter")
if err != nil {
return starlark.None, decorateError(thread, err)
}
}
for _, kv := range kwargs {
var err error
switch kv[0].(starlark.String) {
case "IncludeFiles":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.IncludeFiles, "IncludeFiles")
case "Filter":
err = unmarshalStarlarkValue(kv[1], &rpcArgs.Filter, "Filter")
default:
err = fmt.Errorf("unknown argument %q", kv[0])
}
@ -1293,7 +1301,7 @@ func (env *Env) starlarkPredeclare() (starlark.StringDict, map[string]string) {
}
return env.interfaceToStarlarkValue(rpcRet), nil
})
doc["packages_build_info"] = "builtin packages_build_info(IncludeFiles)\n\npackages_build_info returns the list of packages used by the program along with\nthe directory where each package was compiled and optionally the list of\nfiles constituting the package.\nNote that the directory path is a best guess and may be wrong is a tool\nother than cmd/go is used to perform the build."
doc["packages_build_info"] = "builtin packages_build_info(IncludeFiles, Filter)\n\npackages_build_info returns the list of packages used by the program along with\nthe directory where each package was compiled and optionally the list of\nfiles constituting the package.\nNote that the directory path is a best guess and may be wrong is a tool\nother than cmd/go is used to perform the build."
r["registers"] = starlark.NewBuiltin("registers", func(thread *starlark.Thread, _ *starlark.Builtin, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
if err := isCancelled(thread); err != nil {
return starlark.None, decorateError(thread, err)

@ -108,6 +108,8 @@ type Client interface {
ListFunctions(filter string) ([]string, error)
// ListTypes lists all types in the process matching filter.
ListTypes(filter string) ([]string, error)
// ListPackagesBuildInfo lists all packages in the process matching filter.
ListPackagesBuildInfo(filter string, includeFiles bool) ([]api.PackageBuildInfo, error)
// ListLocalVariables lists all local variables in scope.
ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
// ListFunctionArgs lists all arguments to the current function.

@ -357,6 +357,12 @@ func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]a
return out.Variables, err
}
func (c *RPCClient) ListPackagesBuildInfo(filter string, includeFiles bool) ([]api.PackageBuildInfo, error) {
var out ListPackagesBuildInfoOut
err := c.call("ListPackagesBuildInfo", ListPackagesBuildInfoIn{Filter: filter, IncludeFiles: includeFiles}, &out)
return out.List, err
}
func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
var out ListLocalVarsOut
err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out)

@ -3,6 +3,7 @@ package rpc2
import (
"errors"
"fmt"
"regexp"
"sort"
"time"
@ -912,12 +913,13 @@ func (s *RPCServer) ListDynamicLibraries(in ListDynamicLibrariesIn, out *ListDyn
return nil
}
// ListPackagesBuildInfoIn holds the arguments of ListPackages.
// ListPackagesBuildInfoIn holds the arguments of ListPackagesBuildInfo.
type ListPackagesBuildInfoIn struct {
IncludeFiles bool
Filter string // if not empty, returns only packages matching the regexp.
}
// ListPackagesBuildInfoOut holds the return values of ListPackages.
// ListPackagesBuildInfoOut holds the return values of ListPackagesBuildInfo.
type ListPackagesBuildInfoOut struct {
List []api.PackageBuildInfo
}
@ -928,9 +930,20 @@ type ListPackagesBuildInfoOut struct {
// Note that the directory path is a best guess and may be wrong is a tool
// other than cmd/go is used to perform the build.
func (s *RPCServer) ListPackagesBuildInfo(in ListPackagesBuildInfoIn, out *ListPackagesBuildInfoOut) error {
var pattern *regexp.Regexp
if in.Filter != "" {
p, err := regexp.Compile(in.Filter)
if err != nil {
return fmt.Errorf("invalid Filter pattern: %v", err)
}
pattern = p
}
pkgs := s.debugger.ListPackagesBuildInfo(in.IncludeFiles)
out.List = make([]api.PackageBuildInfo, 0, len(pkgs))
for _, pkg := range pkgs {
if pattern != nil && !pattern.MatchString(pkg.ImportPath) {
continue
}
var files []string
if len(pkg.Files) > 0 {