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) 371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci_RESULT_KEYS = ['bytes_allocated', 'bytes_resident'] 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: 641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci res.AddToMatchingNodes(allocation, 651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci [allocation.size, allocation.resident_size]) 66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return res 67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochdef InferHeuristicRulesFromHeap(nheap, max_depth=3, threshold=0.02): 700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Infers the rules tree from a symbolized heap snapshot. 710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch In lack of a specific set of rules, this method can be invoked to infer a 730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch meaningful rule tree starting from a heap snapshot. It will build a compact 740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch radix tree from the source paths of the stack traces, which height is at most 750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch |max_depth|, selecting only those nodes which contribute for at least 760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch |threshold| (1.0 = 100%) w.r.t. the total allocation of the heap snapshot. 770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch assert(isinstance(nheap, native_heap.NativeHeap)) 790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def RadixTreeInsert(node, path): 810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Inserts a string (path) into a radix tree (a python recursive dict). 820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch e.g.: [/a/b/c, /a/b/d, /z/h] -> {'/a/b/': {'c': {}, 'd': {}}, '/z/h': {}} 840529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 850529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def GetCommonPrefix(args): 870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """Returns the common prefix between two paths (no partial paths). 880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch e.g.: /tmp/bar, /tmp/baz will return /tmp/ (and not /tmp/ba as the dumb 900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch posixpath.commonprefix implementation would do) 910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch """ 920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch parts = posixpath.commonprefix(args).rpartition(posixpath.sep)[0] 930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return parts + posixpath.sep if parts else '' 940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for node_path in node.iterkeys(): 960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch pfx = GetCommonPrefix([node_path, path]) 970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not pfx: 980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch continue 990529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if len(pfx) < len(node_path): 1000529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch node[pfx] = {node_path[len(pfx):] : node[node_path]} 1010529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch del node[node_path] 1020529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if len(path) > len(pfx): 1030529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch RadixTreeInsert(node[pfx], path[len(pfx):]) 1040529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return 1050529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch node[path] = {} # No common prefix, create new child in current node. 1060529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1070529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Given an allocation of size N and its stack trace, heuristically determines 1080529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # the source directory to be blamed for the N bytes. 1090529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # The blamed_dir is the one which appears more times in the top 8 stack frames 1100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # (excluding the first 2, which usually are just the (m|c)alloc call sites). 1110529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # At the end, this will generate a *leaderboard* (|blamed_dirs|) which 1120529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # associates, to each source path directory, the number of bytes allocated. 1130529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1140529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blamed_dirs = collections.Counter() # '/s/path' : bytes_from_this_path (int) 1150529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch total_allocated = 0 1160529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for alloc in nheap.allocations: 1170529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch dir_histogram = collections.Counter() 1180529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for frame in alloc.stack_trace.frames[2:10]: 1190529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Compute a histogram (for each allocation) of the top source dirs. 1200529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not frame.symbol or not frame.symbol.source_info: 1210529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch continue 1220529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch src_file = frame.symbol.source_info[0].source_file_path 1230529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch src_dir = posixpath.dirname(src_file.replace('\\', '/')) + '/' 1240529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch dir_histogram.update([src_dir]) 1250529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not dir_histogram: 1260529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch continue 1270529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Add the blamed dir to the leaderboard. 1280529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch blamed_dir = dir_histogram.most_common()[0][0] 1291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci blamed_dirs.update({blamed_dir : alloc.size}) 1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci total_allocated += alloc.size 1310529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1320529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Select only the top paths from the leaderboard which contribute for more 1330529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # than |threshold| and make a radix tree out of them. 1340529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch radix_tree = {} 1350529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for blamed_dir, alloc_size in blamed_dirs.most_common(): 1360529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if (1.0 * alloc_size / total_allocated) < threshold: 1370529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch break 1380529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch RadixTreeInsert(radix_tree, blamed_dir) 1390529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1400529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # The final step consists in generating a rule tree from the radix tree. This 1410529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # is a pretty straightforward tree-clone operation, they have the same shape. 1420529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch def GenRulesFromRadixTree(radix_tree_node, max_depth, parent_path=''): 1430529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch children = [] 1440529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if max_depth > 0: 1450529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for node_path, node_children in radix_tree_node.iteritems(): 1460529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch child_rule = { 1470529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'name': node_path[-16:], 1480529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'source_path': '^' + re.escape(parent_path + node_path), 1490529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'children': GenRulesFromRadixTree( 1500529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch node_children, max_depth - 1, parent_path + node_path)} 1510529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch children += [child_rule] 1520529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return children 1530529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1540529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch rules_tree = GenRulesFromRadixTree(radix_tree, max_depth) 1550529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return LoadRules(str(rules_tree)) 1560529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1570529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 158a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class _NHeapRule(rules.Rule): 159a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def __init__(self, name, filters): 160a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) super(_NHeapRule, self).__init__(name) 1610529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 162a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # The 'stacktrace' filter can be either a string (simple case, one regex) or 163a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # a list of strings (complex case, see doc in the header of this file). 1640529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch stacktrace_regexs = filters.get('stacktrace', []) 165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if isinstance(stacktrace_regexs, basestring): 166a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) stacktrace_regexs = [stacktrace_regexs] 167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._stacktrace_regexs = [] 168a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for regex in stacktrace_regexs: 169a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) try: 170a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) self._stacktrace_regexs.append(re.compile(regex)) 171a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) except re.error, descr: 172a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) raise exceptions.MemoryInspectorException( 1730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'Stacktrace regex error "%s" : %s' % (regex, descr)) 1740529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # The 'source_path' regex, instead, simply matches the source file path. 1760529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch self._path_regex = None 1770529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch path_regex = filters.get('source_path') 1780529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if path_regex: 1790529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch try: 1800529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch self._path_regex = re.compile(path_regex) 1810529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch except re.error, descr: 1820529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch raise exceptions.MemoryInspectorException( 1830529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 'Path regex error "%s" : %s' % (path_regex, descr)) 184a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 185a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) def Match(self, allocation): 1860529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Match the source file path, if the 'source_path' filter is specified. 1870529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if self._path_regex: 1880529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch path_matches = False 1890529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch for frame in allocation.stack_trace.frames: 1900529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if frame.symbol and frame.symbol.source_info: 1910529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if self._path_regex.search( 1920529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch frame.symbol.source_info[0].source_file_path): 1930529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch path_matches = True 1940529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch break 1950529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch if not path_matches: 1960529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch return False 1970529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch 1980529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch # Match the stack traces symbols, if the 'stacktrace' filter is specified. 199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if not self._stacktrace_regexs: 200a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return True 201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex_idx = 0 202a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex = self._stacktrace_regexs[0] 203a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) for frame in allocation.stack_trace.frames: 204a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if frame.symbol and cur_regex.search(frame.symbol.name): 205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) # The current regex has been matched. 206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if cur_regex_idx == len(self._stacktrace_regexs) - 1: 207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return True # All the provided regexs have been matched, we're happy. 208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex_idx += 1 209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) cur_regex = self._stacktrace_regexs[cur_regex_idx] 210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) return False # Not all the provided regexs have been matched.