• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: Apache-2.0
2#
3# Copyright (C) 2017, ARM Limited, Google and contributors.
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# 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, WITHOUT
13# 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#
17
18import os
19import re
20
21from subprocess import Popen, PIPE
22from time import sleep
23
24from android import Screen, System, Workload
25
26import pandas as pd
27
28
29class SysApp(Workload):
30    """
31    Android system app jank test workload.
32    """
33
34    packages = [
35        Workload.WorkloadPackage("com.android.sysapp.janktests",
36                                 "data/app/SystemAppJankTests/SystemAppJankTests.apk",
37                                 "vendor/google_testing/integration/tests/jank/sysapp"),
38        Workload.WorkloadPackage("com.android.chrome",
39                                 "system/app/Chrome/Chrome.apk",
40                                 "vendor/unbundled_google/packages/Chrome"),
41        Workload.WorkloadPackage("com.google.android.youtube",
42                                 "system/app/Youtube/Youtube.apk",
43                                 "vendor/unbundled_google/packages/YouTube")
44    ]
45
46    test_package = packages[0].package_name
47
48    test_list = [
49        "ChromeJankTests#testChromeOverflowMenuTap",
50        "YouTubeJankTests#testYouTubeRecomendationWindowFling"
51    ]
52
53    def __init__(self, test_env):
54        super(SysApp, self).__init__(test_env)
55
56    def get_test_list(self):
57        return SysApp.test_list
58
59    def _get_test_package(self, test_name):
60        name_start = test_name.partition('JankTests')[0]
61        name_map = {
62            "Chrome": "com.android.chrome",
63            "YouTube": "com.google.android.youtube",
64        }
65        return name_map[name_start]
66
67    def run(self, out_dir, test_name, iterations, collect=''):
68        """
69        Run single system app jank test workload.
70        Performance statistics are stored in self.results, and can be retrieved
71        after the fact by calling SystemUi.get_results()
72
73        :param out_dir: Path to experiment directory where to store results.
74        :type out_dir: str
75
76        :param test_name: Name of the test to run
77        :type test_name: str
78
79        :param iterations: Run benchmark for this required number of iterations
80        :type iterations: int
81
82        :param collect: Specifies what to collect. Possible values:
83            - 'systrace'
84            - 'ftrace'
85            - 'gfxinfo'
86            - 'surfaceflinger'
87            - any combination of the above
88        :type collect: list(str)
89        """
90        if "energy" in collect:
91            raise ValueError('System app workload does not support energy data collection')
92
93        activity = "." + test_name
94        package = self._get_test_package(test_name)
95
96        # Keep track of mandatory parameters
97        self.out_dir = out_dir
98        self.collect = collect
99
100        # Filter out test overhead
101        filter_prop = System.get_boolean_property(self._target, "debug.hwui.filter_test_overhead")
102        if not filter_prop:
103            System.set_property(self._target, "debug.hwui.filter_test_overhead", "true", restart=True)
104
105        # Unlock device screen (assume no password required)
106        Screen.unlock(self._target)
107
108        # Close and clear application
109        System.force_stop(self._target, package, clear=True)
110
111        # Set min brightness
112        Screen.set_brightness(self._target, auto=False, percent=0)
113
114        # Force screen in PORTRAIT mode
115        Screen.set_orientation(self._target, portrait=True)
116
117        # Delete old test results
118        self._target.remove("/sdcard/results.log")
119
120        # Clear logcat
121        self._target.execute("logcat -c")
122
123        # Regexps for benchmark synchronization
124        start_logline = r"TestRunner: started"
125        SYSAPP_BENCHMARK_START_RE = re.compile(start_logline)
126        self._log.debug("START string [%s]", start_logline)
127
128        finish_logline = r"TestRunner: finished"
129        SYSAPP_BENCHMARK_FINISH_RE = re.compile(finish_logline)
130        self._log.debug("FINISH string [%s]", finish_logline)
131
132        # Parse logcat output lines
133        logcat_cmd = self._adb("logcat TestRunner:* System.out:I *:S BENCH:*")
134        self._log.info("%s", logcat_cmd)
135
136        command = "am instrument -e iterations {} -e class {}{} -w {}".format(
137            iterations, self.test_package, activity, self.test_package)
138
139        logcat = Popen(logcat_cmd, shell=True, stdout=PIPE)
140
141        test_proc = self._target.background(command)
142        while True:
143            # read next logcat line (up to max 1024 chars)
144            message = logcat.stdout.readline(1024)
145
146            # Benchmark start
147            match = SYSAPP_BENCHMARK_START_RE.search(message)
148            if match:
149                self.tracingStart()
150                self._log.debug("Benchmark started!")
151
152            # Benchmark finish
153            match = SYSAPP_BENCHMARK_FINISH_RE.search(message)
154            if match:
155                self.tracingStop()
156                self._log.debug("Benchmark finished!")
157                test_proc.wait()
158                break
159        sleep(5)
160        self._target.pull("/sdcard/results.log", os.path.join(out_dir, "results.log"))
161        self.db_file = os.path.join(out_dir, "results.log")
162        self.results = self.get_results(out_dir)
163
164        # Close and clear application
165        System.force_stop(self._target, package, clear=True)
166
167        # Go back to home screen
168        System.home(self._target)
169
170        # Switch back to original settings
171        Screen.set_orientation(self._target, auto=True)
172        Screen.set_brightness(self._target, auto=True)
173
174
175    @staticmethod
176    def get_results(out_dir):
177        """
178        Parse SysApp test output log and return a pandas dataframe of test results.
179
180        :param out_dir: Output directory for a run of the SysApp workload.
181        :type out_dir: str
182        """
183        path = os.path.join(out_dir, "results.log")
184        with open(path, "r") as f:
185            lines = f.readlines()
186        cols = []
187        vals = []
188        for line in lines:
189            name, val = str.split(line)
190            if name == "Result":
191                cols.append("test-name")
192                vals.append(val)
193            elif name.startswith("gfx-"):
194                cols.append(name[4:])
195                vals.append(float(val))
196            else:
197                raise ValueError("Unrecognized line in results file")
198        return pd.DataFrame([vals], columns=cols)
199