Browse Source

Regresses NodeSet interface

This is to keep the new TrimTree functionality from breaking any code
currently using the public interface. We do this by separating NodeSet
from nodePtrSet and creating different functions for each.
Wade Simba Khadder 9 years ago
parent
commit
7c0b6f60fd
4 changed files with 69 additions and 35 deletions
  1. 46
    16
      internal/graph/graph.go
  2. 4
    6
      internal/graph/graph_test.go
  3. 17
    11
      internal/report/report.go
  4. 2
    2
      internal/report/source.go

+ 46
- 16
internal/graph/graph.go View File

143
 type NodeMap map[NodeInfo]*Node
143
 type NodeMap map[NodeInfo]*Node
144
 
144
 
145
 // NodeSet maps is a collection of node info structs.
145
 // NodeSet maps is a collection of node info structs.
146
-type NodeSet struct {
147
-	Info map[NodeInfo]bool
148
-	Ptr  map[*Node]bool
149
-}
146
+type NodeSet map[NodeInfo]bool
147
+
148
+type nodePtrSet map[*Node]bool
150
 
149
 
151
 // FindOrInsertNode takes the info for a node and either returns a matching node
150
 // FindOrInsertNode takes the info for a node and either returns a matching node
152
 // from the node map if one exists, or adds one to the map if one does not.
151
 // from the node map if one exists, or adds one to the map if one does not.
153
 // If kept is non-nil, nodes are only added if they can be located on it.
152
 // If kept is non-nil, nodes are only added if they can be located on it.
154
 func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
153
 func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
155
-	if kept.Info != nil {
156
-		if _, ok := kept.Info[info]; !ok {
154
+	if kept != nil {
155
+		if _, ok := kept[info]; !ok {
157
 			return nil
156
 			return nil
158
 		}
157
 		}
159
 	}
158
 	}
337
 
336
 
338
 // Trims a Graph that is in forest form to contain only the nodes in kept. This
337
 // Trims a Graph that is in forest form to contain only the nodes in kept. This
339
 // will not work correctly in the case that a node has multiple parents.
338
 // will not work correctly in the case that a node has multiple parents.
340
-func (g *Graph) TrimTree(kept NodeSet) {
339
+func (g *Graph) TrimTree(kept nodePtrSet) {
341
 	// Creates a new list of nodes
340
 	// Creates a new list of nodes
342
 	oldNodes := g.Nodes
341
 	oldNodes := g.Nodes
343
-	g.Nodes = make(Nodes, 0, len(kept.Ptr))
342
+	g.Nodes = make(Nodes, 0, len(kept))
344
 
343
 
345
 	for _, cur := range oldNodes {
344
 	for _, cur := range oldNodes {
346
 		// A node may not have multiple parents
345
 		// A node may not have multiple parents
349
 		}
348
 		}
350
 
349
 
351
 		// If a node should be kept, add it to the next list of nodes
350
 		// If a node should be kept, add it to the next list of nodes
352
-		if _, ok := kept.Ptr[cur]; ok {
351
+		if _, ok := kept[cur]; ok {
353
 			g.Nodes = append(g.Nodes, cur)
352
 			g.Nodes = append(g.Nodes, cur)
354
 			continue
353
 			continue
355
 		}
354
 		}
600
 	return makeNodeSet(g.Nodes, nodeCutoff)
599
 	return makeNodeSet(g.Nodes, nodeCutoff)
601
 }
600
 }
602
 
601
 
602
+// discardLowFrequencyNodePtrs returns a NodePtrSet of nodes at or over a
603
+// specific cum value cutoff.
604
+func (g *Graph) DiscardLowFrequencyNodePtrs(nodeCutoff int64) nodePtrSet {
605
+	cutNodes := getNodesWithCumCutoff(g.Nodes, nodeCutoff)
606
+	kept := make(nodePtrSet, len(cutNodes))
607
+	for _, n := range cutNodes {
608
+		kept[n] = true
609
+	}
610
+	return kept
611
+}
612
+
603
 func makeNodeSet(nodes Nodes, nodeCutoff int64) NodeSet {
613
 func makeNodeSet(nodes Nodes, nodeCutoff int64) NodeSet {
604
-	kept := NodeSet{
605
-		Info: make(map[NodeInfo]bool, len(nodes)),
606
-		Ptr:  make(map[*Node]bool, len(nodes)),
614
+	cutNodes := getNodesWithCumCutoff(nodes, nodeCutoff)
615
+	kept := make(NodeSet, len(cutNodes))
616
+	for _, n := range cutNodes {
617
+		kept[n.Info] = true
607
 	}
618
 	}
619
+	return kept
620
+}
621
+
622
+// Returns all the nodes who have a Cum value greater than or equal to cutoff
623
+func getNodesWithCumCutoff(nodes Nodes, nodeCutoff int64) Nodes {
624
+	cutoffNodes := make(Nodes, 0, len(nodes))
608
 	for _, n := range nodes {
625
 	for _, n := range nodes {
609
 		if abs64(n.Cum) < nodeCutoff {
626
 		if abs64(n.Cum) < nodeCutoff {
610
 			continue
627
 			continue
611
 		}
628
 		}
612
-		kept.Info[n.Info] = true
613
-		kept.Ptr[n] = true
629
+		cutoffNodes = append(cutoffNodes, n)
614
 	}
630
 	}
615
-	return kept
631
+	return cutoffNodes
616
 }
632
 }
