Home | History | Annotate | Download | only in profile
      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Implements methods to filter samples from profiles.
      6 
      7 package profile
      8 
      9 import "regexp"
     10 
     11 // FilterSamplesByName filters the samples in a profile and only keeps
     12 // samples where at least one frame matches focus but none match ignore.
     13 // Returns true is the corresponding regexp matched at least one sample.
     14 func (p *Profile) FilterSamplesByName(focus, ignore, hide *regexp.Regexp) (fm, im, hm bool) {
     15 	focusOrIgnore := make(map[uint64]bool)
     16 	hidden := make(map[uint64]bool)
     17 	for _, l := range p.Location {
     18 		if ignore != nil && l.matchesName(ignore) {
     19 			im = true
     20 			focusOrIgnore[l.ID] = false
     21 		} else if focus == nil || l.matchesName(focus) {
     22 			fm = true
     23 			focusOrIgnore[l.ID] = true
     24 		}
     25 		if hide != nil && l.matchesName(hide) {
     26 			hm = true
     27 			l.Line = l.unmatchedLines(hide)
     28 			if len(l.Line) == 0 {
     29 				hidden[l.ID] = true
     30 			}
     31 		}
     32 	}
     33 
     34 	s := make([]*Sample, 0, len(p.Sample))
     35 	for _, sample := range p.Sample {
     36 		if focusedAndNotIgnored(sample.Location, focusOrIgnore) {
     37 			if len(hidden) > 0 {
     38 				var locs []*Location
     39 				for _, loc := range sample.Location {
     40 					if !hidden[loc.ID] {
     41 						locs = append(locs, loc)
     42 					}
     43 				}
     44 				if len(locs) == 0 {
     45 					// Remove sample with no locations (by not adding it to s).
     46 					continue
     47 				}
     48 				sample.Location = locs
     49 			}
     50 			s = append(s, sample)
     51 		}
     52 	}
     53 	p.Sample = s
     54 
     55 	return
     56 }
     57 
     58 // matchesName returns whether the function name or file in the
     59 // location matches the regular expression.
     60 func (loc *Location) matchesName(re *regexp.Regexp) bool {
     61 	for _, ln := range loc.Line {
     62 		if fn := ln.Function; fn != nil {
     63 			if re.MatchString(fn.Name) {
     64 				return true
     65 			}
     66 			if re.MatchString(fn.Filename) {
     67 				return true
     68 			}
     69 		}
     70 	}
     71 	return false
     72 }
     73 
     74 // unmatchedLines returns the lines in the location that do not match
     75 // the regular expression.
     76 func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line {
     77 	var lines []Line
     78 	for _, ln := range loc.Line {
     79 		if fn := ln.Function; fn != nil {
     80 			if re.MatchString(fn.Name) {
     81 				continue
     82 			}
     83 			if re.MatchString(fn.Filename) {
     84 				continue
     85 			}
     86 		}
     87 		lines = append(lines, ln)
     88 	}
     89 	return lines
     90 }
     91 
     92 // focusedAndNotIgnored looks up a slice of ids against a map of
     93 // focused/ignored locations. The map only contains locations that are
     94 // explicitly focused or ignored. Returns whether there is at least
     95 // one focused location but no ignored locations.
     96 func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool {
     97 	var f bool
     98 	for _, loc := range locs {
     99 		if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore {
    100 			if focus {
    101 				// Found focused location. Must keep searching in case there
    102 				// is an ignored one as well.
    103 				f = true
    104 			} else {
    105 				// Found ignored location. Can return false right away.
    106 				return false
    107 			}
    108 		}
    109 	}
    110 	return f
    111 }
    112 
    113 // TagMatch selects tags for filtering
    114 type TagMatch func(key, val string, nval int64) bool
    115 
    116 // FilterSamplesByTag removes all samples from the profile, except
    117 // those that match focus and do not match the ignore regular
    118 // expression.
    119 func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) {
    120 	samples := make([]*Sample, 0, len(p.Sample))
    121 	for _, s := range p.Sample {
    122 		focused, ignored := focusedSample(s, focus, ignore)
    123 		fm = fm || focused
    124 		im = im || ignored
    125 		if focused && !ignored {
    126 			samples = append(samples, s)
    127 		}
    128 	}
    129 	p.Sample = samples
    130 	return
    131 }
    132 
    133 // focusedTag checks a sample against focus and ignore regexps.
    134 // Returns whether the focus/ignore regexps match any tags
    135 func focusedSample(s *Sample, focus, ignore TagMatch) (fm, im bool) {
    136 	fm = focus == nil
    137 	for key, vals := range s.Label {
    138 		for _, val := range vals {
    139 			if ignore != nil && ignore(key, val, 0) {
    140 				im = true
    141 			}
    142 			if !fm && focus(key, val, 0) {
    143 				fm = true
    144 			}
    145 		}
    146 	}
    147 	for key, vals := range s.NumLabel {
    148 		for _, val := range vals {
    149 			if ignore != nil && ignore(key, "", val) {
    150 				im = true
    151 			}
    152 			if !fm && focus(key, "", val) {
    153 				fm = true
    154 			}
    155 		}
    156 	}
    157 	return fm, im
    158 }
    159