| #!/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)) |