blob: 9fc4bcc5ad40dfdf168ee64cdb1b8850774eea3a [file] [log] [blame]
#!/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(
'SimpleperfExampleCpp/app/build/intermediates/cmake/debug'))
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))