1#!/usr/bin/env python
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Tests for the native_heapdump_viewer script."""
18
19import native_heapdump_viewer
20import os
21import sys
22import tempfile
23import unittest
24
25class NativeHeapdumpViewerTest(unittest.TestCase):
26  _tmp_file_name = None
27
28  def CreateTmpFile(self, contents):
29    fd, self._tmp_file_name = tempfile.mkstemp()
30    os.write(fd, contents.encode())
31    os.close(fd)
32    return self._tmp_file_name
33
34  def tearDown(self):
35    if self._tmp_file_name:
36      try:
37        os.unlink(self._tmp_file_name)
38      except Exception:
39        print("Failed to delete %s" % (heap))
40
41class GetNumFieldValidTest(NativeHeapdumpViewerTest):
42  _map_data = """
43MAPS
441000-10000 r-xp 00000000 fd:00 495                            /data/does_not_exist.so
45END
46"""
47
48  _heap_num_field_valid_version10 = """
49Android Native Heap Dump v1.0
50
51Total memory: 33800
52Allocation records: 13
53Backtrace size: 16
54
55z 1  sz   1000  num    4  bt 1000 2000 3000
56z 1  sz   2000  num    6  bt 1100 2100 3100
57z 0  sz   1200  num    1  bt 1200 2200 3200
58z 0  sz   8300  num    2  bt 1300 2300 3300
59"""
60
61  _heap_num_field_invalid_version10 = """
62Android Native Heap Dump v1.0
63
64Total memory: 12500
65Allocation records: 4
66Backtrace size: 16
67
68z 1  sz   1000  num    16  bt 1000 2000 3000
69z 1  sz   2000  num    16  bt 1100 2100 3100
70z 0  sz   1200  num    16  bt 1200 2200 3200
71z 0  sz   8300  num    16  bt 1300 2300 3300
72"""
73
74  _heap_data = """
75
76Total memory: 200000
77Allocation records: 64
78Backtrace size: 16
79
80z 1  sz   1000  num    16  bt 1000 2000 3000
81z 1  sz   2000  num    16  bt 1100 2100 3100
82z 0  sz   1200  num    16  bt 1200 2200 3200
83z 0  sz   8300  num    16  bt 1300 2300 3300
84"""
85
86  def test_version10_valid(self):
87    heap = self.CreateTmpFile(self._heap_num_field_valid_version10 + self._map_data)
88    self.assertTrue(native_heapdump_viewer.GetNumFieldValid(heap))
89
90  def test_version10_invalid(self):
91    heap = self.CreateTmpFile(self._heap_num_field_invalid_version10 + self._map_data)
92    self.assertFalse(native_heapdump_viewer.GetNumFieldValid(heap))
93
94  def test_version11_valid(self):
95    heap = self.CreateTmpFile("Android Native Heap Dump v1.1" + self._heap_data + self._map_data)
96    self.assertTrue(native_heapdump_viewer.GetNumFieldValid(heap))
97
98  def test_version12_valid(self):
99    heap = self.CreateTmpFile("Android Native Heap Dump v1.2" + self._heap_data + self._map_data)
100    self.assertTrue(native_heapdump_viewer.GetNumFieldValid(heap))
101
102class ParseNativeHeapTest(NativeHeapdumpViewerTest):
103  _backtrace_data = """
104z 1  sz   1000  num    4  bt 1000 2000 3000
105z 0  sz   8300  num    5  bt 1300 2300 3300
106"""
107
108
109  def test_backtrace_num_field_valid(self):
110    heap = self.CreateTmpFile(self._backtrace_data)
111    backtraces, mapppings = native_heapdump_viewer.ParseNativeHeap(heap, False, True, "")
112    self.assertTrue(backtraces)
113    self.assertEqual(2, len(backtraces))
114
115    self.assertFalse(backtraces[0].is_zygote)
116    self.assertEqual(1000, backtraces[0].size)
117    self.assertEqual(4, backtraces[0].num_allocs)
118    self.assertEqual([0x1000, 0x2000, 0x3000], backtraces[0].frames)
119
120    self.assertTrue(backtraces[1].is_zygote)
121    self.assertEqual(8300, backtraces[1].size)
122    self.assertEqual(5, backtraces[1].num_allocs)
123    self.assertEqual([0x1300, 0x2300, 0x3300], backtraces[1].frames)
124
125  def test_backtrace_num_field_invalid(self):
126    heap = self.CreateTmpFile(self._backtrace_data)
127    backtraces, mapppings = native_heapdump_viewer.ParseNativeHeap(heap, False, False, "")
128    self.assertTrue(backtraces)
129    self.assertEqual(2, len(backtraces))
130
131    self.assertFalse(backtraces[0].is_zygote)
132    self.assertEqual(1000, backtraces[0].size)
133    self.assertEqual(1, backtraces[0].num_allocs)
134    self.assertEqual([0x1000, 0x2000, 0x3000], backtraces[0].frames)
135
136    self.assertTrue(backtraces[1].is_zygote)
137    self.assertEqual(8300, backtraces[1].size)
138    self.assertEqual(1, backtraces[1].num_allocs)
139    self.assertEqual([0x1300, 0x2300, 0x3300], backtraces[1].frames)
140
141  def test_backtrace_reverse_field_valid(self):
142    heap = self.CreateTmpFile(self._backtrace_data)
143    backtraces, mapppings = native_heapdump_viewer.ParseNativeHeap(heap, True, True, "")
144    self.assertTrue(backtraces)
145    self.assertEqual(2, len(backtraces))
146
147    self.assertFalse(backtraces[0].is_zygote)
148    self.assertEqual(1000, backtraces[0].size)
149    self.assertEqual(4, backtraces[0].num_allocs)
150    self.assertEqual([0x3000, 0x2000, 0x1000], backtraces[0].frames)
151
152    self.assertTrue(backtraces[1].is_zygote)
153    self.assertEqual(8300, backtraces[1].size)
154    self.assertEqual(5, backtraces[1].num_allocs)
155    self.assertEqual([0x3300, 0x2300, 0x1300], backtraces[1].frames)
156
157  def test_mappings(self):
158    map_data = """
159MAPS
1601000-4000 r-xp 00000000 fd:00 495    /system/lib64/libc.so
1616000-8000 r-xp 00000000 fd:00 495
162a000-f000 r-xp 0000b000 fd:00 495    /system/lib64/libutils.so
163END
164"""
165
166    heap = self.CreateTmpFile(map_data)
167    backtraces, mappings = native_heapdump_viewer.ParseNativeHeap(heap, True, True, "")
168
169    self.assertTrue(mappings)
170    self.assertEqual(2, len(mappings))
171
172    self.assertEqual(0x1000, mappings[0].start)
173    self.assertEqual(0x4000, mappings[0].end)
174    self.assertEqual(0, mappings[0].offset)
175    self.assertEqual("/system/lib64/libc.so", mappings[0].name)
176
177    self.assertEqual(0xa000, mappings[1].start)
178    self.assertEqual(0xf000, mappings[1].end)
179    self.assertEqual(0xb000, mappings[1].offset)
180    self.assertEqual("/system/lib64/libutils.so", mappings[1].name)
181
182if __name__ == '__main__':
183  unittest.main(verbosity=2)
184