617
 
633
 
618
 // TrimLowFrequencyTags removes tags that have less than
634
 // TrimLowFrequencyTags removes tags that have less than
667
 	}
683
 	}
668
 }
684
 }
669
 
685
 
686
+// selectTopNodePtrs returns a set of the top maxNodes *Node in a graph.
687
+func (g *Graph) SelectTopNodePtrs(maxNodes int, visualMode bool) nodePtrSet {
688
+	set := make(nodePtrSet)
689
+	for _, node := range g.selectTopNodes(maxNodes, visualMode) {
690
+		set[node] = true
691
+	}
692
+	return set
693
+}
694
+
670
 // SelectTopNodes returns a set of the top maxNodes nodes in a graph.
695
 // SelectTopNodes returns a set of the top maxNodes nodes in a graph.
671
 func (g *Graph) SelectTopNodes(maxNodes int, visualMode bool) NodeSet {
696
 func (g *Graph) SelectTopNodes(maxNodes int, visualMode bool) NodeSet {
697
+	return makeNodeSet(g.selectTopNodes(maxNodes, visualMode), 0)
698
+}
699
+
700
+// selectTopNodes returns a slice of the top maxNodes nodes in a graph
701
+func (g *Graph) selectTopNodes(maxNodes int, visualMode bool) Nodes {
672
 	if maxNodes > 0 {
702
 	if maxNodes > 0 {
673
 		if visualMode {
703
 		if visualMode {
674
 			var count int
704
 			var count int
685
 	if maxNodes > len(g.Nodes) {
715
 	if maxNodes > len(g.Nodes) {
686
 		maxNodes = len(g.Nodes)
716
 		maxNodes = len(g.Nodes)
687
 	}
717
 	}
688
-	return makeNodeSet(g.Nodes[:maxNodes], 0)
718
+	return g.Nodes[:maxNodes]
689
 }
719
 }
690
 
720
 
691
 // countTags counts the tags with flat count. This underestimates the
721
 // countTags counts the tags with flat count. This underestimates the

+ 4
- 6
internal/graph/graph_test.go View File

109
 type TrimTreeTestCase struct {
109
 type TrimTreeTestCase struct {
110
 	Initial  *Graph
110
 	Initial  *Graph
111
 	Expected []ExpectedNode
111
 	Expected []ExpectedNode
112
-	Keep     NodeSet
112
+	Keep nodePtrSet
113
 }
113
 }
114
 
114
 
115
 // Makes the edge from parent to child residual
115
 // Makes the edge from parent to child residual
143
 }
143
 }
144
 
144
 
145
 // Creates an array of ExpectedNodes from nodes.
145
 // Creates an array of ExpectedNodes from nodes.
146
-func createExpectedNodes(nodes ...*Node) ([]ExpectedNode, NodeSet) {
146
+func createExpectedNodes(nodes ...*Node) ([]ExpectedNode, nodePtrSet) {
147
 	Expected := make([]ExpectedNode, len(nodes))
147
 	Expected := make([]ExpectedNode, len(nodes))
148
-	Keep := NodeSet{
149
-		Ptr: make(map[*Node]bool, len(nodes)),
150
-	}
148
+	Keep := make(nodePtrSet, len(nodes))
151
 
149
 
152
 	for i, node := range nodes {
150
 	for i, node := range nodes {
153
 		Expected[i] = ExpectedNode{
151
 		Expected[i] = ExpectedNode{
155
 			In:   make(EdgeMap),
153
 			In:   make(EdgeMap),
156
 			Out:  make(EdgeMap),
154
 			Out:  make(EdgeMap),
157
 		}
155
 		}
158
-		Keep.Ptr[node] = true
156
+		Keep[node] = true
159
 	}
157
 	}
160
 
158
 
161
 	return Expected, Keep
159
 	return Expected, Keep

+ 17
- 11
internal/report/report.go View File

79
 	cumSort := o.CumSort
79
 	cumSort := o.CumSort
80
 
80
 
81
 	// First step: Build complete graph to identify low frequency nodes, based on their cum weight.
81
 	// First step: Build complete graph to identify low frequency nodes, based on their cum weight.
82
-	g = rpt.newGraph(graph.NodeSet{nil, nil})
82
+	g = rpt.newGraph(nil)
83
 	totalValue, _ := g.Nodes.Sum()
83
 	totalValue, _ := g.Nodes.Sum()
84
 	nodeCutoff := abs64(int64(float64(totalValue) * o.NodeFraction))
84
 	nodeCutoff := abs64(int64(float64(totalValue) * o.NodeFraction))
85
 	edgeCutoff := abs64(int64(float64(totalValue) * o.EdgeFraction))
85
 	edgeCutoff := abs64(int64(float64(totalValue) * o.EdgeFraction))
86
 
86
 
87
 	// Filter out nodes with cum value below nodeCutoff.
87
 	// Filter out nodes with cum value below nodeCutoff.
88
 	if nodeCutoff > 0 {
88
 	if nodeCutoff > 0 {
89
-		if nodesKept := g.DiscardLowFrequencyNodes(nodeCutoff); len(g.Nodes) != len(nodesKept.Ptr) {
90
-			droppedNodes = len(g.Nodes) - len(nodesKept.Ptr)
91
-			if o.CallTree {
89
+		if o.CallTree {
90
+			if nodesKept := g.DiscardLowFrequencyNodePtrs(nodeCutoff); len(g.Nodes) != len(nodesKept) {
91
+				droppedNodes = len(g.Nodes) - len(nodesKept)
92
 				g.TrimTree(nodesKept)
92
 				g.TrimTree(nodesKept)
93
-			} else {
93
+			}
94
+		} else {
95
+			if nodesKept := g.DiscardLowFrequencyNodes(nodeCutoff); len(g.Nodes) != len(nodesKept) {
96
+				droppedNodes = len(g.Nodes) - len(nodesKept)
94
 				g = rpt.newGraph(nodesKept)
97
 				g = rpt.newGraph(nodesKept)
95
 			}
98
 			}
96
 		}
99
 		}
104
 		// Remove low frequency tags and edges as they affect selection.
107
 		// Remove low frequency tags and edges as they affect selection.
105
 		g.TrimLowFrequencyTags(nodeCutoff)
108
 		g.TrimLowFrequencyTags(nodeCutoff)
106
 		g.TrimLowFrequencyEdges(edgeCutoff)
109
 		g.TrimLowFrequencyEdges(edgeCutoff)
107
-		if nodesKept := g.SelectTopNodes(nodeCount, visualMode); len(nodesKept.Ptr) != len(g.Nodes) {
108
-			if o.CallTree {
110
+		if o.CallTree {
111
+			if nodesKept := g.SelectTopNodePtrs(nodeCount, visualMode); len(g.Nodes) != len(nodesKept) {
109
 				g.TrimTree(nodesKept)
112
 				g.TrimTree(nodesKept)
110
-			} else {
113
+				g.SortNodes(cumSort, visualMode)
114
+			}
115
+		} else {
116
+			if nodesKept := g.SelectTopNodes(nodeCount, visualMode); len(g.Nodes) != len(nodesKept) {
111
 				g = rpt.newGraph(nodesKept)
117
 				g = rpt.newGraph(nodesKept)
118
+				g.SortNodes(cumSort, visualMode)
112
 			}
119
 			}
113
-			g.SortNodes(cumSort, visualMode)
114
 		}
120
 		}
115
 	}
121
 	}
116
 
122
 
270
 	o := rpt.options
276
 	o := rpt.options
271
 	prof := rpt.prof
277
 	prof := rpt.prof
272
 
278
 
273
-	g := rpt.newGraph(graph.NodeSet{nil, nil})
279
+	g := rpt.newGraph(nil)
274
 
280
 
275
 	// If the regexp source can be parsed as an address, also match
281
 	// If the regexp source can be parsed as an address, also match
276
 	// functions that land on that address.
282
 	// functions that land on that address.
578
 
584
 
579
 	const separator = "-----------+-------------------------------------------------------"
585
 	const separator = "-----------+-------------------------------------------------------"
580
 
586
 
581
-	_, locations := graph.CreateNodes(prof, false, graph.NodeSet{nil, nil})
587
+	_, locations := graph.CreateNodes(prof, false, nil)
582
 	for _, sample := range prof.Sample {
588
 	for _, sample := range prof.Sample {
583
 		var stack graph.Nodes
589
 		var stack graph.Nodes
584
 		for _, loc := range sample.Location {
590
 		for _, loc := range sample.Location {

+ 2
- 2
internal/report/source.go View File

37
 // eliminate potential nondeterminism.
37
 // eliminate potential nondeterminism.
38
 func printSource(w io.Writer, rpt *Report) error {
38
 func printSource(w io.Writer, rpt *Report) error {
39
 	o := rpt.options
39
 	o := rpt.options
40
-	g := rpt.newGraph(graph.NodeSet{nil, nil})
40
+	g := rpt.newGraph(nil)
41
 
41
 
42
 	// Identify all the functions that match the regexp provided.
42
 	// Identify all the functions that match the regexp provided.
43
 	// Group nodes for each matching function.
43
 	// Group nodes for each matching function.
117
 // functions with samples that match the regexp rpt.options.symbol.
117
 // functions with samples that match the regexp rpt.options.symbol.
118
 func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
118
 func printWebSource(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
119
 	o := rpt.options
119
 	o := rpt.options
120
-	g := rpt.newGraph(graph.NodeSet{nil, nil})
120
+	g := rpt.newGraph(nil)
121
 
121
 
122
 	// If the regexp source can be parsed as an address, also match
122
 	// If the regexp source can be parsed as an address, also match
123
 	// functions that land on that address.
123
 	// functions that land on that address.