#!/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)