1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package driver
16
17 import (
18 "fmt"
19 "regexp"
20 "slices"
21 "strconv"
22 "strings"
23
24 "github.com/google/pprof/internal/measurement"
25 "github.com/google/pprof/internal/plugin"
26 "github.com/google/pprof/profile"
27 )
28
29 var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)?")
30
31
32 func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, cfg config, ui plugin.UI) error {
33 focus, err := compileRegexOption("focus", cfg.Focus, nil)
34 ignore, err := compileRegexOption("ignore", cfg.Ignore, err)
35 hide, err := compileRegexOption("hide", cfg.Hide, err)
36 show, err := compileRegexOption("show", cfg.Show, err)
37 showfrom, err := compileRegexOption("show_from", cfg.ShowFrom, err)
38 tagfocus, err := compileTagFilter("tagfocus", cfg.TagFocus, numLabelUnits, ui, err)
39 tagignore, err := compileTagFilter("tagignore", cfg.TagIgnore, numLabelUnits, ui, err)
40 prunefrom, err := compileRegexOption("prune_from", cfg.PruneFrom, err)
41 if err != nil {
42 return err
43 }
44
45 fm, im, hm, hnm := prof.FilterSamplesByName(focus, ignore, hide, show)
46 warnNoMatches(focus == nil || fm, "Focus", ui)
47 warnNoMatches(ignore == nil || im, "Ignore", ui)
48 warnNoMatches(hide == nil || hm, "Hide", ui)
49 warnNoMatches(show == nil || hnm, "Show", ui)
50
51 sfm := prof.ShowFrom(showfrom)
52 warnNoMatches(showfrom == nil || sfm, "ShowFrom", ui)
53
54 tfm, tim := prof.FilterSamplesByTag(tagfocus, tagignore)
55 warnNoMatches(tagfocus == nil || tfm, "TagFocus", ui)
56 warnNoMatches(tagignore == nil || tim, "TagIgnore", ui)
57
58 tagshow, err := compileRegexOption("tagshow", cfg.TagShow, err)
59 taghide, err := compileRegexOption("taghide", cfg.TagHide, err)
60 tns, tnh := prof.FilterTagsByName(tagshow, taghide)
61 warnNoMatches(tagshow == nil || tns, "TagShow", ui)
62 warnNoMatches(taghide == nil || tnh, "TagHide", ui)
63
64 if prunefrom != nil {
65 prof.PruneFrom(prunefrom)
66 }
67 return err
68 }
69
70 func compileRegexOption(name, value string, err error) (*regexp.Regexp, error) {
71 if value == "" || err != nil {
72 return nil, err
73 }
74 rx, err := regexp.Compile(value)
75 if err != nil {
76 return nil, fmt.Errorf("parsing %s regexp: %v", name, err)
77 }
78 return rx, nil
79 }
80
81 func compileTagFilter(name, value string, numLabelUnits map[string]string, ui plugin.UI, err error) (func(*profile.Sample) bool, error) {
82 if value == "" || err != nil {
83 return nil, err
84 }
85
86 tagValuePair := strings.SplitN(value, "=", 2)
87 var wantKey string
88 if len(tagValuePair) == 2 {
89 wantKey = tagValuePair[0]
90 value = tagValuePair[1]
91 }
92
93 if numFilter := parseTagFilterRange(value); numFilter != nil {
94 ui.PrintErr(name, ":Interpreted '", value, "' as range, not regexp")
95 labelFilter := func(vals []int64, unit string) bool {
96 for _, val := range vals {
97 if numFilter(val, unit) {
98 return true
99 }
100 }
101 return false
102 }
103 numLabelUnit := func(key string) string {
104 return numLabelUnits[key]
105 }
106 if wantKey == "" {
107 return func(s *profile.Sample) bool {
108 for key, vals := range s.NumLabel {
109 if labelFilter(vals, numLabelUnit(key)) {
110 return true
111 }
112 }
113 return false
114 }, nil
115 }
116 return func(s *profile.Sample) bool {
117 if vals, ok := s.NumLabel[wantKey]; ok {
118 return labelFilter(vals, numLabelUnit(wantKey))
119 }
120 return false
121 }, nil
122 }
123
124 var rfx []*regexp.Regexp
125 for _, tagf := range strings.Split(value, ",") {
126 fx, err := regexp.Compile(tagf)
127 if err != nil {
128 return nil, fmt.Errorf("parsing %s regexp: %v", name, err)
129 }
130 rfx = append(rfx, fx)
131 }
132 if wantKey == "" {
133 return func(s *profile.Sample) bool {
134 matchedrx:
135 for _, rx := range rfx {
136 for key, vals := range s.Label {
137 for _, val := range vals {
138
139 if rx.MatchString(key + ":" + val) {
140 continue matchedrx
141 }
142 }
143 }
144 return false
145 }
146 return true
147 }, nil
148 }
149 return func(s *profile.Sample) bool {
150 if vals, ok := s.Label[wantKey]; ok {
151 for _, rx := range rfx {
152 if slices.ContainsFunc(vals, rx.MatchString) {
153 return true
154 }
155 }
156 }
157 return false
158 }, nil
159 }
160
161
162
163
164
165
166
167
168 func parseTagFilterRange(filter string) func(int64, string) bool {
169 ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2)
170 if len(ranges) == 0 {
171 return nil
172 }
173 v, err := strconv.ParseInt(ranges[0][1], 10, 64)
174 if err != nil {
175 panic(fmt.Errorf("failed to parse int %s: %v", ranges[0][1], err))
176 }
177 scaledValue, unit := measurement.Scale(v, ranges[0][2], ranges[0][2])
178 if len(ranges) == 1 {
179 switch match := ranges[0][0]; filter {
180 case match:
181 return func(v int64, u string) bool {
182 sv, su := measurement.Scale(v, u, unit)
183 return su == unit && sv == scaledValue
184 }
185 case match + ":":
186 return func(v int64, u string) bool {
187 sv, su := measurement.Scale(v, u, unit)
188 return su == unit && sv >= scaledValue
189 }
190 case ":" + match:
191 return func(v int64, u string) bool {
192 sv, su := measurement.Scale(v, u, unit)
193 return su == unit && sv <= scaledValue
194 }
195 }
196 return nil
197 }
198 if filter != ranges[0][0]+":"+ranges[1][0] {
199 return nil
200 }
201 if v, err = strconv.ParseInt(ranges[1][1], 10, 64); err != nil {
202 panic(fmt.Errorf("failed to parse int %s: %v", ranges[1][1], err))
203 }
204 scaledValue2, unit2 := measurement.Scale(v, ranges[1][2], unit)
205 if unit != unit2 {
206 return nil
207 }
208 return func(v int64, u string) bool {
209 sv, su := measurement.Scale(v, u, unit)
210 return su == unit && sv >= scaledValue && sv <= scaledValue2
211 }
212 }
213
214 func warnNoMatches(match bool, option string, ui plugin.UI) {
215 if !match {
216 ui.PrintErr(option + " expression matched no samples")
217 }
218 }
219
View as plain text