Browse Source

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 years ago
parent
commit
e01c25cbdd
5 changed files with 154 additions and 18 deletions
  1. 1
    1
      appveyor.yml
  2. 48
    2
      doc/pprof.md
  3. 7
    3
      internal/driver/commands.go
  4. 51
    11
      internal/driver/driver_focus.go
  5. 47
    1
      internal/driver/driver_test.go

+ 1
- 1
appveyor.yml View File

2
 
2
 
3
 install:
3
 install:
4
  - cinst graphviz.portable
4
  - cinst graphviz.portable
5
- 
5
+
6
 before_build:
6
 before_build:
7
  - go get github.com/ianlancetaylor/demangle
7
  - go get github.com/ianlancetaylor/demangle
8
 
8
 

+ 48
- 2
doc/pprof.md View File

105
 
105
 
106
 Sample values are numeric values associated to a unit. If pprof can recognize
106
 Sample values are numeric values associated to a unit. If pprof can recognize
107
 these units, it will attempt to scale the values to a suitable unit for
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
 seconds. pprof recognizes most common time and memory size units.
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
 ## Text reports
158
 ## Text reports
113
 
159
 
114
 pprof text reports show the location hierarchy in text format.
160
 pprof text reports show the location hierarchy in text format.

+ 7
- 3
internal/driver/commands.go View File

195
 		"If set, only show nodes that match this location.",
195
 		"If set, only show nodes that match this location.",
196
 		"Matching includes the function name, filename or object name.")},
196
 		"Matching includes the function name, filename or object name.")},
197
 	"tagfocus": &variable{stringKind, "", "", helpText(
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
 	"tagignore": &variable{stringKind, "", "", helpText(
202
 	"tagignore": &variable{stringKind, "", "", helpText(
201
 		"Discard samples with tags in range or matched by regexp",
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
 	"tagshow": &variable{stringKind, "", "", helpText(
207
 	"tagshow": &variable{stringKind, "", "", helpText(
204
 		"Only consider tags matching this regexp",
208
 		"Only consider tags matching this regexp",
205
 		"Discard tags that do not match this regexp")},
209
 		"Discard tags that do not match this regexp")},

+ 51
- 11
internal/driver/driver_focus.go View File

77
 	if value == "" || err != nil {
77
 	if value == "" || err != nil {
78
 		return nil, err
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
 	if numFilter := parseTagFilterRange(value); numFilter != nil {
90
 	if numFilter := parseTagFilterRange(value); numFilter != nil {
81
 		ui.PrintErr(name, ":Interpreted '", value, "' as range, not regexp")
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
 						return true
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
 			return false
114
 			return false
91
 		}, nil
115
 		}, nil
92
 	}
116
 	}
117
+
93
 	var rfx []*regexp.Regexp
118
 	var rfx []*regexp.Regexp
94
 	for _, tagf := range strings.Split(value, ",") {
119
 	for _, tagf := range strings.Split(value, ",") {
95
 		fx, err := regexp.Compile(tagf)
120
 		fx, err := regexp.Compile(tagf)
98
 		}
123
 		}
99
 		rfx = append(rfx, fx)
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
 	return func(s *profile.Sample) bool {
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
 				for _, val := range vals {
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
 	}, nil
154
 	}, nil
115
 }
155
 }
116
 
156
 

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

998
 		{"test3", "tag1,tag3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
998
 		{"test3", "tag1,tag3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
999
 		{"test4", "t..[12],t..3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
999
 		{"test4", "t..[12],t..3", map[string][]string{"value1": {"tag1", "tag2"}, "value2": {"tag3"}}, true},
1000
 		{"test5", "tag2,tag3", map[string][]string{"value1": {"tag1", "tag2"}}, false},
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
 	for _, test := range tagFilterTests {
1007
 	for _, test := range tagFilterTests {
1004
 		filter, err := compileTagFilter(test.name, test.value, &proftest.TestUI{T: t}, nil)
1008
 		filter, err := compileTagFilter(test.name, test.value, &proftest.TestUI{T: t}, nil)
1005
 		if err != nil {
1009
 		if err != nil {
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
 type testSymbolzMergeFetcher struct{}
1065
 type testSymbolzMergeFetcher struct{}
1020
 
1066
 
1021
 func (testSymbolzMergeFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {
1067
 func (testSymbolzMergeFetcher) Fetch(s string, d, t time.Duration) (*profile.Profile, string, error) {