Skip to content

Commit

Permalink
pkg/terminal: add 'packages' command
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hyangah committed Sep 15, 2023
1 parent 3772589 commit 622c3db
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 4 deletions.
2 changes: 1 addition & 1 deletion Documentation/cli/starlark.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions pkg/terminal/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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>]
Expand Down Expand Up @@ -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))
}
Expand Down
28 changes: 28 additions & 0 deletions pkg/terminal/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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'")
}
})
}
10 changes: 9 additions & 1 deletion pkg/terminal/starbind/starlark_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions service/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions service/rpc2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 15 additions & 2 deletions service/rpc2/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rpc2
import (
"errors"
"fmt"
"regexp"
"sort"
"time"

Expand Down Expand Up @@ -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
}
Expand All @@ -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 {
Expand Down

0 comments on commit 622c3db

Please sign in to comment.