You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

271 lines
7.4 KiB

  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package profile
  15. // Implements methods to filter samples from profiles.
  16. import "regexp"
  17. // FilterSamplesByName filters the samples in a profile and only keeps
  18. // samples where at least one frame matches focus but none match ignore.
  19. // Returns true is the corresponding regexp matched at least one sample.
  20. func (p *Profile) FilterSamplesByName(focus, ignore, hide, show *regexp.Regexp) (fm, im, hm, hnm bool) {
  21. focusOrIgnore := make(map[uint64]bool)
  22. hidden := make(map[uint64]bool)
  23. for _, l := range p.Location {
  24. if ignore != nil && l.matchesName(ignore) {
  25. im = true
  26. focusOrIgnore[l.ID] = false
  27. } else if focus == nil || l.matchesName(focus) {
  28. fm = true
  29. focusOrIgnore[l.ID] = true
  30. }
  31. if hide != nil && l.matchesName(hide) {
  32. hm = true
  33. l.Line = l.unmatchedLines(hide)
  34. if len(l.Line) == 0 {
  35. hidden[l.ID] = true
  36. }
  37. }
  38. if show != nil {
  39. l.Line = l.matchedLines(show)
  40. if len(l.Line) == 0 {
  41. hidden[l.ID] = true
  42. } else {
  43. hnm = true
  44. }
  45. }
  46. }
  47. s := make([]*Sample, 0, len(p.Sample))
  48. for _, sample := range p.Sample {
  49. if focusedAndNotIgnored(sample.Location, focusOrIgnore) {
  50. if len(hidden) > 0 {
  51. var locs []*Location
  52. for _, loc := range sample.Location {
  53. if !hidden[loc.ID] {
  54. locs = append(locs, loc)
  55. }
  56. }
  57. if len(locs) == 0 {
  58. // Remove sample with no locations (by not adding it to s).
  59. continue
  60. }
  61. sample.Location = locs
  62. }
  63. s = append(s, sample)
  64. }
  65. }
  66. p.Sample = s
  67. return
  68. }
  69. // ShowFrom drops all stack frames above the highest matching frame and returns
  70. // whether a match was found. If showFrom is nil it returns false and does not
  71. // modify the profile.
  72. //
  73. // Example: consider a sample with frames [A, B, C, B], where A is the root.
  74. // ShowFrom(nil) returns false and has frames [A, B, C, B].
  75. // ShowFrom(A) returns true and has frames [A, B, C, B].
  76. // ShowFrom(B) returns true and has frames [B, C, B].
  77. // ShowFrom(C) returns true and has frames [C, B].
  78. // ShowFrom(D) returns false and drops the sample because no frames remain.
  79. func (p *Profile) ShowFrom(showFrom *regexp.Regexp) (matched bool) {
  80. if showFrom == nil {
  81. return false
  82. }
  83. // showFromLocs stores location IDs that matched ShowFrom.
  84. showFromLocs := make(map[uint64]bool)
  85. // Apply to locations.
  86. for _, loc := range p.Location {
  87. if filterShowFromLocation(loc, showFrom) {
  88. showFromLocs[loc.ID] = true
  89. matched = true
  90. }
  91. }
  92. // For all samples, strip locations after the highest matching one.
  93. s := make([]*Sample, 0, len(p.Sample))
  94. for _, sample := range p.Sample {
  95. for i := len(sample.Location) - 1; i >= 0; i-- {
  96. if showFromLocs[sample.Location[i].ID] {
  97. sample.Location = sample.Location[:i+1]
  98. s = append(s, sample)
  99. break
  100. }
  101. }
  102. }
  103. p.Sample = s
  104. return matched
  105. }
  106. // filterShowFromLocation tests a showFrom regex against a location, removes
  107. // lines after the last match and returns whether a match was found. If the
  108. // mapping is matched, then all lines are kept.
  109. func filterShowFromLocation(loc *Location, showFrom *regexp.Regexp) bool {
  110. if m := loc.Mapping; m != nil && showFrom.MatchString(m.File) {
  111. return true
  112. }
  113. if i := loc.lastMatchedLineIndex(showFrom); i >= 0 {
  114. loc.Line = loc.Line[:i+1]
  115. return true
  116. }
  117. return false
  118. }
  119. // lastMatchedLineIndex returns the index of the last line that matches a regex,
  120. // or -1 if no match is found.
  121. func (loc *Location) lastMatchedLineIndex(re *regexp.Regexp) int {
  122. for i := len(loc.Line) - 1; i >= 0; i-- {
  123. if fn := loc.Line[i].Function; fn != nil {
  124. if re.MatchString(fn.Name) || re.MatchString(fn.Filename) {
  125. return i
  126. }
  127. }
  128. }
  129. return -1
  130. }
  131. // FilterTagsByName filters the tags in a profile and only keeps
  132. // tags that match show and not hide.
  133. func (p *Profile) FilterTagsByName(show, hide *regexp.Regexp) (sm, hm bool) {
  134. matchRemove := func(name string) bool {
  135. matchShow := show == nil || show.MatchString(name)
  136. matchHide := hide != nil && hide.MatchString(name)
  137. if matchShow {
  138. sm = true
  139. }
  140. if matchHide {
  141. hm = true
  142. }
  143. return !matchShow || matchHide
  144. }
  145. for _, s := range p.Sample {
  146. for lab := range s.Label {
  147. if matchRemove(lab) {
  148. delete(s.Label, lab)
  149. }
  150. }
  151. for lab := range s.NumLabel {
  152. if matchRemove(lab) {
  153. delete(s.NumLabel, lab)
  154. }
  155. }
  156. }
  157. return
  158. }
  159. // matchesName returns whether the location matches the regular
  160. // expression. It checks any available function names, file names, and
  161. // mapping object filename.
  162. func (loc *Location) matchesName(re *regexp.Regexp) bool {
  163. for _, ln := range loc.Line {
  164. if fn := ln.Function; fn != nil {
  165. if re.MatchString(fn.Name) || re.MatchString(fn.Filename) {
  166. return true
  167. }
  168. }
  169. }
  170. if m := loc.Mapping; m != nil && re.MatchString(m.File) {
  171. return true
  172. }
  173. return false
  174. }
  175. // unmatchedLines returns the lines in the location that do not match
  176. // the regular expression.
  177. func (loc *Location) unmatchedLines(re *regexp.Regexp) []Line {
  178. if m := loc.Mapping; m != nil && re.MatchString(m.File) {
  179. return nil
  180. }
  181. var lines []Line
  182. for _, ln := range loc.Line {
  183. if fn := ln.Function; fn != nil {
  184. if re.MatchString(fn.Name) || re.MatchString(fn.Filename) {
  185. continue
  186. }
  187. }
  188. lines = append(lines, ln)
  189. }
  190. return lines
  191. }
  192. // matchedLines returns the lines in the location that match
  193. // the regular expression.
  194. func (loc *Location) matchedLines(re *regexp.Regexp) []Line {
  195. if m := loc.Mapping; m != nil && re.MatchString(m.File) {
  196. return loc.Line
  197. }
  198. var lines []Line
  199. for _, ln := range loc.Line {
  200. if fn := ln.Function; fn != nil {
  201. if !re.MatchString(fn.Name) && !re.MatchString(fn.Filename) {
  202. continue
  203. }
  204. }
  205. lines = append(lines, ln)
  206. }
  207. return lines
  208. }
  209. // focusedAndNotIgnored looks up a slice of ids against a map of
  210. // focused/ignored locations. The map only contains locations that are
  211. // explicitly focused or ignored. Returns whether there is at least
  212. // one focused location but no ignored locations.
  213. func focusedAndNotIgnored(locs []*Location, m map[uint64]bool) bool {
  214. var f bool
  215. for _, loc := range locs {
  216. if focus, focusOrIgnore := m[loc.ID]; focusOrIgnore {
  217. if focus {
  218. // Found focused location. Must keep searching in case there
  219. // is an ignored one as well.
  220. f = true
  221. } else {
  222. // Found ignored location. Can return false right away.
  223. return false
  224. }
  225. }
  226. }
  227. return f
  228. }
  229. // TagMatch selects tags for filtering
  230. type TagMatch func(s *Sample) bool
  231. // FilterSamplesByTag removes all samples from the profile, except
  232. // those that match focus and do not match the ignore regular
  233. // expression.
  234. func (p *Profile) FilterSamplesByTag(focus, ignore TagMatch) (fm, im bool) {
  235. samples := make([]*Sample, 0, len(p.Sample))
  236. for _, s := range p.Sample {
  237. focused, ignored := true, false
  238. if focus != nil {
  239. focused = focus(s)
  240. }
  241. if ignore != nil {
  242. ignored = ignore(s)
  243. }
  244. fm = fm || focused
  245. im = im || ignored
  246. if focused && !ignored {
  247. samples = append(samples, s)
  248. }
  249. }
  250. p.Sample = samples
  251. return
  252. }