native_heap_classifier.py revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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']}
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)rule-1 will match any allocation that has 'foo' in one of its  stack frames.
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)rule-2 will match any allocation that has a stack frame matching 'foo' AND a
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)followed by a stack frame matching 'bar baz'. Note that order matters, so rule-2
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)will not match a stacktrace like ['bar baz', 'foo'].
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)TODO(primiano): introduce more filters after the first prototype with UI, for
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)instance, filter by source file path, library file name or by allocation size.
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)"""
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import re
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.classification import results
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.classification import rules
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import exceptions
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)from memory_inspector.core import native_heap
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)_RESULT_KEYS = ['bytes_allocated']
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def LoadRules(content):
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """Loads and parses a native-heap rule tree from a content (string).
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Returns:
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    An instance of |rules.Rule|, which nodes are |_NHeapRule| instances.
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return rules.Load(content, _NHeapRule)
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)def Classify(nativeheap, rule_tree):
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """Create aggregated results of native heaps using the provided rules.
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Args:
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    nativeheap: the heap dump being processed (a |NativeHeap| instance).
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    rule_tree: the user-defined rules that define the filtering categories.
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  Returns:
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    An instance of |AggreatedResults|.
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  """
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  assert(isinstance(nativeheap, native_heap.NativeHeap))
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  assert(isinstance(rule_tree, rules.Rule))
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  res = results.AggreatedResults(rule_tree, _RESULT_KEYS)
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  for allocation in nativeheap.allocations:
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    res.AddToMatchingNodes(allocation, [allocation.total_size])
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return res
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)class _NHeapRule(rules.Rule):
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def __init__(self, name, filters):
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    super(_NHeapRule, self).__init__(name)
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    stacktrace_regexs = filters.get('stacktrace', [])
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # The 'stacktrace' filter can be either a string (simple case, one regex) or
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    # a list of strings (complex case, see doc in the header of this file).
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(stacktrace_regexs, basestring):
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      stacktrace_regexs = [stacktrace_regexs]
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    self._stacktrace_regexs = []
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for regex in stacktrace_regexs:
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      try:
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        self._stacktrace_regexs.append(re.compile(regex))
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      except re.error, descr:
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        raise exceptions.MemoryInspectorException(
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            'Regex parse error "%s" : %s' % (regex, descr))
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  def Match(self, allocation):
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if not self._stacktrace_regexs:
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return True
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    cur_regex_idx = 0
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    cur_regex = self._stacktrace_regexs[0]
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    for frame in allocation.stack_trace.frames:
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      if frame.symbol and cur_regex.search(frame.symbol.name):
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        # The current regex has been matched.
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if cur_regex_idx == len(self._stacktrace_regexs) - 1:
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          return True  # All the provided regexs have been matched, we're happy.
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        cur_regex_idx += 1
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        cur_regex = self._stacktrace_regexs[cur_regex_idx]
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return False  # Not all the provided regexs have been matched.