• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright (C) 2021 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
17import os
18from pathlib import Path
19
20from binary_cache_builder import BinaryCacheBuilder
21from simpleperf_utils import (Addr2Nearestline, BinaryFinder, Objdump, ReadElf,
22                              SourceFileSearcher, is_windows, remove)
23from . test_utils import TestBase, TestHelper
24
25
26class TestTools(TestBase):
27    def test_addr2nearestline(self):
28        self.run_addr2nearestline_test(True)
29        self.run_addr2nearestline_test(False)
30
31    def run_addr2nearestline_test(self, with_function_name):
32        test_map = {
33            '/simpleperf_runtest_two_functions_arm64': [
34                {
35                    'func_addr': 0x112c,
36                    'addr': 0x112c,
37                    'source': 'system/extras/simpleperf/runtest/two_functions.cpp:20',
38                    'function': 'main',
39                },
40                {
41                    'func_addr': 0x104c,
42                    'addr': 0x105c,
43                    'source': "system/extras/simpleperf/runtest/two_functions.cpp:7",
44                    'function': "Function1()",
45                },
46            ],
47            '/simpleperf_runtest_two_functions_arm': [
48                {
49                    'func_addr': 0x784,
50                    'addr': 0x7b0,
51                    'source': """system/extras/simpleperf/runtest/two_functions.cpp:14
52                                 system/extras/simpleperf/runtest/two_functions.cpp:23""",
53                    'function': """Function2()
54                                   main""",
55                },
56                {
57                    'func_addr': 0x784,
58                    'addr': 0x7d0,
59                    'source': """system/extras/simpleperf/runtest/two_functions.cpp:15
60                                 system/extras/simpleperf/runtest/two_functions.cpp:23""",
61                    'function': """Function2()
62                                   main""",
63                }
64            ],
65            '/simpleperf_runtest_two_functions_x86_64': [
66                {
67                    'func_addr': 0x840,
68                    'addr': 0x840,
69                    'source': 'system/extras/simpleperf/runtest/two_functions.cpp:7',
70                    'function': 'Function1()',
71                },
72                {
73                    'func_addr': 0x920,
74                    'addr': 0x94a,
75                    'source': """system/extras/simpleperf/runtest/two_functions.cpp:7
76                                 system/extras/simpleperf/runtest/two_functions.cpp:22""",
77                    'function': """Function1()
78                                   main""",
79                }
80            ],
81            '/simpleperf_runtest_two_functions_x86': [
82                {
83                    'func_addr': 0x6d0,
84                    'addr': 0x6da,
85                    'source': 'system/extras/simpleperf/runtest/two_functions.cpp:14',
86                    'function': 'Function2()',
87                },
88                {
89                    'func_addr': 0x710,
90                    'addr': 0x749,
91                    'source': """system/extras/simpleperf/runtest/two_functions.cpp:8
92                                 system/extras/simpleperf/runtest/two_functions.cpp:22""",
93                    'function': """Function1()
94                                   main""",
95                }
96            ],
97        }
98        binary_finder = BinaryFinder(TestHelper.testdata_dir, ReadElf(TestHelper.ndk_path))
99        addr2line = Addr2Nearestline(TestHelper.ndk_path, binary_finder, with_function_name)
100        for dso_path in test_map:
101            test_addrs = test_map[dso_path]
102            for test_addr in test_addrs:
103                addr2line.add_addr(dso_path, None, test_addr['func_addr'], test_addr['addr'])
104        addr2line.convert_addrs_to_lines()
105        for dso_path in test_map:
106            dso = addr2line.get_dso(dso_path)
107            self.assertIsNotNone(dso, dso_path)
108            test_addrs = test_map[dso_path]
109            for test_addr in test_addrs:
110                expected_files = []
111                expected_lines = []
112                expected_functions = []
113                for line in test_addr['source'].split('\n'):
114                    items = line.split(':')
115                    expected_files.append(items[0].strip())
116                    expected_lines.append(int(items[1]))
117                for line in test_addr['function'].split('\n'):
118                    expected_functions.append(line.strip())
119                self.assertEqual(len(expected_files), len(expected_functions))
120
121                if with_function_name:
122                    expected_source = list(zip(expected_files, expected_lines, expected_functions))
123                else:
124                    expected_source = list(zip(expected_files, expected_lines))
125
126                actual_source = addr2line.get_addr_source(dso, test_addr['addr'])
127                if is_windows():
128                    self.assertIsNotNone(actual_source, 'for %s:0x%x' %
129                                         (dso_path, test_addr['addr']))
130                    for i, source in enumerate(actual_source):
131                        new_source = list(source)
132                        new_source[0] = new_source[0].replace('\\', '/')
133                        actual_source[i] = tuple(new_source)
134
135                self.assertEqual(actual_source, expected_source,
136                                 'for %s:0x%x, expected source %s, actual source %s' %
137                                 (dso_path, test_addr['addr'], expected_source, actual_source))
138
139    def test_objdump(self):
140        test_map = {
141            '/simpleperf_runtest_two_functions_arm64': {
142                'start_addr': 0x112c,
143                'len': 28,
144                'expected_items': [
145                    ('main():', 0),
146                    ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
147                    ('1134:      	add	x29, sp, #16', 0x1134),
148                ],
149            },
150            '/simpleperf_runtest_two_functions_arm': {
151                'start_addr': 0x784,
152                'len': 80,
153                'expected_items': [
154                    ('main():', 0),
155                    ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
156                    ('7ae:	bne.n	7a6 <main+0x22>', 0x7ae),
157                ],
158            },
159            '/simpleperf_runtest_two_functions_x86_64': {
160                'start_addr': 0x920,
161                'len': 201,
162                'expected_items': [
163                    ('main():', 0),
164                    ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
165                    ('96e:      	movl	%edx, (%rbx,%rax,4)', 0x96e),
166                ],
167            },
168            '/simpleperf_runtest_two_functions_x86': {
169                'start_addr': 0x710,
170                'len': 98,
171                'expected_items': [
172                    ('main():', 0),
173                    ('system/extras/simpleperf/runtest/two_functions.cpp:20', 0),
174                    ('748:      	cmpl	$100000000, %ebp', 0x748),
175                ],
176            },
177        }
178        binary_finder = BinaryFinder(TestHelper.testdata_dir, ReadElf(TestHelper.ndk_path))
179        objdump = Objdump(TestHelper.ndk_path, binary_finder)
180        for dso_path in test_map:
181            dso = test_map[dso_path]
182            dso_info = objdump.get_dso_info(dso_path, None)
183            self.assertIsNotNone(dso_info, dso_path)
184            disassemble_code = objdump.disassemble_code(dso_info, dso['start_addr'], dso['len'])
185            self.assertTrue(disassemble_code, dso_path)
186            i = 0
187            for expected_line, expected_addr in dso['expected_items']:
188                found = False
189                while i < len(disassemble_code):
190                    line, addr = disassemble_code[i]
191                    if addr == expected_addr and expected_line in line:
192                        found = True
193                        i += 1
194                        break
195                    i += 1
196                if not found:
197                    s = '\n'.join('%s:0x%x' % item for item in disassemble_code)
198                    self.fail('for %s, %s:0x%x not found in disassemble code:\n%s' %
199                              (dso_path, expected_line, expected_addr, s))
200
201    def test_readelf(self):
202        test_map = {
203            'simpleperf_runtest_two_functions_arm64': {
204                'arch': 'arm64',
205                'build_id': '0xb4f1b49b0fe9e34e78fb14e5374c930c00000000',
206                'sections': ['.note.gnu.build-id', '.dynsym', '.text', '.rodata', '.eh_frame',
207                             '.eh_frame_hdr', '.debug_info',  '.debug_line', '.symtab'],
208            },
209            'simpleperf_runtest_two_functions_arm': {
210                'arch': 'arm',
211                'build_id': '0x718f5b36c4148ee1bd3f51af89ed2be600000000',
212            },
213            'simpleperf_runtest_two_functions_x86_64': {
214                'arch': 'x86_64',
215            },
216            'simpleperf_runtest_two_functions_x86': {
217                'arch': 'x86',
218            }
219        }
220        readelf = ReadElf(TestHelper.ndk_path)
221        for dso_path in test_map:
222            dso_info = test_map[dso_path]
223            path = os.path.join(TestHelper.testdata_dir, dso_path)
224            self.assertEqual(dso_info['arch'], readelf.get_arch(path))
225            if 'build_id' in dso_info:
226                self.assertEqual(dso_info['build_id'], readelf.get_build_id(path), dso_path)
227            if 'sections' in dso_info:
228                sections = readelf.get_sections(path)
229                for section in dso_info['sections']:
230                    self.assertIn(section, sections)
231        self.assertEqual(readelf.get_arch('not_exist_file'), 'unknown')
232        self.assertEqual(readelf.get_build_id('not_exist_file'), '')
233        self.assertEqual(readelf.get_sections('not_exist_file'), [])
234
235    def test_source_file_searcher(self):
236        searcher = SourceFileSearcher(
237            [TestHelper.testdata_path('SimpleperfExampleWithNative'),
238             TestHelper.testdata_path('SimpleperfExampleOfKotlin')])
239
240        def format_path(path):
241            return os.path.join(TestHelper.testdata_dir, path.replace('/', os.sep))
242        # Find a C++ file with pure file name.
243        self.assertEqual(
244            format_path('SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp'),
245            searcher.get_real_path('native-lib.cpp'))
246        # Find a C++ file with an absolute file path.
247        self.assertEqual(
248            format_path('SimpleperfExampleWithNative/app/src/main/cpp/native-lib.cpp'),
249            searcher.get_real_path('/data/native-lib.cpp'))
250        # Find a Java file.
251        self.assertEqual(
252            format_path('SimpleperfExampleWithNative/app/src/main/java/com/example/' +
253                        'simpleperf/simpleperfexamplewithnative/MainActivity.java'),
254            searcher.get_real_path('simpleperfexamplewithnative/MainActivity.java'))
255        # Find a Kotlin file.
256        self.assertEqual(
257            format_path('SimpleperfExampleOfKotlin/app/src/main/java/com/example/' +
258                        'simpleperf/simpleperfexampleofkotlin/MainActivity.kt'),
259            searcher.get_real_path('MainActivity.kt'))
260
261    def test_is_elf_file(self):
262        self.assertTrue(ReadElf.is_elf_file(TestHelper.testdata_path(
263            'simpleperf_runtest_two_functions_arm')))
264        with open('not_elf', 'wb') as fh:
265            fh.write(b'\x90123')
266        try:
267            self.assertFalse(ReadElf.is_elf_file('not_elf'))
268        finally:
269            remove('not_elf')
270
271    def test_binary_finder(self):
272        # Create binary_cache.
273        binary_cache_builder = BinaryCacheBuilder(TestHelper.ndk_path, False)
274        elf_name = 'simpleperf_runtest_two_functions_arm'
275        elf_path = TestHelper.testdata_path(elf_name)
276        readelf = ReadElf(TestHelper.ndk_path)
277        build_id = readelf.get_build_id(elf_path)
278        self.assertGreater(len(build_id), 0)
279        binary_cache_builder.binaries[elf_name] = build_id
280        binary_cache_builder.copy_binaries_from_symfs_dirs([TestHelper.testdata_dir])
281        binary_cache_builder.create_build_id_list()
282
283        # Test BinaryFinder.
284        path_in_binary_cache = Path(binary_cache_builder.binary_cache_dir, elf_name)
285        binary_finder = BinaryFinder(binary_cache_builder.binary_cache_dir, readelf)
286        # Find binary using build id.
287        path = binary_finder.find_binary('[not_exist_file]', build_id)
288        self.assertEqual(path, path_in_binary_cache)
289        # Find binary using path.
290        path = binary_finder.find_binary('/' + elf_name, None)
291        self.assertEqual(path, path_in_binary_cache)
292        # Find binary using absolute path.
293        path = binary_finder.find_binary(str(path_in_binary_cache), None)
294        self.assertEqual(path, path_in_binary_cache)
295
296        # The binary should has a matched build id.
297        path = binary_finder.find_binary('/' + elf_name, 'wrong_build_id')
298        self.assertIsNone(path)
299