gitlab.com/Raven-IO/raven-delve@v1.22.4/service/api/command.go (about)

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