native_heap_unittest.py revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 5""" 6The test scenario is as follows: 7VA space: |0 |4k |8k |12k |16k |20k ... |64k |65k |66k 8Mmaps: [ anon 1 ][anon 2] [anon 3] ... [ exe1 ][ exe2 ] 9Resident: *******-------*******-------******* (*:resident, -:not resident) 10Allocs: <1> <2> < 3 > 11 | | | 12S.Traces: | | +-----------> st1[exe1 + 0, exe1 + 4] 13 | +--------------------------> st1[exe1 + 0, exe1 + 4] 14 +------------------------------> st2[exe1 + 0, exe2 + 4, post-exe2] 15 16Furthermore, the exe2 is a file mapping with non-zero (8k) offset. 17""" 18 19import unittest 20 21from memory_inspector.core import memory_map 22from memory_inspector.core import native_heap 23from memory_inspector.core import stacktrace 24from memory_inspector.core import symbol 25 26from memory_inspector.core.memory_map import PAGE_SIZE 27 28 29class NativeHeapTest(unittest.TestCase): 30 def runTest(self): 31 nheap = native_heap.NativeHeap() 32 33 EXE_1_MM_BASE = 64 * PAGE_SIZE 34 EXE_2_MM_BASE = 65 * PAGE_SIZE 35 EXE_2_FILE_OFF = 8192 36 st1 = stacktrace.Stacktrace() 37 st1.Add(nheap.GetStackFrame(EXE_1_MM_BASE)) 38 st1.Add(nheap.GetStackFrame(EXE_1_MM_BASE + 4)) 39 40 st2 = stacktrace.Stacktrace() 41 st2.Add(nheap.GetStackFrame(EXE_1_MM_BASE)) 42 st2.Add(nheap.GetStackFrame(EXE_2_MM_BASE + 4)) 43 st2.Add(nheap.GetStackFrame(EXE_2_MM_BASE + PAGE_SIZE + 4)) 44 45 # Check that GetStackFrames keeps one unique object instance per address. 46 # This is to guarantee that the symbolization logic (SymbolizeUsingSymbolDB) 47 # can cheaply iterate on distinct stack frames rather than re-processing 48 # every stack frame for each allocation (and save memory as well). 49 self.assertIs(st1[0], st2[0]) 50 self.assertIsNot(st1[0], st1[1]) 51 self.assertIsNot(st2[0], st2[1]) 52 53 alloc1 = native_heap.Allocation(start=4, size=4, stack_trace=st1) 54 alloc2 = native_heap.Allocation(start=4090, size=8, stack_trace=st1) 55 alloc3 = native_heap.Allocation(start=8190, size=10000, stack_trace=st2) 56 nheap.Add(alloc1) 57 nheap.Add(alloc2) 58 nheap.Add(alloc3) 59 60 self.assertEqual(len(nheap.allocations), 3) 61 self.assertIn(alloc1, nheap.allocations) 62 self.assertIn(alloc2, nheap.allocations) 63 self.assertIn(alloc3, nheap.allocations) 64 65 ############################################################################ 66 # Test the relativization (absolute address -> mmap + offset) logic. 67 ############################################################################ 68 mmap = memory_map 69 mmap = memory_map.Map() 70 mmap.Add(memory_map.MapEntry(EXE_1_MM_BASE, EXE_1_MM_BASE + PAGE_SIZE - 1, 71 'rw--', '/d/exe1', 0)) 72 mmap.Add(memory_map.MapEntry(EXE_2_MM_BASE, EXE_2_MM_BASE + PAGE_SIZE - 1, 73 'rw--', 'exe2',EXE_2_FILE_OFF)) 74 # Entry for EXE_3 is deliberately missing to check the fallback behavior. 75 76 nheap.RelativizeStackFrames(mmap) 77 78 self.assertEqual(st1[0].exec_file_rel_path, '/d/exe1') 79 self.assertEqual(st1[0].exec_file_name, 'exe1') 80 self.assertEqual(st1[0].offset, 0) 81 82 self.assertEqual(st1[1].exec_file_rel_path, '/d/exe1') 83 self.assertEqual(st1[1].exec_file_name, 'exe1') 84 self.assertEqual(st1[1].offset, 4) 85 86 self.assertEqual(st2[0].exec_file_rel_path, '/d/exe1') 87 self.assertEqual(st2[0].exec_file_name, 'exe1') 88 self.assertEqual(st2[0].offset, 0) 89 90 self.assertEqual(st2[1].exec_file_rel_path, 'exe2') 91 self.assertEqual(st2[1].exec_file_name, 'exe2') 92 self.assertEqual(st2[1].offset, 4 + EXE_2_FILE_OFF) 93 94 self.assertIsNone(st2[2].exec_file_rel_path) 95 self.assertIsNone(st2[2].exec_file_name) 96 self.assertIsNone(st2[2].offset) 97 98 ############################################################################ 99 # Test the symbolization logic. 100 ############################################################################ 101 syms = symbol.Symbols() 102 syms.Add('/d/exe1', 0, symbol.Symbol('sym1', 'src1.c', 1)) # st1[0] 103 syms.Add('exe2', 4 + EXE_2_FILE_OFF, symbol.Symbol('sym3')) # st2[1] 104 105 nheap.SymbolizeUsingSymbolDB(syms) 106 self.assertEqual(st1[0].symbol.name, 'sym1') 107 self.assertEqual(st1[0].symbol.source_info[0].source_file_path, 'src1.c') 108 self.assertEqual(st1[0].symbol.source_info[0].line_number, 1) 109 110 # st1[1] should have no symbol info, because we didn't provide any above. 111 self.assertIsNone(st1[1].symbol) 112 113 # st2[0] and st1[0] were the same Frame. Expect identical symbols instances. 114 self.assertIs(st2[0].symbol, st1[0].symbol) 115 116 # st2[1] should have a symbols name, but no source line info. 117 self.assertEqual(st2[1].symbol.name, 'sym3') 118 self.assertEqual(len(st2[1].symbol.source_info), 0) 119 120 # st2[2] should have no sym because we didn't even provide a mmap for exe3. 121 self.assertIsNone(st2[2].symbol) 122 123 ############################################################################ 124 # Test the resident size calculation logic (intersects mmaps and allocs). 125 ############################################################################ 126 mmap.Add( 127 memory_map.MapEntry(0, 8191, 'rw--', '', 0, resident_pages=[1])) 128 mmap.Add( 129 memory_map.MapEntry(8192, 12287, 'rw--', '', 0, resident_pages=[1])) 130 # [12k, 16k] is deliberately missing to check the fallback behavior. 131 mmap.Add( 132 memory_map.MapEntry(16384, 20479, 'rw--', '', 0, resident_pages=[1])) 133 nheap.CalculateResidentSize(mmap) 134 135 # alloc1 [4, 8] is fully resident because it lays in the first resident 4k. 136 self.assertEqual(alloc1.resident_size, 4) 137 138 # alloc2 [4090, 4098] should have only 6 resident bytes ([4090,4096]), but 139 # not the last two, which lay on the second page which is noijt resident. 140 self.assertEqual(alloc2.resident_size, 6) 141 142 # alloc3 [8190, 18190] is split as follows (* = resident): 143 # [8190, 8192]: these 2 bytes are NOT resident, they lay in the 2nd page. 144 # *[8192, 12288]: the 3rd page is resident and is fully covered by alloc3. 145 # [12288, 16384]: the 4th page is fully covered as well, but not resident. 146 # *[16384, 18190]: the 5th page is partially covered and resident. 147 self.assertEqual(alloc3.resident_size, (12288 - 8192) + (18190 - 16384)) 148