Example #1
0
// interactive displays a prompt and reads commands for profile
// manipulation/visualization.
func interactive(p *profile.Profile, obj plugin.ObjTool, ui plugin.UI, f *flags) error {
	updateAutoComplete(p)

	// Enter command processing loop.
	ui.Print("Entering interactive mode (type \"help\" for commands)")
	ui.SetAutoComplete(commands.NewCompleter(f.commands))

	for {
		input, err := readCommand(p, ui, f)
		if err != nil {
			if err != io.EOF {
				return err
			}
			if input == "" {
				return nil
			}
		}
		// Process simple commands.
		switch input {
		case "":
			continue
		case ":":
			f.flagFocus = newString("")
			f.flagIgnore = newString("")
			f.flagTagFocus = newString("")
			f.flagTagIgnore = newString("")
			f.flagHide = newString("")
			continue
		}

		fields := splitCommand(input)
		// Process report generation commands.
		if _, ok := f.commands[fields[0]]; ok {
			if err := generateReport(p, fields, obj, ui, f); err != nil {
				if err == io.EOF {
					return nil
				}
				ui.PrintErr(err)
			}
			continue
		}

		switch cmd := fields[0]; cmd {
		case "help":
			commandHelp(fields, ui, f)
			continue
		case "exit", "quit":
			return nil
		}

		// Process option settings.
		if of, err := optFlags(p, input, f); err == nil {
			f = of
		} else {
			ui.PrintErr("Error: ", err.Error())
		}
	}
}
Example #2
0
func (f *flags) usage(ui plugin.UI) {
	var commandMsg []string
	for name, cmd := range f.commands {
		if cmd.HasParam {
			name = name + "=p"
		}
		commandMsg = append(commandMsg,
			fmt.Sprintf("  -%-16s %s", name, cmd.Usage))
	}

	sort.Strings(commandMsg)

	text := usageMsgHdr + strings.Join(commandMsg, "\n") + "\n" + usageMsg + "\n"
	if f.extraUsage != "" {
		text += f.extraUsage + "\n"
	}
	text += usageMsgVars
	ui.Print(text)
}
Example #3
0
func commandHelp(_ []string, ui plugin.UI, f *flags) error {
	help := `
 Commands:
   cmd [n] [--cum] [focus_regex]* [-ignore_regex]*
       Produce a text report with the top n entries.
       Include samples matching focus_regex, and exclude ignore_regex.
       Add --cum to sort using cumulative data.
       Available commands:
`
	var commands []string
	for name, cmd := range f.commands {
		commands = append(commands, fmt.Sprintf("         %-12s %s", name, cmd.Usage))
	}
	sort.Strings(commands)

	help = help + strings.Join(commands, "\n") + `
   peek func_regex
       Display callers and callees of functions matching func_regex.

   dot [n] [focus_regex]* [-ignore_regex]* [>file]
       Produce an annotated callgraph with the top n entries.
       Include samples matching focus_regex, and exclude ignore_regex.
       For other outputs, replace dot with:
       - Graphic formats: dot, svg, pdf, ps, gif, png (use > to name output file)
       - Graph viewer:    gv, web, evince, eog

   callgrind [n] [focus_regex]* [-ignore_regex]* [>file]
       Produce a file in callgrind-compatible format.
       Include samples matching focus_regex, and exclude ignore_regex.

   weblist func_regex [-ignore_regex]*
       Show annotated source with interspersed assembly in a web browser.

   list func_regex [-ignore_regex]*
       Print source for routines matching func_regex, and exclude ignore_regex.

   disasm func_regex [-ignore_regex]*
       Disassemble routines matching func_regex, and exclude ignore_regex.

   tags tag_regex [-ignore_regex]*
       List tags with key:value matching tag_regex and exclude ignore_regex.

   quit/exit/^D
 	     Exit pprof.

   option=value
       The following options can be set individually:
           cum/flat:           Sort entries based on cumulative or flat data
           call_tree:          Build context-sensitive call trees
           nodecount:          Max number of entries to display
           nodefraction:       Min frequency ratio of nodes to display
           edgefraction:       Min frequency ratio of edges to display
           focus/ignore:       Regexp to include/exclude samples by name/file
           tagfocus/tagignore: Regexp or value range to filter samples by tag
                               eg "1mb", "1mb:2mb", ":64kb"

           functions:          Level of aggregation for sample data
           files:
           lines:
           addresses:

           unit:               Measurement unit to use on reports

           Sample value selection by index:
            sample_index:      Index of sample value to display
            mean:              Average sample value over first value

           Sample value selection by name:
            alloc_space        for heap profiles
            alloc_objects
            inuse_space
            inuse_objects

            total_delay        for contention profiles
            mean_delay
            contentions

   :   Clear focus/ignore/hide/tagfocus/tagignore`

	ui.Print(help)
	return nil
}
Example #4
0
// grabProfile fetches and symbolizes a profile.
func grabProfile(source, exec, buildid string, fetch plugin.Fetcher, sym plugin.Symbolizer, obj plugin.ObjTool, ui plugin.UI, f *flags) (*profile.Profile, error) {
	source, host, duration := adjustURL(source, *f.flagSeconds, ui)
	remote := host != ""

	if remote {
		ui.Print("Fetching profile from ", source)
		if duration != 0 {
			ui.Print("Please wait... (" + duration.String() + ")")
		}
	}

	now := time.Now()
	// Fetch profile from source.
	// Give 50% slack on the timeout.
	p, err := fetch(source, duration+duration/2, ui)
	if err != nil {
		return nil, err
	}

	// Update the time/duration if the profile source doesn't include it.
	// TODO(rsilvera): Remove this when we remove support for legacy profiles.
	if remote {
		if p.TimeNanos == 0 {
			p.TimeNanos = now.UnixNano()
		}
		if duration != 0 && p.DurationNanos == 0 {
			p.DurationNanos = int64(duration)
		}
	}

	// Replace executable/buildID with the options provided in the
	// command line. Assume the executable is the first Mapping entry.
	if exec != "" || buildid != "" {
		if len(p.Mapping) == 0 {
			// Create a fake mapping to hold the user option, and associate
			// all samples to it.
			m := &profile.Mapping{
				ID: 1,
			}
			for _, l := range p.Location {
				l.Mapping = m
			}
			p.Mapping = []*profile.Mapping{m}
		}
		if exec != "" {
			p.Mapping[0].File = exec
		}
		if buildid != "" {
			p.Mapping[0].BuildID = buildid
		}
	}

	if err := sym(*f.flagSymbolize, source, p, obj, ui); err != nil {
		return nil, err
	}

	// Save a copy of any remote profiles, unless the user is explicitly
	// saving it.
	if remote && !f.isFormat("proto") {
		prefix := "pprof."
		if len(p.Mapping) > 0 && p.Mapping[0].File != "" {
			prefix = prefix + filepath.Base(p.Mapping[0].File) + "."
		}
		if !strings.ContainsRune(host, os.PathSeparator) {
			prefix = prefix + host + "."
		}
		for _, s := range p.SampleType {
			prefix = prefix + s.Type + "."
		}

		dir := os.Getenv("PPROF_TMPDIR")
		tempFile, err := tempfile.New(dir, prefix, ".pb.gz")
		if err == nil {
			if err = p.Write(tempFile); err == nil {
				ui.PrintErr("Saved profile in ", tempFile.Name())
			}
		}
		if err != nil {
			ui.PrintErr("Could not save profile: ", err)
		}
	}

	if err := p.Demangle(obj.Demangle); err != nil {
		ui.PrintErr("Failed to demangle profile: ", err)
	}

	if err := p.CheckValid(); err != nil {
		return nil, fmt.Errorf("Grab %s: %v", source, err)
	}

	return p, nil
}