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