1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
7 from memory_inspector.classification import native_heap_classifier
8 from memory_inspector.core import native_heap
9 from memory_inspector.core import stacktrace
10 from memory_inspector.core import symbol
17 'source_path': r'content/',
21 'stacktrace': r'content::browser',
22 'source_path': r'content/browser',
26 'stacktrace': r'content::renderer',
31 'name': 'ashmem_in_skia',
32 'stacktrace': [r'sk::', r'ashmem::'],
37 _TEST_STACK_TRACES = [
38 (3, [('stack_frame_0::foo()', '/ignored.c'),
39 ('this_goes_under_totals_other', '/ignored.c')]),
40 (5, [('foo', '/ignored.c'),
41 ('content::browser::something()', '/content/browser/something.cc'),
42 ('bar', '/ignored.c')]),
43 (7, [('content::browser::something_else()', '/content/browser/else.cc')]),
44 (11, [('content::browser::not_really()', '/content/subtle/something.cc'),
45 ('foo', '/ignored.c')]),
46 (13, [('foo', '/ignored.c'),
47 ('content::renderer::something()', '/content/renderer/foo.c'),
48 ('bar', '/ignored.c')]),
49 (17, [('content::renderer::something_else()', '/content/renderer/foo.c')]),
50 (19, [('content::renderer::something_else_2()', '/content/renderer/bar.c'),
51 ('foo', '/ignored.c')]),
52 (23, [('content::something_different', '/content/foo.c')]),
53 (29, [('foo', '/ignored.c'),
54 ('sk::something', '/skia/something.c'),
55 ('not_ashsmem_goes_into_totals_other', '/ignored.c')]),
56 (31, [('foo', '/ignored.c'),
57 ('sk::something', '/skia/something.c'),
58 ('foo::bar', '/ignored.c'),
59 ('sk::foo::ashmem::alloc()', '/skia/ashmem.c')]),
60 (37, [('foo', '/ignored.c'),
61 ('sk::something', '/ignored.c'),
62 ('sk::foo::ashmem::alloc()', '/ignored.c')]),
63 (43, [('foo::ashmem::alloc()', '/ignored.c'),
64 ('sk::foo', '/ignored.c'),
65 ('wrong_order_goes_into_totals', '/ignored.c')])
70 'Total::content': [95],
71 'Total::content::browser': [12], # 5 + 7.
72 'Total::content::renderer': [49], # 13 + 17 + 19.
73 'Total::content::content-other': [34],
74 'Total::ashmem_in_skia': [68], # 31 + 37.
75 'Total::Total-other': [75], # 3 + 29 + 43.
78 _HEURISTIC_TEST_STACK_TRACES = [
79 (10, '/root/base1/foo/bar/file.cc'), # Contrib: 0.13
80 (20, '/root/base1/foo/baz/file.cc'), # Contrib: 0.26
81 (1, '/root/base1/foo/nah/file.cc'), # Contrib: 0.01
82 (3, '/root/base2/file.cc'), # Contrib: 0.03
83 (22, '/root/base2/subpath/file.cc'), # Contrib: 0.28
84 (18, '/root/base2/subpath2/file.cc'), # Contrib: 0.23
85 (2, '/root/whatever/file.cc'), # Contrib: 0.02
88 _HEURISTIC_EXPECTED_RESULTS = {
90 'Total::/root/': [76],
91 'Total::/root/::base1/foo/': [31], # 10 + 20 +1
92 'Total::/root/::base1/foo/::bar/': [10],
93 'Total::/root/::base1/foo/::baz/': [20],
94 'Total::/root/::base1/foo/::base1/foo/-other': [1],
95 'Total::/root/::base2/': [43], # 3 + 22 + 18
96 'Total::/root/::base2/::subpath/': [22],
97 'Total::/root/::base2/::subpath2/': [18],
98 'Total::/root/::base2/::base2/-other': [3],
99 'Total::/root/::/root/-other': [2],
100 'Total::Total-other': [0],
104 class NativeHeapClassifierTest(unittest.TestCase):
105 def testStandardRuleParsingAndProcessing(self):
106 rule_tree = native_heap_classifier.LoadRules(_TEST_RULES)
107 nheap = native_heap.NativeHeap()
109 for test_entry in _TEST_STACK_TRACES:
110 mock_strace = stacktrace.Stacktrace()
111 for (mock_btstr, mock_source_path) in test_entry[1]:
112 mock_addr += 4 # Addr is irrelevant, just keep it distinct.
113 mock_frame = stacktrace.Frame(mock_addr)
114 mock_frame.SetSymbolInfo(symbol.Symbol(mock_btstr, mock_source_path))
115 mock_strace.Add(mock_frame)
116 nheap.Add(native_heap.Allocation(
117 size=test_entry[0], count=1, stack_trace=mock_strace))
119 res = native_heap_classifier.Classify(nheap, rule_tree)
120 self._CheckResult(res.total, '', _EXPECTED_RESULTS)
122 def testInferHeuristicRules(self):
123 nheap = native_heap.NativeHeap()
125 for (mock_alloc_size, mock_source_path) in _HEURISTIC_TEST_STACK_TRACES:
126 mock_strace = stacktrace.Stacktrace()
127 mock_addr += 4 # Addr is irrelevant, just keep it distinct.
128 mock_frame = stacktrace.Frame(mock_addr)
129 mock_frame.SetSymbolInfo(symbol.Symbol(str(mock_addr), mock_source_path))
130 for _ in xrange(10): # Just repeat the same stack frame 10 times
131 mock_strace.Add(mock_frame)
132 nheap.Add(native_heap.Allocation(
133 size=mock_alloc_size, count=1, stack_trace=mock_strace))
135 rule_tree = native_heap_classifier.InferHeuristicRulesFromHeap(
136 nheap, threshold=0.05)
137 res = native_heap_classifier.Classify(nheap, rule_tree)
138 self._CheckResult(res.total, '', _HEURISTIC_EXPECTED_RESULTS)
140 def _CheckResult(self, node, prefix, expected_results):
141 node_name = prefix + node.name
142 self.assertIn(node_name, expected_results)
143 self.assertEqual(node.values, expected_results[node_name])
144 for child in node.children:
145 self._CheckResult(child, node_name + '::', expected_results)