golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/internal/driver/cli.go (about)

     1  // Copyright 2014 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package driver
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"strings"
    21  
    22  	"github.com/google/pprof/internal/binutils"
    23  	"github.com/google/pprof/internal/plugin"
    24  )
    25  
    26  type source struct {
    27  	Sources  []string
    28  	ExecName string
    29  	BuildID  string
    30  	Base     []string
    31  
    32  	Seconds   int
    33  	Timeout   int
    34  	Symbolize string
    35  }
    36  
    37  // Parse parses the command lines through the specified flags package
    38  // and returns the source of the profile and optionally the command
    39  // for the kind of report to generate (nil for interactive use).
    40  func parseFlags(o *plugin.Options) (*source, []string, error) {
    41  	flag := o.Flagset
    42  	// Comparisons.
    43  	flagBase := flag.StringList("base", "", "Source for base profile for comparison")
    44  	// Internal options.
    45  	flagSymbolize := flag.String("symbolize", "", "Options for profile symbolization")
    46  	flagBuildID := flag.String("buildid", "", "Override build id for first mapping")
    47  	// CPU profile options
    48  	flagSeconds := flag.Int("seconds", -1, "Length of time for dynamic profiles")
    49  	// Heap profile options
    50  	flagInUseSpace := flag.Bool("inuse_space", false, "Display in-use memory size")
    51  	flagInUseObjects := flag.Bool("inuse_objects", false, "Display in-use object counts")
    52  	flagAllocSpace := flag.Bool("alloc_space", false, "Display allocated memory size")
    53  	flagAllocObjects := flag.Bool("alloc_objects", false, "Display allocated object counts")
    54  	// Contention profile options
    55  	flagTotalDelay := flag.Bool("total_delay", false, "Display total delay at each region")
    56  	flagContentions := flag.Bool("contentions", false, "Display number of delays at each region")
    57  	flagMeanDelay := flag.Bool("mean_delay", false, "Display mean delay at each region")
    58  	flagTools := flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames")
    59  
    60  	flagTimeout := flag.Int("timeout", -1, "Timeout in seconds for fetching a profile")
    61  
    62  	// Flags used during command processing
    63  	installedFlags := installFlags(flag)
    64  
    65  	flagCommands := make(map[string]*bool)
    66  	flagParamCommands := make(map[string]*string)
    67  	for name, cmd := range pprofCommands {
    68  		if cmd.hasParam {
    69  			flagParamCommands[name] = flag.String(name, "", "Generate a report in "+name+" format, matching regexp")
    70  		} else {
    71  			flagCommands[name] = flag.Bool(name, false, "Generate a report in "+name+" format")
    72  		}
    73  	}
    74  
    75  	args := flag.Parse(func() {
    76  		o.UI.Print(usageMsgHdr +
    77  			usage(true) +
    78  			usageMsgSrc +
    79  			flag.ExtraUsage() +
    80  			usageMsgVars)
    81  	})
    82  	if len(args) == 0 {
    83  		return nil, nil, fmt.Errorf("no profile source specified")
    84  	}
    85  
    86  	var execName string
    87  	// Recognize first argument as an executable or buildid override.
    88  	if len(args) > 1 {
    89  		arg0 := args[0]
    90  		if file, err := o.Obj.Open(arg0, 0, ^uint64(0), 0); err == nil {
    91  			file.Close()
    92  			execName = arg0
    93  			args = args[1:]
    94  		} else if *flagBuildID == "" && isBuildID(arg0) {
    95  			*flagBuildID = arg0
    96  			args = args[1:]
    97  		}
    98  	}
    99  
   100  	// Report conflicting options
   101  	if err := updateFlags(installedFlags); err != nil {
   102  		return nil, nil, err
   103  	}
   104  
   105  	cmd, err := outputFormat(flagCommands, flagParamCommands)
   106  	if err != nil {
   107  		return nil, nil, err
   108  	}
   109  
   110  	si := pprofVariables["sample_index"].value
   111  	si = sampleIndex(flagTotalDelay, si, "delay", "-total_delay", o.UI)
   112  	si = sampleIndex(flagMeanDelay, si, "delay", "-mean_delay", o.UI)
   113  	si = sampleIndex(flagContentions, si, "contentions", "-contentions", o.UI)
   114  	si = sampleIndex(flagInUseSpace, si, "inuse_space", "-inuse_space", o.UI)
   115  	si = sampleIndex(flagInUseObjects, si, "inuse_objects", "-inuse_objects", o.UI)
   116  	si = sampleIndex(flagAllocSpace, si, "alloc_space", "-alloc_space", o.UI)
   117  	si = sampleIndex(flagAllocObjects, si, "alloc_objects", "-alloc_objects", o.UI)
   118  	pprofVariables.set("sample_index", si)
   119  
   120  	if *flagMeanDelay {
   121  		pprofVariables.set("mean", "true")
   122  	}
   123  
   124  	source := &source{
   125  		Sources:   args,
   126  		ExecName:  execName,
   127  		BuildID:   *flagBuildID,
   128  		Seconds:   *flagSeconds,
   129  		Timeout:   *flagTimeout,
   130  		Symbolize: *flagSymbolize,
   131  	}
   132  
   133  	for _, s := range *flagBase {
   134  		if *s != "" {
   135  			source.Base = append(source.Base, *s)
   136  		}
   137  	}
   138  
   139  	if bu, ok := o.Obj.(*binutils.Binutils); ok {
   140  		bu.SetTools(*flagTools)
   141  	}
   142  	return source, cmd, nil
   143  }
   144  
   145  // installFlags creates command line flags for pprof variables.
   146  func installFlags(flag plugin.FlagSet) flagsInstalled {
   147  	f := flagsInstalled{
   148  		ints:    make(map[string]*int),
   149  		bools:   make(map[string]*bool),
   150  		floats:  make(map[string]*float64),
   151  		strings: make(map[string]*string),
   152  	}
   153  	for n, v := range pprofVariables {
   154  		switch v.kind {
   155  		case boolKind:
   156  			if v.group != "" {
   157  				// Set all radio variables to false to identify conflicts.
   158  				f.bools[n] = flag.Bool(n, false, v.help)
   159  			} else {
   160  				f.bools[n] = flag.Bool(n, v.boolValue(), v.help)
   161  			}
   162  		case intKind:
   163  			f.ints[n] = flag.Int(n, v.intValue(), v.help)
   164  		case floatKind:
   165  			f.floats[n] = flag.Float64(n, v.floatValue(), v.help)
   166  		case stringKind:
   167  			f.strings[n] = flag.String(n, v.value, v.help)
   168  		}
   169  	}
   170  	return f
   171  }
   172  
   173  // updateFlags updates the pprof variables according to the flags
   174  // parsed in the command line.
   175  func updateFlags(f flagsInstalled) error {
   176  	vars := pprofVariables
   177  	groups := map[string]string{}
   178  	for n, v := range f.bools {
   179  		vars.set(n, fmt.Sprint(*v))
   180  		if *v {
   181  			g := vars[n].group
   182  			if g != "" && groups[g] != "" {
   183  				return fmt.Errorf("conflicting options %q and %q set", n, groups[g])
   184  			}
   185  			groups[g] = n
   186  		}
   187  	}
   188  	for n, v := range f.ints {
   189  		vars.set(n, fmt.Sprint(*v))
   190  	}
   191  	for n, v := range f.floats {
   192  		vars.set(n, fmt.Sprint(*v))
   193  	}
   194  	for n, v := range f.strings {
   195  		vars.set(n, *v)
   196  	}
   197  	return nil
   198  }
   199  
   200  type flagsInstalled struct {
   201  	ints    map[string]*int
   202  	bools   map[string]*bool
   203  	floats  map[string]*float64
   204  	strings map[string]*string
   205  }
   206  
   207  // isBuildID determines if the profile may contain a build ID, by
   208  // checking that it is a string of hex digits.
   209  func isBuildID(id string) bool {
   210  	return strings.Trim(id, "0123456789abcdefABCDEF") == ""
   211  }
   212  
   213  func sampleIndex(flag *bool, si string, sampleType, option string, ui plugin.UI) string {
   214  	if *flag {
   215  		if si == "" {
   216  			return sampleType
   217  		}
   218  		ui.PrintErr("Multiple value selections, ignoring ", option)
   219  	}
   220  	return si
   221  }
   222  
   223  func outputFormat(bcmd map[string]*bool, acmd map[string]*string) (cmd []string, err error) {
   224  	for n, b := range bcmd {
   225  		if *b {
   226  			if cmd != nil {
   227  				return nil, fmt.Errorf("must set at most one output format")
   228  			}
   229  			cmd = []string{n}
   230  		}
   231  	}
   232  	for n, s := range acmd {
   233  		if *s != "" {
   234  			if cmd != nil {
   235  				return nil, fmt.Errorf("must set at most one output format")
   236  			}
   237  			cmd = []string{n, *s}
   238  		}
   239  	}
   240  	return cmd, nil
   241  }
   242  
   243  var usageMsgHdr = "usage: pprof [options] [-base source] [binary] <source> ...\n"
   244  
   245  var usageMsgSrc = "\n\n" +
   246  	"  Source options:\n" +
   247  	"    -seconds              Duration for time-based profile collection\n" +
   248  	"    -timeout              Timeout in seconds for profile collection\n" +
   249  	"    -buildid              Override build id for main binary\n" +
   250  	"    -base source          Source of profile to use as baseline\n" +
   251  	"    profile.pb.gz         Profile in compressed protobuf format\n" +
   252  	"    legacy_profile        Profile in legacy pprof format\n" +
   253  	"    http://host/profile   URL for profile handler to retrieve\n" +
   254  	"    -symbolize=           Controls source of symbol information\n" +
   255  	"      none                  Do not attempt symbolization\n" +
   256  	"      local                 Examine only local binaries\n" +
   257  	"      fastlocal             Only get function names from local binaries\n" +
   258  	"      remote                Do not examine local binaries\n" +
   259  	"      force                 Force re-symbolization\n" +
   260  	"    Binary                  Local path or build id of binary for symbolization\n"
   261  
   262  var usageMsgVars = "\n\n" +
   263  	"  Misc options:\n" +
   264  	"   -tools                 Search path for object tools\n" +
   265  	"\n" +
   266  	"  Environment Variables:\n" +
   267  	"   PPROF_TMPDIR       Location for saved profiles (default $HOME/pprof)\n" +
   268  	"   PPROF_TOOLS        Search path for object-level tools\n" +
   269  	"   PPROF_BINARY_PATH  Search path for local binary files\n" +
   270  	"                      default: $HOME/pprof/binaries\n" +
   271  	"                      finds binaries by $name and $buildid/$name\n" +
   272  	"   * On Windows, %USERPROFILE% is used instead of $HOME"