#!/usr/bin/env python3 # # Copyright (C) 2021 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. from app_profiler import NativeLibDownloader import shutil from simpleperf_utils import str_to_bytes, bytes_to_str, remove from . test_utils import TestBase, TestHelper, INFERNO_SCRIPT class TestNativeProfiling(TestBase): def setUp(self): super(TestNativeProfiling, self).setUp() self.is_rooted_device = TestHelper.adb.switch_to_root() def test_profile_cmd(self): self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--disable_adb_root"]) self.run_cmd(["report.py", "-g", "-o", "report.txt"]) def test_profile_native_program(self): if not self.is_rooted_device: return self.run_cmd(["app_profiler.py", "-np", "surfaceflinger"]) self.run_cmd(["report.py", "-g", "-o", "report.txt"]) self.run_cmd([INFERNO_SCRIPT, "-sc"]) self.run_cmd([INFERNO_SCRIPT, "-np", "surfaceflinger"]) def test_profile_pids(self): if not self.is_rooted_device: return pid = int(TestHelper.adb.check_run_and_return_output(['shell', 'pidof', 'system_server'])) self.run_cmd(['app_profiler.py', '--pid', str(pid), '-r', '--duration 1']) self.run_cmd(['app_profiler.py', '--pid', str(pid), str(pid), '-r', '--duration 1']) self.run_cmd(['app_profiler.py', '--tid', str(pid), '-r', '--duration 1']) self.run_cmd(['app_profiler.py', '--tid', str(pid), str(pid), '-r', '--duration 1']) self.run_cmd([INFERNO_SCRIPT, '--pid', str(pid), '-t', '1']) def test_profile_system_wide(self): if not self.is_rooted_device: return self.run_cmd(['app_profiler.py', '--system_wide', '-r', '--duration 1']) class TestNativeLibDownloader(TestBase): def setUp(self): super(TestNativeLibDownloader, self).setUp() self.adb = TestHelper.adb self.adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs']) self.ndk_path = TestHelper.ndk_path def tearDown(self): self.adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs']) super(TestNativeLibDownloader, self).tearDown() def list_lib_on_device(self, path): result, output = self.adb.run_and_return_output(['shell', 'ls', '-llc', path]) return output if result else '' def test_smoke(self): # Sync all native libs on device. downloader = NativeLibDownloader(self.ndk_path, 'arm64', self.adb) downloader.collect_native_libs_on_host(TestHelper.testdata_path( 'SimpleperfExampleWithNative/app/build/intermediates/cmake/profiling')) self.assertEqual(len(downloader.host_build_id_map), 2) for entry in downloader.host_build_id_map.values(): self.assertEqual(entry.score, 3) downloader.collect_native_libs_on_device() self.assertEqual(len(downloader.device_build_id_map), 0) lib_list = list(downloader.host_build_id_map.items()) for sync_count in [0, 1, 2]: build_id_map = {} for i in range(sync_count): build_id_map[lib_list[i][0]] = lib_list[i][1] downloader.host_build_id_map = build_id_map downloader.sync_native_libs_on_device() downloader.collect_native_libs_on_device() self.assertEqual(len(downloader.device_build_id_map), sync_count) for i, item in enumerate(lib_list): build_id = item[0] name = item[1].name if i < sync_count: self.assertTrue(build_id in downloader.device_build_id_map) self.assertEqual(name, downloader.device_build_id_map[build_id]) self.assertTrue(self.list_lib_on_device(downloader.dir_on_device + name)) else: self.assertTrue(build_id not in downloader.device_build_id_map) self.assertFalse(self.list_lib_on_device(downloader.dir_on_device + name)) if sync_count == 1: self.adb.run(['pull', '/data/local/tmp/native_libs/build_id_list', 'build_id_list']) with open('build_id_list', 'rb') as fh: self.assertEqual(bytes_to_str(fh.read()), '{}={}\n'.format(lib_list[0][0], lib_list[0][1].name)) remove('build_id_list') def test_handle_wrong_build_id_list(self): with open('build_id_list', 'wb') as fh: fh.write(str_to_bytes('fake_build_id=binary_not_exist\n')) self.adb.check_run(['shell', 'mkdir', '-p', '/data/local/tmp/native_libs']) self.adb.check_run(['push', 'build_id_list', '/data/local/tmp/native_libs']) remove('build_id_list') downloader = NativeLibDownloader(self.ndk_path, 'arm64', self.adb) downloader.collect_native_libs_on_device() self.assertEqual(len(downloader.device_build_id_map), 0) def test_download_file_without_build_id(self): downloader = NativeLibDownloader(self.ndk_path, 'x86_64', self.adb) name = 'elf.so' shutil.copyfile(TestHelper.testdata_path('data/symfs_without_build_id/elf'), name) downloader.collect_native_libs_on_host('.') downloader.collect_native_libs_on_device() self.assertIn(name, downloader.no_build_id_file_map) # Check if file without build id can be downloaded. downloader.sync_native_libs_on_device() target_file = downloader.dir_on_device + name target_file_stat = self.list_lib_on_device(target_file) self.assertTrue(target_file_stat) # No need to re-download if file size doesn't change. downloader.sync_native_libs_on_device() self.assertEqual(target_file_stat, self.list_lib_on_device(target_file)) # Need to re-download if file size changes. self.adb.check_run(['shell', 'truncate', '-s', '0', target_file]) target_file_stat = self.list_lib_on_device(target_file) downloader.sync_native_libs_on_device() self.assertNotEqual(target_file_stat, self.list_lib_on_device(target_file))