Browse Source

Make '-noinlines' a separate flag, introduce '-filefunctions' granularity. (#420)

This change consists of two relatively independent parts, but they are
both about handling the granularity, so bundling them together.

First, in #415 there is a discussion that having a granularity mode by
source lines but with inlines hidden would be useful. The agreement is
also that adding `-linenoinlines` granularity would make the granularity
flags too messy (and they are already somewhat messy with `-addresses`
and `-addressnoinlines`. So, it was proposed to make `-noinlines` a
separate flag, which is what this change does. Note that the flag is now
pulled out of the granularity group so it's a bit of backward
incompatible change but I think it is acceptable. For the example in
issue #415 the user would now be able to specify `-list foo -noinlines`
to produce annotated source where the metrics from the inlined functions
are attributed to the calling inliner line.

With this change, I am also dropping the `-addressnoinlines` granularity
which is now supported as `-addresses -noinlines` combination. I
couldn't find any usage of this option at least internally at Google, so
I think it's safe to remove it.

Second, I am adding a separate `-filefunctions` granularity which groups
the data by both function and file. This is a follow-up to #110 from the
past where we changed the `-functions` granularity to not group by file
(it used to), and since then there was a couple of reports where using
just function name alone would over-aggregate the data in cases when a
function with the same name is contained in multiple source files (e.g.
see b/18874275 internally).

Also, make a number of assorted documentation and `-help` fixes.
Alexey Alexandrov 6 years ago
parent
commit
7dadf64105
No account linked to committer's email address

+ 15
- 15
doc/README.md View File

80
 
80
 
81
 Some common pprof options are:
81
 Some common pprof options are:
82
 
82
 
83
-* **-flat [default]:** Sort entries based on their flat weight, on text reports.
84
-* **-cum:** Sort entries based on cumulative weight, on text reports.
85
-* **-functions [default]:** Accumulate samples at the function level; profile
86
-  locations that describe the same function will be merged into a report entry.
87
-* **-lines:** Accumulate samples at the source line level; profile locations that
88
-  describe the same function will be merged into a report entry.
89
-* **-addresses:** Accumulate samples at the instruction address; profile locations
90
-  that describe the same function address will be merged into a report entry.
83
+* **-flat** [default], **-cum**: Sort entries based on their flat or cumulative
84
+  weight respectively, on text reports.
85
+* **-functions** [default], **-filefunctions**, **-files**, **-lines**,
86
+  **-addresses**: Generate the report using the specified granularity.
87
+* **-noinlines**: Attribute inlined functions to their first out-of-line caller.
88
+  For example, a command like `pprof -list foo -noinlines profile.pb.gz` can be
89
+  used to produce the annotated source listing attributing the metrics in the
90
+  inlined functions to the out-of-line calling line.
91
 * **-nodecount= _int_:** Maximum number of entries in the report. pprof will only print
91
 * **-nodecount= _int_:** Maximum number of entries in the report. pprof will only print
92
   this many entries and will use heuristics to select which entries to trim.
92
   this many entries and will use heuristics to select which entries to trim.
93
 * **-focus= _regex_:** Only include samples that include a report entry matching
93
 * **-focus= _regex_:** Only include samples that include a report entry matching
94
   *regex*.
94
   *regex*.
95
 * **-ignore= _regex_:** Do not include samples that include a report entry matching
95
 * **-ignore= _regex_:** Do not include samples that include a report entry matching
96
   *regex*.
96
   *regex*.
97
-* **-show\_from= _regex_:** Do not show entries above the first one that 
97
+* **-show\_from= _regex_:** Do not show entries above the first one that
98
   matches *regex*.
98
   matches *regex*.
99
 * **-show= _regex_:** Only show entries that match *regex*.
99
 * **-show= _regex_:** Only show entries that match *regex*.
100
 * **-hide= _regex_:** Do not show entries that match *regex*.
100
 * **-hide= _regex_:** Do not show entries that match *regex*.
209
 search for them in a directory pointed to by the environment variable
209
 search for them in a directory pointed to by the environment variable
210
 `$PPROF_TOOLS`.
210
 `$PPROF_TOOLS`.
211
 
211
 
212
-* **-disasm= _regex_:** Generates an annotated source listing for functions matching
213
-  regex, with flat/cum weights for each source line.
214
-* **-list= _regex_:** Generates an annotated disassembly listing for functions
215
-  matching *regex*.
216
-* **-weblist= _regex_:** Generates a source/assembly combined annotated listing for
217
-  functions matching *regex*, and starts a web browser to display it.
212
+* **-list= _regex_:** Generates an annotated source listing for functions
213
+  matching *regex*, with flat/cum weights for each source line.
214
+* **-disasm= _regex_:** Generates an annotated disassembly listing for
215
+  functions matching *regex*.
216
+* **-weblist= _regex_:** Generates a source/assembly combined annotated listing
217
+  for functions matching *regex*, and starts a web browser to display it.
218
 
218
 
219
 ## Comparing profiles
219
 ## Comparing profiles
220
 
220
 

+ 7
- 7
internal/driver/commands.go View File

228
 	// Output granularity
228
 	// Output granularity
229
 	"functions": &variable{boolKind, "t", "granularity", helpText(
229
 	"functions": &variable{boolKind, "t", "granularity", helpText(
230
 		"Aggregate at the function level.",
230
 		"Aggregate at the function level.",
231
-		"Takes into account the filename/lineno where the function was defined.")},
231
+		"Ignores the filename where the function was defined.")},
232
+	"filefunctions": &variable{boolKind, "t", "granularity", helpText(
233
+		"Aggregate at the function level.",
234
+		"Takes into account the filename where the function was defined.")},
232
 	"files": &variable{boolKind, "f", "granularity", "Aggregate at the file level."},
235
 	"files": &variable{boolKind, "f", "granularity", "Aggregate at the file level."},
233
 	"lines": &variable{boolKind, "f", "granularity", "Aggregate at the source code line level."},
236
 	"lines": &variable{boolKind, "f", "granularity", "Aggregate at the source code line level."},
234
 	"addresses": &variable{boolKind, "f", "granularity", helpText(
237
 	"addresses": &variable{boolKind, "f", "granularity", helpText(
235
-		"Aggregate at the function level.",
238
+		"Aggregate at the address level.",
236
 		"Includes functions' addresses in the output.")},
239
 		"Includes functions' addresses in the output.")},
237
-	"noinlines": &variable{boolKind, "f", "granularity", helpText(
238
-		"Aggregate at the function level.",
239
-		"Attributes inlined functions to their first out-of-line caller.")},
240
-	"addressnoinlines": &variable{boolKind, "f", "granularity", helpText(
241
-		"Aggregate at the function level, including functions' addresses in the output.",
240
+	"noinlines": &variable{boolKind, "f", "", helpText(
241
+		"Ignore inlines.",
242
 		"Attributes inlined functions to their first out-of-line caller.")},
242
 		"Attributes inlined functions to their first out-of-line caller.")},
243
 }
243
 }
