Преглед на файлове

Add mechanism to reduce profile size

Add new trimproto option that generates a new profile while removing
symbol information for functions below nodefraction. This reduces the
profile sizes significantly.
Raul Silvera преди 9 години
родител
ревизия
9c191ebbce
променени са 4 файла, в които са добавени 95 реда и са изтрити 23 реда
  1. 1
    0
      internal/driver/commands.go
  2. 65
    21
      internal/graph/graph.go
  3. 18
    1
      internal/report/report.go
  4. 11
    1
      profile/merge.go

+ 1
- 0
internal/driver/commands.go Целия файл

@@ -93,6 +93,7 @@ var pprofCommands = commands{
93 93
 	// Save binary formats to a file
94 94
 	"callgrind": {report.Callgrind, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format", reportHelp("callgrind", false, true)},
95 95
 	"proto":     {report.Proto, awayFromTTY("pb.gz"), false, "Outputs the profile in compressed protobuf format", ""},
96
+	"trimproto": {report.TrimProto, awayFromTTY("pb.gz"), false, "Outputs the profile in compressed protobuf format, removing info about infrequent functions to reduce profile size", ""},
96 97
 
97 98
 	// Generate report in DOT format and postprocess with dot
98 99
 	"gif": {report.Dot, invokeDot("gif"), false, "Outputs a graph image in GIF format", reportHelp("gif", false, true)},

+ 65
- 21
internal/graph/graph.go Целия файл

@@ -204,11 +204,18 @@ func SortTags(t []*Tag, flat bool) []*Tag {
204 204
 }
205 205
 
206 206
 // New summarizes performance data from a profile into a graph.
207
-func New(prof *profile.Profile, o *Options) (g *Graph) {
207
+func New(prof *profile.Profile, o *Options) *Graph {
208 208
 	if o.CallTree {
209 209
 		return newTree(prof, o)
210 210
 	}
211
+	g, _ := newGraph(prof, o)
212
+	return g
213
+}
211 214
 
215
+// newGraph computes a graph from a profile. It returns the graph, and
216
+// a map from the profile location indices to the corresponding graph
217
+// nodes.
218
+func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
212 219
 	nodes, locationMap := CreateNodes(prof, o.ObjNames, o.KeptNodes)
213 220
 	for _, sample := range prof.Sample {
214 221
 		weight := o.SampleValue(sample.Value)
@@ -252,7 +259,7 @@ func New(prof *profile.Profile, o *Options) (g *Graph) {
252 259
 		}
253 260
 	}
254 261
 
255
-	return selectNodesForGraph(nodes, o.DropNegative)
262
+	return selectNodesForGraph(nodes, o.DropNegative), locationMap
256 263
 }
257 264
 
258 265
 func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph {
@@ -340,6 +347,31 @@ func joinLabels(s *profile.Sample) string {
340 347
 	return strings.Join(labels, `\n`)
341 348
 }
342 349
 
350
+// TrimProfile reduces the size of a profile by removing information
351
+// about locations that contribute to infrequent graph nodes,
352
+// determined by the value of nodefraction. The locations are
353
+// preserved, but their line information is removed.
354
+func TrimProfile(p *profile.Profile, o *Options, nodeFraction float64) *profile.Profile {
355
+	g, locationMap := newGraph(p, o)
356
+
357
+	totalValue, _ := g.Nodes.Sum()
358
+	cutoff := abs64(int64(float64(totalValue) * nodeFraction))
359
+
360
+	for _, l := range p.Location {
361
+		nodes := locationMap[l.ID]
362
+		if len(nodes) == 0 || len(l.Line) != len(nodes) {
363
+			continue
364
+		}
365
+		for i, n := range nodes {
366
+			if n.Cum < cutoff {
367
+				l.Line[i] = profile.Line{}
368
+			}
369
+		}
370
+	}
371
+
372
+	return p.Compact()
373
+}
374
+
343 375
 // isNegative returns true if the node is considered as "negative" for the
344 376
 // purposes of drop_negative.
345 377
 func isNegative(n *Node) bool {
@@ -391,29 +423,41 @@ func (nm NodeMap) findOrInsertLocation(l *profile.Location, keepBinary bool, kep
391 423
 		}
392 424
 		return Nodes{nm.FindOrInsertNode(ni, kept)}
393 425
 	}
394
-	var locNodes Nodes
395
-	for _, line := range l.Line {
396
-		ni := NodeInfo{
397
-			Address: l.Address,
398
-			Lineno:  int(line.Line),
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)
399 430
 		}
431
+	}
432
+	return locNodes
433
+}
400 434
 
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)
406
-			}
407
-			if keepBinary {
408
-				ni.StartLine = int(line.Function.StartLine)
409
-			}
410
-		}
411
-		if keepBinary || line.Function == nil {
412
-			ni.Objfile = objfile
435
+func nodeInfo(l *profile.Location, li int, objfile string, keepBinary bool) *NodeInfo {
436
+	if !keepBinary {
437
+		objfile = ""
438
+	}
439
+	line := l.Line[li]
440
+	if line.Function == nil {
441
+		if l.Address == 0 {
442
+			return nil
413 443
 		}
414
-		locNodes = append(locNodes, nm.FindOrInsertNode(ni, kept))
444
+		return &NodeInfo{Address: l.Address, Objfile: objfile}
415 445
 	}
416
-	return locNodes
446
+
447
+	ni := &NodeInfo{
448
+		Address:  l.Address,
449
+		Lineno:   int(line.Line),
450
+		Name:     line.Function.Name,
451
+		OrigName: line.Function.SystemName,
452
+		Objfile:  objfile,
453
+	}
454
+	if fname := line.Function.Filename; fname != "" {
455
+		ni.File = filepath.Clean(fname)
456
+	}
457
+	if keepBinary {
458
+		ni.StartLine = int(line.Function.StartLine)
459
+	}
460
+	return ni
417 461
 }
418 462
 
419 463
 type tags struct {

+ 18
- 1
internal/report/report.go Целия файл

@@ -54,6 +54,8 @@ func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
54 54
 		return printTags(w, rpt)
55 55
 	case Proto:
56 56
 		return rpt.prof.Write(w)
57
+	case TrimProto:
58
+		return printTrimmedProto(w, rpt)
57 59
 	case TopProto:
58 60
 		return printTopProto(w, rpt)
59 61
 	case Dis:
@@ -184,7 +186,10 @@ func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph {
184 186
 		}
185 187
 		s.NumLabel = numLabels
186 188
 	}
189
+	return graph.New(rpt.prof, graphOptions(o, nodes))
190
+}
187 191
 
