158 lines
4 KiB
Go
158 lines
4 KiB
Go
// 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
|
|
}
|