| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Implements methods to filter samples from profiles. |
| |
| package profile |
| |
| import "regexp" |
| |
| // FilterSamplesByName filters the samples in a profile and only keeps |
| // samples where at least one frame matches focus but none match ignore. |
| // Returns true is the corresponding regexp matched at least one sample. |
| func (p *Profile) FilterSamplesByName(focus, ignore, hide *regexp.Regexp) (fm, im, hm bool) { |
| focusOrIgnore := make(map[uint64]bool) |
| hidden := make(map[uint64]bool) |
| for _, l := range p.Location { |
| if ignore != nil && l.matchesName(ignore) { |
| im = true |
| focusOrIgnore[l.ID] = false |
| } else if focus == nil || l.matchesName(focus) { |
| fm = true |
| focusOrIgnore[l.ID] = true |
| } |
| if hide != nil && l.matchesName(hide) { |
| hm = true |
| l.Line = l.unmatchedLines(hide) |
| if len(l.Line) == 0 { |
| hidden[l.ID] = true |
| } |
| } |
| } |
| |
| s := make([]*Sample, 0, len(p.Sample)) |
| for _, sample := range p.Sample { |
| if focusedAndNotIgnored(sample.Location, focusOrIgnore) { |
| if len(hidden) > 0 { |
| var locs []*Location |
| for _, loc := range sample.Location { |
| if !hidden[loc.ID] { |
| locs = append(locs, loc) |
| } |
| } |
| if len(locs) == 0 { |
| // Remove sample with no locations (by not adding it to s). |
| continue |
| } |
| sample.Location = locs |
| } |
| s = append(s, sample) |
| } |
| } |
| p.Sample = s |
| |
| return |
| } |
| |
| // matchesName reports whether the function name or file in the |
| // location matches the regular expression. |
| func (loc *Location) matchesName(re *regexp.Regexp) bool { |
| for _, ln := range loc.Line { |
| if fn := ln.Function; fn != nil { |
| if re.MatchString(fn.Name) { |
| return true |
| } |
| if re.MatchString(fn.Filename) { |
| return true |
| } |
| } |
| } |
| return false |
| } |
| |
| // unmatchedLines returns the lines in the location that do not match |
| // the regular expression. |
| func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line { |
| var lines []Line |
| for _, ln := range loc.Line { |
| if fn := ln.Function; fn != nil { |
| if re.MatchString(fn.Name) { |
| continue |
| } |
| if re.MatchString(fn.Filename) { |
| continue |
| } |
| } |
| lines = append(lines, ln) |
| } |
| return lines |
| } |
| |
| // focusedAndNotIgnored looks up a slice of ids against a map of |
| // focused/ignored locations. The map only contains locations that are |
| // explicitly focused or ignored. Returns whether there is at least |
| // one focused location but no ignored locations. |
| func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool { |
| var f bool |
| for _, loc := range locs { |
| if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore { |
| if focus { |
| // Found focused location. Must keep searching in case there |
| // is an ignored one as well. |
| f = true |
| } else { |
| // Found ignored location. Can return false right away. |
| return false |
| } |
| } |
| } |
| return f |
| } |
| |
| // TagMatch selects tags for filtering |
| type TagMatch func(key, val string, nval int64) bool |
| |
| // FilterSamplesByTag removes all samples from the profile, except |
| // those that match focus and do not match the ignore regular |
| // expression. |
| func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) { |
| samples := make([]*Sample, 0, len(p.Sample)) |
| for _, s := range p.Sample { |
| focused, ignored := focusedSample(s, focus, ignore) |
| fm = fm || focused |
| im = im || ignored |
| if focused && !ignored { |
| samples = append(samples, s) |
| } |
| } |
| p.Sample = samples |
| return |
| } |
| |
| // focusedSample checks a sample against focus and ignore regexps. |
| // Returns whether the focus/ignore regexps match any tags. |
| func focusedSample(s *Sample, focus, ignore TagMatch) (fm, im bool) { |
| fm = focus == nil |
| for key, vals := range s.Label { |
| for _, val := range vals { |
| if ignore != nil && ignore(key, val, 0) { |
| im = true |
| } |
| if !fm && focus(key, val, 0) { |
| fm = true |
| } |
| } |
| } |
| for key, vals := range s.NumLabel { |
| for _, val := range vals { |
| if ignore != nil && ignore(key, "", val) { |
| im = true |
| } |
| if !fm && focus(key, "", val) { |
| fm = true |
| } |
| } |
| } |
| return fm, im |
| } |