192
+func graphOptions(o *Options, nodes graph.NodeSet) *graph.Options {
188 193
 	gopt := &graph.Options{
189 194
 		SampleValue:  o.SampleValue,
190 195
 		FormatTag:    formatTag,
@@ -200,7 +205,7 @@ func (rpt *Report) newGraph(nodes graph.NodeSet) *graph.Graph {
200 205
 		gopt.ObjNames = true
201 206
 	}
202 207
 
203
-	return graph.New(rpt.prof, gopt)
208
+	return gopt
204 209
 }
205 210
 
206 211
 func formatTag(v int64, key string) string {
@@ -256,6 +261,17 @@ func printTopProto(w io.Writer, rpt *Report) error {
256 261
 	return out.Write(w)
257 262
 }
258 263
 
264
+// printTrimmedProto writes a profile in a serialize profile.proto,
265
+// removing symbol information from infrequent nodes to reduce the
266
+// size of the profile.
267
+func printTrimmedProto(w io.Writer, rpt *Report) error {
268
+	o := rpt.options
269
+	prof := rpt.prof
270
+	prof = graph.TrimProfile(prof, graphOptions(o, nil), o.NodeFraction)
271
+
272
+	return prof.Write(w)
273
+}
274
+
259 275
 // printAssembly prints an annotated assembly listing.
260 276
 func printAssembly(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
261 277
 	o := rpt.options
@@ -844,6 +860,7 @@ const (
844 860
 	WebList
845 861
 	Callgrind
846 862
 	TopProto
863
+	TrimProto
847 864
 )
848 865
 
849 866
 // Options are the formatting and filtering options used to generate a

+ 11
- 1
profile/merge.go Целия файл

@@ -22,6 +22,14 @@ import (
22 22
 	"strings"
23 23
 )
24 24
 
25
+// Compact performs garbage collection on a profile to remove any
26
+// unreferenced fields. This is useful to reduce the size of a profile
27
+// after samples or locations have been removed.
28
+func (p *Profile) Compact() *Profile {
29
+	p, _ = Merge([]*Profile{p})
30
+	return p
31
+}
32
+
25 33
 // Merge merges all the profiles in profs into a single Profile.
26 34
 // Returns a new profile independent of the input profiles. The merged
27 35
 // profile is compacted to eliminate unused samples, locations,
@@ -220,7 +228,9 @@ func (l *Location) key() locationKey {
220 228
 	}
221 229
 	lines := make([]string, len(l.Line)*2)
222 230
 	for i, line := range l.Line {
223
-		lines[i*2] = strconv.FormatUint(line.Function.ID, 16)
231
+		if line.Function != nil {
232
+			lines[i*2] = strconv.FormatUint(line.Function.ID, 16)
233
+		}
224 234
 		lines[i*2+1] = strconv.FormatInt(line.Line, 16)
225 235
 	}
226 236
 	key.lines = strings.Join(lines, "|")