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 17from app_profiler import NativeLibDownloader 18import shutil 19 20from simpleperf_utils import str_to_bytes, bytes_to_str, remove 21from . test_utils import TestBase, TestHelper, INFERNO_SCRIPT 22 23 24class TestNativeProfiling(TestBase): 25 def setUp(self): 26 super(TestNativeProfiling, self).setUp() 27 self.is_rooted_device = TestHelper.adb.switch_to_root() 28 29 def test_profile_cmd(self): 30 self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--disable_adb_root"]) 31 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 32 33 def test_profile_native_program(self): 34 if not self.is_rooted_device: 35 return 36 self.run_cmd(["app_profiler.py", "-np", "surfaceflinger"]) 37 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 38 self.run_cmd([INFERNO_SCRIPT, "-sc"]) 39 self.run_cmd([INFERNO_SCRIPT, "-np", "surfaceflinger"]) 40 41 def test_profile_pids(self): 42 if not self.is_rooted_device: 43 return 44 pid = int(TestHelper.adb.check_run_and_return_output(['shell', 'pidof', 'system_server'])) 45 self.run_cmd(['app_profiler.py', '--pid', str(pid), '-r', '--duration 1']) 46 self.run_cmd(['app_profiler.py', '--pid', str(pid), str(pid), '-r', '--duration 1']) 47 self.run_cmd(['app_profiler.py', '--tid', str(pid), '-r', '--duration 1']) 48 self.run_cmd(['app_profiler.py', '--tid', str(pid), str(pid), '-r', '--duration 1']) 49 self.run_cmd([INFERNO_SCRIPT, '--pid', str(pid), '-t', '1']) 50 51 def test_profile_system_wide(self): 52 if not self.is_rooted_device: 53 return 54 self.run_cmd(['app_profiler.py', '--system_wide', '-r', '--duration 1']) 55 56 57class TestNativeLibDownloader(TestBase): 58 def setUp(self): 59 super(TestNativeLibDownloader, self).setUp() 60 self.adb = TestHelper.adb 61 self.adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs']) 62 self.ndk_path = TestHelper.ndk_path 63 64 def tearDown(self): 65 self.adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs']) 66 super(TestNativeLibDownloader, self).tearDown() 67 68 def list_lib_on_device(self, path): 69 result, output = self.adb.run_and_return_output(['shell', 'ls', '-llc', path]) 70 return output if result else '' 71 72 def test_smoke(self): 73 # Sync all native libs on device. 74 downloader = NativeLibDownloader(self.ndk_path, 'arm64', self.adb) 75 downloader.collect_native_libs_on_host(TestHelper.testdata_path( 76 'SimpleperfExampleCpp/app/build/intermediates/cmake/debug')) 77 self.assertEqual(len(downloader.host_build_id_map), 2) 78 for entry in downloader.host_build_id_map.values(): 79 self.assertEqual(entry.score, 3) 80 downloader.collect_native_libs_on_device() 81 self.assertEqual(len(downloader.device_build_id_map), 0) 82 83 lib_list = list(downloader.host_build_id_map.items()) 84 for sync_count in [0, 1, 2]: 85 build_id_map = {} 86 for i in range(sync_count): 87 build_id_map[lib_list[i][0]] = lib_list[i][1] 88 downloader.host_build_id_map = build_id_map 89 downloader.sync_native_libs_on_device() 90 downloader.collect_native_libs_on_device() 91 self.assertEqual(len(downloader.device_build_id_map), sync_count) 92 for i, item in enumerate(lib_list): 93 build_id = item[0] 94 name = item[1].name 95 if i < sync_count: 96 self.assertTrue(build_id in downloader.device_build_id_map) 97 self.assertEqual(name, downloader.device_build_id_map[build_id]) 98 self.assertTrue(self.list_lib_on_device(downloader.dir_on_device + name)) 99 else: 100 self.assertTrue(build_id not in downloader.device_build_id_map) 101 self.assertFalse(self.list_lib_on_device(downloader.dir_on_device + name)) 102 if sync_count == 1: 103 self.adb.run(['pull', '/data/local/tmp/native_libs/build_id_list', 104 'build_id_list']) 105 with open('build_id_list', 'rb') as fh: 106 self.assertEqual(bytes_to_str(fh.read()), 107 '{}={}\n'.format(lib_list[0][0], lib_list[0][1].name)) 108 remove('build_id_list') 109 110 def test_handle_wrong_build_id_list(self): 111 with open('build_id_list', 'wb') as fh: 112 fh.write(str_to_bytes('fake_build_id=binary_not_exist\n')) 113 self.adb.check_run(['shell', 'mkdir', '-p', '/data/local/tmp/native_libs']) 114 self.adb.check_run(['push', 'build_id_list', '/data/local/tmp/native_libs']) 115 remove('build_id_list') 116 downloader = NativeLibDownloader(self.ndk_path, 'arm64', self.adb) 117 downloader.collect_native_libs_on_device() 118 self.assertEqual(len(downloader.device_build_id_map), 0) 119 120 def test_download_file_without_build_id(self): 121 downloader = NativeLibDownloader(self.ndk_path, 'x86_64', self.adb) 122 name = 'elf.so' 123 shutil.copyfile(TestHelper.testdata_path('data/symfs_without_build_id/elf'), name) 124 downloader.collect_native_libs_on_host('.') 125 downloader.collect_native_libs_on_device() 126 self.assertIn(name, downloader.no_build_id_file_map) 127 # Check if file without build id can be downloaded. 128 downloader.sync_native_libs_on_device() 129 target_file = downloader.dir_on_device + name 130 target_file_stat = self.list_lib_on_device(target_file) 131 self.assertTrue(target_file_stat) 132 133 # No need to re-download if file size doesn't change. 134 downloader.sync_native_libs_on_device() 135 self.assertEqual(target_file_stat, self.list_lib_on_device(target_file)) 136 137 # Need to re-download if file size changes. 138 self.adb.check_run(['shell', 'truncate', '-s', '0', target_file]) 139 target_file_stat = self.list_lib_on_device(target_file) 140 downloader.sync_native_libs_on_device() 141 self.assertNotEqual(target_file_stat, self.list_lib_on_device(target_file)) 142