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  	"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  
    28  var tagFilterRangeRx = regexp.MustCompile("([+-]?[[:digit:]]+)([[:alpha:]]+)?")
    29  
    30  // applyFocus filters samples based on the focus/ignore options
    31  func 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  
    69  func 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  
    80  func 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).
   169  func 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  
   215  func warnNoMatches(match bool, option string, ui plugin.UI) {
   216  	if !match {
   217  		ui.PrintErr(option + " expression matched no samples")
   218  	}
   219  }
   220  

View as plain text