| // 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 | |
| } | |
| // focusedTag 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 | |
| } |