| #!/usr/bin/env python |
| # |
| # Copyright (C) 2018 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Tests for the native_heapdump_viewer script.""" |
| |
| import native_heapdump_viewer |
| import os |
| import sys |
| import tempfile |
| import unittest |
| |
| class NativeHeapdumpViewerTest(unittest.TestCase): |
| _tmp_file_name = None |
| |
| def CreateTmpFile(self, contents): |
| fd, self._tmp_file_name = tempfile.mkstemp() |
| os.write(fd, contents.encode()) |
| os.close(fd) |
| return self._tmp_file_name |
| |
| def tearDown(self): |
| if self._tmp_file_name: |
| try: |
| os.unlink(self._tmp_file_name) |
| except Exception: |
| print("Failed to delete %s" % (heap)) |
| |
| class GetNumFieldValidTest(NativeHeapdumpViewerTest): |
| _map_data = """ |
| MAPS |
| 1000-10000 r-xp 00000000 fd:00 495 /data/does_not_exist.so |
| END |
| """ |
| |
| _heap_num_field_valid_version10 = """ |
| Android Native Heap Dump v1.0 |
| |
| Total memory: 33800 |
| Allocation records: 13 |
| Backtrace size: 16 |
| |
| z 1 sz 1000 num 4 bt 1000 2000 3000 |
| z 1 sz 2000 num 6 bt 1100 2100 3100 |
| z 0 sz 1200 num 1 bt 1200 2200 3200 |
| z 0 sz 8300 num 2 bt 1300 2300 3300 |
| """ |
| |
| _heap_num_field_invalid_version10 = """ |
| Android Native Heap Dump v1.0 |
| |
| Total memory: 12500 |
| Allocation records: 4 |
| Backtrace size: 16 |
| |
| z 1 sz 1000 num 16 bt 1000 2000 3000 |
| z 1 sz 2000 num 16 bt 1100 2100 3100 |
| z 0 sz 1200 num 16 bt 1200 2200 3200 |
| z 0 sz 8300 num 16 bt 1300 2300 3300 |
| """ |
| |
| _heap_data = """ |
| |
| Total memory: 200000 |
| Allocation records: 64 |
| Backtrace size: 16 |
| |
| z 1 sz 1000 num 16 bt 1000 2000 3000 |
| z 1 sz 2000 num 16 bt 1100 2100 3100 |
| z 0 sz 1200 num 16 bt 1200 2200 3200 |
| z 0 sz 8300 num 16 bt 1300 2300 3300 |
| """ |
| |
| def test_version10_valid(self): |
| heap = self.CreateTmpFile(self._heap_num_field_valid_version10 + self._map_data) |
| self.assertTrue(native_heapdump_viewer.GetNumFieldValid(heap)) |
| |
| def test_version10_invalid(self): |
| heap = self.CreateTmpFile(self._heap_num_field_invalid_version10 + self._map_data) |
| self.assertFalse(native_heapdump_viewer.GetNumFieldValid(heap)) |
| |
| def test_version11_valid(self): |
| heap = self.CreateTmpFile("Android Native Heap Dump v1.1" + self._heap_data + self._map_data) |
| self.assertTrue(native_heapdump_viewer.GetNumFieldValid(heap)) |
| |
| def test_version12_valid(self): |
| heap = self.CreateTmpFile("Android Native Heap Dump v1.2" + self._heap_data + self._map_data) |
| self.assertTrue(native_heapdump_viewer.GetNumFieldValid(heap)) |
| |
| class ParseNativeHeapTest(NativeHeapdumpViewerTest): |
| _backtrace_data = """ |
| z 1 sz 1000 num 4 bt 1000 2000 3000 |
| z 0 sz 8300 num 5 bt 1300 2300 3300 |
| """ |
| |
| |
| def test_backtrace_num_field_valid(self): |
| heap = self.CreateTmpFile(self._backtrace_data) |
| backtraces, mapppings = native_heapdump_viewer.ParseNativeHeap(heap, False, True, "") |
| self.assertTrue(backtraces) |
| self.assertEqual(2, len(backtraces)) |
| |
| self.assertFalse(backtraces[0].is_zygote) |
| self.assertEqual(1000, backtraces[0].size) |
| self.assertEqual(4, backtraces[0].num_allocs) |
| self.assertEqual([0x1000, 0x2000, 0x3000], backtraces[0].frames) |
| |
| self.assertTrue(backtraces[1].is_zygote) |
| self.assertEqual(8300, backtraces[1].size) |
| self.assertEqual(5, backtraces[1].num_allocs) |
| self.assertEqual([0x1300, 0x2300, 0x3300], backtraces[1].frames) |
| |
| def test_backtrace_num_field_invalid(self): |
| heap = self.CreateTmpFile(self._backtrace_data) |
| backtraces, mapppings = native_heapdump_viewer.ParseNativeHeap(heap, False, False, "") |
| self.assertTrue(backtraces) |
| self.assertEqual(2, len(backtraces)) |
| |
| self.assertFalse(backtraces[0].is_zygote) |
| self.assertEqual(1000, backtraces[0].size) |
| self.assertEqual(1, backtraces[0].num_allocs) |
| self.assertEqual([0x1000, 0x2000, 0x3000], backtraces[0].frames) |
| |
| self.assertTrue(backtraces[1].is_zygote) |
| self.assertEqual(8300, backtraces[1].size) |
| self.assertEqual(1, backtraces[1].num_allocs) |
| self.assertEqual([0x1300, 0x2300, 0x3300], backtraces[1].frames) |
| |
| def test_backtrace_reverse_field_valid(self): |
| heap = self.CreateTmpFile(self._backtrace_data) |
| backtraces, mapppings = native_heapdump_viewer.ParseNativeHeap(heap, True, True, "") |
| self.assertTrue(backtraces) |
| self.assertEqual(2, len(backtraces)) |
| |
| self.assertFalse(backtraces[0].is_zygote) |
| self.assertEqual(1000, backtraces[0].size) |
| self.assertEqual(4, backtraces[0].num_allocs) |
| self.assertEqual([0x3000, 0x2000, 0x1000], backtraces[0].frames) |
| |
| self.assertTrue(backtraces[1].is_zygote) |
| self.assertEqual(8300, backtraces[1].size) |
| self.assertEqual(5, backtraces[1].num_allocs) |
| self.assertEqual([0x3300, 0x2300, 0x1300], backtraces[1].frames) |
| |
| def test_mappings(self): |
| map_data = """ |
| MAPS |
| 1000-4000 r-xp 00000000 fd:00 495 /system/lib64/libc.so |
| 6000-8000 r-xp 00000000 fd:00 495 |
| a000-f000 r-xp 0000b000 fd:00 495 /system/lib64/libutils.so |
| END |
| """ |
| |
| heap = self.CreateTmpFile(map_data) |
| backtraces, mappings = native_heapdump_viewer.ParseNativeHeap(heap, True, True, "") |
| |
| self.assertTrue(mappings) |
| self.assertEqual(2, len(mappings)) |
| |
| self.assertEqual(0x1000, mappings[0].start) |
| self.assertEqual(0x4000, mappings[0].end) |
| self.assertEqual(0, mappings[0].offset) |
| self.assertEqual("/system/lib64/libc.so", mappings[0].name) |
| |
| self.assertEqual(0xa000, mappings[1].start) |
| self.assertEqual(0xf000, mappings[1].end) |
| self.assertEqual(0xb000, mappings[1].offset) |
| self.assertEqual("/system/lib64/libutils.so", mappings[1].name) |
| |
| if __name__ == '__main__': |
| unittest.main(verbosity=2) |