Ver código fonte

Picking tags with tag focus and ignore (#183)

* Modified variable type to have []string value

* added repeatableStringKind

* modify repeatableStringKind to actually handle multiple values

* Modify applyFocus to work with multiple values for tagfocus/tagignore

* Modify applyFocus to work with multiple values for tagfocus/tagignore

* Modified CompileTagFilter to handle tag filters of the form -tagfocus=key1=val1

* Modified StringList

* add testing for compileTagFilter

* fixed typo

* fixed code style error

* Revert "fixed code style error"

This reverts commit 0b1826c163.

* Revert "fixed typo"

This reverts commit 2593652bd0.

* Revert "add testing for compileTagFilter"

This reverts commit 152d7b0363.

* Revert "Modified StringList"

This reverts commit b57d35a762.

* Revert "Modified CompileTagFilter to handle tag filters of the form -tagfocus=key1=val1"

This reverts commit ebe1e344bf.

* Revert "Modify applyFocus to work with multiple values for tagfocus/tagignore"

This reverts commit 3b62091b67.

* Revert "Modify applyFocus to work with multiple values for tagfocus/tagignore"

This reverts commit 7172159cd3.

* Revert "modify repeatableStringKind to actually handle multiple values"

This reverts commit abb1e3ec4b.

* Revert "added repeatableStringKind"

This reverts commit 8b8e4e76fc.

* Revert "Modified variable type to have []string value"

This reverts commit 5f213dc4d1.

* added support for filtering by specific tags

* Fixed formatting problems

* Updated variable name for clarity

* Added test cases for tag filtering

* Update tagfocus and tagignore description

* Fixed formatting error

* Updated docs to explain new tag filtering

* Updated docs to clarify tag filtering

* Updated docs to clarify tag filtering

* Updated docs and help comments for tag filtering

* Updated docs for tag filtering

* Update pprof docs for tag filtering

* Update tagfocus and tagignore help descriptions

* Update tagfocus and tagignore descriptions in docs

* Update tagfocus and tagignore descriptions in docs and help

* Updated tagfocus and tagignore descriptions in docs; added TODO for regexp matching

* Updated tagfocus and tagignore descriptions in docs

* Modified docs fo tagfocus for clarity
Margaret Nolan 7 anos atrás
pai
commit
e01c25cbdd

+ 1
- 1
appveyor.yml Ver arquivo

@@ -2,7 +2,7 @@ clone_folder: c:\go\src\github.com\google\pprof
2 2
 
3 3
 install:
4 4
  - cinst graphviz.portable
5
- 
5
+
6 6
 before_build:
7 7
  - go get github.com/ianlancetaylor/demangle
8 8
 

+ 48
- 2
doc/pprof.md Ver arquivo

@@ -105,10 +105,56 @@ number of values - 1) or the name of the sample value.
105 105
 
106 106
 Sample values are numeric values associated to a unit. If pprof can recognize
107 107
 these units, it will attempt to scale the values to a suitable unit for
108
-visualization. The `unite=` option will force the use of a specific unit. For
109
-example, `sample_index=sec` will force any time values to be reported in
108
+visualization. The `unit=` option will force the use of a specific unit. For
109
+example, `unit=sec` will force any time values to be reported in
110 110
 seconds. pprof recognizes most common time and memory size units.
111 111
 
112
+## Tag filtering
113
+
114
+Samples in a profile may have tags. These tags have a name and a value; this
115
+value can be either numeric or a string. pprof can select samples from a
116
+profile based on these tags using the `-tagfocus` and `-tagignore` options.
117
+
118
+Generally, these options work as follows:
119
+* **-tagfocus=_regex_** or **-tagfocus=_range_:** Restrict to samples with tags
120
+  matched by regexp or in range.
121
+* **-tagignore=_regex_** or **-tagignore=_range_:** Discard samples with tags
122
+  matched by regexp or in range.
123
+
124
+When using `-tagfocus=regex` and `-tagignore=regex`, the regex will be compared
125
+to each value associated with each tag. If one specifies a value
126
+like `regex1,regex2`, then only samples with a tag value matching `regex1`
127
+and a tag value matching `regex2` will be kept.
128
+
129
+In addition to being able to filter on tag values, one can specify the name of
130
+the tag which a certain value must be associated with using the notation
131
+`-tagfocus=tagName=value`. Here, the `tagName` must match the tag's name
132
+exactly, and the value can be either a regex or a range. If one specifies
133
+a value like `regex1,regex2`, then samples with a tag value (paired with the
134
+specified tag name) matching either `regex1` or matching `regex2` will match.
135
+
136
+Here are examples explaining how `tagfocus` can be used:
137
+
138
+* `-tagfocus 128kb:512kb` accepts a sample iff it has any numeric tag with
139
+  memory value in the specified range.
140
+* `-tagfocus mytag=128kb:512kb` accepts a sample iff it has a numeric tag
141
+  `mytag` with memory value in the specified range. There isn't a way to say
142
+   `-tagfocus mytag=128kb:512kb,16kb:32kb`
143
+   or `-tagfocus mytag=128kb:512kb,mytag2=128kb:512kb`. Just single value or
144
+   range for numeric tags.
145
+* `-tagfocus someregex` accepts a sample iff it has any string tag with
146
+  `tagName:tagValue` string matching specified regexp. In the future, this
147
+  will change to accept sample iff it has any string tag with `tagValue` string
148
+  matching specified regexp.
149
+* `-tagfocus mytag=myvalue1,myvalue2` matches if either of the two tag values
150
+  are present.
151
+
152
+`-tagignore` works similarly, except that it discards matching samples, instead
153
+of keeping them.
154
+
155
+If both the `-tagignore` and `-tagfocus` expressions (either a regexp or a
156
+range) match a given sample, then the sample will be discarded.
157
+
112 158
 ## Text reports
