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 ## goroutines
List program 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: 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. Groups goroutines by the value of the label with the specified key.
EXEC
goroutines -exec <command>
Runs the command on every goroutine.
Aliases: grs 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. {aliases: []string{"toggle"}, group: breakCmds, cmdFn: toggle, helpMsg: `Toggles on or off a breakpoint.
toggle <breakpoint name or id>`}, 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: 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 goroutines -group label key
Groups goroutines by the value of the label with the specified 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 {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) 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 (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 { for _, g := range gs {
prefix := indent + " " prefix := indent + " "
if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID { 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) 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 return nil
} }
func goroutines(t *Term, ctx callContext, argstr string) error { func (c *Commands) goroutines(t *Term, ctx callContext, argstr string) error {
filters, group, fgl, flags, depth, batchSize, err := api.ParseGoroutineArgs(argstr) filters, group, fgl, flags, depth, batchSize, cmd, err := api.ParseGoroutineArgs(argstr)
if err != nil { if err != nil {
return err return err
} }
@ -847,7 +859,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
if len(groups) > 0 { if len(groups) > 0 {
for i := range groups { for i := range groups {
fmt.Fprintf(t.stdout, "%s\n", groups[i].Name) 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 { if err != nil {
return err return err
} }
@ -861,7 +873,7 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
} }
} else { } else {
sort.Sort(byGoroutineID(gs)) 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 { if err != nil {
return err return err
} }

View File

@ -11,6 +11,7 @@ type PrintGoroutinesFlags uint8
const ( const (
PrintGoroutinesStack PrintGoroutinesFlags = 1 << iota PrintGoroutinesStack PrintGoroutinesFlags = 1 << iota
PrintGoroutinesLabels PrintGoroutinesLabels
PrintGoroutinesExec
) )
type FormatGoroutineLoc int type FormatGoroutineLoc int
@ -30,7 +31,7 @@ const (
// The number of goroutines we're going to request on each RPC call // The number of goroutines we're going to request on each RPC call
const goroutineBatchSize = 10000 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, " ") args := strings.Split(argstr, " ")
var filters []ListGoroutinesFilter var filters []ListGoroutinesFilter
var group GoroutineGroupingOptions var group GoroutineGroupingOptions
@ -38,6 +39,7 @@ func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupin
var flags PrintGoroutinesFlags var flags PrintGoroutinesFlags
var depth = 10 var depth = 10
var batchSize = goroutineBatchSize var batchSize = goroutineBatchSize
var cmd string
group.MaxGroupMembers = maxGroupMembers group.MaxGroupMembers = maxGroupMembers
group.MaxGroups = maxGoroutineGroups group.MaxGroups = maxGoroutineGroups
@ -69,14 +71,14 @@ func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupin
case "-w", "-with": case "-w", "-with":
filter, err := readGoroutinesFilter(args, &i) filter, err := readGoroutinesFilter(args, &i)
if err != nil { 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) filters = append(filters, *filter)
case "-wo", "-without": case "-wo", "-without":
filter, err := readGoroutinesFilter(args, &i) filter, err := readGoroutinesFilter(args, &i)
if err != nil { 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 filter.Negated = true
filters = append(filters, *filter) filters = append(filters, *filter)
@ -85,25 +87,30 @@ func ParseGoroutineArgs(argstr string) ([]ListGoroutinesFilter, GoroutineGroupin
var err error var err error
group.GroupBy, err = readGoroutinesFilterKind(args, i+1) group.GroupBy, err = readGoroutinesFilterKind(args, i+1)
if err != nil { 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++ i++
if group.GroupBy == GoroutineLabel { if group.GroupBy == GoroutineLabel {
if i+1 >= len(args) { 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] group.GroupByKey = args[i+1]
i++ i++
} }
batchSize = 0 // grouping only works well if run on all goroutines 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 "": case "":
// nothing to do // nothing to do
default: 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) { 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) gs, next, err = s.debugger.Goroutines(0, maxGoroutines)
if err == nil { if err == nil {
// Parse the goroutine arguments. // Parse the goroutine arguments.
filters, _, _, _, _, _, parseErr := api.ParseGoroutineArgs(s.args.GoroutineFilters) filters, _, _, _, _, _, _, parseErr := api.ParseGoroutineArgs(s.args.GoroutineFilters)
if parseErr != nil { if parseErr != nil {
s.logToConsole(parseErr.Error()) s.logToConsole(parseErr.Error())
} }