11eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko# Copyright 2014 The Chromium Authors. All rights reserved. 21eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko# Use of this source code is governed by a BSD-style license that can be 31eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko# found in the LICENSE file. 41eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 51eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenkoimport unittest 61eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 71eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenkofrom memory_inspector.classification import native_heap_classifier 81eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenkofrom memory_inspector.core import native_heap 91eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenkofrom memory_inspector.core import stacktrace 101eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenkofrom memory_inspector.core import symbol 111eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 121eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 131eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko_TEST_RULES = """ 141eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko[ 151eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko{ 161eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 'name': 'content', 171eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 'source_path': r'content/', 181eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 'children': [ 191eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko { 201eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 'name': 'browser', 211eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 'stacktrace': r'content::browser', 221eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko 'source_path': r'content/browser', 231eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko }, 241eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko { 251eb60825f0b858a4568c1a9497cc61b0d90c9b3aDmitri Gribenko '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)