浏览代码

Merge pull request #5 from rauls5382/master

Ensure pprof call_tree output is a tree
Josef Jelinek 9 年前
父节点
当前提交
0f3114e0d9
共有 3 个文件被更改,包括 55 次插入56 次删除
  1. 29
    41
      internal/graph/graph.go
  2. 14
    3
      internal/report/report.go
  3. 12
    12
      internal/report/testdata/source.dot

+ 29
- 41
internal/graph/graph.go 查看文件

288
 	kept := o.KeptNodes
288
 	kept := o.KeptNodes
289
 	keepBinary := o.ObjNames
289
 	keepBinary := o.ObjNames
290
 	parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
290
 	parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
291
+nextSample:
291
 	for _, sample := range prof.Sample {
292
 	for _, sample := range prof.Sample {
292
 		weight := o.SampleValue(sample.Value)
293
 		weight := o.SampleValue(sample.Value)
293
 		if weight == 0 {
294
 		if weight == 0 {
294
 			continue
295
 			continue
295
 		}
296
 		}
296
 		var parent *Node
297
 		var parent *Node
297
-		// A residual edge goes over one or more nodes that were not kept.
298
-		residual := false
299
 		labels := joinLabels(sample)
298
 		labels := joinLabels(sample)
300
 		// Group the sample frames, based on a per-node map.
299
 		// Group the sample frames, based on a per-node map.
301
 		for i := len(sample.Location) - 1; i >= 0; i-- {
300
 		for i := len(sample.Location) - 1; i >= 0; i-- {
302
 			l := sample.Location[i]
301
 			l := sample.Location[i]
303
-			nodeMap := parentNodeMap[parent]
304
-			if nodeMap == nil {
305
-				nodeMap = make(NodeMap)
306
-				parentNodeMap[parent] = nodeMap
302
+			lines := l.Line
303
+			if len(lines) == 0 {
304
+				lines = []profile.Line{{}} // Create empty line to include location info.
307
 			}
305
 			}
308
-			locNodes := nodeMap.findOrInsertLocation(l, keepBinary, kept)
309
-			for ni := len(locNodes) - 1; ni >= 0; ni-- {
310
-				n := locNodes[ni]
306
+			for lidx := len(lines) - 1; lidx >= 0; lidx-- {
307
+				nodeMap := parentNodeMap[parent]
308
+				if nodeMap == nil {
309
+					nodeMap = make(NodeMap)
310
+					parentNodeMap[parent] = nodeMap
311
+				}
312
+				n := nodeMap.findOrInsertLine(l, lines[lidx], keepBinary, kept)
311
 				if n == nil {
313
 				if n == nil {
312
-					residual = true
313
-					continue
314
+					continue nextSample
314
 				}
315
 				}
315
 				n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
316
 				n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
316
 				if parent != nil {
317
 				if parent != nil {
317
-					parent.AddToEdge(n, weight, residual, ni != len(locNodes)-1)
318
+					parent.AddToEdge(n, weight, false, lidx != len(lines)-1)
318
 				}
319
 				}
319
 				parent = n
320
 				parent = n
320
-				residual = false
321
 			}
321
 			}
322
 		}
322
 		}
323
-		if parent != nil && !residual {
323
+		if parent != nil {
324
 			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
324
 			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
325
 		}
325
 		}
326
 	}
326
 	}
395
 
395
 
396
 	nm := make(NodeMap, len(prof.Location))
396
 	nm := make(NodeMap, len(prof.Location))
397
 	for _, l := range prof.Location {
397
 	for _, l := range prof.Location {
398
-		if nodes := nm.findOrInsertLocation(l, keepBinary, kept); nodes != nil {
399
-			locations[l.ID] = nodes
398
+		lines := l.Line
399
+		if len(lines) == 0 {
400
+			lines = []profile.Line{{}} // Create empty line to include location info.
400
 		}
401
 		}
402
+		nodes := make(Nodes, len(lines))
403
+		for ln := range lines {
404
+			nodes[ln] = nm.findOrInsertLine(l, lines[ln], keepBinary, kept)
405
+		}
406
+		locations[l.ID] = nodes
401
 	}
407
 	}
402
 	return nm.nodes(), locations
408
 	return nm.nodes(), locations
403
 }
409
 }
410
 	return nodes
416
 	return nodes
411
 }
417
 }
412
 
418
 
