Skip to content

Commit

Permalink
Added an interactive web interface (#154)
Browse files Browse the repository at this point in the history
* Added an interactive web interface triggered by passing -http=port
on the command line.  The interface is available by visiting
localhost:port in a browser.

Requirements:
* Graphviz must be installed.
* Browser must support Javascript.
* Tested in recent stable versions of chrome and firefox.

Features:
* The entry point is a dot graph display (equivalent to "web" output).
* Nodes in the graph can be selected by clicking.
* A regular expression can also be typed in for selection.
* The current selection (either list of nodes or a regexp)
  can be focused, ignored, or hidden.
* Source code or disassembly of the current selection can be displayed.

* Remove unused function.

* Skip graph generation test if graphviz is not installed.

* Added -http port and the various modes of using pprof to the
usage message.

* Web interface now supports "show" option.

* Web interface automatically opens the browser pointed at
the page corresponding to command line arguments.

* Some tweaks for firefox.

* Handle review comments (better usage message, more testing).

* Handled review comments:

1. Capture and display errors like "Focus expression matched no samples".
2. Re-ordered buttons to match other interfaces.
3. Use UI.PrintErr to print error messages.

* Handle javascript code review comments (a bunch of cleanups).
Also added pprof binary to .gitignore.
  • Loading branch information
ghemawat authored and aalexand committed Jul 14, 2017
1 parent 5bd319a commit f83a3d8
Show file tree
Hide file tree
Showing 12 changed files with 1,128 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -5,3 +5,4 @@
.*.swp
core
coverage.txt
pprof
13 changes: 13 additions & 0 deletions README.md
Expand Up @@ -77,6 +77,19 @@ This will open a simple shell that takes pprof commands to generate reports.
Type 'help' for available commands/options.
```

## Run pprof via a web interface

If the `-http=port` option is specified, pprof starts a web server at
the specified port that provides an interactive web-based interface to pprof.

```
pprof -http=[port] [main_binary] profile.pb.gz
```

The preceding command should automatically open your web browser at
the right page; if not, you can manually visit the specified port in
your web browser.

## Using pprof with Linux Perf

pprof can read `perf.data` files generated by the
Expand Down
47 changes: 39 additions & 8 deletions doc/pprof.md
Expand Up @@ -29,7 +29,40 @@ location. pprof is agnostic to the profile semantics, so other uses are
possible. The interpretation of the reports generated by pprof depends on the
semantics defined by the source of the profile.

# General usage
# Usage Modes

There are few different ways of using `pprof`.

## Report generation

If a report format is requested on the command line:

pprof <format> [options] source

pprof will generate a report in the specified format and exit.
Formats can be either text, or graphical. See below for details about
supported formats, options, and sources.

## Interactive terminal use

Without a format specifier:

pprof [options] source

pprof will start an interactive shell in which the user can type
commands. Type `help` to get online help.

## Web interface

If a port is specified on the command line:

pprof -http=<port> [options] source

pprof will start serving HTTP requests on the specified port. Visit
the HTTP url corresponding to the port (typically `http://localhost:<port>/`)
in a browser to see the interface.

# Details

The objective of pprof is to generate a report for a profile. The report is
generated from a location hierarchy, which is reconstructed from the profile
Expand All @@ -38,14 +71,12 @@ itself, while *cum* is the value of the location plus all its
descendants. Samples that include a location multiple times (eg for recursive
functions) are counted only once per location.

The basic usage of pprof is

pprof <format> [options] source
## Options

Where *format* selects the nature of the report, and *options* configure the
contents of the report. Each option has a value, which can be boolean, numeric,
or strings. While only one format can be specified, most options can be selected
independently of each other.
*options* configure the contents of a report. Each option has a value,
which can be boolean, numeric, or strings. While only one format can
be specified, most options can be selected independently of each
other.

Some common pprof options are:

Expand Down
27 changes: 26 additions & 1 deletion internal/driver/cli.go
Expand Up @@ -32,6 +32,7 @@ type source struct {
Seconds int
Timeout int
Symbolize string
HTTPPort int
}

// Parse parses the command lines through the specified flags package
Expand All @@ -58,6 +59,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
flagTools := flag.String("tools", os.Getenv("PPROF_TOOLS"), "Path for object tool pathnames")

flagTimeout := flag.Int("timeout", -1, "Timeout in seconds for fetching a profile")
flagHTTPPort := flag.Int("http", 0, "Present interactive web based UI at the specified http port")

// Flags used during command processing
installedFlags := installFlags(flag)
Expand Down Expand Up @@ -106,6 +108,9 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
if err != nil {
return nil, nil, err
}
if cmd != nil && *flagHTTPPort != 0 {
return nil, nil, fmt.Errorf("--http is not compatible with an output format on the command line")
}

si := pprofVariables["sample_index"].value
si = sampleIndex(flagTotalDelay, si, "delay", "-total_delay", o.UI)
Expand All @@ -128,6 +133,7 @@ func parseFlags(o *plugin.Options) (*source, []string, error) {
Seconds: *flagSeconds,
Timeout: *flagTimeout,
Symbolize: *flagSymbolize,
HTTPPort: *flagHTTPPort,
}

for _, s := range *flagBase {
Expand Down Expand Up @@ -240,7 +246,25 @@ func outputFormat(bcmd map[string]*bool, acmd map[string]*string) (cmd []string,
return cmd, nil
}

var usageMsgHdr = "usage: pprof [options] [-base source] [binary] <source> ...\n"
var usageMsgHdr = `usage:
Produce output in the specified format.
pprof <format> [options] [binary] <source> ...
Omit the format to get an interactive shell whose commands can be used
to generate various views of a profile
pprof [options] [binary] <source> ...
Omit the format and provide the "-http" flag to get an interactive web
interface at the specified port that can be used to navigate through
various views of a profile.
pprof -http <port> [options] [binary] <source> ...
Details:
`

var usageMsgSrc = "\n\n" +
" Source options:\n" +
Expand All @@ -261,6 +285,7 @@ var usageMsgSrc = "\n\n" +

var usageMsgVars = "\n\n" +
" Misc options:\n" +
" -http port Provide web based interface at port\n" +
" -tools Search path for object tools\n" +
"\n" +
" Environment Variables:\n" +
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/commands.go
Expand Up @@ -263,7 +263,7 @@ func usage(commandLine bool) string {

var help string
if commandLine {
help = " Output formats (select only one):\n"
help = " Output formats (select at most one):\n"
} else {
help = " Commands:\n"
commands = append(commands, fmtHelp("o/options", "List options and their current values"))
Expand Down
22 changes: 17 additions & 5 deletions internal/driver/driver.go
Expand Up @@ -52,10 +52,13 @@ func PProf(eo *plugin.Options) error {
return generateReport(p, cmd, pprofVariables, o)
}

if src.HTTPPort > 0 {
return serveWebInterface(src.HTTPPort, p, o)
}
return interactive(p, o)
}

func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
func generateRawReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) (*command, *report.Report, error) {
p = p.Copy() // Prevent modification to the incoming profile.

vars = applyCommandOverrides(cmd, vars)
Expand All @@ -64,12 +67,12 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
relative := vars["relative_percentages"].boolValue()
if relative {
if err := applyFocus(p, vars, o.UI); err != nil {
return err
return nil, nil, err
}
}
ropt, err := reportOptions(p, vars)
if err != nil {
return err
return nil, nil, err
}
c := pprofCommands[cmd[0]]
if c == nil {
Expand All @@ -79,18 +82,27 @@ func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.
if len(cmd) == 2 {
s, err := regexp.Compile(cmd[1])
if err != nil {
return fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
return nil, nil, fmt.Errorf("parsing argument regexp %s: %v", cmd[1], err)
}
ropt.Symbol = s
}

rpt := report.New(p, ropt)
if !relative {
if err := applyFocus(p, vars, o.UI); err != nil {
return err
return nil, nil, err
}
}
if err := aggregate(p, vars); err != nil {
return nil, nil, err
}

return c, rpt, nil
}

func generateReport(p *profile.Profile, cmd []string, vars variables, o *plugin.Options) error {
c, rpt, err := generateRawReport(p, cmd, vars, o)
if err != nil {
return err
}

Expand Down

0 comments on commit f83a3d8

Please sign in to comment.