native_heap_classifier.py revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Copyright 2014 The Chromium Authors. All rights reserved. 2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# found in the LICENSE file. 4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)"""This module classifies NativeHeap objects filtering their allocations. 6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)The only filter currently available is 'stacktrace', which works as follows: 8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles){'name': 'rule-1', 'stacktrace': 'foo' } 10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles){'name': 'rule-2', 'stacktrace': ['foo', r'bar\s+baz']} 110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{'name': 'rule-3', 'source_path': 'sk.*allocator'} 120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch{'name': 'rule-3', 'source_path': 'sk', 'stacktrace': 'SkAllocator'} 130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)rule-1 will match any allocation that has 'foo' in one of its stack frames. 16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)rule-2 will match any allocation that has a stack frame matching 'foo' AND a 17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)followed by a stack frame matching 'bar baz'. Note that order matters, so rule-2 18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)will not match a stacktrace like ['bar baz', 'foo']. 190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochrule-3 will match any allocation in which at least one of the source paths in 200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochits stack frames matches the regex sk.*allocator. 210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochrule-4 will match any allocation which satisfies both the conditions. 22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)TODO(primiano): introduce more filters after the first prototype with UI, for 240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochinstance, filter by library file name or by allocation size. 25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)""" 26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport collections 280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport posixpath 29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import re 30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.classification import results 32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.classification import rules 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import exceptions 34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import native_heap 35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)_RESULT_KEYS = ['bytes_allocated'] 38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def LoadRules(content): 41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """Loads and parses a native-heap rule tree from a content (string). 42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Returns: 44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) An instance of |rules.Rule|, which nodes are |_NHeapRule| instances. 45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """ 46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return rules.Load(content, _NHeapRule) 47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def Classify(nativeheap, rule_tree): 500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Creates aggregated results of native heaps using the provided rules. 51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Args: 53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) nativeheap: the heap dump being processed (a |NativeHeap| instance). 54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) rule_tree: the user-defined rules that define the filtering categories. 55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) Returns: 57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) An instance of |AggreatedResults|. 58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) """ 59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert(isinstance(nativeheap, native_heap.NativeHeap)) 60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) assert(isinstance(rule_tree, rules.Rule)) 61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) res = results.AggreatedResults(rule_tree, _RESULT_KEYS) 63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for allocation in nativeheap.allocations: 64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) res.AddToMatchingNodes(allocation, [allocation.total_size]) 65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return res 66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochdef InferHeuristicRulesFromHeap(nheap, max_depth=3, threshold=0.02): 690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Infers the rules tree from a symbolized heap snapshot. 700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch In lack of a specific set of rules, this method can be invoked to infer a 720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch meaningful rule tree starting from a heap snapshot. It will build a compact 730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch radix tree from the source paths of the stack traces, which height is at most 740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch |max_depth|, selecting only those nodes which contribute for at least 750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch |threshold| (1.0 = 100%) w.r.t. the total allocation of the heap snapshot. 760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch assert(isinstance(nheap, native_heap.NativeHeap)) 780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def RadixTreeInsert(node, path): 800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Inserts a string (path) into a radix tree (a python recursive dict). 810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch e.g.: [/a/b/c, /a/b/d, /z/h] -> {'/a/b/': {'c': {}, 'd': {}}, '/z/h': {}} 830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def GetCommonPrefix(args): 860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Returns the common prefix between two paths (no partial paths). 870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch e.g.: /tmp/bar, /tmp/baz will return /tmp/ (and not /tmp/ba as the dumb 890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch posixpath.commonprefix implementation would do) 900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch parts = posixpath.commonprefix(args).rpartition(posixpath.sep)[0] 920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return parts + posixpath.sep if parts else '' 930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for node_path in node.iterkeys(): 950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch pfx = GetCommonPrefix([node_path, path]) 960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not pfx: 970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch continue 980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if len(pfx) < len(node_path): 990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch node[pfx] = {node_path[len(pfx):] : node[node_path]} 1000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch del node[node_path] 1010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if len(path) > len(pfx): 1020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch RadixTreeInsert(node[pfx], path[len(pfx):]) 1030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return 1040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch node[path] = {} # No common prefix, create new child in current node. 1050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Given an allocation of size N and its stack trace, heuristically determines 1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # the source directory to be blamed for the N bytes. 1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # The blamed_dir is the one which appears more times in the top 8 stack frames 1090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # (excluding the first 2, which usually are just the (m|c)alloc call sites). 1100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # At the end, this will generate a *leaderboard* (|blamed_dirs|) which 1110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # associates, to each source path directory, the number of bytes allocated. 1120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blamed_dirs = collections.Counter() # '/s/path' : bytes_from_this_path (int) 1140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch total_allocated = 0 1150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for alloc in nheap.allocations: 1160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch dir_histogram = collections.Counter() 1170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for frame in alloc.stack_trace.frames[2:10]: 1180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Compute a histogram (for each allocation) of the top source dirs. 1190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not frame.symbol or not frame.symbol.source_info: 1200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch continue 1210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch src_file = frame.symbol.source_info[0].source_file_path 1220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch src_dir = posixpath.dirname(src_file.replace('\\', '/')) + '/' 1230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch dir_histogram.update([src_dir]) 1240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not dir_histogram: 1250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch continue 1260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Add the blamed dir to the leaderboard. 1270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blamed_dir = dir_histogram.most_common()[0][0] 1280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blamed_dirs.update({blamed_dir : alloc.total_size}) 1290529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch total_allocated += alloc.total_size 1300529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Select only the top paths from the leaderboard which contribute for more 1320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # than |threshold| and make a radix tree out of them. 1330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch radix_tree = {} 1340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for blamed_dir, alloc_size in blamed_dirs.most_common(): 1350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if (1.0 * alloc_size / total_allocated) < threshold: 1360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch break 1370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch RadixTreeInsert(radix_tree, blamed_dir) 1380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # The final step consists in generating a rule tree from the radix tree. This 1400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # is a pretty straightforward tree-clone operation, they have the same shape. 1410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def GenRulesFromRadixTree(radix_tree_node, max_depth, parent_path=''): 1420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch children = [] 1430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if max_depth > 0: 1440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for node_path, node_children in radix_tree_node.iteritems(): 1450529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch child_rule = { 1460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'name': node_path[-16:], 1470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'source_path': '^' + re.escape(parent_path + node_path), 1480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'children': GenRulesFromRadixTree( 1490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch node_children, max_depth - 1, parent_path + node_path)} 1500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch children += [child_rule] 1510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return children 1520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch rules_tree = GenRulesFromRadixTree(radix_tree, max_depth) 1540529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return LoadRules(str(rules_tree)) 1550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 157a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class _NHeapRule(rules.Rule): 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def __init__(self, name, filters): 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) super(_NHeapRule, self).__init__(name) 1600529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # The 'stacktrace' filter can be either a string (simple case, one regex) or 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # a list of strings (complex case, see doc in the header of this file). 1630529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch stacktrace_regexs = filters.get('stacktrace', []) 164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if isinstance(stacktrace_regexs, basestring): 165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) stacktrace_regexs = [stacktrace_regexs] 166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._stacktrace_regexs = [] 167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for regex in stacktrace_regexs: 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try: 169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._stacktrace_regexs.append(re.compile(regex)) 170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) except re.error, descr: 171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) raise exceptions.MemoryInspectorException( 1720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'Stacktrace regex error "%s" : %s' % (regex, descr)) 1730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # The 'source_path' regex, instead, simply matches the source file path. 1750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch self._path_regex = None 1760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch path_regex = filters.get('source_path') 1770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if path_regex: 1780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch try: 1790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch self._path_regex = re.compile(path_regex) 1800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch except re.error, descr: 1810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch raise exceptions.MemoryInspectorException( 1820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'Path regex error "%s" : %s' % (path_regex, descr)) 183a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def Match(self, allocation): 1850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Match the source file path, if the 'source_path' filter is specified. 1860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if self._path_regex: 1870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch path_matches = False 1880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for frame in allocation.stack_trace.frames: 1890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if frame.symbol and frame.symbol.source_info: 1900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if self._path_regex.search( 1910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch frame.symbol.source_info[0].source_file_path): 1920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch path_matches = True 1930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch break 1940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not path_matches: 1950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return False 1960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Match the stack traces symbols, if the 'stacktrace' filter is specified. 198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if not self._stacktrace_regexs: 199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return True 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex_idx = 0 201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex = self._stacktrace_regexs[0] 202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for frame in allocation.stack_trace.frames: 203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if frame.symbol and cur_regex.search(frame.symbol.name): 204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # The current regex has been matched. 205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if cur_regex_idx == len(self._stacktrace_regexs) - 1: 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return True # All the provided regexs have been matched, we're happy. 207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex_idx += 1 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex = self._stacktrace_regexs[cur_regex_idx] 209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return False # Not all the provided regexs have been matched.