瀏覽代碼

Fix mean computation during graph creation

When -mean is selected, currently pprof divides the sample value
by value[0], which is expected to be the number of samples. This
is intended to produce mean value per sample. These means cannot
be added. Instead, we should add the value and the number of samples
independently and perform the division at the end.

To do this we will create a separate function to get the number of samples,
and accumulate it independently from the sample value (weigth) and apply
the division after the accumulation is completed.
Raul Silvera 8 年之前
父節點
當前提交
e10c124884
共有 5 個文件被更改,包括 204 次插入111 次删除
  1. 12
    18
      internal/driver/driver.go
  2. 26
    18
      internal/graph/dotgraph.go
  3. 23
    18
      internal/graph/dotgraph_test.go
  4. 97
    31
      internal/graph/graph.go
  5. 46
    26
      internal/report/report.go

+ 12
- 18
internal/driver/driver.go 查看文件

206
 
206
 
207
 func reportOptions(p *profile.Profile, vars variables) (*report.Options, error) {
207
 func reportOptions(p *profile.Profile, vars variables) (*report.Options, error) {
208
 	si, mean := vars["sample_index"].value, vars["mean"].boolValue()
208
 	si, mean := vars["sample_index"].value, vars["mean"].boolValue()
209
-	value, sample, err := sampleFormat(p, si, mean)
209
+	value, meanDiv, sample, err := sampleFormat(p, si, mean)
210
 	if err != nil {
210
 	if err != nil {
211
 		return nil, err
211
 		return nil, err
212
 	}
212
 	}
233
 		NodeFraction: vars["nodefraction"].floatValue(),
233
 		NodeFraction: vars["nodefraction"].floatValue(),
234
 		EdgeFraction: vars["edgefraction"].floatValue(),
234
 		EdgeFraction: vars["edgefraction"].floatValue(),
235
 
235
 
236
-		SampleValue: value,
237
-		SampleType:  stype,
238
-		SampleUnit:  sample.Unit,
236
+		SampleValue:       value,
237
+		SampleMeanDivisor: meanDiv,
238
+		SampleType:        stype,
239
+		SampleUnit:        sample.Unit,
239
 
240
 
240
 		OutputUnit: vars["unit"].value,
241
 		OutputUnit: vars["unit"].value,
241
 
242
 
253
 
254
 
254
 // sampleFormat returns a function to extract values out of a profile.Sample,
255
 // sampleFormat returns a function to extract values out of a profile.Sample,
255
 // and the type/units of those values.
256
 // and the type/units of those values.
256
-func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (sampleValueFunc, *profile.ValueType, error) {
257
+func sampleFormat(p *profile.Profile, sampleIndex string, mean bool) (value, meanDiv sampleValueFunc, v *profile.ValueType, err error) {
257
 	if len(p.SampleType) == 0 {
258
 	if len(p.SampleType) == 0 {
258
-		return nil, nil, fmt.Errorf("profile has no samples")
259
+		return nil, nil, nil, fmt.Errorf("profile has no samples")
259
 	}
260
 	}
260
 	index, err := locateSampleIndex(p, sampleIndex)
261
 	index, err := locateSampleIndex(p, sampleIndex)
261
 	if err != nil {
262
 	if err != nil {
262
-		return nil, nil, err
263
+		return nil, nil, nil, err
263
 	}
264
 	}
265
+	value = valueExtractor(index)
264
 	if mean {
266
 	if mean {
265
-		return meanExtractor(index), p.SampleType[index], nil
267
+		meanDiv = valueExtractor(0)
266
 	}
268
 	}
267
-	return valueExtractor(index), p.SampleType[index], nil
269
+	v = p.SampleType[index]
270
+	return
268
 }
271
 }
269
 
272
 
270
 func valueExtractor(ix int) sampleValueFunc {
273
 func valueExtractor(ix int) sampleValueFunc {
272
 		return v[ix]
275
 		return v[ix]
273
 	}
276
 	}
274
 }
277
 }
275
-
276
-func meanExtractor(ix int) sampleValueFunc {
277
-	return func(v []int64) int64 {
278
-		if v[0] == 0 {
279
-			return 0
280
-		}
281
-		return v[ix] / v[0]
282
-	}
283
-}

+ 26
- 18
internal/graph/dotgraph.go 查看文件

67
 	nodeIDMap := make(map[*Node]int)
67
 	nodeIDMap := make(map[*Node]int)
68
 	hasNodelets := make(map[*Node]bool)
68
 	hasNodelets := make(map[*Node]bool)
69
 
69
 
70
-	maxFlat := float64(abs64(g.Nodes[0].Flat))
70
+	maxFlat := float64(abs64(g.Nodes[0].FlatValue()))
71
 	for i, n := range g.Nodes {
71
 	for i, n := range g.Nodes {
72
 		nodeIDMap[n] = i + 1
72
 		nodeIDMap[n] = i + 1
73
-		if float64(abs64(n.Flat)) > maxFlat {
74
-			maxFlat = float64(abs64(n.Flat))
73
+		if float64(abs64(n.FlatValue())) > maxFlat {
74
+			maxFlat = float64(abs64(n.FlatValue()))
75
 		}
75
 		}
76
 	}
76
 	}
