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 17import os 18import signal 19import subprocess 20import sys 21import time 22import unittest 23 24from simpleperf_utils import is_windows, remove 25from . app_test import TestExampleBase 26from . test_utils import TestHelper, INFERNO_SCRIPT 27 28 29class TestExampleJava(TestExampleBase): 30 @classmethod 31 def setUpClass(cls): 32 cls.prepare("SimpleperfExampleJava", 33 "simpleperf.example.java", 34 ".MainActivity") 35 36 def test_app_profiler(self): 37 self.common_test_app_profiler() 38 39 def test_app_profiler_profile_from_launch(self): 40 self.run_app_profiler(start_activity=True, build_binary_cache=False) 41 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 42 self.check_strings_in_file("report.txt", [ 43 "simpleperf.example.java.MainActivity$1.run", 44 "__start_thread"]) 45 46 def test_app_profiler_multiprocesses(self): 47 self.adb.check_run(['shell', 'am', 'force-stop', self.package_name]) 48 self.adb.check_run(['shell', 'am', 'start', '-n', 49 self.package_name + '/.MultiProcessActivity']) 50 # Wait until both MultiProcessActivity and MultiProcessService set up. 51 time.sleep(3) 52 self.run_app_profiler(start_activity=False) 53 self.run_cmd(["report.py", "-o", "report.txt"]) 54 self.check_strings_in_file("report.txt", ["BusyService", "BusyThread"]) 55 56 def test_app_profiler_with_ctrl_c(self): 57 if is_windows(): 58 return 59 self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity']) 60 time.sleep(1) 61 args = [sys.executable, TestHelper.script_path("app_profiler.py"), 62 "--app", self.package_name, "-r", "--duration 10000", "--disable_adb_root"] 63 if TestHelper.ndk_path: 64 args += ['--ndk_path', TestHelper.ndk_path] 65 subproc = subprocess.Popen(args) 66 time.sleep(3) 67 68 subproc.send_signal(signal.SIGINT) 69 subproc.wait() 70 self.assertEqual(subproc.returncode, 0) 71 self.run_cmd(["report.py"]) 72 73 def test_app_profiler_stop_after_app_exit(self): 74 self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity']) 75 time.sleep(1) 76 args = [sys.executable, TestHelper.script_path('app_profiler.py'), 77 '--app', self.package_name, '-r', '--duration 10000', '--disable_adb_root'] 78 if TestHelper.ndk_path: 79 args += ['--ndk_path', TestHelper.ndk_path] 80 subproc = subprocess.Popen(args) 81 time.sleep(3) 82 self.adb.check_run(['shell', 'am', 'force-stop', self.package_name]) 83 subproc.wait() 84 self.assertEqual(subproc.returncode, 0) 85 self.run_cmd(["report.py"]) 86 87 def test_app_profiler_with_ndk_path(self): 88 # Although we pass an invalid ndk path, it should be able to find tools in default ndk path. 89 self.run_cmd(['app_profiler.py', '--app', self.package_name, '-a', self.activity_name, 90 '--ndk_path', '.']) 91 92 def test_report(self): 93 self.common_test_report() 94 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 95 self.check_strings_in_file("report.txt", [ 96 "simpleperf.example.java.MainActivity$1.run", 97 "__start_thread"]) 98 99 def test_profile_with_process_id(self): 100 self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity']) 101 time.sleep(1) 102 pid = self.adb.check_run_and_return_output( 103 ['shell', 'pidof', 'simpleperf.example.java']).strip() 104 self.run_app_profiler(start_activity=False, record_arg='-g --duration 10 -p ' + pid) 105 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 106 self.check_strings_in_file("report.txt", [ 107 "simpleperf.example.java.MainActivity$1.run", 108 "__start_thread"]) 109 110 def test_annotate(self): 111 self.common_test_annotate() 112 if not self.use_compiled_java_code: 113 # Currently annotating Java code is only supported when the Java code is compiled. 114 return 115 self.check_file_under_dir("annotated_files", "MainActivity.java") 116 summary_file = os.path.join("annotated_files", "summary") 117 self.check_annotation_summary(summary_file, [ 118 ("MainActivity.java", 80, 80), 119 ("run", 80, 0), 120 ("callFunction", 0, 0), 121 ("line 23", 80, 0)]) 122 123 def test_report_sample(self): 124 self.common_test_report_sample( 125 ["simpleperf.example.java.MainActivity$1.run", 126 "__start_thread"]) 127 128 def test_pprof_proto_generator(self): 129 check_strings_with_lines = [] 130 if self.use_compiled_java_code: 131 check_strings_with_lines = [ 132 "simpleperf/example/java/MainActivity.java", 133 "run"] 134 self.common_test_pprof_proto_generator( 135 check_strings_with_lines=check_strings_with_lines, 136 check_strings_without_lines=["simpleperf.example.java.MainActivity$1.run"]) 137 138 def test_inferno(self): 139 self.common_test_inferno() 140 self.run_app_profiler() 141 self.run_cmd([INFERNO_SCRIPT, "-sc"]) 142 self.check_inferno_report_html( 143 [('simpleperf.example.java.MainActivity$1.run', 80)]) 144 self.run_cmd([INFERNO_SCRIPT, "-sc", "-o", "report2.html"]) 145 self.check_inferno_report_html( 146 [('simpleperf.example.java.MainActivity$1.run', 80)], 147 "report2.html") 148 149 def test_inferno_in_another_dir(self): 150 test_dir = 'inferno_testdir' 151 os.mkdir(test_dir) 152 os.chdir(test_dir) 153 self.run_cmd(['app_profiler.py', '--app', self.package_name, 154 '-r', '-e task-clock:u -g --duration 3']) 155 self.check_exist(filename="perf.data") 156 self.run_cmd([INFERNO_SCRIPT, "-sc"]) 157 158 def test_report_html(self): 159 self.common_test_report_html() 160 161 def test_run_simpleperf_without_usb_connection(self): 162 self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity']) 163 self.run_cmd(['run_simpleperf_without_usb_connection.py', 'start', '-p', 164 self.package_name, '--size_limit', '1M']) 165 self.adb.check_run(['kill-server']) 166 time.sleep(3) 167 # Start adb process outside self.test_dir. Because it will be removed after testing. 168 os.chdir(self.test_dir.parent) 169 self.adb.check_run(['devices']) 170 os.chdir(self.test_dir) 171 self.run_cmd(['run_simpleperf_without_usb_connection.py', 'stop']) 172 self.check_exist(filename="perf.data") 173 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 174 175 176class TestExampleJavaProfileableApk(TestExampleJava): 177 """ Test profiling a profileable released apk.""" 178 @classmethod 179 def setUpClass(cls): 180 if TestHelper.android_version >= 10: 181 cls.prepare("SimpleperfExampleJava", 182 "simpleperf.example.java", 183 ".MainActivity", apk_name='app-release.apk') 184 185 def setUp(self): 186 if TestHelper().android_version < 10: 187 raise unittest.SkipTest("Profileable apk isn't supported on Android < Q.") 188 super().setUp() 189 190 191class TestExampleJavaRoot(TestExampleBase): 192 @classmethod 193 def setUpClass(cls): 194 cls.prepare("SimpleperfExampleJava", 195 "simpleperf.example.java", 196 ".MainActivity", 197 adb_root=True) 198 199 def test_app_profiler(self): 200 self.common_test_app_profiler() 201 202 203class TestExampleJavaTraceOffCpu(TestExampleBase): 204 @classmethod 205 def setUpClass(cls): 206 cls.prepare("SimpleperfExampleJava", 207 "simpleperf.example.java", 208 ".SleepActivity") 209 210 def test_smoke(self): 211 self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-clock:u --trace-offcpu") 212 self.run_cmd(["report.py", "-g", "-o", "report.txt"]) 213 self.check_strings_in_file("report.txt", [ 214 "simpleperf.example.java.SleepActivity$1.run", 215 "simpleperf.example.java.SleepActivity$1.RunFunction", 216 "simpleperf.example.java.SleepActivity$1.SleepFunction" 217 ]) 218 remove("annotated_files") 219 self.run_cmd(["annotate.py", "-s", self.example_path, '--summary-width', '1000']) 220 self.check_exist(dirname="annotated_files") 221 if self.use_compiled_java_code: 222 self.check_file_under_dir("annotated_files", "SleepActivity.java") 223 summary_file = os.path.join("annotated_files", "summary") 224 self.check_annotation_summary(summary_file, [ 225 ("SleepActivity.java", 80, 20), 226 ("run", 80, 0), 227 ("RunFunction", 20, 20), 228 ("SleepFunction", 20, 0), 229 ("line 24", 1, 0), 230 ("line 31", 20, 0)]) 231 self.run_cmd([INFERNO_SCRIPT, "-sc"]) 232 self.check_inferno_report_html( 233 [('simpleperf.example.java.SleepActivity$1.run', 80), 234 ('simpleperf.example.java.SleepActivity$1.RunFunction', 20), 235 ('simpleperf.example.java.SleepActivity$1.SleepFunction', 20)]) 236