1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
package api
import (
"errors"
"fmt"
"strconv"
"strings"
)
type PrintGoroutinesFlags uint8
const (
PrintGoroutinesStack PrintGoroutinesFlags = 1 << iota
PrintGoroutinesLabels
PrintGoroutinesExec
)
type FormatGoroutineLoc int
const (
FglRuntimeCurrent = FormatGoroutineLoc(iota)
FglUserCurrent
FglGo
FglStart
)
const (
maxGroupMembers = 5
maxGoroutineGroups = 50
)
// 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, string, error) {
args := strings.Split(argstr, " ")
var filters []ListGoroutinesFilter
var group GoroutineGroupingOptions
var fgl = FglUserCurrent
var flags PrintGoroutinesFlags
var depth = 10
var batchSize = goroutineBatchSize
var cmd string
group.MaxGroupMembers = maxGroupMembers
group.MaxGroups = maxGoroutineGroups
for i := 0; i < len(args); i++ {
arg := args[i]
switch arg {
case "-u":
fgl = FglUserCurrent
case "-r":
fgl = FglRuntimeCurrent
case "-g":
fgl = FglGo
case "-s":
fgl = FglStart
case "-l":
flags |= PrintGoroutinesLabels
case "-t":
flags |= PrintGoroutinesStack
// optional depth argument
if i+1 < len(args) && len(args[i+1]) > 0 {
n, err := strconv.Atoi(args[i+1])
if err == nil {
depth = n
i++
}
}
case "-w", "-with":
filter, err := readGoroutinesFilter(args, &i)
if err != nil {
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)
}
filter.Negated = true
filters = append(filters, *filter)
case "-group":
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)
}
i++
if group.GroupBy == GoroutineLabel {
if i+1 >= len(args) {
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 "-chan":
i++
if i >= len(args) {
return nil, GoroutineGroupingOptions{}, 0, 0, 0, 0, "", errors.New("not enough arguments after -chan")
}
filters = append(filters, ListGoroutinesFilter{Kind: GoroutineWaitingOnChannel, Arg: args[i]})
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 filters, group, fgl, flags, depth, batchSize, cmd, nil
}
func readGoroutinesFilterKind(args []string, i int) (GoroutineField, error) {
if i >= len(args) {
return GoroutineFieldNone, fmt.Errorf("%s must be followed by an argument", args[i-1])
}
switch args[i] {
case "curloc":
return GoroutineCurrentLoc, nil
case "userloc":
return GoroutineUserLoc, nil
case "goloc":
return GoroutineGoLoc, nil
case "startloc":
return GoroutineStartLoc, nil
case "label":
return GoroutineLabel, nil
case "running":
return GoroutineRunning, nil
case "user":
return GoroutineUser, nil
default:
return GoroutineFieldNone, fmt.Errorf("unrecognized argument to %s %s", args[i-1], args[i])
}
}
func readGoroutinesFilter(args []string, pi *int) (*ListGoroutinesFilter, error) {
r := new(ListGoroutinesFilter)
var err error
r.Kind, err = readGoroutinesFilterKind(args, *pi+1)
if err != nil {
return nil, err
}
*pi++
switch r.Kind {
case GoroutineRunning, GoroutineUser:
return r, nil
}
if *pi+1 >= len(args) {
return nil, fmt.Errorf("%s %s needs to be followed by an expression", args[*pi-1], args[*pi])
}
r.Arg = args[*pi+1]
*pi++
return r, nil
}
|