413
-func (nm NodeMap) findOrInsertLocation(l *profile.Location, keepBinary bool, kept NodeSet) Nodes {
419
+func (nm NodeMap) findOrInsertLine(l *profile.Location, li profile.Line, keepBinary bool, kept NodeSet) *Node {
414
 	var objfile string
420
 	var objfile string
415
 	if m := l.Mapping; m != nil && m.File != "" {
421
 	if m := l.Mapping; m != nil && m.File != "" {
416
 		objfile = filepath.Base(m.File)
422
 		objfile = filepath.Base(m.File)
417
 	}
423
 	}
418
 
424
 
419
-	if len(l.Line) == 0 {
420
-		ni := NodeInfo{
421
-			Address: l.Address,
422
-			Objfile: objfile,
423
-		}
424
-		return Nodes{nm.FindOrInsertNode(ni, kept)}
425
+	if ni := nodeInfo(l, li, objfile, keepBinary); ni != nil {
426
+		return nm.FindOrInsertNode(*ni, kept)
425
 	}
427
 	}
426
-	locNodes := make(Nodes, len(l.Line))
427
-	for li := range l.Line {
428
-		if ni := nodeInfo(l, li, objfile, keepBinary); ni != nil {
429
-			locNodes[li] = nm.FindOrInsertNode(*ni, kept)
430
-		}
431
-	}
432
-	return locNodes
428
+	return nil
433
 }
429
 }
434
 
430
 
435
-func nodeInfo(l *profile.Location, li int, objfile string, keepBinary bool) *NodeInfo {
436
-	if !keepBinary {
437
-		objfile = ""
438
-	}
439
-	line := l.Line[li]
431
+func nodeInfo(l *profile.Location, line profile.Line, objfile string, keepBinary bool) *NodeInfo {
440
 	if line.Function == nil {
432
 	if line.Function == nil {
441
-		if l.Address == 0 {
442
-			return nil
443
-		}
444
 		return &NodeInfo{Address: l.Address, Objfile: objfile}
433
 		return &NodeInfo{Address: l.Address, Objfile: objfile}
445
 	}
434
 	}
446
-
447
 	ni := &NodeInfo{
435
 	ni := &NodeInfo{
448
 		Address:  l.Address,
436
 		Address:  l.Address,
449
 		Lineno:   int(line.Line),
437
 		Lineno:   int(line.Line),
450
 		Name:     line.Function.Name,
438
 		Name:     line.Function.Name,
451
 		OrigName: line.Function.SystemName,
439
 		OrigName: line.Function.SystemName,
452
-		Objfile:  objfile,
453
 	}
440
 	}
454
 	if fname := line.Function.Filename; fname != "" {
441
 	if fname := line.Function.Filename; fname != "" {
455
 		ni.File = filepath.Clean(fname)
442
 		ni.File = filepath.Clean(fname)
456
 	}
443
 	}
457
 	if keepBinary {
444
 	if keepBinary {
445
+		ni.Objfile = objfile
458
 		ni.StartLine = int(line.Function.StartLine)
446
 		ni.StartLine = int(line.Function.StartLine)
459
 	}
447
 	}
460
 	return ni
448
 	return ni
479
 	return t.t[i].Name < t.t[j].Name
467
 	return t.t[i].Name < t.t[j].Name
480
 }
468
 }
481
 
469
 
482
-// Sum adds the Flat and sum values on a report.
470
+// Sum adds the flat and cum values of a set of nodes.
483
 func (ns Nodes) Sum() (flat int64, cum int64) {
471
 func (ns Nodes) Sum() (flat int64, cum int64) {
484
 	for _, n := range ns {
472
 	for _, n := range ns {
485
 		flat += n.Flat
473
 		flat += n.Flat

+ 14
- 3
internal/report/report.go 查看文件

77
 
77
 
78
 	// Build a graph and refine it. On each refinement step we must rebuild the graph from the samples,
78
 	// Build a graph and refine it. On each refinement step we must rebuild the graph from the samples,
79
 	// as the graph itself doesn't contain enough information to preserve full precision.
79
 	// as the graph itself doesn't contain enough information to preserve full precision.
80
+	visualMode := o.OutputFormat == Dot
81
+	cumSort := o.CumSort
80
 
82
 
81
 	// First step: Build complete graph to identify low frequency nodes, based on their cum weight.
83
 	// First step: Build complete graph to identify low frequency nodes, based on their cum weight.
82
 	g = rpt.newGraph(nil)
84
 	g = rpt.newGraph(nil)
84
 	nodeCutoff := abs64(int64(float64(totalValue) * o.NodeFraction))
86
 	nodeCutoff := abs64(int64(float64(totalValue) * o.NodeFraction))
85
 	edgeCutoff := abs64(int64(float64(totalValue) * o.EdgeFraction))
87
 	edgeCutoff := abs64(int64(float64(totalValue) * o.EdgeFraction))
86
 
88
 
89
+	// Visual mode optimization only supports graph output, not tree.
90
+	// Do not apply edge cutoff to preserve tree structure.
91
+	if o.CallTree {
92
+		visualMode = false
93
+		if o.OutputFormat == Dot {
94
+			cumSort = true
95
+		}
96
+		edgeCutoff = 0
97
+	}
98
+
87
 	// Filter out nodes with cum value below nodeCutoff.
99
 	// Filter out nodes with cum value below nodeCutoff.
88
 	if nodeCutoff > 0 {
100
 	if nodeCutoff > 0 {
89
 		if nodesKept := g.DiscardLowFrequencyNodes(nodeCutoff); len(g.Nodes) != len(nodesKept) {
101
 		if nodesKept := g.DiscardLowFrequencyNodes(nodeCutoff); len(g.Nodes) != len(nodesKept) {
95
 
107
 
96
 	// Second step: Limit the total number of nodes. Apply specialized heuristics to improve
108
 	// Second step: Limit the total number of nodes. Apply specialized heuristics to improve
97
 	// visualization when generating dot output.
109
 	// visualization when generating dot output.
98
-	visualMode := o.OutputFormat == Dot
99
-	g.SortNodes(o.CumSort, visualMode)
110
+	g.SortNodes(cumSort, visualMode)
100
 	if nodeCount := o.NodeCount; nodeCount > 0 {
111
 	if nodeCount := o.NodeCount; nodeCount > 0 {
101
 		// Remove low frequency tags and edges as they affect selection.
112
 		// Remove low frequency tags and edges as they affect selection.
102
 		g.TrimLowFrequencyTags(nodeCutoff)
113
 		g.TrimLowFrequencyTags(nodeCutoff)
103
 		g.TrimLowFrequencyEdges(edgeCutoff)
114
 		g.TrimLowFrequencyEdges(edgeCutoff)
104
 		if nodesKept := g.SelectTopNodes(nodeCount, visualMode); len(nodesKept) != len(g.Nodes) {
115
 		if nodesKept := g.SelectTopNodes(nodeCount, visualMode); len(nodesKept) != len(g.Nodes) {
105
 			g = rpt.newGraph(nodesKept)
116
 			g = rpt.newGraph(nodesKept)
106
-			g.SortNodes(o.CumSort, visualMode)
117
+			g.SortNodes(cumSort, visualMode)
107
 		}
118
 		}
108
 	}
119
 	}
109
 
120
 

+ 12
- 12
internal/report/testdata/source.dot 查看文件

1
 digraph "unnamed" {
1
 digraph "unnamed" {
2
 node [style=filled fillcolor="#f8f8f8"]
2
 node [style=filled fillcolor="#f8f8f8"]
3
 subgraph cluster_L { "Duration: 10s, Total samples = 11111 " [shape=box fontsize=16 label="Duration: 10s, Total samples = 11111 \lShowing nodes accounting for 11111, 100% of 11111 total\l"] }
3
 subgraph cluster_L { "Duration: 10s, Total samples = 11111 " [shape=box fontsize=16 label="Duration: 10s, Total samples = 11111 \lShowing nodes accounting for 11111, 100% of 11111 total\l"] }
4
-N1 [label="tee\nsource2:8\n10000 (90.00%)" fontsize=24 shape=box tooltip="tee testdata/source2:8 (10000)" color="#b20500" fillcolor="#edd6d5"]
5
-N2 [label="main\nsource1:2\n1 (0.009%)\nof 11111 (100%)" fontsize=9 shape=box tooltip="main testdata/source1:2 (11111)" color="#b20000" fillcolor="#edd5d5"]
6
-N3 [label="tee\nsource2:2\n1000 (9.00%)\nof 11000 (99.00%)" fontsize=14 shape=box tooltip="tee testdata/source2:2 (11000)" color="#b20000" fillcolor="#edd5d5"]
7
-N4 [label="tee\nsource2:8\n100 (0.9%)" fontsize=10 shape=box tooltip="tee testdata/source2:8 (100)" color="#b2b0aa" fillcolor="#edecec"]
8
-N5 [label="bar\nsource1:10\n10 (0.09%)" fontsize=9 shape=box tooltip="bar testdata/source1:10 (10)" color="#b2b2b1" fillcolor="#ededed"]
9
-N6 [label="bar\nsource1:10\n0 of 100 (0.9%)" fontsize=8 shape=box tooltip="bar testdata/source1:10 (100)" color="#b2b0aa" fillcolor="#edecec"]
4
+N1 [label="main\nsource1:2\n1 (0.009%)\nof 11111 (100%)" fontsize=9 shape=box tooltip="main testdata/source1:2 (11111)" color="#b20000" fillcolor="#edd5d5"]
5
+N2 [label="tee\nsource2:2\n1000 (9.00%)\nof 11000 (99.00%)" fontsize=14 shape=box tooltip="tee testdata/source2:2 (11000)" color="#b20000" fillcolor="#edd5d5"]
6
+N3 [label="tee\nsource2:8\n10000 (90.00%)" fontsize=24 shape=box tooltip="tee testdata/source2:8 (10000)" color="#b20500" fillcolor="#edd6d5"]
7
+N4 [label="bar\nsource1:10\n0 of 100 (0.9%)" fontsize=8 shape=box tooltip="bar testdata/source1:10 (100)" color="#b2b0aa" fillcolor="#edecec"]
8
+N5 [label="tee\nsource2:8\n100 (0.9%)" fontsize=10 shape=box tooltip="tee testdata/source2:8 (100)" color="#b2b0aa" fillcolor="#edecec"]
9
+N6 [label="bar\nsource1:10\n10 (0.09%)" fontsize=9 shape=box tooltip="bar testdata/source1:10 (10)" color="#b2b2b1" fillcolor="#ededed"]
10
 N7 [label="foo\nsource1:4\n0 of 10 (0.09%)" fontsize=8 shape=box tooltip="foo testdata/source1:4 (10)" color="#b2b2b1" fillcolor="#ededed"]
10
 N7 [label="foo\nsource1:4\n0 of 10 (0.09%)" fontsize=8 shape=box tooltip="foo testdata/source1:4 (10)" color="#b2b2b1" fillcolor="#ededed"]
11
-N2 -> N3 [label=" 11000" weight=100 penwidth=5 color="#b20000" tooltip="main testdata/source1:2 -> tee testdata/source2:2 (11000)" labeltooltip="main testdata/source1:2 -> tee testdata/source2:2 (11000)"]
12
-N3 -> N1 [label=" 10000" weight=91 penwidth=5 color="#b20500" tooltip="tee testdata/source2:2 -> tee testdata/source2:8 (10000)" labeltooltip="tee testdata/source2:2 -> tee testdata/source2:8 (10000)"]
13
-N6 -> N4 [label=" 100" color="#b2b0aa" tooltip="bar testdata/source1:10 -> tee testdata/source2:8 (100)" labeltooltip="bar testdata/source1:10 -> tee testdata/source2:8 (100)"]
14
-N2 -> N6 [label=" 100" color="#b2b0aa" tooltip="main testdata/source1:2 -> bar testdata/source1:10 (100)" labeltooltip="main testdata/source1:2 -> bar testdata/source1:10 (100)"]
15
-N7 -> N5 [label=" 10" color="#b2b2b1" tooltip="foo testdata/source1:4 -> bar testdata/source1:10 (10)" labeltooltip="foo testdata/source1:4 -> bar testdata/source1:10 (10)"]
16
-N2 -> N7 [label=" 10" color="#b2b2b1" tooltip="main testdata/source1:2 -> foo testdata/source1:4 (10)" labeltooltip="main testdata/source1:2 -> foo testdata/source1:4 (10)"]
11
+N1 -> N2 [label=" 11000" weight=100 penwidth=5 color="#b20000" tooltip="main testdata/source1:2 -> tee testdata/source2:2 (11000)" labeltooltip="main testdata/source1:2 -> tee testdata/source2:2 (11000)"]
12
+N2 -> N3 [label=" 10000" weight=91 penwidth=5 color="#b20500" tooltip="tee testdata/source2:2 -> tee testdata/source2:8 (10000)" labeltooltip="tee testdata/source2:2 -> tee testdata/source2:8 (10000)"]
13
+N4 -> N5 [label=" 100" color="#b2b0aa" tooltip="bar testdata/source1:10 -> tee testdata/source2:8 (100)" labeltooltip="bar testdata/source1:10 -> tee testdata/source2:8 (100)"]
14
+N1 -> N4 [label=" 100" color="#b2b0aa" tooltip="main testdata/source1:2 -> bar testdata/source1:10 (100)" labeltooltip="main testdata/source1:2 -> bar testdata/source1:10 (100)"]
15
+N7 -> N6 [label=" 10" color="#b2b2b1" tooltip="foo testdata/source1:4 -> bar testdata/source1:10 (10)" labeltooltip="foo testdata/source1:4 -> bar testdata/source1:10 (10)"]
16
+N1 -> N7 [label=" 10" color="#b2b2b1" tooltip="main testdata/source1:2 -> foo testdata/source1:4 (10)" labeltooltip="main testdata/source1:2 -> foo testdata/source1:4 (10)"]
17
 }
17
 }