244
 
244
 

+ 26
- 16
internal/driver/driver.go View File

152
 }
152
 }
153
 
153
 
154
 func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
154
 func applyCommandOverrides(cmd string, outputFormat int, v variables) variables {
155
+	// Some report types override the trim flag to false below. This is to make
156
+	// sure the default heuristics of excluding insignificant nodes and edges
157
+	// from the call graph do not apply. One example where it is important is
158
+	// annotated source or disassembly listing. Those reports run on a specific
159
+	// function (or functions), but the trimming is applied before the function
160
+	// data is selected. So, with trimming enabled, the report could end up
161
+	// showing no data if the specified function is "uninteresting" as far as the
162
+	// trimming is concerned.
155
 	trim := v["trim"].boolValue()
163
 	trim := v["trim"].boolValue()
156
 
164
 
157
 	switch cmd {
165
 	switch cmd {
158
-	case "callgrind", "kcachegrind":
159
-		trim = false
160
-		v.set("addresses", "t")
161
 	case "disasm", "weblist":
166
 	case "disasm", "weblist":
162
 		trim = false
167
 		trim = false
163
-		v.set("addressnoinlines", "t")
168
+		v.set("addresses", "t")
169
+		v.set("noinlines", "t")
164
 	case "peek":
170
 	case "peek":
165
 		trim = false
171
 		trim = false
166
 	case "list":
172
 	case "list":
167
-		v.set("nodecount", "0")
173
+		trim = false
168
 		v.set("lines", "t")
174
 		v.set("lines", "t")
175
+		// Do not force 'noinlines' to be false so that specifying
176
+		// "-list foo -noinlines" is supported and works as expected.
169
 	case "text", "top", "topproto":
177
 	case "text", "top", "topproto":
170
 		if v["nodecount"].intValue() == -1 {
178
 		if v["nodecount"].intValue() == -1 {
171
 			v.set("nodecount", "0")
179
 			v.set("nodecount", "0")
176
 		}
184
 		}
177
 	}
185
 	}
178
 
186
 
179
-	if outputFormat == report.Proto || outputFormat == report.Raw {
187
+	switch outputFormat {
188
+	case report.Proto, report.Raw, report.Callgrind:
180
 		trim = false
189
 		trim = false
181
 		v.set("addresses", "t")
190
 		v.set("addresses", "t")
191
+		v.set("noinlines", "f")
182
 	}
192
 	}
183
 
193
 
184
 	if !trim {
194
 	if !trim {
190
 }
200
 }
191
 
201
 
192
 func aggregate(prof *profile.Profile, v variables) error {
202
 func aggregate(prof *profile.Profile, v variables) error {
193
-	var inlines, function, filename, linenumber, address bool
203
+	var function, filename, linenumber, address bool
204
+	inlines := !v["noinlines"].boolValue()
194
 	switch {
205
 	switch {
195
 	case v["addresses"].boolValue():
206
 	case v["addresses"].boolValue():
196
-		return nil
207
+		if inlines {
208
+			return nil
209
+		}
210
+		function = true
211
+		filename = true
212
+		linenumber = true
213
+		address = true
197
 	case v["lines"].boolValue():
214
 	case v["lines"].boolValue():
198
-		inlines = true
199
 		function = true
215
 		function = true
200
 		filename = true
216
 		filename = true
201
 		linenumber = true
217
 		linenumber = true
202
 	case v["files"].boolValue():
218
 	case v["files"].boolValue():
203
-		inlines = true
204
 		filename = true
219
 		filename = true
205
 	case v["functions"].boolValue():
220
 	case v["functions"].boolValue():
206
-		inlines = true
207
 		function = true
221
 		function = true
208
-	case v["noinlines"].boolValue():
209
-		function = true
210
-	case v["addressnoinlines"].boolValue():
222
+	case v["filefunctions"].boolValue():
211
 		function = true
223
 		function = true
212
 		filename = true
224
 		filename = true
213
-		linenumber = true
214
-		address = true
215
 	default:
225
 	default:
216
 		return fmt.Errorf("unexpected granularity")
226
 		return fmt.Errorf("unexpected granularity")
217
 	}
227
 	}

+ 5
- 1
internal/driver/driver_test.go View File

53
 		flags, source string
53
 		flags, source string
54
 	}{
54
 	}{
55
 		{"text,functions,flat", "cpu"},
55
 		{"text,functions,flat", "cpu"},
56
+		{"text,functions,noinlines,flat", "cpu"},
57
+		{"text,filefunctions,noinlines,flat", "cpu"},
58
+		{"text,addresses,noinlines,flat", "cpu"},
56
 		{"tree,addresses,flat,nodecount=4", "cpusmall"},
59
 		{"tree,addresses,flat,nodecount=4", "cpusmall"},
57
 		{"text,functions,flat,nodecount=5,call_tree", "unknown"},
60
 		{"text,functions,flat,nodecount=5,call_tree", "unknown"},
58
 		{"text,alloc_objects,flat", "heap_alloc"},
61
 		{"text,alloc_objects,flat", "heap_alloc"},
248
 func solutionFilename(source string, f *testFlags) string {
251
 func solutionFilename(source string, f *testFlags) string {
249
 	name := []string{"pprof", strings.TrimPrefix(source, testSourceURL(8000))}
252
 	name := []string{"pprof", strings.TrimPrefix(source, testSourceURL(8000))}
250
 	name = addString(name, f, []string{"flat", "cum"})
253
 	name = addString(name, f, []string{"flat", "cum"})
251
-	name = addString(name, f, []string{"functions", "files", "lines", "addresses"})
254
+	name = addString(name, f, []string{"functions", "filefunctions", "files", "lines", "addresses"})
255
+	name = addString(name, f, []string{"noinlines"})
252
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
256
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
253
 	name = addString(name, f, []string{"relative_percentages"})
257
 	name = addString(name, f, []string{"relative_percentages"})
254
 	name = addString(name, f, []string{"seconds"})
258
 	name = addString(name, f, []string{"seconds"})

+ 7
- 6
internal/driver/interactive_test.go View File

259
 		{
259
 		{
260
 			"weblist  find -test",
260
 			"weblist  find -test",
261
 			map[string]string{
261
 			map[string]string{
262
-				"functions":        "false",
263
-				"addressnoinlines": "true",
264
-				"nodecount":        "0",
265
-				"cum":              "false",
266
-				"flat":             "true",
267
-				"ignore":           "test",
262
+				"functions": "false",
263
+				"addresses": "true",
264
+				"noinlines": "true",
265
+				"nodecount": "0",
266
+				"cum":       "false",
267
+				"flat":      "true",
268
+				"ignore":    "test",
268
 			},
269
 			},
269
 		},
270
 		},
270
 		{
271
 		{

+ 7
- 0
internal/driver/testdata/pprof.cpu.flat.addresses.noinlines.text View File

1
+Showing nodes accounting for 1.12s, 100% of 1.12s total
2
+Dropped 1 node (cum <= 0.06s)
3
+      flat  flat%   sum%        cum   cum%
4
+     1.10s 98.21% 98.21%      1.10s 98.21%  0000000000001000 line1000 testdata/file1000.src:1
5
+     0.01s  0.89% 99.11%      1.01s 90.18%  0000000000002000 line2000 testdata/file2000.src:4
6
+     0.01s  0.89%   100%      1.01s 90.18%  0000000000003000 line3000 testdata/file3000.src:6
7
+         0     0%   100%      0.10s  8.93%  0000000000003001 line3000 testdata/file3000.src:9

+ 5
- 0
internal/driver/testdata/pprof.cpu.flat.filefunctions.noinlines.text View File

1
+Showing nodes accounting for 1.12s, 100% of 1.12s total
2
+      flat  flat%   sum%        cum   cum%
3
+     1.10s 98.21% 98.21%      1.10s 98.21%  line1000 testdata/file1000.src
4
+     0.01s  0.89% 99.11%      1.01s 90.18%  line2000 testdata/file2000.src
5
+     0.01s  0.89%   100%      1.12s   100%  line3000 testdata/file3000.src

+ 5
- 0
internal/driver/testdata/pprof.cpu.flat.functions.noinlines.text View File

1
+Showing nodes accounting for 1.12s, 100% of 1.12s total
2
+      flat  flat%   sum%        cum   cum%
3
+     1.10s 98.21% 98.21%      1.10s 98.21%  line1000
4
+     0.01s  0.89% 99.11%      1.01s 90.18%  line2000
5
+     0.01s  0.89%   100%      1.12s   100%  line3000