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.