77
 
77
 
128
 
128
 
129
 // addNode generates a graph node in DOT format.
129
 // addNode generates a graph node in DOT format.
130
 func (b *builder) addNode(node *Node, nodeID int, maxFlat float64) {
130
 func (b *builder) addNode(node *Node, nodeID int, maxFlat float64) {
131
-	flat, cum := node.Flat, node.Cum
131
+	flat, cum := node.FlatValue(), node.CumValue()
132
 	attrs := b.attributes.Nodes[node]
132
 	attrs := b.attributes.Nodes[node]
133
 
133
 
134
 	// Populate label for node.
134
 	// Populate label for node.
177
 	// Create DOT attribute for node.
177
 	// Create DOT attribute for node.
178
 	attr := fmt.Sprintf(`label="%s" fontsize=%d shape=%s tooltip="%s (%s)" color="%s" fillcolor="%s"`,
178
 	attr := fmt.Sprintf(`label="%s" fontsize=%d shape=%s tooltip="%s (%s)" color="%s" fillcolor="%s"`,
179
 		label, fontSize, shape, node.Info.PrintableName(), cumValue,
179
 		label, fontSize, shape, node.Info.PrintableName(), cumValue,
180
-		dotColor(float64(node.Cum)/float64(abs64(b.config.Total)), false),
181
-		dotColor(float64(node.Cum)/float64(abs64(b.config.Total)), true))
180
+		dotColor(float64(node.CumValue())/float64(abs64(b.config.Total)), false),
181
+		dotColor(float64(node.CumValue())/float64(abs64(b.config.Total)), true))
182
 
182
 
183
 	// Add on extra attributes if provided.
183
 	// Add on extra attributes if provided.
184
 	if attrs != nil {
184
 	if attrs != nil {
230
 		ts = ts[:maxNodelets]
230
 		ts = ts[:maxNodelets]
231
 	}
231
 	}
232
 	for i, t := range ts {
232
 	for i, t := range ts {
233
-		w := t.Cum
233
+		w := t.CumValue()
234
 		if flatTags {
234
 		if flatTags {
235
-			w = t.Flat
235
+			w = t.FlatValue()
236
 		}
236
 		}
237
 		if w == 0 {
237
 		if w == 0 {
238
 			continue
238
 			continue
259
 	// Collapse numeric labels into maxNumNodelets buckets, of the form:
259
 	// Collapse numeric labels into maxNumNodelets buckets, of the form:
260
 	// 1MB..2MB, 3MB..5MB, ...
260
 	// 1MB..2MB, 3MB..5MB, ...
261
 	for j, t := range collapsedTags(nts, maxNumNodelets, flatTags) {
261
 	for j, t := range collapsedTags(nts, maxNumNodelets, flatTags) {
262
-		w, attr := t.Cum, ` style="dotted"`
263
-		if flatTags || t.Flat == t.Cum {
264
-			w, attr = t.Flat, ""
262
+		w, attr := t.CumValue(), ` style="dotted"`
263
+		if flatTags || t.FlatValue() == t.CumValue() {
264
+			w, attr = t.FlatValue(), ""
265
 		}
265
 		}
266
 		if w != 0 {
266
 		if w != 0 {
267
 			weight := b.config.FormatValue(w)
267
 			weight := b.config.FormatValue(w)
278
 	if edge.Inline {
278
 	if edge.Inline {
279
 		inline = `\n (inline)`
279
 		inline = `\n (inline)`
280
 	}
280
 	}
281
-	w := b.config.FormatValue(edge.Weight)
281
+	w := b.config.FormatValue(edge.WeightValue())
282
 	attr := fmt.Sprintf(`label=" %s%s"`, w, inline)
282
 	attr := fmt.Sprintf(`label=" %s%s"`, w, inline)
283
 	if b.config.Total != 0 {
283
 	if b.config.Total != 0 {
284
 		// Note: edge.weight > b.config.Total is possible for profile diffs.
284
 		// Note: edge.weight > b.config.Total is possible for profile diffs.
285
-		if weight := 1 + int(min64(abs64(edge.Weight*100/b.config.Total), 100)); weight > 1 {
285
+		if weight := 1 + int(min64(abs64(edge.WeightValue()*100/b.config.Total), 100)); weight > 1 {
286
 			attr = fmt.Sprintf(`%s weight=%d`, attr, weight)
286
 			attr = fmt.Sprintf(`%s weight=%d`, attr, weight)
287
 		}
287
 		}
288
-		if width := 1 + int(min64(abs64(edge.Weight*5/b.config.Total), 5)); width > 1 {
288
+		if width := 1 + int(min64(abs64(edge.WeightValue()*5/b.config.Total), 5)); width > 1 {
289
 			attr = fmt.Sprintf(`%s penwidth=%d`, attr, width)
289
 			attr = fmt.Sprintf(`%s penwidth=%d`, attr, width)
290
 		}
290
 		}
291
 		attr = fmt.Sprintf(`%s color="%s"`, attr,
291
 		attr = fmt.Sprintf(`%s color="%s"`, attr,
292
-			dotColor(float64(edge.Weight)/float64(abs64(b.config.Total)), false))
292
+			dotColor(float64(edge.WeightValue())/float64(abs64(b.config.Total)), false))
293
 	}
293
 	}
294
 	arrow := "->"
294
 	arrow := "->"
295
 	if edge.Residual {
295
 	if edge.Residual {
442
 func tagGroupLabel(g []*Tag) (label string, flat, cum int64) {
442
 func tagGroupLabel(g []*Tag) (label string, flat, cum int64) {
443
 	if len(g) == 1 {
443
 	if len(g) == 1 {
444
 		t := g[0]
444
 		t := g[0]
445
-		return measurement.Label(t.Value, t.Unit), t.Flat, t.Cum
445
+		return measurement.Label(t.Value, t.Unit), t.FlatValue(), t.CumValue()
446
 	}
446
 	}
447
 	min := g[0]
447
 	min := g[0]
448
 	max := g[0]
448
 	max := g[0]
449
-	f := min.Flat
450
-	c := min.Cum
449
+	df, f := min.FlatDiv, min.Flat
450
+	dc, c := min.CumDiv, min.Cum
451
 	for _, t := range g[1:] {
451
 	for _, t := range g[1:] {
452
 		if v, _ := measurement.Scale(t.Value, t.Unit, min.Unit); int64(v) < min.Value {
452
 		if v, _ := measurement.Scale(t.Value, t.Unit, min.Unit); int64(v) < min.Value {
453
 			min = t
453
 			min = t
456
 			max = t
456
 			max = t
457
 		}
457
 		}
458
 		f += t.Flat
458
 		f += t.Flat
459
+		df += t.FlatDiv
459
 		c += t.Cum
460
 		c += t.Cum
461
+		dc += t.CumDiv
462
+	}
463
+	if df != 0 {
464
+		f = f / df
465
+	}
466
+	if dc != 0 {
467
+		c = c / dc
460
 	}
468
 	}
461
 	return measurement.Label(min.Value, min.Unit) + ".." + measurement.Label(max.Value, max.Unit), f, c
469
 	return measurement.Label(min.Value, min.Unit) + ".." + measurement.Label(max.Value, max.Unit), f, c
462
 }
470
 }

+ 23
- 18
internal/graph/dotgraph_test.go 查看文件

224
 }
224
 }
225
 
225
 
226
 func TestTagCollapse(t *testing.T) {
226
 func TestTagCollapse(t *testing.T) {
227
+
228
+	makeTag := func(name, unit string, value, flat, cum int64) *Tag {
229
+		return &Tag{name, unit, value, flat, 0, cum, 0}
230
+	}
231
+
227
 	tagSource := []*Tag{
232
 	tagSource := []*Tag{
228
-		{"12mb", "mb", 12, 100, 100},
229
-		{"1kb", "kb", 1, 1, 1},
230
-		{"1mb", "mb", 1, 1000, 1000},
231
-		{"2048mb", "mb", 2048, 1000, 1000},
232
-		{"1b", "b", 1, 100, 100},
233
-		{"2b", "b", 2, 100, 100},
234
-		{"7b", "b", 7, 100, 100},
233
+		makeTag("12mb", "mb", 12, 100, 100),
234
+		makeTag("1kb", "kb", 1, 1, 1),
235
+		makeTag("1mb", "mb", 1, 1000, 1000),
236
+		makeTag("2048mb", "mb", 2048, 1000, 1000),
237
+		makeTag("1b", "b", 1, 100, 100),
238
+		makeTag("2b", "b", 2, 100, 100),
239
+		makeTag("7b", "b", 7, 100, 100),
235
 	}
240
 	}
236
 
241
 
237
 	tagWant := [][]*Tag{
242
 	tagWant := [][]*Tag{
238
 		[]*Tag{
243
 		[]*Tag{
239
-			{"1B..2GB", "", 0, 2401, 2401},
244
+			makeTag("1B..2GB", "", 0, 2401, 2401),
240
 		},
245
 		},
241
 		[]*Tag{
246
 		[]*Tag{
242
-			{"2GB", "", 0, 1000, 1000},
243
-			{"1B..12MB", "", 0, 1401, 1401},
247
+			makeTag("2GB", "", 0, 1000, 1000),
248
+			makeTag("1B..12MB", "", 0, 1401, 1401),
244
 		},
249
 		},
245
 		[]*Tag{
250
 		[]*Tag{
246
-			{"2GB", "", 0, 1000, 1000},
247
-			{"12MB", "", 0, 100, 100},
248
-			{"1B..1MB", "", 0, 1301, 1301},
251
+			makeTag("2GB", "", 0, 1000, 1000),
252
+			makeTag("12MB", "", 0, 100, 100),
253
+			makeTag("1B..1MB", "", 0, 1301, 1301),
249
 		},
254
 		},
250
 		[]*Tag{
255
 		[]*Tag{
251
-			{"2GB", "", 0, 1000, 1000},
252
-			{"1MB", "", 0, 1000, 1000},
253
-			{"2B..1kB", "", 0, 201, 201},
254
-			{"1B", "", 0, 100, 100},
255
-			{"12MB", "", 0, 100, 100},
256
+			makeTag("2GB", "", 0, 1000, 1000),
257
+			makeTag("1MB", "", 0, 1000, 1000),
258
+			makeTag("2B..1kB", "", 0, 201, 201),
259
+			makeTag("1B", "", 0, 100, 100),
260
+			makeTag("12MB", "", 0, 100, 100),
256
 		},
261
 		},
257
 	}
262
 	}
258
 
263
 

+ 97
- 31
internal/graph/graph.go 查看文件

34
 
34
 
35
 // Options encodes the options for constructing a graph
35
 // Options encodes the options for constructing a graph
36
 type Options struct {
36
 type Options struct {
37
-	SampleValue func(s []int64) int64      // Function to compute the value of a sample
38
-	FormatTag   func(int64, string) string // Function to format a sample tag value into a string
39
-	ObjNames    bool                       // Always preserve obj filename
40
-	OrigFnNames bool                       // Preserve original (eg mangled) function names
37
+	SampleValue       func(s []int64) int64      // Function to compute the value of a sample
38
+	SampleMeanDivisor func(s []int64) int64      // Function to compute the divisor for mean graphs, or nil
39
+	FormatTag         func(int64, string) string // Function to format a sample tag value into a string
40
+	ObjNames          bool                       // Always preserve obj filename
41
+	OrigFnNames       bool                       // Preserve original (eg mangled) function names
41
 
42
 
42
 	CallTree     bool // Build a tree instead of a graph
43
 	CallTree     bool // Build a tree instead of a graph
43
 	DropNegative bool // Drop nodes with overall negative values
44
 	DropNegative bool // Drop nodes with overall negative values
63
 
64
 
64
 	// Values associated to this node. Flat is exclusive to this node,
65
 	// Values associated to this node. Flat is exclusive to this node,
65
 	// Cum includes all descendents.
66
 	// Cum includes all descendents.
66
-	Flat, Cum int64
67
+	Flat, FlatDiv, Cum, CumDiv int64
67
 
68
 
68
 	// In and out Contains the nodes immediately reaching or reached by
69
 	// In and out Contains the nodes immediately reaching or reached by
69
 	// this node.
70
 	// this node.
79
 	NumericTags map[string]TagMap
80
 	NumericTags map[string]TagMap
80
 }
81
 }
81
 
82
 
83
+// FlatValue returns the exclusive value for this node, computing the
84
+// mean if a divisor is available.
85
+func (n *Node) FlatValue() int64 {
86
+	if n.FlatDiv == 0 {
87
+		return n.Flat
88
+	}
89
+	return n.Flat / n.FlatDiv
90
+}
91
+
92
+// CumValue returns the inclusive value for this node, computing the
93
+// mean if a divisor is available.
94
+func (n *Node) CumValue() int64 {
95
+	if n.CumDiv == 0 {
96
+		return n.Cum
97
+	}
98
+	return n.Cum / n.CumDiv
99
+}
100
+
82
 // AddToEdge increases the weight of an edge between two nodes. If
101
 // AddToEdge increases the weight of an edge between two nodes. If
83
 // there isn't such an edge one is created.
102
 // there isn't such an edge one is created.
84
-func (n *Node) AddToEdge(to *Node, w int64, residual, inline bool) {
103
+func (n *Node) AddToEdge(to *Node, v int64, residual, inline bool) {
104
+	n.AddToEdgeDiv(to, 0, v, residual, inline)
105
+}
106
+
107
+// AddToEdgeDiv increases the weight of an edge between two nodes. If
108
+// there isn't such an edge one is created.
109
+func (n *Node) AddToEdgeDiv(to *Node, dv, v int64, residual, inline bool) {
85
 	if n.Out[to] != to.In[n] {
110
 	if n.Out[to] != to.In[n] {
86
 		panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
111
 		panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
87
 	}
112
 	}
88
 
113
 
89
 	if e := n.Out[to]; e != nil {
114
 	if e := n.Out[to]; e != nil {
90
-		e.Weight += w
115
+		e.WeightDiv += dv
116
+		e.Weight += v
91
 		if residual {
117
 		if residual {
92
 			e.Residual = true
118
 			e.Residual = true
93
 		}
119
 		}
97
 		return
123
 		return
98
 	}
124
 	}
99
 
125
 
100
-	info := &Edge{Src: n, Dest: to, Weight: w, Residual: residual, Inline: inline}
126
+	info := &Edge{Src: n, Dest: to, WeightDiv: dv, Weight: v, Residual: residual, Inline: inline}
101
 	n.Out[to] = info
127
 	n.Out[to] = info
102
 	to.In[n] = info
128
 	to.In[n] = info
103
 }
129
 }
205
 type Edge struct {
231
 type Edge struct {
206
 	Src, Dest *Node
232
 	Src, Dest *Node
207
 	// The summary weight of the edge
233
 	// The summary weight of the edge
208
-	Weight int64
234
+	Weight, WeightDiv int64
235
+
209
 	// residual edges connect nodes that were connected through a
236
 	// residual edges connect nodes that were connected through a
210
 	// separate node, which has been removed from the report.
237
 	// separate node, which has been removed from the report.
211
 	Residual bool
238
 	Residual bool
213
 	Inline bool
240
 	Inline bool
214
 }
241
 }
215
 
242
 
243
+func (e *Edge) WeightValue() int64 {
244
+	if e.WeightDiv == 0 {
245
+		return e.Weight
246
+	}
247
+	return e.Weight / e.WeightDiv
248
+}
249
+
216
 // Tag represent sample annotations
250
 // Tag represent sample annotations
217
 type Tag struct {
251
 type Tag struct {
218
-	Name  string
219
-	Unit  string // Describe the value, "" for non-numeric tags
220
-	Value int64
221
-	Flat  int64
222
-	Cum   int64
252
+	Name          string
253
+	Unit          string // Describe the value, "" for non-numeric tags
254
+	Value         int64
255
+	Flat, FlatDiv int64
256
+	Cum, CumDiv   int64
257
+}
258
+
259
+// FlatValue returns the exclusive value for this tag, computing the
260
+// mean if a divisor is available.
261
+func (t *Tag) FlatValue() int64 {
262
+	if t.FlatDiv == 0 {
263
+		return t.Flat
264
+	}
265
+	return t.Flat / t.FlatDiv
266
+}
267
+
268
+// CumValue returns the inclusive value for this tag, computing the
269
+// mean if a divisor is available.
270
+func (t *Tag) CumValue() int64 {
271
+	if t.CumDiv == 0 {
272
+		return t.Cum
273
+	}
274
+	return t.Cum / t.CumDiv
223
 }
275
 }
224
 
276
 
225
 // TagMap is a collection of tags, classified by their name.
277
 // TagMap is a collection of tags, classified by their name.
247
 func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
299
 func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
248
 	nodes, locationMap := CreateNodes(prof, o)
300
 	nodes, locationMap := CreateNodes(prof, o)
249
 	for _, sample := range prof.Sample {
301
 	for _, sample := range prof.Sample {
250
-		weight := o.SampleValue(sample.Value)
251
-		if weight == 0 {
302
+		var w, dw int64
303
+		w = o.SampleValue(sample.Value)
304
+		if o.SampleMeanDivisor != nil {
305
+			dw = o.SampleMeanDivisor(sample.Value)
306
+		}
307
+		if dw == 0 && w == 0 {
252
 			continue
308
 			continue
253
 		}
309
 		}
254
 		seenNode := make(map[*Node]bool, len(sample.Location))
310
 		seenNode := make(map[*Node]bool, len(sample.Location))
271
 				// Add cum weight to all nodes in stack, avoiding double counting.
327
 				// Add cum weight to all nodes in stack, avoiding double counting.
272
 				if _, ok := seenNode[n]; !ok {
328
 				if _, ok := seenNode[n]; !ok {
273
 					seenNode[n] = true
329
 					seenNode[n] = true
274
-					n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
330
+					n.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, false)
275
 				}
331
 				}
276
 				// Update edge weights for all edges in stack, avoiding double counting.
332
 				// Update edge weights for all edges in stack, avoiding double counting.
277
 				if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent {
333
 				if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent {
278
 					seenEdge[nodePair{n, parent}] = true
334
 					seenEdge[nodePair{n, parent}] = true
279
-					parent.AddToEdge(n, weight, residual, ni != len(locNodes)-1)
335
+					parent.AddToEdgeDiv(n, dw, w, residual, ni != len(locNodes)-1)
280
 				}
336
 				}
281
 				parent = n
337
 				parent = n
282
 				residual = false
338
 				residual = false
284
 		}
340
 		}
285
 		if parent != nil && !residual {
341
 		if parent != nil && !residual {
286
 			// Add flat weight to leaf node.
342
 			// Add flat weight to leaf node.
287
-			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
343
+			parent.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, true)
288
 		}
344
 		}
289
 	}
345
 	}
290
 
346
 
316
 func newTree(prof *profile.Profile, o *Options) (g *Graph) {
372
 func newTree(prof *profile.Profile, o *Options) (g *Graph) {
317
 	parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
373
 	parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
318
 	for _, sample := range prof.Sample {
374
 	for _, sample := range prof.Sample {
319
-		weight := o.SampleValue(sample.Value)
320
-		if weight == 0 {
375
+		var w, dw int64
376
+		w = o.SampleValue(sample.Value)
377
+		if o.SampleMeanDivisor != nil {
378
+			dw = o.SampleMeanDivisor(sample.Value)
379
+		}
380
+		if dw == 0 && w == 0 {
321
 			continue
381
 			continue
322
 		}
382
 		}
323
 		var parent *Node
383
 		var parent *Node
339
 				if n == nil {
399
 				if n == nil {
340
 					continue
400
 					continue
341
 				}
401
 				}
342
-				n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
402
+				n.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, false)
343
 				if parent != nil {
403
 				if parent != nil {
344
-					parent.AddToEdge(n, weight, false, lidx != len(lines)-1)
404
+					parent.AddToEdgeDiv(n, dw, w, false, lidx != len(lines)-1)
345
 				}
405
 				}
346
 				parent = n
406
 				parent = n
347
 			}
407
 			}
348
 		}
408
 		}
349
 		if parent != nil {
409
 		if parent != nil {
350
-			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
410
+			parent.addSample(dw, w, labels, sample.NumLabel, o.FormatTag, true)
351
 		}
411
 		}
352
 	}
412
 	}
353
 
413
 
540
 	return
600
 	return
541
 }
601
 }
542
 
602
 
543
-func (n *Node) addSample(value int64, labels string, numLabel map[string][]int64, format func(int64, string) string, flat bool) {
603
+func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64, format func(int64, string) string, flat bool) {
544
 	// Update sample value
604
 	// Update sample value
545
 	if flat {
605
 	if flat {
546
-		n.Flat += value
606
+		n.FlatDiv += dw
607
+		n.Flat += w
547
 	} else {
608
 	} else {
548
-		n.Cum += value
609
+		n.CumDiv += dw
610
+		n.Cum += w
549
 	}
611
 	}
550
 
612
 
551
 	// Add string tags
613
 	// Add string tags
552
 	if labels != "" {
614
 	if labels != "" {
553
 		t := n.LabelTags.findOrAddTag(labels, "", 0)
615
 		t := n.LabelTags.findOrAddTag(labels, "", 0)
554
 		if flat {
616
 		if flat {
555
-			t.Flat += value
617
+			t.FlatDiv += dw
618
+			t.Flat += w
556
 		} else {
619
 		} else {
557
-			t.Cum += value
620
+			t.CumDiv += dw
621
+			t.Cum += w
558
 		}
622
 		}
559
 	}
623
 	}
560
 
624
 
571
 		for _, v := range nvals {
635
 		for _, v := range nvals {
572
 			t := numericTags.findOrAddTag(format(v, key), key, v)
636
 			t := numericTags.findOrAddTag(format(v, key), key, v)
573
 			if flat {
637
 			if flat {
574
-				t.Flat += value
638
+				t.FlatDiv += dw
639
+				t.Flat += w
575
 			} else {
640
 			} else {
576
-				t.Cum += value
641
+				t.CumDiv += dw
642
+				t.Cum += w
577
 			}
643
 			}
578
 		}
644
 		}
579
 	}
645
 	}

+ 46
- 26
internal/report/report.go 查看文件

141
 	var minValue int64
141
 	var minValue int64
142
 
142
 
143
 	for _, n := range g.Nodes {
143
 	for _, n := range g.Nodes {
144
-		nodeMin := abs64(n.Flat)
144
+		nodeMin := abs64(n.FlatValue())
145
 		if nodeMin == 0 {
145
 		if nodeMin == 0 {
146
-			nodeMin = abs64(n.Cum)
146
+			nodeMin = abs64(n.CumValue())
147
 		}
147
 		}
148
 		if nodeMin > 0 && (minValue == 0 || nodeMin < minValue) {
148
 		if nodeMin > 0 && (minValue == 0 || nodeMin < minValue) {
149
 			minValue = nodeMin
149
 			minValue = nodeMin
201
 	}
201
 	}
202
 
202
 
203
 	gopt := &graph.Options{
203
 	gopt := &graph.Options{
204
-		SampleValue:  o.SampleValue,
205
-		FormatTag:    formatTag,
206
-		CallTree:     o.CallTree && (o.OutputFormat == Dot || o.OutputFormat == Callgrind),
207
-		DropNegative: o.DropNegative,
208
-		KeptNodes:    nodes,
204
+		SampleValue:       o.SampleValue,
205
+		SampleMeanDivisor: o.SampleMeanDivisor,
206
+		FormatTag:         formatTag,
207
+		CallTree:          o.CallTree && (o.OutputFormat == Dot || o.OutputFormat == Callgrind),
208
+		DropNegative:      o.DropNegative,
209
+		KeptNodes:         nodes,
209
 	}
210
 	}
210
 
211
 
211
 	// Only keep binary names for disassembly-based reports, otherwise
212
 	// Only keep binary names for disassembly-based reports, otherwise
240
 	}
241
 	}
241
 	var flatSum int64
242
 	var flatSum int64
242
 	for i, n := range g.Nodes {
243
 	for i, n := range g.Nodes {
243
-		name, flat, cum := n.Info.PrintableName(), n.Flat, n.Cum
244
+		name, flat, cum := n.Info.PrintableName(), n.FlatValue(), n.CumValue()
244
 
245
 
245
 		flatSum += flat
246
 		flatSum += flat
246
 		f := &profile.Function{
247
 		f := &profile.Function{
319
 			percentage(cumSum, rpt.total))
320
 			percentage(cumSum, rpt.total))
320
 
321
 
321
 		for _, n := range ns {
322
 		for _, n := range ns {
322
-			fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.Flat, rpt), valueOrDot(n.Cum, rpt), n.Info.Address, n.Info.Name)
323
+			fmt.Fprintf(w, "%10s %10s %10x: %s\n", valueOrDot(n.FlatValue(), rpt), valueOrDot(n.CumValue(), rpt), n.Info.Address, n.Info.Name)
323
 		}
324
 		}
324
 	}
325
 	}
