Browse Source

Merge pull request #38 from rauls5382/mean

Fix mean computation during graph creation
Hyoun Kyu Cho 8 years ago
parent
commit
35a78323ac
5 changed files with 204 additions and 111 deletions
  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 View File

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 View File

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 View File

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 View File

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 View File

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