diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md index 100d189a..92da65ad 100644 --- a/Documentation/cli/README.md +++ b/Documentation/cli/README.md @@ -375,7 +375,7 @@ Aliases: gr ## goroutines List program goroutines. - goroutines [-u|-r|-g|-s] [-t [depth]] [-l] [-with loc expr] [-without loc expr] [-group argument] + goroutines [-u|-r|-g|-s] [-t [depth]] [-l] [-with loc expr] [-without loc expr] [-group argument] [-exec command] Print out info for every goroutine. The flag controls what information is shown along with each goroutine: @@ -431,6 +431,12 @@ Groups goroutines by the given location, running status or user classification, Groups goroutines by the value of the label with the specified key. +EXEC + + goroutines -exec + +Runs the command on every goroutine. + Aliases: grs diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go index 104fae3a..bfa5ddc3 100644 --- a/pkg/terminal/command.go +++ b/pkg/terminal/command.go @@ -224,9 +224,9 @@ If called with the locspec argument it will delete all the breakpoints matching {aliases: []string{"toggle"}, group: breakCmds, cmdFn: toggle, helpMsg: `Toggles on or off a breakpoint. toggle `}, - {aliases: []string{"goroutines", "grs"}, group: goroutineCmds, cmdFn: goroutines, helpMsg: `List program goroutines. + {aliases: []string{"goroutines", "grs"}, group: goroutineCmds, cmdFn: c.goroutines, helpMsg: `List program goroutines. - goroutines [-u|-r|-g|-s] [-t [depth]] [-l] [-with loc expr] [-without loc expr] [-group argument] + goroutines [-u|-r|-g|-s] [-t [depth]] [-l] [-with loc expr] [-without loc expr] [-group argument] [-exec command] Print out info for every goroutine. The flag controls what information is shown along with each goroutine: @@ -281,6 +281,12 @@ Groups goroutines by the given location, running status or user classification, goroutines -group label key Groups goroutines by the value of the label with the specified key. + +EXEC + + goroutines -exec + +Runs the command on every goroutine. `}, {aliases: []string{"goroutine", "gr"}, group: goroutineCmds, allowedPrefixes: onPrefix, cmdFn: c.goroutine, helpMsg: `Shows or changes current goroutine @@ -796,7 +802,7 @@ func (a byGoroutineID) Len() int { return len(a) } func (a byGoroutineID) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a byGoroutineID) Less(i, j int) bool { return a[i].ID < a[j].ID } -func printGoroutines(t *Term, indent string, gs []*api.Goroutine, fgl api.FormatGoroutineLoc, flags api.PrintGoroutinesFlags, depth int, state *api.DebuggerState) error { +func (c *Commands) printGoroutines(t *Term, ctx callContext, indent string, gs []*api.Goroutine, fgl api.FormatGoroutineLoc, flags api.PrintGoroutinesFlags, depth int, cmd string, state *api.DebuggerState) error { for _, g := range gs { prefix := indent + " " if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID { @@ -813,12 +819,18 @@ func printGoroutines(t *Term, indent string, gs []*api.Goroutine, fgl api.Format } printStack(t, t.stdout, stack, indent+"\t", false) } + if cmd != "" { + ctx.Scope.GoroutineID = g.ID + if err := c.CallWithContext(cmd, t, ctx); err != nil { + return err + } + } } return nil } -func goroutines(t *Term, ctx callContext, argstr string) error { - filters, group, fgl, flags, depth, batchSize, err := api.ParseGoroutineArgs(argstr) +func (c *Commands) goroutines(t *Term, ctx callContext, argstr string) error { + filters, group, fgl, flags, depth, batchSize, cmd, err := api.ParseGoroutineArgs(argstr) if err != nil { return err } @@ -847,7 +859,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error { if len(groups) > 0 { for i := range groups { fmt.Fprintf(t.stdout, "%s\n", groups[i].Name) - err = printGoroutines(t, "\t", gs[groups[i].Offset:][:groups[i].Count], fgl, flags, depth, state) + err = c.printGoroutines(t, ctx, "\t", gs[groups[i].Offset:][:groups[i].Count], fgl, flags, depth, cmd, state) if err != nil { return err } @@ -861,7 +873,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error { } } else { sort.Sort(byGoroutineID(gs)) - err = printGoroutines(t, "", gs, fgl, flags, depth, state) + err = c.printGoroutines(t, ctx, "", gs, fgl, flags, depth, cmd, state) if err != nil { return err } diff --git a/service/api/command.go b/service/api/command.go index 503dc54c..86e43cd0 100644 --- a/service/api/command.go +++ b/service/api/command.go @@ -11,6 +11,7 @@ type PrintGoroutinesFlags uint8 const ( PrintGoroutinesStack PrintGoroutinesFlags = 1 << iota PrintGoroutinesLabels + PrintGoroutinesExec ) type FormatGoroutineLoc int @@ -30,7 +31,7 @@ const ( // The number of goroutines we're going to request on each RPC call const goroutineBatchSize = 10000 -func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupingOptions, FormatGoroutineLoc, PrintGoroutinesFlags, int, int, error) { +func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupingOptions, FormatGoroutineLoc, PrintGoroutinesFlags, int, int, string, error) { args := strings.Split(argstr, " ") var filters []ListGoroutinesFilter var group GoroutineGroupingOptions @@ -38,6 +39,7 @@ func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupin var flags PrintGoroutinesFlags var depth = 10 var batchSize = goroutineBatchSize + var cmd string group.MaxGroupMembers = maxGroupMembers group.MaxGroups = maxGoroutineGroups @@ -69,14 +71,14 @@ func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupin case "-w", "-with": filter, err := readGoroutinesFilter(args, &i) if err != nil { - return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, fmt.Errorf("wrong argument: '%s'", arg) + return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, "", fmt.Errorf("wrong argument: '%s'", arg) } filters = append(filters, *filter) case "-wo", "-without": filter, err := readGoroutinesFilter(args, &i) if err != nil { - return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, fmt.Errorf("wrong argument: '%s'", arg) + return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, "", fmt.Errorf("wrong argument: '%s'", arg) } filter.Negated = true filters = append(filters, *filter) @@ -85,25 +87,30 @@ func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupin var err error group.GroupBy, err = readGoroutinesFilterKind(args, i+1) if err != nil { - return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, fmt.Errorf("wrong argument: '%s'", arg) + return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, "", fmt.Errorf("wrong argument: '%s'", arg) } i++ if group.GroupBy == GoroutineLabel { if i+1 >= len(args) { - return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, fmt.Errorf("wrong argument: '%s'", arg) + return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, "", fmt.Errorf("wrong argument: '%s'", arg) } group.GroupByKey = args[i+1] i++ } batchSize = 0 // grouping only works well if run on all goroutines + case "-exec": + flags |= PrintGoroutinesExec + cmd = strings.Join(args[i+1:], " ") + i = len(args) + case "": // nothing to do default: - return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, fmt.Errorf("wrong argument: '%s'", arg) + return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, "", fmt.Errorf("wrong argument: '%s'", arg) } } - return filters, group, fgl, flags, depth, batchSize, nil + return filters, group, fgl, flags, depth, batchSize, cmd, nil } func readGoroutinesFilterKind(args []string, i int) (GoroutineField, error) { diff --git a/service/dap/server.go b/service/dap/server.go index 2dda5736..1862e5a4 100644 --- a/service/dap/server.go +++ b/service/dap/server.go @@ -1608,7 +1608,7 @@ func (s *Session) onThreadsRequest(request *dap.ThreadsRequest) { gs, next, err = s.debugger.Goroutines(0, maxGoroutines) if err == nil { // Parse the goroutine arguments. - filters, _, _, _, _, _, parseErr := api.ParseGoroutineArgs(s.args.GoroutineFilters) + filters, _, _, _, _, _, _, parseErr := api.ParseGoroutineArgs(s.args.GoroutineFilters) if parseErr != nil { s.logToConsole(parseErr.Error()) }