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.
4
5import unittest
6
7from memory_inspector.classification import native_heap_classifier
8from memory_inspector.core import native_heap
9from memory_inspector.core import stacktrace
10from memory_inspector.core import symbol
11
12
13_TEST_RULES = """
14[
15{
16  'name': 'content',
17  'source_path': r'content/',
18  'children': [
19    {
20      'name': 'browser',
21      'stacktrace': r'content::browser',
22      'source_path': r'content/browser',
23    },
24    {
25      'name': 'renderer',
26      'stacktrace': r'content::renderer',
27    },
28  ],
29},
30{
31  'name': 'ashmem_in_skia',
32  'stacktrace': [r'sk::', r'ashmem::'],
33},
34]
35"""
36
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')])
66]
67
68_EXPECTED_RESULTS = {
69    'Total':                         [238, 0],
70    'Total::content':                [95, 0],
71    'Total::content::browser':       [12, 0],  # 5 + 7.
72    'Total::content::renderer':      [49, 0],  # 13 + 17 + 19.
73    'Total::content::content-other': [34, 0],
74    'Total::ashmem_in_skia':         [68, 0],  # 31 + 37.
75    'Total::Total-other':            [75, 0],  # 3 + 29 + 43.
76}
77
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
86]
87
88_HEURISTIC_EXPECTED_RESULTS = {
89    'Total':                                        [76, 0],
90    'Total::/root/':                                [76, 0],
91    'Total::/root/::base1/foo/':                    [31, 0],  # 10 + 20 +1
92    'Total::/root/::base1/foo/::bar/':              [10, 0],
93    'Total::/root/::base1/foo/::baz/':              [20, 0],
94    'Total::/root/::base1/foo/::base1/foo/-other':  [1, 0],
95    'Total::/root/::base2/':                        [43, 0],  # 3 + 22 + 18
96    'Total::/root/::base2/::subpath/':              [22, 0],
97    'Total::/root/::base2/::subpath2/':             [18, 0],
98    'Total::/root/::base2/::base2/-other':          [3, 0],
99    'Total::/root/::/root/-other':                  [2, 0],
100    'Total::Total-other':                           [0, 0],
101}
102
103
104class NativeHeapClassifierTest(unittest.TestCase):
105  def testStandardRuleParsingAndProcessing(self):
106    rule_tree = native_heap_classifier.LoadRules(_TEST_RULES)
107    nheap = native_heap.NativeHeap()
108    mock_addr = 0
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], stack_trace=mock_strace))
118
119    res = native_heap_classifier.Classify(nheap, rule_tree)
120    self._CheckResult(res.total, '', _EXPECTED_RESULTS)
121
122  def testInferHeuristicRules(self):
123    nheap = native_heap.NativeHeap()
124    mock_addr = 0
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, stack_trace=mock_strace))
134
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)
139
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)