|
@@ -26,6 +26,8 @@ import (
|
26
|
26
|
"github.com/google/pprof/profile"
|
27
|
27
|
)
|
28
|
28
|
|
|
29
|
+var commentStart = "//:" // Sentinel for comments on options
|
|
30
|
+
|
29
|
31
|
// interactive starts a shell to read pprof commands.
|
30
|
32
|
func interactive(p *profile.Profile, o *plugin.Options) error {
|
31
|
33
|
// Enter command processing loop.
|
|
@@ -40,7 +42,7 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
40
|
42
|
|
41
|
43
|
greetings(p, o.UI)
|
42
|
44
|
for {
|
43
|
|
- input, err := o.UI.ReadLine(pprofPrompt(p))
|
|
45
|
+ input, err := o.UI.ReadLine("(pprof) ")
|
44
|
46
|
if err != nil {
|
45
|
47
|
if err != io.EOF {
|
46
|
48
|
return err
|
|
@@ -54,17 +56,36 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
54
|
56
|
// Process assignments of the form variable=value
|
55
|
57
|
if s := strings.SplitN(input, "=", 2); len(s) > 0 {
|
56
|
58
|
name := strings.TrimSpace(s[0])
|
57
|
|
-
|
|
59
|
+ var value string
|
|
60
|
+ if len(s) == 2 {
|
|
61
|
+ value = s[1]
|
|
62
|
+ if comment := strings.LastIndex(value, commentStart); comment != -1 {
|
|
63
|
+ value = value[:comment]
|
|
64
|
+ }
|
|
65
|
+ value = strings.TrimSpace(value)
|
|
66
|
+ }
|
58
|
67
|
if v := pprofVariables[name]; v != nil {
|
59
|
|
- var value string
|
60
|
|
- if len(s) == 2 {
|
61
|
|
- value = strings.TrimSpace(s[1])
|
|
68
|
+ if name == "sample_index" {
|
|
69
|
+ // Error check sample_index=xxx to ensure xxx is a valid sample type.
|
|
70
|
+ index, err := locateSampleIndex(p, value)
|
|
71
|
+ if err != nil {
|
|
72
|
+ o.UI.PrintErr(err)
|
|
73
|
+ continue
|
|
74
|
+ }
|
|
75
|
+ value = p.SampleType[index].Type
|
62
|
76
|
}
|
63
|
77
|
if err := pprofVariables.set(name, value); err != nil {
|
64
|
78
|
o.UI.PrintErr(err)
|
65
|
79
|
}
|
66
|
80
|
continue
|
67
|
81
|
}
|
|
82
|
+ // Allow group=variable syntax by converting into variable="".
|
|
83
|
+ if v := pprofVariables[value]; v != nil && v.group == name {
|
|
84
|
+ if err := pprofVariables.set(value, ""); err != nil {
|
|
85
|
+ o.UI.PrintErr(err)
|
|
86
|
+ }
|
|
87
|
+ continue
|
|
88
|
+ }
|
68
|
89
|
}
|
69
|
90
|
|
70
|
91
|
tokens := strings.Fields(input)
|
|
@@ -73,6 +94,9 @@ func interactive(p *profile.Profile, o *plugin.Options) error {
|
73
|
94
|
}
|
74
|
95
|
|
75
|
96
|
switch tokens[0] {
|
|
97
|
+ case "o", "options":
|
|
98
|
+ printCurrentOptions(p, o.UI)
|
|
99
|
+ continue
|
76
|
100
|
case "exit", "quit":
|
77
|
101
|
return nil
|
78
|
102
|
case "help":
|
|
@@ -100,9 +124,8 @@ func greetings(p *profile.Profile, ui plugin.UI) {
|
100
|
124
|
ropt, err := reportOptions(p, pprofVariables)
|
101
|
125
|
if err == nil {
|
102
|
126
|
ui.Print(strings.Join(report.ProfileLabels(report.New(p, ropt)), "\n"))
|
103
|
|
- ui.Print(fmt.Sprintf("Sample types: %v\n", sampleTypes(p)))
|
104
|
127
|
}
|
105
|
|
- ui.Print("Entering interactive mode (type \"help\" for commands)")
|
|
128
|
+ ui.Print("Entering interactive mode (type \"help\" for commands, \"o\" for options)")
|
106
|
129
|
}
|
107
|
130
|
|
108
|
131
|
// shortcuts represents composite commands that expand into a sequence
|
|
@@ -110,6 +133,7 @@ func greetings(p *profile.Profile, ui plugin.UI) {
|
110
|
133
|
type shortcuts map[string][]string
|
111
|
134
|
|
112
|
135
|
func (a shortcuts) expand(input string) []string {
|
|
136
|
+ input = strings.TrimSpace(input)
|
113
|
137
|
if a != nil {
|
114
|
138
|
if r, ok := a[input]; ok {
|
115
|
139
|
return r
|
|
@@ -135,45 +159,57 @@ func profileShortcuts(p *profile.Profile) shortcuts {
|
135
|
159
|
return s
|
136
|
160
|
}
|
137
|
161
|
|
138
|
|
-// pprofPrompt returns the prompt displayed to accept commands.
|
139
|
|
-// hides some default values to reduce clutter.
|
140
|
|
-func pprofPrompt(p *profile.Profile) string {
|
|
162
|
+func printCurrentOptions(p *profile.Profile, ui plugin.UI) {
|
141
|
163
|
var args []string
|
|
164
|
+ type groupInfo struct {
|
|
165
|
+ set string
|
|
166
|
+ values []string
|
|
167
|
+ }
|
|
168
|
+ groups := make(map[string]*groupInfo)
|
142
|
169
|
for n, o := range pprofVariables {
|
143
|
170
|
v := o.stringValue()
|
144
|
|
- if v == "" {
|
|
171
|
+ comment := ""
|
|
172
|
+ if g := o.group; g != "" {
|
|
173
|
+ gi, ok := groups[g]
|
|
174
|
+ if !ok {
|
|
175
|
+ gi = &groupInfo{}
|
|
176
|
+ groups[g] = gi
|
|
177
|
+ }
|
|
178
|
+ if o.boolValue() {
|
|
179
|
+ gi.set = n
|
|
180
|
+ }
|
|
181
|
+ gi.values = append(gi.values, n)
|
145
|
182
|
continue
|
146
|
183
|
}
|
147
|
|
- // Do not show some default values.
|
148
|
184
|
switch {
|
149
|
|
- case n == "unit" && v == "minimum":
|
150
|
|
- continue
|
151
|
|
- case n == "divide_by" && v == "1":
|
152
|
|
- continue
|
153
|
|
- case n == "nodecount" && v == "-1":
|
154
|
|
- continue
|
155
|
185
|
case n == "sample_index":
|
156
|
|
- index, err := locateSampleIndex(p, v)
|
157
|
|
- if err != nil {
|
158
|
|
- v = "ERROR: " + err.Error()
|
159
|
|
- } else {
|
160
|
|
- v = fmt.Sprintf("%s (%d)", p.SampleType[index].Type, index)
|
161
|
|
- }
|
162
|
|
- case n == "trim" || n == "compact_labels":
|
163
|
|
- if o.boolValue() == true {
|
164
|
|
- continue
|
165
|
|
- }
|
166
|
|
- case o.kind == boolKind:
|
167
|
|
- if o.boolValue() == false {
|
168
|
|
- continue
|
|
186
|
+ st := sampleTypes(p)
|
|
187
|
+ if v == "" {
|
|
188
|
+ // Apply default (last sample index).
|
|
189
|
+ v = st[len(st)-1]
|
169
|
190
|
}
|
|
191
|
+ // Add comments for all sample types in profile.
|
|
192
|
+ comment = "[" + strings.Join(st, " | ") + "]"
|
170
|
193
|
case n == "source_path":
|
171
|
194
|
continue
|
|
195
|
+ case n == "nodecount" && v == "-1":
|
|
196
|
+ comment = "default"
|
|
197
|
+ case v == "":
|
|
198
|
+ // Add quotes for empty values.
|
|
199
|
+ v = `""`
|
|
200
|
+ }
|
|
201
|
+ if comment != "" {
|
|
202
|
+ comment = commentStart + " " + comment
|
172
|
203
|
}
|
173
|
|
- args = append(args, fmt.Sprintf(" %-25s : %s", n, v))
|
|
204
|
+ args = append(args, fmt.Sprintf(" %-25s = %-20s %s", n, v, comment))
|
|
205
|
+ }
|
|
206
|
+ for g, vars := range groups {
|
|
207
|
+ sort.Strings(vars.values)
|
|
208
|
+ comment := commentStart + " [" + strings.Join(vars.values, " | ") + "]"
|
|
209
|
+ args = append(args, fmt.Sprintf(" %-25s = %-20s %s", g, vars.set, comment))
|
174
|
210
|
}
|
175
|
211
|
sort.Strings(args)
|
176
|
|
- return "Options:\n" + strings.Join(args, "\n") + "\nPPROF>"
|
|
212
|
+ ui.Print(strings.Join(args, "\n"))
|
177
|
213
|
}
|
178
|
214
|
|
179
|
215
|
// parseCommandLine parses a command and returns the pprof command to
|