Bläddra i källkod

Merge pull request #5 from rauls5382/master

Ensure pprof call_tree output is a tree
Josef Jelinek 9 år sedan
förälder
incheckning
0f3114e0d9
3 ändrade filer med 55 tillägg och 56 borttagningar
  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 Visa fil

@@ -288,39 +288,39 @@ func newTree(prof *profile.Profile, o *Options) (g *Graph) {
288 288
 	kept := o.KeptNodes
289 289
 	keepBinary := o.ObjNames
290 290
 	parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
291
+nextSample:
291 292
 	for _, sample := range prof.Sample {
292 293
 		weight := o.SampleValue(sample.Value)
293 294
 		if weight == 0 {
294 295
 			continue
295 296
 		}
296 297
 		var parent *Node
297
-		// A residual edge goes over one or more nodes that were not kept.
298
-		residual := false
299 298
 		labels := joinLabels(sample)
300 299
 		// Group the sample frames, based on a per-node map.
301 300
 		for i := len(sample.Location) - 1; i >= 0; i-- {
302 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 313
 				if n == nil {
312
-					residual = true
313
-					continue
314
+					continue nextSample
314 315
 				}
315 316
 				n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
316 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 320
 				parent = n
320
-				residual = false
321 321
 			}
322 322
 		}
323
-		if parent != nil && !residual {
323
+		if parent != nil {
324 324
 			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
325 325
 		}
326 326
 	}
@@ -395,9 +395,15 @@ func CreateNodes(prof *profile.Profile, keepBinary bool, kept NodeSet) (Nodes, m
395 395
 
396 396
 	nm := make(NodeMap, len(prof.Location))
397 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 408
 	return nm.nodes(), locations
403 409
 }
@@ -410,51 +416,33 @@ func (nm NodeMap) nodes() Nodes {
410 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 420
 	var objfile string
415 421
 	if m := l.Mapping; m != nil && m.File != "" {
416 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 432
 	if line.Function == nil {
441
-		if l.Address == 0 {
442
-			return nil
443
-		}
444 433
 		return &NodeInfo{Address: l.Address, Objfile: objfile}
445 434
 	}
446
-
447 435
 	ni := &NodeInfo{
448 436
 		Address:  l.Address,
449 437
 		Lineno:   int(line.Line),
450 438
 		Name:     line.Function.Name,
451 439
 		OrigName: line.Function.SystemName,
452
-		Objfile:  objfile,
453 440
 	}
454 441
 	if fname := line.Function.Filename; fname != "" {
455 442
 		ni.File = filepath.Clean(fname)
456 443
 	}
457 444
 	if keepBinary {
445
+		ni.Objfile = objfile
458 446
 		ni.StartLine = int(line.Function.StartLine)
459 447
 	}
460 448
 	return ni
@@ -479,7 +467,7 @@ func (t tags) Less(i, j int) bool {
479 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 471
 func (ns Nodes) Sum() (flat int64, cum int64) {
484 472
 	for _, n := range ns {
485 473
 		flat += n.Flat

+ 14
- 3
internal/report/report.go Visa fil

@@ -77,6 +77,8 @@ func (rpt *Report) newTrimmedGraph() (g *graph.Graph, origCount, droppedNodes, d
77 77
 
78 78
 	// Build a graph and refine it. On each refinement step we must rebuild the graph from the samples,
79 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 83
 	// First step: Build complete graph to identify low frequency nodes, based on their cum weight.
82 84
 	g = rpt.newGraph(nil)
@@ -84,6 +86,16 @@ func (rpt *Report) newTrimmedGraph() (g *graph.Graph, origCount, droppedNodes, d
84 86
 	nodeCutoff := abs64(int64(float64(totalValue) * o.NodeFraction))
85 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 99
 	// Filter out nodes with cum value below nodeCutoff.
88 100
 	if nodeCutoff > 0 {
89 101
 		if nodesKept := g.DiscardLowFrequencyNodes(nodeCutoff); len(g.Nodes) != len(nodesKept) {
@@ -95,15 +107,14 @@ func (rpt *Report) newTrimmedGraph() (g *graph.Graph, origCount, droppedNodes, d
95 107
 
96 108
 	// Second step: Limit the total number of nodes. Apply specialized heuristics to improve
97 109
 	// visualization when generating dot output.
98
-	visualMode := o.OutputFormat == Dot
99
-	g.SortNodes(o.CumSort, visualMode)
110
+	g.SortNodes(cumSort, visualMode)
100 111
 	if nodeCount := o.NodeCount; nodeCount > 0 {
101 112
 		// Remove low frequency tags and edges as they affect selection.
102 113
 		g.TrimLowFrequencyTags(nodeCutoff)
103 114
 		g.TrimLowFrequencyEdges(edgeCutoff)
104 115
 		if nodesKept := g.SelectTopNodes(nodeCount, visualMode); len(nodesKept) != len(g.Nodes) {
105 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 Visa fil

@@ -1,17 +1,17 @@
1 1
 digraph "unnamed" {
2 2
 node [style=filled fillcolor="#f8f8f8"]
3 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 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
 }