浏览代码

Separate graph and tree creation

Use separate mechanisms to implement graph and tree
creation, which speeds up graph creation by using a
single map from location indices to graph nodes.
Raul Silvera 9 年前
父节点
当前提交
926e6903af
共有 3 个文件被更改,包括 193 次插入136 次删除
  1. 0
    2
      internal/driver/testdata/pprof.cpu.cum.lines.topproto.hide
  2. 179
    129
      internal/graph/graph.go
  3. 14
    5
      internal/report/report.go

+ 0
- 2
internal/driver/testdata/pprof.cpu.cum.lines.topproto.hide 查看文件

@@ -1,5 +1,3 @@
1 1
 Showing nodes accounting for 1s, 100% of 1s total
2 2
       flat  flat%   sum%        cum   cum%
3 3
         1s   100%   100%         1s   100%  mangled1000 testdata/file1000.src:1
4
-         0     0%   100%          0     0%  mangled2000 testdata/file2000.src:4
5
-         0     0%   100%          0     0%  mangled2001 testdata/file2000.src:9

+ 179
- 129
internal/graph/graph.go 查看文件

@@ -70,9 +70,9 @@ type Node struct {
70 70
 	NumericTags map[string]TagMap
71 71
 }
72 72
 
73
-// BumpWeight increases the weight of an edge between two nodes. If
73
+// AddToEdge increases the weight of an edge between two nodes. If
74 74
 // there isn't such an edge one is created.