325
 	return nil
326
 	return nil
442
 		// Sum all the samples until the next instruction (to account
443
 		// Sum all the samples until the next instruction (to account
443
 		// for samples attributed to the middle of an instruction).
444
 		// for samples attributed to the middle of an instruction).
444
 		for next := insns[ix+1].Addr; s < len(samples) && samples[s].Info.Address-base < next; s++ {
445
 		for next := insns[ix+1].Addr; s < len(samples) && samples[s].Info.Address-base < next; s++ {
446
+			n.FlatDiv += samples[s].FlatDiv
445
 			n.Flat += samples[s].Flat
447
 			n.Flat += samples[s].Flat
448
+			n.CumDiv += samples[s].CumDiv
446
 			n.Cum += samples[s].Cum
449
 			n.Cum += samples[s].Cum
447
 			if samples[s].Info.File != "" {
450
 			if samples[s].Info.File != "" {
448
 				n.Info.File = trimPath(samples[s].Info.File)
451
 				n.Info.File = trimPath(samples[s].Info.File)
521
 		fmt.Fprintf(w, "%s: Total %d\n", key, total)
524
 		fmt.Fprintf(w, "%s: Total %d\n", key, total)
522
 		for _, t := range graph.SortTags(tags, true) {
525
 		for _, t := range graph.SortTags(tags, true) {
523
 			if total > 0 {
526
 			if total > 0 {
524
-				fmt.Fprintf(w, "  %8d (%s): %s\n", t.Flat,
525
-					percentage(t.Flat, total), t.Name)
527
+				fmt.Fprintf(w, "  %8d (%s): %s\n", t.FlatValue(),
528
+					percentage(t.FlatValue(), total), t.Name)
526
 			} else {
529
 			} else {
527
-				fmt.Fprintf(w, "  %8d: %s\n", t.Flat, t.Name)
530
+				fmt.Fprintf(w, "  %8d: %s\n", t.FlatValue(), t.Name)
528
 			}
531
 			}
529
 		}
532
 		}
530
 		fmt.Fprintln(w)
533
 		fmt.Fprintln(w)
544
 
547
 
545
 	var flatSum int64
548
 	var flatSum int64
546
 	for _, n := range g.Nodes {
549
 	for _, n := range g.Nodes {
547
-		name, flat, cum := n.Info.PrintableName(), n.Flat, n.Cum
550
+		name, flat, cum := n.Info.PrintableName(), n.FlatValue(), n.CumValue()
548
 
551
 
549
 		var inline, noinline bool
552
 		var inline, noinline bool
550
 		for _, e := range n.In {
553
 		for _, e := range n.In {
604
 		}
607
 		}
605
 		sort.Strings(labels)
608
 		sort.Strings(labels)
606
 		fmt.Fprint(w, strings.Join(labels, ""))
609
 		fmt.Fprint(w, strings.Join(labels, ""))
610
+		var d, v int64
611
+		v = o.SampleValue(sample.Value)
612
+		if o.SampleMeanDivisor != nil {
613
+			d = o.SampleMeanDivisor(sample.Value)
614
+		}
607
 		// Print call stack.
615
 		// Print call stack.
616
+		if d != 0 {
617
+			v = v / d
618
+		}
608
 		fmt.Fprintf(w, "%10s   %s\n",
619
 		fmt.Fprintf(w, "%10s   %s\n",
609
-			rpt.formatValue(o.SampleValue(sample.Value)),
610
-			stack[0].Info.PrintableName())
611
-
620
+			rpt.formatValue(v), stack[0].Info.PrintableName())
612
 		for _, s := range stack[1:] {
621
 		for _, s := range stack[1:] {
613
 			fmt.Fprintf(w, "%10s   %s\n", "", s.Info.PrintableName())
622
 			fmt.Fprintf(w, "%10s   %s\n", "", s.Info.PrintableName())
614
 		}
623
 		}
648
 		}
657
 		}
649
 
658
 
650
 		addr := callgrindAddress(prevInfo, n.Info.Address)
659
 		addr := callgrindAddress(prevInfo, n.Info.Address)
651
-		sv, _ := measurement.Scale(n.Flat, o.SampleUnit, o.OutputUnit)
660
+		sv, _ := measurement.Scale(n.FlatValue(), o.SampleUnit, o.OutputUnit)
652
 		fmt.Fprintf(w, "%s %d %d\n", addr, n.Info.Lineno, int64(sv))
661
 		fmt.Fprintf(w, "%s %d %d\n", addr, n.Info.Lineno, int64(sv))
653
 
662
 
654
 		// Print outgoing edges.
663
 		// Print outgoing edges.
772
 
781
 
773
 	rx := rpt.options.Symbol
782
 	rx := rpt.options.Symbol
774
 	for _, n := range g.Nodes {
783
 	for _, n := range g.Nodes {
775
-		name, flat, cum := n.Info.PrintableName(), n.Flat, n.Cum
784
+		name, flat, cum := n.Info.PrintableName(), n.FlatValue(), n.CumValue()
776
 
785
 
777
 		// Skip any entries that do not match the regexp (for the "peek" command).
786
 		// Skip any entries that do not match the regexp (for the "peek" command).
778
 		if rx != nil && !rx.MatchString(name) {
787
 		if rx != nil && !rx.MatchString(name) {
902
 
911
 
903
 	var flatSum int64
912
 	var flatSum int64
904
 	for _, n := range g.Nodes {
913
 	for _, n := range g.Nodes {
905
-		flatSum = flatSum + n.Flat
914
+		flatSum = flatSum + n.FlatValue()
906
 	}
915
 	}
907
 
916
 
908
 	label = append(label, fmt.Sprintf("Showing nodes accounting for %s, %s of %s total", rpt.formatValue(flatSum), strings.TrimSpace(percentage(flatSum, rpt.total)), rpt.formatValue(rpt.total)))
917
 	label = append(label, fmt.Sprintf("Showing nodes accounting for %s, %s of %s total", rpt.formatValue(flatSum), strings.TrimSpace(percentage(flatSum, rpt.total)), rpt.formatValue(rpt.total)))
965
 	NodeFraction float64
974
 	NodeFraction float64
966
 	EdgeFraction float64
975
 	EdgeFraction float64
967
 
976
 
968
-	SampleValue func(s []int64) int64
969
-	SampleType  string
970
-	SampleUnit  string // Unit for the sample data from the profile.
977
+	SampleValue       func(s []int64) int64
978
+	SampleMeanDivisor func(s []int64) int64
979
+	SampleType        string
980
+	SampleUnit        string // Unit for the sample data from the profile.
971
 
981
 
972
 	OutputUnit string // Units for data formatting in report.
982
 	OutputUnit string // Units for data formatting in report.
973
 
983
 
985
 		}
995
 		}
986
 		return measurement.ScaledLabel(v, o.SampleUnit, o.OutputUnit)
996
 		return measurement.ScaledLabel(v, o.SampleUnit, o.OutputUnit)
987
 	}
997
 	}
988
-	return &Report{prof, computeTotal(prof, o.SampleValue, !o.PositivePercentages),
998
+	return &Report{prof, computeTotal(prof, o.SampleValue, o.SampleMeanDivisor, !o.PositivePercentages),
989
 		o, format}
999
 		o, format}
990
 }
1000
 }
991
 
1001
 
1010
 // absolute values to provide a meaningful percentage for both
1020
 // absolute values to provide a meaningful percentage for both
1011
 // negative and positive values. Otherwise only use positive values,
1021
 // negative and positive values. Otherwise only use positive values,
1012
 // which is useful when comparing profiles from different jobs.
1022
 // which is useful when comparing profiles from different jobs.
1013
-func computeTotal(prof *profile.Profile, value func(v []int64) int64, includeNegative bool) int64 {
1014
-	var ret int64
1023
+func computeTotal(prof *profile.Profile, value, meanDiv func(v []int64) int64, includeNegative bool) int64 {
1024
+	var div, ret int64
1015
 	for _, sample := range prof.Sample {
1025
 	for _, sample := range prof.Sample {
1016
-		if v := value(sample.Value); v > 0 {
1026
+		var d, v int64
1027
+		v = value(sample.Value)
1028
+		if meanDiv != nil {
1029
+			d = meanDiv(sample.Value)
1030
+		}
1031
+		if v >= 0 {
1017
 			ret += v
1032
 			ret += v
1033
+			div += d
1018
 		} else if includeNegative {
1034
 		} else if includeNegative {
1019
 			ret -= v
1035
 			ret -= v
1036
+			div += d
1020
 		}
1037
 		}
1021
 	}
1038
 	}
1039
+	if div != 0 {
1040
+		return ret / div
1041
+	}
1022
 	return ret
1042
 	return ret
1023
 }
1043
 }
1024
 
1044