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 filecmp 18import os 19from pathlib import Path 20import shutil 21import tempfile 22import zipfile 23 24from binary_cache_builder import BinaryCacheBuilder 25from simpleperf_utils import ReadElf, remove, ToolFinder 26from . test_utils import TestBase, TestHelper 27 28 29class TestBinaryCacheBuilder(TestBase): 30 def test_copy_binaries_from_symfs_dirs(self): 31 readelf = ReadElf(TestHelper.ndk_path) 32 strip = ToolFinder.find_tool_path('llvm-strip', ndk_path=TestHelper.ndk_path, arch='arm') 33 self.assertIsNotNone(strip) 34 symfs_dir = os.path.join(self.test_dir, 'symfs_dir') 35 remove(symfs_dir) 36 os.mkdir(symfs_dir) 37 filename = 'simpleperf_runtest_two_functions_arm' 38 origin_file = TestHelper.testdata_path(filename) 39 source_file = os.path.join(symfs_dir, filename) 40 build_id = readelf.get_build_id(origin_file) 41 binary_cache_builder = BinaryCacheBuilder(TestHelper.ndk_path, False) 42 binary_cache_builder.binaries['simpleperf_runtest_two_functions_arm'] = build_id 43 44 # Copy binary if target file doesn't exist. 45 target_file = binary_cache_builder.find_path_in_cache(filename) 46 remove(target_file) 47 self.run_cmd([strip, '--strip-all', '-o', source_file, origin_file]) 48 binary_cache_builder.copy_binaries_from_symfs_dirs([symfs_dir]) 49 self.assertTrue(filecmp.cmp(target_file, source_file)) 50 51 # Copy binary if target file doesn't have .symtab and source file has .symtab. 52 self.run_cmd([strip, '--strip-debug', '-o', source_file, origin_file]) 53 binary_cache_builder.copy_binaries_from_symfs_dirs([symfs_dir]) 54 self.assertTrue(filecmp.cmp(target_file, source_file)) 55 56 # Copy binary if target file doesn't have .debug_line and source_files has .debug_line. 57 shutil.copy(origin_file, source_file) 58 binary_cache_builder.copy_binaries_from_symfs_dirs([symfs_dir]) 59 self.assertTrue(filecmp.cmp(target_file, source_file)) 60 61 def test_copy_elf_without_build_id_from_symfs_dir(self): 62 binary_cache_builder = BinaryCacheBuilder(TestHelper.ndk_path, False) 63 binary_cache_builder.binaries['elf'] = '' 64 symfs_dir = TestHelper.testdata_path('data/symfs_without_build_id') 65 source_file = os.path.join(symfs_dir, 'elf') 66 target_file = binary_cache_builder.find_path_in_cache('elf') 67 binary_cache_builder.copy_binaries_from_symfs_dirs([symfs_dir]) 68 self.assertTrue(filecmp.cmp(target_file, source_file)) 69 binary_cache_builder.pull_binaries_from_device() 70 self.assertTrue(filecmp.cmp(target_file, source_file)) 71 72 def test_copy_binary_with_different_name(self): 73 # Build symfs_dir. 74 symfs_dir = self.test_dir / 'symfs_dir' 75 remove(symfs_dir) 76 symfs_dir.mkdir() 77 filename = 'simpleperf_runtest_two_functions_arm' 78 origin_file = TestHelper.testdata_path(filename) 79 modified_name = 'two_functions_arm' 80 source_file = os.path.join(symfs_dir, modified_name) 81 shutil.copy(origin_file, source_file) 82 83 # Copy binary with the same build id but a different name. 84 builder = BinaryCacheBuilder(TestHelper.ndk_path, False) 85 builder.binaries[filename] = builder.readelf.get_build_id(origin_file) 86 builder.copy_binaries_from_symfs_dirs([symfs_dir]) 87 88 target_file = builder.find_path_in_cache(filename) 89 self.assertTrue(filecmp.cmp(target_file, source_file)) 90 91 def test_copy_binary_for_native_lib_embedded_in_apk(self): 92 apk_path = TestHelper.testdata_path('data/app/com.example.hellojni-1/base.apk') 93 symfs_dir = self.test_dir / 'symfs_dir' 94 with zipfile.ZipFile(apk_path, 'r') as zip_ref: 95 zip_ref.extractall(symfs_dir) 96 builder = BinaryCacheBuilder(TestHelper.ndk_path, False) 97 builder.collect_used_binaries( 98 TestHelper.testdata_path('has_embedded_native_libs_apk_perf.data')) 99 builder.copy_binaries_from_symfs_dirs([symfs_dir]) 100 101 device_path = [p for p in builder.binaries if 'libhello-jni.so' in p][0] 102 target_file = builder.find_path_in_cache(device_path) 103 self.assertTrue(target_file.is_file()) 104 # Check that we are not using path format of embedded lib in apk. Because 105 # simpleperf can't use it from binary_cache. 106 self.assertNotIn('!/', str(target_file)) 107 108 def test_prefer_binary_with_debug_info(self): 109 binary_cache_builder = BinaryCacheBuilder(TestHelper.ndk_path, False) 110 binary_cache_builder.collect_used_binaries( 111 TestHelper.testdata_path('runtest_two_functions_arm64_perf.data')) 112 filename = 'simpleperf_runtest_two_functions_arm64' 113 114 # Create a symfs_dir, which contains elf file with and without debug info. 115 with tempfile.TemporaryDirectory() as tmp_dir: 116 shutil.copy( 117 TestHelper.testdata_path( 118 'simpleperf_runtest_two_functions_arm64_without_debug_info'), 119 Path(tmp_dir) / filename) 120 121 debug_dir = Path(tmp_dir) / 'debug' 122 debug_dir.mkdir() 123 debug_file = TestHelper.testdata_path(filename) 124 shutil.copy(debug_file, debug_dir) 125 # Check if the elf file with debug info is chosen. 126 binary_cache_builder.copy_binaries_from_symfs_dirs([tmp_dir]) 127 target_file = binary_cache_builder.find_path_in_cache('/data/local/tmp/' + filename) 128 self.assertTrue(filecmp.cmp(target_file, debug_file)) 129 130 def test_create_build_id_list(self): 131 symfs_dir = TestHelper.testdata_dir 132 binary_cache_builder = BinaryCacheBuilder(TestHelper.ndk_path, False) 133 binary_cache_builder.collect_used_binaries( 134 TestHelper.testdata_path('runtest_two_functions_arm64_perf.data')) 135 binary_cache_builder.copy_binaries_from_symfs_dirs([symfs_dir]) 136 137 target_file = binary_cache_builder.find_path_in_cache( 138 '/data/local/tmp/simpleperf_runtest_two_functions_arm64') 139 self.assertTrue(target_file.is_file()) 140 141 binary_cache_builder.create_build_id_list() 142 build_id_list_path = Path(binary_cache_builder.binary_cache_dir) / 'build_id_list' 143 self.assertTrue(build_id_list_path.is_file()) 144 with open(build_id_list_path, 'r') as fh: 145 self.assertIn('simpleperf_runtest_two_functions_arm64', fh.read()) 146