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 logging
19
20from vts.runners.host import asserts
21from vts.runners.host import base_test
22from vts.runners.host import const
23from vts.runners.host import test_runner
24from vts.utils.python.controllers import android_device
25from vts.utils.python.cpu import cpu_frequency_scaling
26
27
28class FmqPerformanceTest(base_test.BaseTestClass):
29    """A testcase for the Fast Message Queue(fmq) Performance Benchmarking.
30
31    Attributes:
32        dut: the target DUT (device under test) instance.
33        _cpu_freq: CpuFrequencyScalingController instance of self.dut.
34    """
35    # Latency threshold for the benchmark,  unit: nanoseconds.
36    THRESHOLD = {
37        32: {
38            "64": 300,
39            "128": 300,
40            "256": 300,
41            "512": 350,
42        },
43        64: {
44            "64": 150,
45            "128": 150,
46            "256": 150,
47            "512": 150,
48        }
49    }
50
51    def setUpClass(self):
52        self.dut = self.android_devices[0]
53        self._cpu_freq = cpu_frequency_scaling.CpuFrequencyScalingController(
54            self.dut)
55        self._cpu_freq.DisableCpuScaling()
56
57    def tearDownClass(self):
58        self._cpu_freq.EnableCpuScaling()
59
60    def setUp(self):
61        self._cpu_freq.SkipIfThermalThrottling(retry_delay_secs=30)
62
63    def tearDown(self):
64        self._cpu_freq.SkipIfThermalThrottling()
65
66    def testRunBenchmark32Bit(self):
67        """A testcase which runs the 32-bit benchmark."""
68        self.RunBenchmark(32)
69
70    def testRunBenchmark64Bit(self):
71        """A testcase which runs the 64-bit benchmark."""
72        self.RunBenchmark(64)
73
74    def RunBenchmark(self, bits):
75        """Runs the native binary and parses its result.
76
77        Args:
78            bits: integer (32 or 64), the number of bits in a word chosen
79                  at the compile time (e.g., 32- vs. 64-bit library).
80        """
81        # Start the benchmark service.
82        logging.info("Start the benchmark service(%s bit mode)", bits)
83        binary = "/data/local/tmp/%s/mq_benchmark_service%s" % (bits, bits)
84        results = self.dut.shell.Execute([
85            "chmod 755 %s" % binary,
86            "VTS_ROOT_PATH=/data/local/tmp TREBLE_TESTING_OVERRIDE=true " \
87            "LD_LIBRARY_PATH=/data/local/tmp/%s:"
88            "$LD_LIBRARY_PATH %s&" % (bits, binary)
89        ])
90        asserts.assertEqual(len(results[const.STDOUT]), 2)
91        asserts.assertFalse(
92            any(results[const.EXIT_CODE]),
93            "Failed to start the benchmark service.")
94
95        # Runs the benchmark.
96        logging.info("Start to run the benchmark (%s bit mode)", bits)
97        binary = "/data/local/tmp/%s/mq_benchmark_client%s" % (bits, bits)
98
99        results = self.dut.shell.Execute([
100            "chmod 755 %s" % binary,
101            "TREBLE_TESTING_OVERRIDE=true LD_LIBRARY_PATH=/data/local/tmp/%s:"
102            "$LD_LIBRARY_PATH %s" % (bits, binary)
103        ])
104
105        # Stop the benchmark service.
106        self.dut.shell.Execute("kill -9 `pidof mq_benchmark_service%s`" %
107                                   bits)
108
109        # Parses the result.
110        asserts.assertEqual(len(results[const.STDOUT]), 2)
111        asserts.assertFalse(
112            any(results[const.EXIT_CODE]), "FmqPerformanceTest failed.")
113        read_label = []
114        read_latency = []
115        write_label = []
116        write_latency = []
117        stdout_lines = results[const.STDOUT][1].split("\n")
118        for line in stdout_lines:
119            if line.startswith("Average time to read"):
120                read_result = line.replace("Average time to read", "").replace(
121                    "bytes", "").replace("ns", "")
122                (label, value) = read_result.split(": ")
123                read_label.append(label)
124                read_latency.append(int(value))
125            if line.startswith("Average time to write"):
126                write_result = line.replace("Average time to write ",
127                                            "").replace("bytes",
128                                                        "").replace("ns", "")
129                (label, value) = write_result.split(": ")
130                write_label.append(label)
131                write_latency.append(int(value))
132
133        # To upload to the web DB.
134        self.web.AddProfilingDataLabeledVector(
135            "fmq_read_latency_benchmark_%sbits" % bits,
136            read_label,
137            read_latency,
138            x_axis_label="Message Size (Bytes)",
139            y_axis_label="Average Latency (nanoseconds)")
140
141        self.web.AddProfilingDataLabeledVector(
142            "fmq_write_latency_benchmark_%sbits" % bits,
143            write_label,
144            write_latency,
145            x_axis_label="Message Size (Bytes)",
146            y_axis_label="Average Latency (nanoseconds)")
147
148        # Assertions to check the performance requirements
149        for label, value in zip(read_label, read_latency):
150            if label in self.THRESHOLD[bits]:
151                asserts.assertLess(
152                    value, self.THRESHOLD[bits][label],
153                    "%s ns for %s is longer than the threshold %s ns" % (
154                        value, label, self.THRESHOLD[bits][label]))
155
156        for label, value in zip(write_label, write_latency):
157            if label in self.THRESHOLD[bits]:
158                asserts.assertLess(
159                    value, self.THRESHOLD[bits][label],
160                    "%s ns for %s is longer than the threshold %s ns" % (
161                        value, label, self.THRESHOLD[bits][label]))
162
163
164if __name__ == "__main__":
165    test_runner.main()
166