Source file src/cmd/vendor/github.com/google/pprof/internal/driver/driver_focus.go

     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  
    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  // applyFocus filters samples based on the focus/ignore options
    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  						// TODO: Match against val, not key:val in future
   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  // parseTagFilterRange returns a function to checks if a value is
   162  // contained on the range described by a string. It can recognize
   163  // strings of the form:
   164  // "32kb" -- matches values == 32kb
   165  // ":64kb" -- matches values <= 64kb
   166  // "4mb:" -- matches values >= 4mb
   167  // "12kb:64mb" -- matches values between 12kb and 64mb (both included).
   168  func parseTagFilterRange(filter string) func(int64, string) bool {
   169  	ranges := tagFilterRangeRx.FindAllStringSubmatch(filter, 2)
   170  	if len(ranges) == 0 {
   171  		return nil // No ranges were identified
   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