75
-func (n *Node) BumpWeight(to *Node, w int64, residual, inline bool) {
75
+func (n *Node) AddToEdge(to *Node, w int64, residual, inline bool) {
76 76
 	if n.Out[to] != to.In[n] {
77 77
 		panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
78 78
 	}
@@ -137,38 +137,24 @@ func (i *NodeInfo) NameComponents() []string {
137 137
 	return name
138 138
 }
139 139
 
140
-// ExtendedNodeInfo extends the NodeInfo with a pointer to a parent node, to
141
-// identify nodes with identical information and different callers. This is
142
-// used when creating call trees.
143
-type ExtendedNodeInfo struct {
144
-	NodeInfo
145
-	parent *Node
146
-}
147
-
148 140
 // NodeMap maps from a node info struct to a node. It is used to merge
149 141
 // report entries with the same info.
150
-type NodeMap map[ExtendedNodeInfo]*Node
142
+type NodeMap map[NodeInfo]*Node
151 143
 
152 144
 // NodeSet maps is a collection of node info structs.
153 145
 type NodeSet map[NodeInfo]bool
154 146
 
155 147
 // FindOrInsertNode takes the info for a node and either returns a matching node
156 148
 // from the node map if one exists, or adds one to the map if one does not.
157
-// If parent is non-nil, return a match with the same parent.
158 149
 // If kept is non-nil, nodes are only added if they can be located on it.
159
-func (m NodeMap) FindOrInsertNode(info NodeInfo, parent *Node, kept NodeSet) *Node {
150
+func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
160 151
 	if kept != nil {
161 152
 		if _, ok := kept[info]; !ok {
162 153
 			return nil
163 154
 		}
164 155
 	}
165 156
 
166
-	extendedInfo := ExtendedNodeInfo{
167
-		info,
168
-		parent,
169
-	}
170
-
171
-	if n, ok := m[extendedInfo]; ok {
157
+	if n, ok := nm[info]; ok {
172 158
 		return n
173 159
 	}
174 160
 
@@ -179,7 +165,7 @@ func (m NodeMap) FindOrInsertNode(info NodeInfo, parent *Node, kept NodeSet) *No
179 165
 		LabelTags:   make(TagMap),
180 166
 		NumericTags: make(map[string]TagMap),
181 167
 	}
182
-	m[extendedInfo] = n
168
+	nm[info] = n
183 169
 	return n
184 170
 }
185 171
 
@@ -219,84 +205,141 @@ func SortTags(t []*Tag, flat bool) []*Tag {
219 205
 
220 206
 // New summarizes performance data from a profile into a graph.
221 207
 func New(prof *profile.Profile, o *Options) (g *Graph) {
222
-	const averageNodesPerLocation = 2
223
-	locations := NewLocInfo(prof, o.ObjNames)
224
-	nm := make(NodeMap, len(prof.Location)*averageNodesPerLocation)
208
+	if o.CallTree {
209
+		return newTree(prof, o)
210
+	}
211
+
212
+	nodes, locationMap := CreateNodes(prof, o.ObjNames, o.KeptNodes)
225 213
 	for _, sample := range prof.Sample {
226
-		if sample.Location == nil {
214
+		weight := o.SampleValue(sample.Value)
215
+		if weight == 0 {
227 216
 			continue
228 217
 		}
218
+		seenNode := make(map[*Node]bool, len(sample.Location))
219
+		seenEdge := make(map[nodePair]bool, len(sample.Location))
220
+		var parent *Node
221
+		// A residual edge goes over one or more nodes that were not kept.
222
+		residual := false
229 223
 
230
-		// Construct list of node names for sample.
231
-		// Keep track of the index on the Sample for each frame,
232
-		// to determine inlining status.
233
-
234
-		stack := make([]NodeInfo, 0, len(sample.Location))
235
-		locIndex := make([]int, 0, len(sample.Location)*averageNodesPerLocation)
236
-		for i, loc := range sample.Location {
237
-			id := loc.ID
238
-			stack = append(stack, locations[id]...)
239
-			for _ = range locations[id] {
240
-				locIndex = append(locIndex, i)
224
+		labels := joinLabels(sample)
225
+		// Group the sample frames, based on a global map.
226
+		for i := len(sample.Location) - 1; i >= 0; i-- {
227
+			l := sample.Location[i]
228
+			locNodes := locationMap[l.ID]
229
+			for ni := len(locNodes) - 1; ni >= 0; ni-- {
230
+				n := locNodes[ni]
231
+				if n == nil {
232
+					residual = true
233
+					continue
234
+				}
235
+				// Add cum weight to all nodes in stack, avoiding double counting.
236
+				if _, ok := seenNode[n]; !ok {
237
+					seenNode[n] = true
238
+					n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
239
+				}
240
+				// Update edge weights for all edges in stack, avoiding double counting.
241
+				if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent {
242
+					seenEdge[nodePair{n, parent}] = true
243
+					parent.AddToEdge(n, weight, residual, ni != len(locNodes)-1)
244
+				}
245
+				parent = n
246
+				residual = false
241 247
 			}
242 248
 		}
243
-
244
-		weight := o.SampleValue(sample.Value)
245
-		seenNode := make(map[*Node]bool, len(stack))
246
-		seenEdge := make(map[nodePair]bool, len(stack))
247
-		var nn *Node
248
-		nlocIndex := -1
249
-		residual := false
250
-		// Walk top-down over the frames in a sample, keeping track
251
-		// of the current parent if we're building a tree.
252
-		for i := len(stack); i > 0; i-- {
253
-			var parent *Node
254
-			if o.CallTree {
255
-				parent = nn
256
-			}
257
-			n := nm.FindOrInsertNode(stack[i-1], parent, o.KeptNodes)
258
-			if n == nil {
259
-				residual = true
260
-				continue
261
-			}
249
+		if parent != nil && !residual {
262 250
 			// Add flat weight to leaf node.
263
-			if i == 1 {
264
-				n.addSample(sample, weight, o.FormatTag, true)
265
-			}
266
-			// Add cum weight to all nodes in stack, avoiding double counting.
267
-			if _, ok := seenNode[n]; !ok {
268
-				seenNode[n] = true
269
-				n.addSample(sample, weight, o.FormatTag, false)
270
-			}
271
-			// Update edge weights for all edges in stack, avoiding double counting.
272
-			if _, ok := seenEdge[nodePair{n, nn}]; !ok && nn != nil && n != nn {
273
-				seenEdge[nodePair{n, nn}] = true
274
-				// This is an inlined edge if the caller and the callee
275
-				// correspond to the same entry in the sample.
276
-				nn.BumpWeight(n, weight, residual, locIndex[i-1] == nlocIndex)
277
-			}
278
-			nn = n
279
-			nlocIndex = locIndex[i-1]
280
-			residual = false
251
+			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
281 252
 		}
282 253
 	}
283 254
 
255
+	return selectNodesForGraph(nodes, o.DropNegative)
256
+}
257
+
258
+func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph {
284 259
 	// Collect nodes into a graph.
285
-	ns := make(Nodes, 0, len(nm))
286
-	for _, n := range nm {
287
-		if o.DropNegative && isNegative(n) {
260
+	gNodes := make(Nodes, 0, len(nodes))
261
+	for _, n := range nodes {
262
+		if n == nil {
288 263
 			continue
289 264
 		}
290
-		ns = append(ns, n)
265
+		if n.Cum == 0 && n.Flat == 0 {
266
+			continue
267
+		}
268
+		if dropNegative && isNegative(n) {
269
+			continue
270
+		}
271
+		gNodes = append(gNodes, n)
291 272
 	}
292
-
293
-	return &Graph{ns}
273
+	return &Graph{gNodes}
294 274
 }
295 275
 
296 276
 type nodePair struct {
297 277
 	src, dest *Node
298 278
 }
299 279
 
280
+func newTree(prof *profile.Profile, o *Options) (g *Graph) {
281
+	kept := o.KeptNodes
282
+	keepBinary := o.ObjNames
283
+	parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
284
+	for _, sample := range prof.Sample {
285
+		weight := o.SampleValue(sample.Value)
286
+		if weight == 0 {
287
+			continue
288
+		}
289
+		var parent *Node
290
+		// A residual edge goes over one or more nodes that were not kept.
291
+		residual := false
292
+		labels := joinLabels(sample)
293
+		// Group the sample frames, based on a per-node map.
294
+		for i := len(sample.Location) - 1; i >= 0; i-- {
295
+			l := sample.Location[i]
296
+			nodeMap := parentNodeMap[parent]
297
+			if nodeMap == nil {
298
+				nodeMap = make(NodeMap)
299
+				parentNodeMap[parent] = nodeMap
300
+			}
301
+			locNodes := nodeMap.findOrInsertLocation(l, keepBinary, kept)
302
+			for ni := len(locNodes) - 1; ni >= 0; ni-- {
303
+				n := locNodes[ni]
304
+				if n == nil {
305
+					residual = true
306
+					continue
307
+				}
308
+				n.addSample(weight, labels, sample.NumLabel, o.FormatTag, false)
309
+				if parent != nil {
310
+					parent.AddToEdge(n, weight, residual, ni != len(locNodes)-1)
311
+				}
312
+				parent = n
313
+				residual = false
314
+			}
315
+		}
316
+		if parent != nil && !residual {
317
+			parent.addSample(weight, labels, sample.NumLabel, o.FormatTag, true)
318
+		}
319
+	}
320
+
321
+	nodes := make(Nodes, len(prof.Location))
322
+	for _, nm := range parentNodeMap {
323
+		nodes = append(nodes, nm.nodes()...)
324
+	}
325
+	return selectNodesForGraph(nodes, o.DropNegative)
326
+}
327
+
328
+func joinLabels(s *profile.Sample) string {
329
+	if len(s.Label) == 0 {
330
+		return ""
331
+	}
332
+
333
+	var labels []string
334
+	for key, vals := range s.Label {
335
+		for _, v := range vals {
336
+			labels = append(labels, key+":"+v)
337
+		}
338
+	}
339
+	sort.Strings(labels)
340
+	return strings.Join(labels, `\n`)
341
+}
342
+
300 343
 // isNegative returns true if the node is considered as "negative" for the
301 344
 // purposes of drop_negative.
302 345
 func isNegative(n *Node) bool {
@@ -310,51 +353,67 @@ func isNegative(n *Node) bool {
310 353
 	}
311 354
 }
312 355
 
313
-// NewLocInfo creates a slice of formatted names for a location.
314
-func NewLocInfo(prof *profile.Profile, keepBinary bool) map[uint64][]NodeInfo {
315
-	locations := make(map[uint64][]NodeInfo)
356
+// CreateNodes creates graph nodes for all locations in a profile.  It
357
+// returns set of all nodes, plus a mapping of each location to the
358
+// set of corresponding nodes (one per location.Line). If kept is
359
+// non-nil, only nodes in that set are included; nodes that do not
360
+// match are represented as a nil.
361
+func CreateNodes(prof *profile.Profile, keepBinary bool, kept NodeSet) (Nodes, map[uint64]Nodes) {
362
+	locations := make(map[uint64]Nodes, len(prof.Location))
316 363
 
364
+	nm := make(NodeMap, len(prof.Location))
317 365
 	for _, l := range prof.Location {
318
-		var objfile string
319
-
320
-		if m := l.Mapping; m != nil {
321
-			objfile = filepath.Base(m.File)
366
+		if nodes := nm.findOrInsertLocation(l, keepBinary, kept); nodes != nil {
367
+			locations[l.ID] = nodes
322 368
 		}
369
+	}
370
+	return nm.nodes(), locations
371
+}
323 372
 
324
-		if len(l.Line) == 0 {
325
-			locations[l.ID] = []NodeInfo{
326
-				{
327
-					Address: l.Address,
328
-					Objfile: objfile,
329
-				},
330
-			}
331
-			continue
373
+func (nm NodeMap) nodes() Nodes {
374
+	nodes := make(Nodes, 0, len(nm))
375
+	for _, n := range nm {
376
+		nodes = append(nodes, n)
377
+	}
378
+	return nodes
379
+}
380
+
381
+func (nm NodeMap) findOrInsertLocation(l *profile.Location, keepBinary bool, kept NodeSet) Nodes {
382
+	var objfile string
383
+	if m := l.Mapping; m != nil {
384
+		objfile = filepath.Base(m.File)
385
+	}
386
+
387
+	if len(l.Line) == 0 {
388
+		ni := NodeInfo{
389
+			Address: l.Address,
390
+			Objfile: objfile,
391
+		}
392
+		return Nodes{nm.FindOrInsertNode(ni, kept)}
393
+	}
394
+	var locNodes Nodes
395
+	for _, line := range l.Line {
396
+		ni := NodeInfo{
397
+			Address: l.Address,
398
+			Lineno:  int(line.Line),
332 399
 		}
333
-		var info []NodeInfo
334
-		for _, line := range l.Line {
335
-			ni := NodeInfo{
336
-				Address: l.Address,
337
-				Lineno:  int(line.Line),
338
-			}
339 400
 
340
-			if line.Function != nil {
341
-				ni.Name = line.Function.Name
342
-				ni.OrigName = line.Function.SystemName
343
-				if fname := line.Function.Filename; fname != "" {
344
-					ni.File = filepath.Clean(fname)
345
-				}
346
-				if keepBinary {
347
-					ni.StartLine = int(line.Function.StartLine)
348
-				}
401
+		if line.Function != nil {
402
+			ni.Name = line.Function.Name
403
+			ni.OrigName = line.Function.SystemName
404
+			if fname := line.Function.Filename; fname != "" {
405
+				ni.File = filepath.Clean(fname)
349 406
 			}
350
-			if keepBinary || line.Function == nil {
351
-				ni.Objfile = objfile
407
+			if keepBinary {
408
+				ni.StartLine = int(line.Function.StartLine)
352 409
 			}
353
-			info = append(info, ni)
354 410
 		}
355
-		locations[l.ID] = info
411
+		if keepBinary || line.Function == nil {
412
+			ni.Objfile = objfile
413
+		}
414
+		locNodes = append(locNodes, nm.FindOrInsertNode(ni, kept))
356 415
 	}
357
-	return locations
416
+	return locNodes
358 417
 }
359 418
 
360 419
 type tags struct {
@@ -385,7 +444,7 @@ func (ns Nodes) Sum() (flat int64, cum int64) {
385 444
 	return
386 445
 }
387 446
 
388
-func (n *Node) addSample(s *profile.Sample, value int64, format func(int64, string) string, flat bool) {
447
+func (n *Node) addSample(value int64, labels string, numLabel map[string][]int64, format func(int64, string) string, flat bool) {
389 448
 	// Update sample value
390 449
 	if flat {
391 450
 		n.Flat += value
@@ -394,17 +453,8 @@ func (n *Node) addSample(s *profile.Sample, value int64, format func(int64, stri
394 453
 	}
395 454
 
396 455
 	// Add string tags
397
-	var labels []string
398
-	for key, vals := range s.Label {
399
-		for _, v := range vals {
400
-			labels = append(labels, key+":"+v)
401
-		}
402
-	}
403
-	var joinedLabels string
404
-	if len(labels) > 0 {
405
-		sort.Strings(labels)
406
-		joinedLabels = strings.Join(labels, `\n`)
407
-		t := n.LabelTags.findOrAddTag(joinedLabels, "", 0)
456
+	if labels != "" {
457
+		t := n.LabelTags.findOrAddTag(labels, "", 0)
408 458
 		if flat {
409 459
 			t.Flat += value
410 460
 		} else {
@@ -412,16 +462,16 @@ func (n *Node) addSample(s *profile.Sample, value int64, format func(int64, stri
412 462
 		}
413 463
 	}
414 464
 
415
-	numericTags := n.NumericTags[joinedLabels]
465
+	numericTags := n.NumericTags[labels]
416 466
 	if numericTags == nil {
417 467
 		numericTags = TagMap{}
418
-		n.NumericTags[joinedLabels] = numericTags
468
+		n.NumericTags[labels] = numericTags
419 469
 	}
420 470
 	// Add numeric tags
421 471
 	if format == nil {
422 472
 		format = defaultLabelFormat
423 473
 	}
424
-	for key, nvals := range s.NumLabel {
474
+	for key, nvals := range numLabel {
425 475
 		for _, v := range nvals {
426 476
 			t := numericTags.findOrAddTag(format(v, key), key, v)
427 477
 			if flat {
@@ -640,7 +690,7 @@ func isRedundant(e *Edge) bool {
640 690
 // predecessors collects all the predecessors to node n, excluding edge e.
641 691
 func predecessors(e *Edge, n *Node) map[*Node]bool {
642 692
 	seen := map[*Node]bool{n: true}
643
-	queue := []*Node{n}
693
+	queue := Nodes{n}
644 694
 	for len(queue) > 0 {
645 695
 		n := queue[0]
646 696
 		queue = queue[1:]

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

@@ -174,6 +174,16 @@ func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph {
174 174
 	for _, f := range prof.Function {
175 175
 		f.Filename = trimPath(f.Filename)
176 176
 	}
177
+	// Remove numeric tags not recognized by pprof.
178
+	for _, s := range prof.Sample {
179
+		numLabels := make(map[string][]int64, len(s.NumLabel))
180
+		for k, v := range s.NumLabel {
181
+			if k == "bytes" {
182
+				numLabels[k] = append(numLabels[k], v...)
183
+			}
184
+		}
185
+		s.NumLabel = numLabels
186
+	}
177 187
 
178 188
 	gopt := &graph.Options{
179 189
 		SampleValue:  o.SampleValue,
@@ -559,10 +569,9 @@ func printTraces(w io.Writer, rpt *Report) error {
559 569
 
560 570
 	const separator = "-----------+-------------------------------------------------------"
561 571
 
562
-	locations := graph.NewLocInfo(prof, false)
563
-
572
+	_, locations := graph.CreateNodes(prof, false, nil)
564 573
 	for _, sample := range prof.Sample {
565
-		var stack []graph.NodeInfo
574
+		var stack graph.Nodes
566 575
 		for _, loc := range sample.Location {
567 576
 			id := loc.ID
568 577
 			stack = append(stack, locations[id]...)
@@ -583,10 +592,10 @@ func printTraces(w io.Writer, rpt *Report) error {
583 592
 		// Print call stack.
584 593
 		fmt.Fprintf(w, "%10s   %s\n",
585 594
 			rpt.formatValue(o.SampleValue(sample.Value)),
586
-			stack[0].PrintableName())
595
+			stack[0].Info.PrintableName())
587 596
 
588 597
 		for _, s := range stack[1:] {
589
-			fmt.Fprintf(w, "%10s   %s\n", "", s.PrintableName())
598
+			fmt.Fprintf(w, "%10s   %s\n", "", s.Info.PrintableName())
590 599
 		}
591 600
 	}
592 601
 	fmt.Fprintln(w, separator)