浏览代码

Fix unstable nodefraction handling. (#131)

There is an issue where decreasing nodefraction (which should produce
more nodes) ended up reducing the number of displayed nodes.  This is
because we have an upper limit on number of nodes to display and the
number of node-lets counts against that limit.  This can cause a problem
if a node with large number of node-lets shows up near the front of
the list of nodes (e.g., std::allocator_traits::allocate in a heapz
profile had 102 node-lets, which caused all subsequent nodes to be
dropped).

Fixed by limiting the count of charged node-lets per node to
maxNodelets (4), which is the maximum number of node-lets we
display per node.
Alexey Alexandrov 8 年前
父节点
当前提交
e48932ed15
共有 3 个文件被更改,包括 62 次插入5 次删除
  1. 4
    4
      internal/graph/dotgraph.go
  2. 53
    0
      internal/graph/dotgraph_test.go
  3. 5
    1
      internal/graph/graph.go

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

50
 	Total       int64                      // The total weight of the graph, used to compute percentages
50
 	Total       int64                      // The total weight of the graph, used to compute percentages
51
 }
51
 }
52
 
52
 
53
+const maxNodelets = 4 // Number of nodelets for labels (both numeric and non)
54
+
53
 // ComposeDot creates and writes a in the DOT format to the writer, using
55
 // ComposeDot creates and writes a in the DOT format to the writer, using
54
 // the configurations given.
56
 // the configurations given.
55
 func ComposeDot(w io.Writer, g *Graph, a *DotAttributes, c *DotConfig) {
57
 func ComposeDot(w io.Writer, g *Graph, a *DotAttributes, c *DotConfig) {
204
 
206
 
205
 // addNodelets generates the DOT boxes for the node tags if they exist.
207
 // addNodelets generates the DOT boxes for the node tags if they exist.
206
 func (b *builder) addNodelets(node *Node, nodeID int) bool {
208
 func (b *builder) addNodelets(node *Node, nodeID int) bool {
207
-	const maxNodelets = 4    // Number of nodelets for alphanumeric labels
208
-	const maxNumNodelets = 4 // Number of nodelets for numeric labels
209
 	var nodelets string
209
 	var nodelets string
210
 
210
 
211
 	// Populate two Tag slices, one for LabelTags and one for NumericTags.
211
 	// Populate two Tag slices, one for LabelTags and one for NumericTags.
242
 		nodelets += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", nodeID, i, t.Name, weight)
242
 		nodelets += fmt.Sprintf(`N%d_%d [label = "%s" fontsize=8 shape=box3d tooltip="%s"]`+"\n", nodeID, i, t.Name, weight)
243
 		nodelets += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="%s" labeltooltip="%s"]`+"\n", nodeID, nodeID, i, weight, weight, weight)
243
 		nodelets += fmt.Sprintf(`N%d -> N%d_%d [label=" %s" weight=100 tooltip="%s" labeltooltip="%s"]`+"\n", nodeID, nodeID, i, weight, weight, weight)
244
 		if nts := lnts[t.Name]; nts != nil {
244
 		if nts := lnts[t.Name]; nts != nil {
245
-			nodelets += b.numericNodelets(nts, maxNumNodelets, flatTags, fmt.Sprintf(`N%d_%d`, nodeID, i))
245
+			nodelets += b.numericNodelets(nts, maxNodelets, flatTags, fmt.Sprintf(`N%d_%d`, nodeID, i))
246
 		}
246
 		}
247
 	}
247
 	}
248
 
248
 
249
 	if nts := lnts[""]; nts != nil {
249
 	if nts := lnts[""]; nts != nil {
250
-		nodelets += b.numericNodelets(nts, maxNumNodelets, flatTags, fmt.Sprintf(`N%d`, nodeID))
250
+		nodelets += b.numericNodelets(nts, maxNodelets, flatTags, fmt.Sprintf(`N%d`, nodeID))
251
 	}
251
 	}
252
 
252
 
253
 	fmt.Fprint(b, nodelets)
253
 	fmt.Fprint(b, nodelets)

+ 53
- 0
internal/graph/dotgraph_test.go 查看文件

209
 	}
209
 	}
210
 }
210
 }
211
 
211
 
212
+func TestNodeletCountCapping(t *testing.T) {
213
+	labelTags := make(TagMap)
214
+	for i := 0; i < 10; i++ {
215
+		name := fmt.Sprintf("tag-%d", i)
216
+		labelTags[name] = &Tag{
217
+			Name: name,
218
+			Flat: 10,
219
+			Cum:  10,
220
+		}
221
+	}
222
+	numTags := make(TagMap)
223
+	for i := 0; i < 10; i++ {
224
+		name := fmt.Sprintf("num-tag-%d", i)
225
+		numTags[name] = &Tag{
226
+			Name:  name,
227
+			Unit:  "mb",
228
+			Value: 16,
229
+			Flat:  10,
230
+			Cum:   10,
231
+		}
232
+	}
233
+	node1 := &Node{
234
+		Info:        NodeInfo{Name: "node1-with-tags"},
235
+		Flat:        10,
236
+		Cum:         10,
237
+		NumericTags: map[string]TagMap{"": numTags},
238
+		LabelTags:   labelTags,
239
+	}
240
+	node2 := &Node{
241
+		Info: NodeInfo{Name: "node2"},
242
+		Flat: 15,
243
+		Cum:  15,
244
+	}
245
+	node3 := &Node{
246
+		Info: NodeInfo{Name: "node3"},
247
+		Flat: 15,
248
+		Cum:  15,
249
+	}
250
+	g := &Graph{
251
+		Nodes: Nodes{
252
+			node1,
253
+			node2,
254
+			node3,
255
+		},
256
+	}
257
+	for n := 1; n <= 3; n++ {
258
+		input := maxNodelets + n
259
+		if got, want := len(g.SelectTopNodes(input, true)), n; got != want {
260
+			t.Errorf("SelectTopNodes(%d): got %d nodes, want %d", input, got, want)
261
+		}
262
+	}
263
+}
264
+
212
 func TestMultilinePrintableName(t *testing.T) {
265
 func TestMultilinePrintableName(t *testing.T) {
213
 	ni := &NodeInfo{
266
 	ni := &NodeInfo{
214
 		Name:    "test1.test2::test3",
267
 		Name:    "test1.test2::test3",

+ 5
- 1
internal/graph/graph.go 查看文件

802
 			// If generating a visual graph, count tags as nodes. Update
802
 			// If generating a visual graph, count tags as nodes. Update
803
 			// maxNodes to account for them.
803
 			// maxNodes to account for them.
804
 			for i, n := range g.Nodes {
804
 			for i, n := range g.Nodes {
805
-				if count += countTags(n) + 1; count >= maxNodes {
805
+				tags := countTags(n)
806
+				if tags > maxNodelets {
807
+					tags = maxNodelets
808
+				}
809
+				if count += tags + 1; count >= maxNodes {
806
 					maxNodes = i + 1
810
 					maxNodes = i + 1
807
 					break
811
 					break
808
 				}
812
 				}