terminal/command: 'goroutines' command add new flags '-exec command' (#3044)

* terminal/command: 'goroutines' command add new flags '-exec command'

Support run command on every goroutine.

Fixes #3043

* terminal/command: 'goroutines' command add new flags '-exec command'

Support run command on every goroutine.

Fixes #3043

* terminal/command: add -per-g-hitcount option to condition command

Support use per goroutine hitcount as hintcond operand.

Fixes #3050

Co-authored-by: roketyyang <roketyyang@tencent.com>
This commit is contained in:
roketyyang
2022-07-26 01:14:43 +08:00
committed by GitHub
parent 2900418e78
commit ec1d1efb7f
4 changed files with 41 additions and 16 deletions

View File

@ -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 <command>
Runs the command on every goroutine.
Aliases: grs

View File

@ -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 <breakpoint name or id>`},
{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 <command>
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
}

View File

@ -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) {

View File

@ -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())
}