• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2014 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package driver
16
17import (
18	"fmt"
19	"regexp"
20	"strconv"
21	"strings"
22
23	"github.com/google/pprof/internal/measurement"
24	"github.com/google/pprof/internal/plugin"
25	"github.com/google/pprof/profile"
26)
27
28var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)?")
29
30// applyFocus filters samples based on the focus/ignore options
31func applyFocus(prof *profile.Profile, numLabelUnits map[string]string, cfg config, ui plugin.UI) error {
32	focus, err := compileRegexOption("focus", cfg.Focus, nil)
33	ignore, err := compileRegexOption("ignore", cfg.Ignore, err)
34	hide, err := compileRegexOption("hide", cfg.Hide, err)
35	show, err := compileRegexOption("show", cfg.Show, err)
36	showfrom, err := compileRegexOption("show_from", cfg.ShowFrom, err)
37	tagfocus, err := compileTagFilter("tagfocus", cfg.TagFocus, numLabelUnits, ui, err)
38	tagignore, err := compileTagFilter("tagignore", cfg.TagIgnore, numLabelUnits, ui, err)
39	prunefrom, err := compileRegexOption("prune_from", cfg.PruneFrom, err)
40	if err != nil {
41		return err
42	}
43
44	fm, im, hm, hnm := prof.FilterSamplesByName(focus, ignore, hide, show)
45	warnNoMatches(focus == nil || fm, "Focus", ui)
46	warnNoMatches(ignore == nil || im, "Ignore", ui)
47	warnNoMatches(hide == nil || hm, "Hide", ui)
48	warnNoMatches(show == nil || hnm, "Show", ui)
49
50	sfm := prof.ShowFrom(showfrom)
51	warnNoMatches(showfrom == nil || sfm, "ShowFrom", ui)
52
53	tfm, tim := prof.FilterSamplesByTag(tagfocus, tagignore)
54	warnNoMatches(tagfocus == nil || tfm, "TagFocus", ui)
55	warnNoMatches(tagignore == nil || tim, "TagIgnore", ui)
56
57	tagshow, err := compileRegexOption("tagshow", cfg.TagShow, err)
58	taghide, err := compileRegexOption("taghide", cfg.TagHide, err)
59	tns, tnh := prof.FilterTagsByName(tagshow, taghide)
60	warnNoMatches(tagshow == nil || tns, "TagShow", ui)
61	warnNoMatches(taghide == nil || tnh, "TagHide", ui)
62
63	if prunefrom != nil {
64		prof.PruneFrom(prunefrom)
65	}
66	return err
67}
68
69func compileRegexOption(name, value string, err error) (*regexp.Regexp, error) {
70	if value == "" || err != nil {
71		return nil, err
72	}
73	rx, err := regexp.Compile(value)
74	if err != nil {
75		return nil, fmt.Errorf("parsing %s regexp: %v", name, err)
76	}
77	return rx, nil
78}
79
80func compileTagFilter(name, value string, numLabelUnits map[string]string, ui plugin.UI, err error) (func(*profile.Sample) bool, error) {
81	if value == "" || err != nil {
82		return nil, err
83	}
84
85	tagValuePair := strings.SplitN(value, "=", 2)
86	var wantKey string
87	if len(tagValuePair) == 2 {
88		wantKey = tagValuePair[0]
89		value = tagValuePair[1]
90	}
91
92	if numFilter := parseTagFilterRange(value); numFilter != nil {
93		ui.PrintErr(name, ":Interpreted '", value, "' as range, not regexp")
94		labelFilter := func(vals []int64, unit string) bool {
95			for _, val := range vals {
96				if numFilter(val, unit) {
97					return true
98				}
99			}
100			return false
101		}
102		numLabelUnit := func(key string) string {
103			return numLabelUnits[key]
104		}
105		if wantKey == "" {
106			return func(s *profile.Sample) bool {
107				for key, vals := range s.NumLabel {
108					if labelFilter(vals, numLabelUnit(key)) {
109						return true
110					}
111				}
112				return false
113			}, nil
114		}
115		return func(s *profile.Sample) bool {
116			if vals, ok := s.NumLabel[wantKey]; ok {
117				return labelFilter(vals, numLabelUnit(wantKey))
118			}
119			return false
120		}, nil
121	}
122
123	var rfx []*regexp.Regexp
124	for _, tagf := range strings.Split(value, ",") {
125		fx, err := regexp.Compile(tagf)
126		if err != nil {
127			return nil, fmt.Errorf("parsing %s regexp: %v", name, err)
128		}
129		rfx = append(rfx, fx)
130	}
131	if wantKey == "" {
132		return func(s *profile.Sample) bool {
133		matchedrx:
134			for _, rx := range rfx {
135				for key, vals := range s.Label {
136					for _, val := range vals {
137						// TODO: Match against val, not key:val in future
138						if rx.MatchString(key + ":" + val) {
139							continue matchedrx
140						}
141					}
142				}
143				return false
144			}
145			return true
146		}, nil
147	}
148	return func(s *profile.Sample) bool {
149		if vals, ok := s.Label[wantKey]; ok {
150			for _, rx := range rfx {
151				for _, val := range vals {
152					if rx.MatchString(val) {
153						return true
154					}
155				}
156			}
157		}
158		return false
159	}, nil
160}
161
162// parseTagFilterRange returns a function to checks if a value is
163// contained on the range described by a string. It can recognize
164// strings of the form:
165// "32kb" -- matches values == 32kb
166// ":64kb" -- matches values <= 64kb
167// "4mb:" -- matches values >= 4mb
168// "12kb:64mb" -- matches values between 12kb and 64mb (both included).
169func parseTagFilterRange(filter string) func(int64, string) bool {
170	ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2)
171	if len(ranges) == 0 {
172		return nil // No ranges were identified
173	}
174	v, err := strconv.ParseInt(ranges[0][1], 10, 64)
175	if err != nil {
176		panic(fmt.Errorf("failed to parse int %s: %v", ranges[0][1], err))
177	}
178	scaledValue, unit := measurement.Scale(v, ranges[0][2], ranges[0][2])
179	if len(ranges) == 1 {
180		switch match := ranges[0][0]; filter {
181		case match:
182			return func(v int64, u string) bool {
183				sv, su := measurement.Scale(v, u, unit)
184				return su == unit && sv == scaledValue
185			}
186		case match + ":":
187			return func(v int64, u string) bool {
188				sv, su := measurement.Scale(v, u, unit)
189				return su == unit && sv >= scaledValue
190			}
191		case ":" + match:
192			return func(v int64, u string) bool {
193				sv, su := measurement.Scale(v, u, unit)
194				return su == unit && sv <= scaledValue
195			}
196		}
197		return nil
198	}
199	if filter != ranges[0][0]+":"+ranges[1][0] {
200		return nil
201	}
202	if v, err = strconv.ParseInt(ranges[1][1], 10, 64); err != nil {
203		panic(fmt.Errorf("failed to parse int %s: %v", ranges[1][1], err))
204	}
205	scaledValue2, unit2 := measurement.Scale(v, ranges[1][2], unit)
206	if unit != unit2 {
207		return nil
208	}
209	return func(v int64, u string) bool {
210		sv, su := measurement.Scale(v, u, unit)
211		return su == unit && sv >= scaledValue && sv <= scaledValue2
212	}
213}
214
215func warnNoMatches(match bool, option string, ui plugin.UI) {
216	if !match {
217		ui.PrintErr(option + " expression matched no samples")
218	}
219}
220