소스 검색

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
 Showing nodes accounting for 1s, 100% of 1s total
1
 Showing nodes accounting for 1s, 100% of 1s total
2
       flat  flat%   sum%        cum   cum%
2
       flat  flat%   sum%        cum   cum%
3
         1s   100%   100%         1s   100%  mangled1000 testdata/file1000.src:1
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
 	NumericTags map[string]TagMap
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
 // there isn't such an edge one is created.
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
 	if n.Out[to] != to.In[n] {
76
 	if n.Out[to] != to.In[n] {
77
 		panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
77
 		panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
78
 	}
78
 	}
137
 	return name
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
 // NodeMap maps from a node info struct to a node. It is used to merge
140
 // NodeMap maps from a node info struct to a node. It is used to merge
149
 // report entries with the same info.
141
 // report entries with the same info.
150
-type NodeMap map[ExtendedNodeInfo]*Node
142
+type NodeMap map[NodeInfo]*Node
151
 
143
 
152
 // NodeSet maps is a collection of node info structs.
144
 // NodeSet maps is a collection of node info structs.
153
 type NodeSet map[NodeInfo]bool
145
 type NodeSet map[NodeInfo]bool
154
 
146
 
155
 // FindOrInsertNode takes the info for a node and either returns a matching node
147
 // FindOrInsertNode takes the info for a node and either returns a matching node
156
 // from the node map if one exists, or adds one to the map if one does not.
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
 // If kept is non-nil, nodes are only added if they can be located on it.
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
 	if kept != nil {
151
 	if kept != nil {
161
 		if _, ok := kept[info]; !ok {
152
 		if _, ok := kept[info]; !ok {
162
 			return nil
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
 		return n
158
 		return n
173
 	}
159
 	}
174
 
160
 
179
 		LabelTags:   make(TagMap),
165
 		LabelTags:   make(TagMap),
180
 		NumericTags: make(map[string]TagMap),
166
 		NumericTags: make(map[string]TagMap),
181
 	}
167
 	}
182
-	m[extendedInfo] = n
168
+	nm[info] = n
183
 	return n
169
 	return n
184
 }
170
 }
185
 
171
 
219
 
205
 
220
 // New summarizes performance data from a profile into a graph.
206
 // New summarizes performance data from a profile into a graph.
221
 func New(prof *profile.Profile, o *Options) (g *Graph) {
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
 	for _, sample := range prof.Sample {
213
 	for _, sample := range prof.Sample {
226
-		if sample.Location == nil {
214
+		weight := o.SampleValue(sample.Value)
215
+		if weight == 0 {
227
 			continue
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
 			// Add flat weight to leaf node.
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
 	// Collect nodes into a graph.
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
 			continue
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
 type nodePair struct {
276
 type nodePair struct {
297
 	src, dest *Node
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
 // isNegative returns true if the node is considered as "negative" for the
343
 // isNegative returns true if the node is considered as "negative" for the
301
 // purposes of drop_negative.
344
 // purposes of drop_negative.
302
 func isNegative(n *Node) bool {
345
 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
 	for _, l := range prof.Location {
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
 type tags struct {
419
 type tags struct {
385
 	return
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
 	// Update sample value
448
 	// Update sample value
390
 	if flat {
449
 	if flat {
391
 		n.Flat += value
450
 		n.Flat += value
394
 	}
453
 	}
395
 
454
 
396
 	// Add string tags
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
 		if flat {
458
 		if flat {
409
 			t.Flat += value
459
 			t.Flat += value
410
 		} else {
460
 		} else {
412
 		}
462
 		}
413
 	}
463
 	}
414
 
464
 
415
-	numericTags := n.NumericTags[joinedLabels]
465
+	numericTags := n.NumericTags[labels]
416
 	if numericTags == nil {
466
 	if numericTags == nil {
417
 		numericTags = TagMap{}
467
 		numericTags = TagMap{}
418
-		n.NumericTags[joinedLabels] = numericTags
468
+		n.NumericTags[labels] = numericTags
419
 	}
469
 	}
420
 	// Add numeric tags
470
 	// Add numeric tags
421
 	if format == nil {
471
 	if format == nil {
422
 		format = defaultLabelFormat
472
 		format = defaultLabelFormat
423
 	}
473
 	}
424
-	for key, nvals := range s.NumLabel {
474
+	for key, nvals := range numLabel {
425
 		for _, v := range nvals {
475
 		for _, v := range nvals {
426
 			t := numericTags.findOrAddTag(format(v, key), key, v)
476
 			t := numericTags.findOrAddTag(format(v, key), key, v)
427
 			if flat {
477
 			if flat {
640
 // predecessors collects all the predecessors to node n, excluding edge e.
690
 // predecessors collects all the predecessors to node n, excluding edge e.
641
 func predecessors(e *Edge, n *Node) map[*Node]bool {
691
 func predecessors(e *Edge, n *Node) map[*Node]bool {
642
 	seen := map[*Node]bool{n: true}
692
 	seen := map[*Node]bool{n: true}
643
-	queue := []*Node{n}
693
+	queue := Nodes{n}
644
 	for len(queue) > 0 {
694
 	for len(queue) > 0 {
645
 		n := queue[0]
695
 		n := queue[0]
646
 		queue = queue[1:]
696
 		queue = queue[1:]

+ 14
- 5
internal/report/report.go 파일 보기

174
 	for _, f := range prof.Function {
174
 	for _, f := range prof.Function {
175
 		f.Filename = trimPath(f.Filename)
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
 	gopt := &graph.Options{
188
 	gopt := &graph.Options{
179
 		SampleValue:  o.SampleValue,
189
 		SampleValue:  o.SampleValue,
559
 
569
 
560
 	const separator = "-----------+-------------------------------------------------------"
570
 	const separator = "-----------+-------------------------------------------------------"
561
 
571
 
562
-	locations := graph.NewLocInfo(prof, false)
563
-
572
+	_, locations := graph.CreateNodes(prof, false, nil)
564
 	for _, sample := range prof.Sample {
573
 	for _, sample := range prof.Sample {
565
-		var stack []graph.NodeInfo
574
+		var stack graph.Nodes
566
 		for _, loc := range sample.Location {
575
 		for _, loc := range sample.Location {
567
 			id := loc.ID
576
 			id := loc.ID
568
 			stack = append(stack, locations[id]...)
577
 			stack = append(stack, locations[id]...)
583
 		// Print call stack.
592
 		// Print call stack.
584
 		fmt.Fprintf(w, "%10s   %s\n",
593
 		fmt.Fprintf(w, "%10s   %s\n",
585
 			rpt.formatValue(o.SampleValue(sample.Value)),
594
 			rpt.formatValue(o.SampleValue(sample.Value)),
586
-			stack[0].PrintableName())
595
+			stack[0].Info.PrintableName())
587
 
596
 
588
 		for _, s := range stack[1:] {
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
 	fmt.Fprintln(w, separator)
601
 	fmt.Fprintln(w, separator)