Browse Source

Improve support for the Comments field

By default hide entries in the comment field that starts with #, and
add a "comments" command to show all comments.
Raul Silvera 8 years ago
parent
commit
8ef477bf90

+ 9
- 8
internal/driver/commands.go View File

86
 // pprofCommands are the report generation commands recognized by pprof.
86
 // pprofCommands are the report generation commands recognized by pprof.
87
 var pprofCommands = commands{
87
 var pprofCommands = commands{
88
 	// Commands that require no post-processing.
88
 	// Commands that require no post-processing.
89
-	"tags":     {report.Tags, nil, false, "Outputs all tags in the profile", "tags [tag_regex]* [-ignore_regex]* [>file]\nList tags with key:value matching tag_regex and exclude ignore_regex."},
90
-	"raw":      {report.Raw, nil, false, "Outputs a text representation of the raw profile", ""},
91
-	"dot":      {report.Dot, nil, false, "Outputs a graph in DOT format", reportHelp("dot", false, true)},
92
-	"top":      {report.Text, nil, false, "Outputs top entries in text form", reportHelp("top", true, true)},
93
-	"tree":     {report.Tree, nil, false, "Outputs a text rendering of call graph", reportHelp("tree", true, true)},
94
-	"text":     {report.Text, nil, false, "Outputs top entries in text form", reportHelp("text", true, true)},
95
-	"traces":   {report.Traces, nil, false, "Outputs all profile samples in text form", ""},
96
-	"topproto": {report.TopProto, awayFromTTY("pb.gz"), false, "Outputs top entries in compressed protobuf format", ""},
89
+	"comments": {report.Comments, nil, false, "Output all profile comments", ""},
97
 	"disasm":   {report.Dis, nil, true, "Output assembly listings annotated with samples", listHelp("disasm", true)},
90
 	"disasm":   {report.Dis, nil, true, "Output assembly listings annotated with samples", listHelp("disasm", true)},
91
+	"dot":      {report.Dot, nil, false, "Outputs a graph in DOT format", reportHelp("dot", false, true)},
98
 	"list":     {report.List, nil, true, "Output annotated source for functions matching regexp", listHelp("list", false)},
92
 	"list":     {report.List, nil, true, "Output annotated source for functions matching regexp", listHelp("list", false)},
99
 	"peek":     {report.Tree, nil, true, "Output callers/callees of functions matching regexp", "peek func_regex\nDisplay callers and callees of functions matching func_regex."},
93
 	"peek":     {report.Tree, nil, true, "Output callers/callees of functions matching regexp", "peek func_regex\nDisplay callers and callees of functions matching func_regex."},
94
+	"raw":      {report.Raw, nil, false, "Outputs a text representation of the raw profile", ""},
95
+	"tags":     {report.Tags, nil, false, "Outputs all tags in the profile", "tags [tag_regex]* [-ignore_regex]* [>file]\nList tags with key:value matching tag_regex and exclude ignore_regex."},
96
+	"text":     {report.Text, nil, false, "Outputs top entries in text form", reportHelp("text", true, true)},
97
+	"top":      {report.Text, nil, false, "Outputs top entries in text form", reportHelp("top", true, true)},
98
+	"topproto": {report.TopProto, awayFromTTY("pb.gz"), false, "Outputs top entries in compressed protobuf format", ""},
99
+	"traces":   {report.Traces, nil, false, "Outputs all profile samples in text form", ""},
100
+	"tree":     {report.Tree, nil, false, "Outputs a text rendering of call graph", reportHelp("tree", true, true)},
100
 
101
 
101
 	// Save binary formats to a file
102
 	// Save binary formats to a file
102
 	"callgrind": {report.Callgrind, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format", reportHelp("callgrind", false, true)},
103
 	"callgrind": {report.Callgrind, awayFromTTY("callgraph.out"), false, "Outputs a graph in callgrind format", reportHelp("callgrind", false, true)},

+ 4
- 1
internal/driver/driver_test.go View File

63
 		{"dot,lines,flat,focus=[12]00", "heap"},
63
 		{"dot,lines,flat,focus=[12]00", "heap"},
64
 		{"dot,addresses,flat,ignore=[X3]002,focus=[X1]000", "contention"},
64
 		{"dot,addresses,flat,ignore=[X3]002,focus=[X1]000", "contention"},
65
 		{"dot,files,cum", "contention"},
65
 		{"dot,files,cum", "contention"},
66
+		{"comments", "cpu"},
67
+		{"comments", "heap"},
66
 		{"tags", "cpu"},
68
 		{"tags", "cpu"},
67
 		{"tags,tagignore=tag[13],tagfocus=key[12]", "cpu"},
69
 		{"tags,tagignore=tag[13],tagfocus=key[12]", "cpu"},
68
 		{"tags", "heap"},
70
 		{"tags", "heap"},
215
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
217
 	name = addString(name, f, []string{"inuse_space", "inuse_objects", "alloc_space", "alloc_objects"})
216
 	name = addString(name, f, []string{"relative_percentages"})
218
 	name = addString(name, f, []string{"relative_percentages"})
217
 	name = addString(name, f, []string{"seconds"})
219
 	name = addString(name, f, []string{"seconds"})
218
-	name = addString(name, f, []string{"text", "tree", "callgrind", "dot", "svg", "tags", "dot", "traces", "disasm", "peek", "weblist", "topproto"})
220
+	name = addString(name, f, []string{"text", "tree", "callgrind", "dot", "svg", "tags", "dot", "traces", "disasm", "peek", "weblist", "topproto", "comments"})
219
 	if f.strings["focus"] != "" || f.strings["tagfocus"] != "" {
221
 	if f.strings["focus"] != "" || f.strings["tagfocus"] != "" {
220
 		name = append(name, "focus")
222
 		name = append(name, "focus")
221
 	}
223
 	}
726
 	}
728
 	}
727
 
729
 
728
 	return &profile.Profile{
730
 	return &profile.Profile{
731
+		Comments:   []string{"comment", "#hidden comment"},
729
 		PeriodType: &profile.ValueType{Type: "allocations", Unit: "bytes"},
732
 		PeriodType: &profile.ValueType{Type: "allocations", Unit: "bytes"},
730
 		Period:     524288,
733
 		Period:     524288,
731
 		SampleType: []*profile.ValueType{
734
 		SampleType: []*profile.ValueType{

+ 0
- 0
internal/driver/testdata/pprof.cpu.comments View File


+ 2
- 0
internal/driver/testdata/pprof.heap.comments View File

1
+comment
2
+#hidden comment

+ 1
- 1
internal/driver/testdata/pprof.heap.flat.inuse_space.dot.focus View File

1
 digraph "unnamed" {
1
 digraph "unnamed" {
2
 node [style=filled fillcolor="#f8f8f8"]
2
 node [style=filled fillcolor="#f8f8f8"]
3
-subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lType: inuse_space\lShowing nodes accounting for 62.50MB, 63.37% of 98.63MB total\l"] }
3
+subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lShowing nodes accounting for 62.50MB, 63.37% of 98.63MB total\l"] }
4
 N1 [label="line2001\nfile2000.src\n62.50MB (63.37%)" fontsize=24 shape=box tooltip="line2001 testdata/file2000.src (62.50MB)" color="#b21600" fillcolor="#edd8d5"]
4
 N1 [label="line2001\nfile2000.src\n62.50MB (63.37%)" fontsize=24 shape=box tooltip="line2001 testdata/file2000.src (62.50MB)" color="#b21600" fillcolor="#edd8d5"]
5
 NN1_0 [label = "1.56MB" fontsize=8 shape=box3d tooltip="62.50MB"]
5
 NN1_0 [label = "1.56MB" fontsize=8 shape=box3d tooltip="62.50MB"]
6
 N1 -> NN1_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
6
 N1 -> NN1_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]

+ 1
- 1
internal/driver/testdata/pprof.heap.flat.inuse_space.dot.focus.ignore View File

1
 digraph "unnamed" {
1
 digraph "unnamed" {
2
 node [style=filled fillcolor="#f8f8f8"]
2
 node [style=filled fillcolor="#f8f8f8"]
3
-subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lType: inuse_space\lShowing nodes accounting for 36.13MB, 36.63% of 98.63MB total\lDropped 2 nodes (cum <= 4.93MB)\l"] }
3
+subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lShowing nodes accounting for 36.13MB, 36.63% of 98.63MB total\lDropped 2 nodes (cum <= 4.93MB)\l"] }
4
 N1 [label="line3002\nfile3000.src\n31.25MB (31.68%)\nof 32.23MB (32.67%)" fontsize=24 shape=box tooltip="line3002 testdata/file3000.src (32.23MB)" color="#b23200" fillcolor="#eddcd5"]
4
 N1 [label="line3002\nfile3000.src\n31.25MB (31.68%)\nof 32.23MB (32.67%)" fontsize=24 shape=box tooltip="line3002 testdata/file3000.src (32.23MB)" color="#b23200" fillcolor="#eddcd5"]
5
 NN1_0 [label = "400kB" fontsize=8 shape=box3d tooltip="31.25MB"]
5
 NN1_0 [label = "400kB" fontsize=8 shape=box3d tooltip="31.25MB"]
6
 N1 -> NN1_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]
6
 N1 -> NN1_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]

+ 1
- 1
internal/driver/testdata/pprof.heap.flat.lines.dot.focus View File

1
 digraph "unnamed" {
1
 digraph "unnamed" {
2
 node [style=filled fillcolor="#f8f8f8"]
2
 node [style=filled fillcolor="#f8f8f8"]
3
-subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lType: inuse_space\lShowing nodes accounting for 67.38MB, 68.32% of 98.63MB total\l"] }
3
+subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: inuse_space\lShowing nodes accounting for 67.38MB, 68.32% of 98.63MB total\l"] }
4
 N1 [label="line3000\nfile3000.src:4\n0 of 67.38MB (68.32%)" fontsize=8 shape=box tooltip="line3000 testdata/file3000.src:4 (67.38MB)" color="#b21300" fillcolor="#edd7d5"]
4
 N1 [label="line3000\nfile3000.src:4\n0 of 67.38MB (68.32%)" fontsize=8 shape=box tooltip="line3000 testdata/file3000.src:4 (67.38MB)" color="#b21300" fillcolor="#edd7d5"]
5
 N2 [label="line2001\nfile2000.src:2\n62.50MB (63.37%)\nof 63.48MB (64.36%)" fontsize=24 shape=box tooltip="line2001 testdata/file2000.src:2 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
5
 N2 [label="line2001\nfile2000.src:2\n62.50MB (63.37%)\nof 63.48MB (64.36%)" fontsize=24 shape=box tooltip="line2001 testdata/file2000.src:2 (63.48MB)" color="#b21600" fillcolor="#edd8d5"]
6
 NN2_0 [label = "1.56MB" fontsize=8 shape=box3d tooltip="62.50MB"]
6
 NN2_0 [label = "1.56MB" fontsize=8 shape=box3d tooltip="62.50MB"]

+ 1
- 1
internal/driver/testdata/pprof.heap_alloc.flat.alloc_space.dot.focus View File

1
 digraph "unnamed" {
1
 digraph "unnamed" {
2
 node [style=filled fillcolor="#f8f8f8"]
2
 node [style=filled fillcolor="#f8f8f8"]
3
-subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lType: alloc_space\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
3
+subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: alloc_space\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
4
 N1 [label="line3002\nfile3000.src\n31.25MB (31.68%)\nof 94.73MB (96.04%)" fontsize=20 shape=box tooltip="line3002 testdata/file3000.src (94.73MB)" color="#b20200" fillcolor="#edd5d5"]
4
 N1 [label="line3002\nfile3000.src\n31.25MB (31.68%)\nof 94.73MB (96.04%)" fontsize=20 shape=box tooltip="line3002 testdata/file3000.src (94.73MB)" color="#b20200" fillcolor="#edd5d5"]
5
 NN1_0 [label = "400kB" fontsize=8 shape=box3d tooltip="31.25MB"]
5
 NN1_0 [label = "400kB" fontsize=8 shape=box3d tooltip="31.25MB"]
6
 N1 -> NN1_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]
6
 N1 -> NN1_0 [label=" 31.25MB" weight=100 tooltip="31.25MB" labeltooltip="31.25MB"]

+ 1
- 1
internal/driver/testdata/pprof.heap_alloc.flat.alloc_space.dot.hide View File

1
 digraph "unnamed" {
1
 digraph "unnamed" {
2
 node [style=filled fillcolor="#f8f8f8"]
2
 node [style=filled fillcolor="#f8f8f8"]
3
-subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lType: alloc_space\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
3
+subgraph cluster_L { "Build ID: buildid" [shape=box fontsize=16 label="Build ID: buildid\lcomment\lType: alloc_space\lShowing nodes accounting for 93.75MB, 95.05% of 98.63MB total\lDropped 1 node (cum <= 4.93MB)\l"] }
4
 N1 [label="line3000\nfile3000.src\n62.50MB (63.37%)\nof 98.63MB (100%)" fontsize=24 shape=box tooltip="line3000 testdata/file3000.src (98.63MB)" color="#b20000" fillcolor="#edd5d5"]
4
 N1 [label="line3000\nfile3000.src\n62.50MB (63.37%)\nof 98.63MB (100%)" fontsize=24 shape=box tooltip="line3000 testdata/file3000.src (98.63MB)" color="#b20000" fillcolor="#edd5d5"]
5
 NN1_0 [label = "1.56MB" fontsize=8 shape=box3d tooltip="62.50MB"]
5
 NN1_0 [label = "1.56MB" fontsize=8 shape=box3d tooltip="62.50MB"]
6
 N1 -> NN1_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]
6
 N1 -> NN1_0 [label=" 62.50MB" weight=100 tooltip="62.50MB" labeltooltip="62.50MB"]

+ 64
- 46
internal/report/report.go View File

33
 	"github.com/google/pprof/profile"
33
 	"github.com/google/pprof/profile"
34
 )
34
 )
35
 
35
 
36
+// Output formats.
37
+const (
38
+	Callgrind = iota
39
+	Comments
40
+	Dis
41
+	Dot
42
+	List
43
+	Proto
44
+	Raw
45
+	Tags
46
+	Text
47
+	TopProto
48
+	Traces
49
+	Tree
50
+	WebList
51
+)
52
+
53
+// Options are the formatting and filtering options used to generate a
54
+// profile.
55
+type Options struct {
56
+	OutputFormat int
57
+
58
+	CumSort             bool
59
+	CallTree            bool
60
+	DropNegative        bool
61
+	PositivePercentages bool
62
+	CompactLabels       bool
63
+	Ratio               float64
64
+	Title               string
65
+	ProfileLabels       []string
66
+
67
+	NodeCount    int
68
+	NodeFraction float64
69
+	EdgeFraction float64
70
+
71
+	SampleValue       func(s []int64) int64
72
+	SampleMeanDivisor func(s []int64) int64
73
+	SampleType        string
74
+	SampleUnit        string // Unit for the sample data from the profile.
75
+
76
+	OutputUnit string // Units for data formatting in report.
77
+
78
+	Symbol     *regexp.Regexp // Symbols to include on disassembly report.
79
+	SourcePath string         // Search path for source files.
80
+}
81
+
36
 // Generate generates a report as directed by the Report.
82
 // Generate generates a report as directed by the Report.
37
 func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
83
 func Generate(w io.Writer, rpt *Report, obj plugin.ObjTool) error {
38
 	o := rpt.options
84
 	o := rpt.options
39
 
85
 
40
 	switch o.OutputFormat {
86
 	switch o.OutputFormat {
87
+	case Comments:
88
+		return printComments(w, rpt)
41
 	case Dot:
89
 	case Dot:
42
 		return printDOT(w, rpt)
90
 		return printDOT(w, rpt)
43
 	case Tree:
91
 	case Tree:
593
 	return nil
641
 	return nil
594
 }
642
 }
595
 
643
 
644
+// printComments prints all freeform comments in the profile.
645
+func printComments(w io.Writer, rpt *Report) error {
646
+	p := rpt.prof
647
+
648
+	for _, c := range p.Comments {
649
+		fmt.Fprintln(w, c)
650
+	}
651
+	return nil
652
+}
653
+
596
 // printText prints a flat text report for a profile.
654
 // printText prints a flat text report for a profile.
597
 func printText(w io.Writer, rpt *Report) error {
655
 func printText(w io.Writer, rpt *Report) error {
598
 	g, origCount, droppedNodes, _ := rpt.newTrimmedGraph()
656
 	g, origCount, droppedNodes, _ := rpt.newTrimmedGraph()
937
 			label = append(label, "Build ID: "+prof.Mapping[0].BuildID)
995
 			label = append(label, "Build ID: "+prof.Mapping[0].BuildID)
938
 		}
996
 		}
939
 	}
997
 	}
940
-	label = append(label, prof.Comments...)
998
+	// Only include comments that do not start with '#'.
999
+	for _, c := range prof.Comments {
1000
+		if !strings.HasPrefix(c, "#") {
1001
+			label = append(label, c)
1002
+		}
1003
+	}
941
 	if o.SampleType != "" {
1004
 	if o.SampleType != "" {
942
 		label = append(label, "Type: "+o.SampleType)
1005
 		label = append(label, "Type: "+o.SampleType)
943
 	}
1006
 	}
1004
 	return fmt.Sprintf("Dropped %d %s (%s <= %s)", d, n, l, f)
1067
 	return fmt.Sprintf("Dropped %d %s (%s <= %s)", d, n, l, f)
1005
 }
1068
 }
1006
 
1069
 
1007
-// Output formats.
1008
-const (
1009
-	Proto = iota
1010
-	Dot
1011
-	Tags
1012
-	Tree
1013
-	Text
1014
-	Traces
1015
-	Raw
1016
-	Dis
1017
-	List
1018
-	WebList
1019
-	Callgrind
1020
-	TopProto
1021
-)
1022
-
1023
-// Options are the formatting and filtering options used to generate a
1024
-// profile.
1025
-type Options struct {
1026
-	OutputFormat int
1027
-
1028
-	CumSort             bool
1029
-	CallTree            bool
1030
-	DropNegative        bool
1031
-	PositivePercentages bool
1032
-	CompactLabels       bool
1033
-	Ratio               float64
1034
-	Title               string
1035
-	ProfileLabels       []string
1036
-
1037
-	NodeCount    int
1038
-	NodeFraction float64
1039
-	EdgeFraction float64
1040
-
1041
-	SampleValue       func(s []int64) int64
1042
-	SampleMeanDivisor func(s []int64) int64
1043
-	SampleType        string
1044
-	SampleUnit        string // Unit for the sample data from the profile.
1045
-
1046
-	OutputUnit string // Units for data formatting in report.
1047
-
1048
-	Symbol     *regexp.Regexp // Symbols to include on disassembly report.
1049
-	SourcePath string         // Search path for source files.
1050
-}
1051
-
1052
 // New builds a new report indexing the sample values interpreting the
1070
 // New builds a new report indexing the sample values interpreting the
1053
 // samples with the provided function.
1071
 // samples with the provided function.
1054
 func New(prof *profile.Profile, o *Options) *Report {
1072
 func New(prof *profile.Profile, o *Options) *Report {

+ 7
- 4
profile/profile.go View File

391
 // String dumps a text representation of a profile. Intended mainly
391
 // String dumps a text representation of a profile. Intended mainly
392
 // for debugging purposes.
392
 // for debugging purposes.
393
 func (p *Profile) String() string {
393
 func (p *Profile) String() string {
394
-	ss := make([]string, 0, len(p.Sample)+len(p.Mapping)+len(p.Location))
394
+	ss := make([]string, 0, len(p.Comments)+len(p.Sample)+len(p.Mapping)+len(p.Location))
395
+	for _, c := range p.Comments {
396
+		ss = append(ss, "Comment: "+c)
397
+	}
395
 	if pt := p.PeriodType; pt != nil {
398
 	if pt := p.PeriodType; pt != nil {
396
 		ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
399
 		ss = append(ss, fmt.Sprintf("PeriodType: %s %s", pt.Type, pt.Unit))
397
 	}
400
 	}
430
 				ls = append(ls, fmt.Sprintf("%s:%v", k, v))
433
 				ls = append(ls, fmt.Sprintf("%s:%v", k, v))
431
 			}
434
 			}
432
 			sort.Strings(ls)
435
 			sort.Strings(ls)
433
-			ss = append(ss, labelHeader + strings.Join(ls, " "))
436
+			ss = append(ss, labelHeader+strings.Join(ls, " "))
434
 		}
437
 		}
435
 		if len(s.NumLabel) > 0 {
438
 		if len(s.NumLabel) > 0 {
436
 			ls := []string{}
439
 			ls := []string{}
437
 			for k, v := range s.NumLabel {
440
 			for k, v := range s.NumLabel {
438
-				ls = append(ls,fmt.Sprintf("%s:%v", k, v))
441
+				ls = append(ls, fmt.Sprintf("%s:%v", k, v))
439
 			}
442
 			}
440
 			sort.Strings(ls)
443
 			sort.Strings(ls)
441
-			ss = append(ss, labelHeader + strings.Join(ls, " "))
444
+			ss = append(ss, labelHeader+strings.Join(ls, " "))
442
 		}
445
 		}
443
 	}
446
 	}
444
 
447