113 159
 
114 160
 pprof text reports show the location hierarchy in text format.

+ 7
- 3
internal/driver/commands.go Ver arquivo

@@ -195,11 +195,15 @@ var pprofVariables = variables{
195 195
 		"If set, only show nodes that match this location.",
196 196
 		"Matching includes the function name, filename or object name.")},
197 197
 	"tagfocus": &variable{stringKind, "", "", helpText(
198
-		"Restrict to samples with tags in range or matched by regexp",
199
-		"Discard samples that do not include a node with a tag matching this regexp.")},
198
+		"Restricts to samples with tags in range or matched by regexp",
199
+		"Use name=value syntax to limit the matching to a specific tag.",
200
+		"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
201
+		"String tag filter examples: foo, foo.*bar, mytag=foo.*bar")},
200 202
 	"tagignore": &variable{stringKind, "", "", helpText(
201 203
 		"Discard samples with tags in range or matched by regexp",
202
-		"Discard samples that do include a node with a tag matching this regexp.")},
204
+		"Use name=value syntax to limit the matching to a specific tag.",
205
+		"Numeric tag filter examples: 1kb, 1kb:10kb, memory=32mb:",
206
+		"String tag filter examples: foo, foo.*bar, mytag=foo.*bar")},
203 207
 	"tagshow": &variable{stringKind, "", "", helpText(
204 208
 		"Only consider tags matching this regexp",
205 209
 		"Discard tags that do not match this regexp")},

+ 51
- 11
internal/driver/driver_focus.go Ver arquivo

