LEFT | RIGHT |
(no file at all) | |
| 1 // Copyright 2014 The Go Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style |
| 3 // license that can be found in the LICENSE file. |
| 4 |
| 5 // Package commands defines and manages the basic pprof commands |
| 6 package commands |
| 7 |
| 8 import ( |
| 9 "bytes" |
| 10 "fmt" |
| 11 "io" |
| 12 "os" |
| 13 "os/exec" |
| 14 "strings" |
| 15 |
| 16 "cmd/pprof/internal/plugin" |
| 17 "cmd/pprof/internal/report" |
| 18 "cmd/pprof/internal/svg" |
| 19 "cmd/pprof/internal/tempfile" |
| 20 ) |
| 21 |
| 22 // Commands describes the commands accepted by pprof. |
| 23 type Commands map[string]*Command |
| 24 |
| 25 // Command describes the actions for a pprof command. Includes a |
| 26 // function for command-line completion, the report format to use |
| 27 // during report generation, any postprocessing functions, and whether |
| 28 // the command expects a regexp parameter (typically a function name). |
| 29 type Command struct { |
| 30 Complete Completer // autocomplete for interactive mode |
| 31 Format int // report format to generate |
| 32 PostProcess PostProcessor // postprocessing to run on report |
| 33 HasParam bool // Collect a parameter from the CLI |
| 34 Usage string // Help text |
| 35 } |
| 36 |
| 37 // Completer is a function for command-line autocompletion |
| 38 type Completer func(prefix string) string |
| 39 |
| 40 // PostProcessor is a function that applies post-processing to the report output |
| 41 type PostProcessor func(input *bytes.Buffer, output io.Writer, ui plugin.UI) err
or |
| 42 |
| 43 // PProf returns the basic pprof report-generation commands |
| 44 func PProf(c Completer, interactive **bool, svgpan **string) Commands { |
| 45 return Commands{ |
| 46 // Commands that require no post-processing. |
| 47 "tags": {nil, report.Tags, nil, false, "Outputs all tags in th
e profile"}, |
| 48 "raw": {c, report.Raw, nil, false, "Outputs a text representa
tion of the raw profile"}, |
| 49 "dot": {c, report.Dot, nil, false, "Outputs a graph in DOT fo
rmat"}, |
| 50 "top": {c, report.Text, nil, false, "Outputs top entries in t
ext form"}, |
| 51 "tree": {c, report.Tree, nil, false, "Outputs a text rendering
of call graph"}, |
| 52 "text": {c, report.Text, nil, false, "Outputs top entries in t
ext form"}, |
| 53 "disasm": {c, report.Dis, nil, true, "Output annotated assembly
for functions matching regexp or address"}, |
| 54 "list": {c, report.List, nil, true, "Output annotated source f
or functions matching regexp"}, |
| 55 "peek": {c, report.Tree, nil, true, "Output callers/callees of
functions matching regexp"}, |
| 56 |
| 57 // Save binary formats to a file |
| 58 "callgrind": {c, report.Callgrind, awayFromTTY("callgraph.out"),
false, "Outputs a graph in callgrind format"}, |
| 59 "proto": {c, report.Proto, awayFromTTY("pb.gz"), false, "Out
puts the profile in compressed protobuf format"}, |
| 60 |
| 61 // Generate report in DOT format and postprocess with dot |
| 62 "gif": {c, report.Dot, invokeDot("gif"), false, "Outputs a graph
image in GIF format"}, |
| 63 "pdf": {c, report.Dot, invokeDot("pdf"), false, "Outputs a graph
in PDF format"}, |
| 64 "png": {c, report.Dot, invokeDot("png"), false, "Outputs a graph
image in PNG format"}, |
| 65 "ps": {c, report.Dot, invokeDot("ps"), false, "Outputs a graph
in PS format"}, |
| 66 |
| 67 // Save SVG output into a file after including svgpan library |
| 68 "svg": {c, report.Dot, saveSVGToFile(svgpan), false, "Outputs a
graph in SVG format"}, |
| 69 |
| 70 // Visualize postprocessed dot output |
| 71 "eog": {c, report.Dot, invokeVisualizer(interactive, invokeDo
t("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"}, |
| 72 "evince": {c, report.Dot, invokeVisualizer(interactive, invokeDo
t("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"}, |
| 73 "gv": {c, report.Dot, invokeVisualizer(interactive, invokeDo
t("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv
"}, |
| 74 "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGT
oFile(svgpan), "svg", browsers), false, "Visualize graph through web browser"}, |
| 75 |
| 76 // Visualize HTML directly generated by report. |
| 77 "weblist": {c, report.WebList, invokeVisualizer(interactive, awa
yFromTTY("html"), "html", browsers), true, "Output annotated source in HTML for
functions matching regexp or address"}, |
| 78 } |
| 79 } |
| 80 |
| 81 // List of web browsers to attempt for web visualization |
| 82 var browsers = []string{"chrome", "google-chrome", "firefox", "/usr/bin/open"} |
| 83 |
| 84 // NewCompleter creates an autocompletion function for a set of commands. |
| 85 func NewCompleter(cs Commands) Completer { |
| 86 return func(line string) string { |
| 87 switch tokens := strings.Fields(line); len(tokens) { |
| 88 case 0: |
| 89 // Nothing to complete |
| 90 case 1: |
| 91 // Single token -- complete command name |
| 92 found := "" |
| 93 for c := range cs { |
| 94 if strings.HasPrefix(c, tokens[0]) { |
| 95 if found != "" { |
| 96 return line |
| 97 } |
| 98 found = c |
| 99 } |
| 100 } |
| 101 if found != "" { |
| 102 return found |
| 103 } |
| 104 default: |
| 105 // Multiple tokens -- complete using command completer |
| 106 if c, ok := cs[tokens[0]]; ok { |
| 107 if c.Complete != nil { |
| 108 lastTokenIdx := len(tokens) - 1 |
| 109 lastToken := tokens[lastTokenIdx] |
| 110 if strings.HasPrefix(lastToken, "-") { |
| 111 lastToken = "-" + c.Complete(las
tToken[1:]) |
| 112 } else { |
| 113 lastToken = c.Complete(lastToken
) |
| 114 } |
| 115 return strings.Join(append(tokens[:lastT
okenIdx], lastToken), " ") |
| 116 } |
| 117 } |
| 118 } |
| 119 return line |
| 120 } |
| 121 } |
| 122 |
| 123 // awayFromTTY saves the output in a file if it would otherwise go to |
| 124 // the terminal screen. This is used to avoid dumping binary data on |
| 125 // the screen. |
| 126 func awayFromTTY(format string) PostProcessor { |
| 127 return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { |
| 128 if output == os.Stdout && ui.IsTerminal() { |
| 129 tempFile, err := tempfile.New("", "profile", "."+format) |
| 130 if err != nil { |
| 131 return err |
| 132 } |
| 133 ui.PrintErr("Generating report in ", tempFile.Name()) |
| 134 _, err = fmt.Fprint(tempFile, input) |
| 135 return err |
| 136 } |
| 137 _, err := fmt.Fprint(output, input) |
| 138 return err |
| 139 } |
| 140 } |
| 141 |
| 142 func invokeDot(format string) PostProcessor { |
| 143 divert := awayFromTTY(format) |
| 144 return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { |
| 145 cmd := exec.Command("dot", "-T"+format) |
| 146 var buf bytes.Buffer |
| 147 cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr |
| 148 if err := cmd.Run(); err != nil { |
| 149 return err |
| 150 } |
| 151 return divert(&buf, output, ui) |
| 152 } |
| 153 } |
| 154 |
| 155 func saveSVGToFile(svgpan **string) PostProcessor { |
| 156 generateSVG := invokeDot("svg") |
| 157 divert := awayFromTTY("svg") |
| 158 return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { |
| 159 baseSVG := &bytes.Buffer{} |
| 160 generateSVG(input, baseSVG, ui) |
| 161 massaged := &bytes.Buffer{} |
| 162 fmt.Fprint(massaged, svg.Massage(*baseSVG, **svgpan)) |
| 163 return divert(massaged, output, ui) |
| 164 } |
| 165 } |
| 166 |
| 167 func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, v
isualizers []string) PostProcessor { |
| 168 return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error { |
| 169 tempFile, err := tempfile.New(os.Getenv("PPROF_TMPDIR"), "pprof"
, "."+suffix) |
| 170 if err != nil { |
| 171 return err |
| 172 } |
| 173 tempfile.DeferDelete(tempFile.Name()) |
| 174 if err = format(input, tempFile, ui); err != nil { |
| 175 return err |
| 176 } |
| 177 // Try visualizers until one is successful |
| 178 for _, v := range visualizers { |
| 179 // Separate command and arguments for exec.Command. |
| 180 args := strings.Split(v, " ") |
| 181 if len(args) == 0 { |
| 182 continue |
| 183 } |
| 184 viewer := exec.Command(args[0], append(args[1:], tempFil
e.Name())...) |
| 185 viewer.Stderr = os.Stderr |
| 186 if err = viewer.Start(); err == nil { |
| 187 if !**interactive { |
| 188 // In command-line mode, wait for the vi
ewer to be closed |
| 189 // before proceeding |
| 190 return viewer.Wait() |
| 191 } |
| 192 return nil |
| 193 } |
| 194 } |
| 195 return err |
| 196 } |
| 197 } |
LEFT | RIGHT |