github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/profile/filter.go (about) 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 reports 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 // focusedSample 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 }