@@ -77,19 +77,44 @@ func compileTagFilter(name, value string, ui plugin.UI, err error) (func(*profil
77 77
 	if value == "" || err != nil {
78 78
 		return nil, err
79 79
 	}
80
+
81
+	tagValuePair := strings.SplitN(value, "=", 2)
82
+	var wantKey string
83
+	if len(tagValuePair) == 2 {
84
+		wantKey = tagValuePair[0]
85
+		value = tagValuePair[1]
86
+	} else {
87
+		wantKey = ""
88
+	}
89
+
80 90
 	if numFilter := parseTagFilterRange(value); numFilter != nil {
81 91
 		ui.PrintErr(name, ":Interpreted '", value, "' as range, not regexp")
82
-		return func(s *profile.Sample) bool {
83
-			for key, vals := range s.NumLabel {
84
-				for _, val := range vals {
85
-					if numFilter(val, key) {
92
+		labelFilter := func(vals []int64, key string) bool {
93
+			for _, val := range vals {
94
+				if numFilter(val, key) {
95
+					return true
96
+				}
97
+			}
98
+			return false
99
+		}
100
+		if wantKey == "" {
101
+			return func(s *profile.Sample) bool {
102
+				for key, vals := range s.NumLabel {
103
+					if labelFilter(vals, key) {
86 104
 						return true
87 105
 					}
88 106
 				}
107
+				return false
108
+			}, nil
109
+		}
110
+		return func(s *profile.Sample) bool {
111
+			if vals, ok := s.NumLabel[wantKey]; ok {
112
+				return labelFilter(vals, wantKey)
89 113
 			}
90 114
 			return false
91 115
 		}, nil
92 116
 	}
117
+
93 118
 	var rfx []*regexp.Regexp
94 119
 	for _, tagf := range strings.Split(value, ",") {
95 120
 		fx, err := regexp.Compile(tagf)
@@ -98,19 +123,34 @@ func compileTagFilter(name, value string, ui plugin.UI, err error) (func(*profil
98 123
 		}
99 124
 		rfx = append(rfx, fx)
100 125
 	}
126
+	if wantKey == "" {
127
+		return func(s *profile.Sample) bool {
128
+		matchedrx:
129
+			for _, rx := range rfx {
130
+				for key, vals := range s.Label {
131
+					for _, val := range vals {
132
+						// TODO: Match against val, not key:val in future
133
+						if rx.MatchString(key + ":" + val) {
134
+							continue matchedrx
135
+						}
136
+					}
137
+				}
138
+				return false
139
+			}
140
+			return true
141
+		}, nil
142
+	}
101 143
 	return func(s *profile.Sample) bool {
102
-	matchedrx:
103
-		for _, rx := range rfx {
104
-			for key, vals := range s.Label {
144
+		if vals, ok := s.Label[wantKey]; ok {
145
+			for _, rx := range rfx {
105 146
 				for _, val := range vals {
106
-					if rx.MatchString(key + ":" + val) {
107
-						continue matchedrx
147
+					if rx.MatchString(val) {
148
+						return true
108 149
 					}
109 150
 				}
110 151
 			}
111
-			return false
112 152
 		}
113
-		return true
153
+		return false
114 154
 	}, nil
115 155
 }
116 156
 

+ 47
- 1
internal/driver/driver_test.go Ver arquivo

@@ -998,8 +998,12 @@ func TestTagFilter(t *testing.T) {
998 998
 		{"test3", "tag1,tag3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
999 999
 		{"test4", "t..[12],t..3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
1000 1000
 		{"test5", "tag2,tag3", map[string][]string{"value1": {"tag1", "tag2"}}, false},
1001
+		{"test6", "key1=tag1,tag2", map[string][]string{"key1": {"tag1", "tag2"}}, true},
1002
+		{"test7", "key1=tag1,tag2", map[string][]string{"key1": {"tag1"}}, true},
1003
+		{"test8", "key1:tag1,tag2", map[string][]string{"key1": {"tag1", "tag2"}}, true},
1004
+		{"test9", "key1:tag1,tag2", map[string][]string{"key1": {"tag2"}}, false},
1005
+		{"test10", "key1:tag1,tag2", map[string][]string{"key1": {"tag1"}}, false},
1001 1006
 	}
1002
-
1003 1007
 	for _, test := range tagFilterTests {
1004 1008
 		filter, err := compileTagFilter(test.name, test.value, &proftest.TestUI{T: t}, nil)
1005 1009
 		if err != nil {
@@ -1016,6 +1020,48 @@ func TestTagFilter(t *testing.T) {
1016 1020
 	}
1017 1021
 }
1018 1022
 
1023
+func TestNumericTagFilter(t *testing.T) {
1024
+	var tagFilterTests = []struct {
1025
+		name, value string
1026
+		tags        map[string][]int64
1027
+		want        bool
1028
+	}{
1029
+		{"test1", "128kb", map[string][]int64{"bytes": {131072}, "kilobytes": {128}}, true},
1030
+		{"test2", "512kb", map[string][]int64{"bytes": {512}, "kilobytes": {128}}, false},
1031
+		{"test3", "10b", map[string][]int64{"bytes": {10, 20}, "kilobytes": {128}}, true},
1032
+		{"test4", ":10b", map[string][]int64{"bytes": {8}}, true},
1033
+		{"test5", ":10kb", map[string][]int64{"bytes": {8}}, true},
1034
+		{"test6", "10b:", map[string][]int64{"kilobytes": {8}}, true},
1035
+		{"test7", "10b:", map[string][]int64{"bytes": {12}}, true},
1036
+		{"test8", "10b:", map[string][]int64{"bytes": {8}}, false},
1037
+		{"test9", "10kb:", map[string][]int64{"bytes": {8}}, false},
1038
+		{"test10", ":10b", map[string][]int64{"kilobytes": {8}}, false},
1039
+		{"test11", ":10b", map[string][]int64{"bytes": {12}}, false},
1040
+		{"test12", "bytes=5b", map[string][]int64{"bytes": {5, 10}}, true},
1041
+		{"test13", "bytes=1024b", map[string][]int64{"kilobytes": {1, 1024}}, false},
1042
+		{"test14", "bytes=1024b", map[string][]int64{"kilobytes": {5}, "bytes": {1024}}, true},
1043
+		{"test15", "bytes=512b:1024b", map[string][]int64{"bytes": {780}}, true},
1044
+		{"test16", "bytes=1kb:2kb", map[string][]int64{"bytes": {4096}}, false},
1045
+		{"test17", "bytes=512b:1024b", map[string][]int64{"bytes": {256}}, false},
1046
+	}
1047
+	for _, test := range tagFilterTests {
1048
+		expectedErrMsg := fmt.Sprint([]string{test.name, ":Interpreted '", test.value, "' as range, not regexp"})
1049
+		filter, err := compileTagFilter(test.name, test.value, &proftest.TestUI{T: t,
1050
+			IgnoreRx: expectedErrMsg}, nil)
1051
+		if err != nil {
1052
+			t.Errorf("tagFilter %s:%v", test.name, err)
1053
+			continue
1054
+		}
1055
+		s := profile.Sample{
1056
+			NumLabel: test.tags,
1057
+		}
1058
+
1059
+		if got := filter(&s); got != test.want {
1060
+			t.Errorf("tagFilter %s: got %v, want %v", test.name, got, test.want)
1061
+		}
1062
+	}
1063
+}
1064
+
1019 1065
 type testSymbolzMergeFetcher struct{}
1020 1066
 
1021 1067
 func (testSymbolzMergeFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {