• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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