1#!/usr/bin/env python
2#
3# Copyright (C) 2017 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#
17
18import json
19import logging
20
21from vts.runners.host import asserts
22from vts.runners.host import base_test
23from vts.runners.host import const
24from vts.runners.host import test_runner
25from vts.utils.python.controllers import android_device
26from vts.utils.python.cpu import cpu_frequency_scaling
27
28
29class HwBinderLatencyTest(base_test.BaseTestClass):
30    """A test case for the hwbinder latency benchmarking.
31
32    Sample output of libhwbinder_latency
33    {
34      "cfg":{"pair":6,"iterations":166,"deadline_us":2500,"passthrough":1},
35      "fifo_0_data": [15052, ...],
36      "fifo_1_data": [...],
37      ...
38      "ALL":{"SYNC":"GOOD","S":1992,"I":1992,"R":1,
39        "other_ms":{ "avg":0.0048, "wst":0.32, "bst":0.0022, "miss":0, "meetR":1},
40        "fifo_ms": { "avg":0.0035, "wst":0.037, "bst":0.0021, "miss":0, "meetR":1},
41        "otherdis":{ "p50":0.19531, "p90":0.19531, "p95":0.19531, "p99": 0.19531},
42        "fifodis": { "p50":0.19531, "p90":0.19531, "p95":0.19531, "p99": 0.19531}
43      },
44      "P0":{...
45      },
46      ...
47      "inheritance": "PASS"
48    }
49    """
50    # The order of the columns in the output table
51    _MS_COLUMNS = ["avg", "wst", "bst", "miss", "meetR"]
52    _DIS_COLUMNS = ["p50", "p90", "p95", "p99"]
53    # The keys in the JSON object
54    _CFG = "cfg"
55    _PAIR = "pair"
56    _ALL = "ALL"
57    _OTHER_MS = "other_ms"
58    _FIFO_MS = "fifo_ms"
59    _OTHERDIS = "otherdis"
60    _FIFODIS = "fifodis"
61    _INHERITANCE = "inheritance"
62
63    def setUpClass(self):
64        required_params = ["hidl_hal_mode"]
65        self.getUserParams(required_params)
66        self.dut = self.android_devices[0]
67        self._cpu_freq = cpu_frequency_scaling.CpuFrequencyScalingController(self.dut)
68        self._cpu_freq.DisableCpuScaling()
69
70    def setUp(self):
71        self._cpu_freq.SkipIfThermalThrottling(retry_delay_secs=30)
72
73    def tearDown(self):
74        self._cpu_freq.SkipIfThermalThrottling()
75
76    def tearDownClass(self):
77        self._cpu_freq.EnableCpuScaling()
78
79    def testRunBenchmark32Bit(self):
80        result = self._runBenchmark(32)
81        self._addBenchmarkTableToResult(result, 32)
82        self._uploadResult(result, 32)
83
84    def testRunBenchmark64Bit(self):
85        result = self._runBenchmark(64)
86        self._addBenchmarkTableToResult(result, 64)
87        self._uploadResult(result, 64)
88
89    def _runBenchmark(self, bits):
90        """Runs the native binary and parses its result.
91
92        Args:
93            bits: integer (32 or 64), the bitness of the binary to run.
94
95        Returns:
96            dict, the benchmarking result converted from native binary's JSON
97            output.
98        """
99        logging.info("Start %d-bit hwbinder latency test with HIDL mode=%s",
100                     bits, self.hidl_hal_mode)
101        binary = "/data/local/tmp/%s/libhwbinder_latency%s" % (bits, bits)
102        min_cpu, max_cpu = self._cpu_freq.GetMinAndMaxCpuNo()
103        iterations = 1000 // (max_cpu - min_cpu)
104        results = self.dut.shell.Execute([
105            "chmod 755 %s" % binary,
106            "VTS_ROOT_PATH=/data/local/tmp " \
107            "LD_LIBRARY_PATH=/system/lib%s:/data/local/tmp/%s/hw:"
108            "/data/local/tmp/%s:$LD_LIBRARY_PATH "
109            "%s -raw_data -pair %d -i %d -m %s" % (bits, bits, bits,
110                binary, max_cpu - min_cpu, iterations,
111                self.hidl_hal_mode.encode("utf-8"))])
112        # Parses the result.
113        asserts.assertEqual(len(results[const.STDOUT]), 2)
114        logging.info("stderr: %s", results[const.STDERR][1])
115        logging.info("stdout: %s", results[const.STDOUT][1])
116        asserts.assertFalse(
117            any(results[const.EXIT_CODE]),
118            "testRunBenchmark%sBit failed." % (bits))
119        json_result = json.loads(results[const.STDOUT][1]);
120        asserts.assertTrue(json_result[self._INHERITANCE] == "PASS",
121            "Scheduler does not support priority inheritance.");
122        return json_result
123
124    def _createRow(self, pair_name, ms_result, dis_result):
125        """Creates a row from the JSON output.
126
127        Args:
128            pair_name: string, the pair name in the first column.
129            ms_result: dict, the fifo_ms or other_ms object.
130            dis_result: dict, the fifodis or otherdis object.
131
132        Returns:
133            the list containing pair_name and the values in the objects.
134        """
135        row = [pair_name]
136        row.extend([ms_result[x] for x in self._MS_COLUMNS])
137        row.extend([dis_result[x] for x in self._DIS_COLUMNS])
138        return row
139
140    def _addBenchmarkTableToResult(self, result, bits):
141        pair_cnt = result[self._CFG][self._PAIR]
142        row_names = ["P" + str(i) for i in range(pair_cnt)] + [self._ALL]
143        col_names = ["pair"] + self._MS_COLUMNS + self._DIS_COLUMNS
144        fifo_table = [col_names]
145        other_table = [col_names]
146        for row_name in row_names:
147            pair_result = result[row_name]
148            fifo_table.append(self._createRow(row_name,
149                pair_result[self._FIFO_MS], pair_result[self._FIFODIS]))
150            other_table.append(self._createRow(row_name,
151                pair_result[self._OTHER_MS], pair_result[self._OTHERDIS]))
152        self.addTableToResult(
153            "hwbinder_latency_%sbits_fifo" % bits,fifo_table)
154        self.addTableToResult(
155            "hwbinder_latency_%sbits_other" % bits, other_table)
156
157    def _uploadResult(self, result, bits):
158        """Uploads the output of benchmark program to web DB.
159
160        Args:
161            result: dict which is the benchmarking result.
162            bits: integer (32 or 64).
163        """
164        opts = ["hidl_hal_mode=%s" % self.hidl_hal_mode.encode("utf-8")];
165        min_cpu, max_cpu = self._cpu_freq.GetMinAndMaxCpuNo()
166        for i in range(max_cpu - min_cpu):
167            self.web.AddProfilingDataUnlabeledVector(
168                "hwbinder_latency_%sbits" % bits,
169                result["fifo_%d_data" % i], options=opts,
170                x_axis_label="hwbinder latency",
171                y_axis_label="Frequency")
172
173
174if __name__ == "__main__":
175    test_runner.main()
176