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