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.registerController(android_device)[0]
53        self.dut.shell.InvokeTerminal("one")
54        self._cpu_freq = cpu_frequency_scaling.CpuFrequencyScalingController(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.one.Execute([
85            "chmod 755 %s" % binary, "LD_LIBRARY_PATH=/data/local/tmp/%s:"
86            "$LD_LIBRARY_PATH %s&" % (bits, binary)
87        ])
88        asserts.assertEqual(len(results[const.STDOUT]), 2)
89        asserts.assertFalse(any(results[const.EXIT_CODE]),
90            "Failed to start the benchmark service.")
91
92        # Runs the benchmark.
93        logging.info("Start to run the benchmark (%s bit mode)", bits)
94        binary = "/data/local/tmp/%s/mq_benchmark_client%s" % (bits, bits)
95
96        results = self.dut.shell.one.Execute([
97            "chmod 755 %s" % binary, "LD_LIBRARY_PATH=/data/local/tmp/%s:"
98            "$LD_LIBRARY_PATH %s" % (bits, binary)
99        ])
100
101        # Stop the benchmark service.
102        self.dut.shell.one.Execute("kill -9 `pidof mq_benchmark_service%s`" % bits)
103
104        # Parses the result.
105        asserts.assertEqual(len(results[const.STDOUT]), 2)
106        asserts.assertFalse(any(results[const.EXIT_CODE]),
107            "FmqPerformanceTest failed.")
108        read_label = []
109        read_latency = []
110        write_label = []
111        write_latency = []
112        stdout_lines = results[const.STDOUT][1].split("\n")
113        for line in stdout_lines:
114            if line.startswith("Average time to read"):
115                read_result = line.replace(
116                    "Average time to read", "").replace(
117                    "bytes", "").replace("ns", "")
118                (label, value) = read_result.split(": ")
119                read_label.append(label)
120                read_latency.append(int(value))
121            if line.startswith("Average time to write"):
122                write_result = line.replace(
123                    "Average time to write ", "").replace(
124                    "bytes", "").replace("ns", "")
125                (label, value) = write_result.split(": ")
126                write_label.append(label)
127                write_latency.append(int(value))
128
129        # To upload to the web DB.
130        self.web.AddProfilingDataLabeledVector(
131            "fmq_read_latency_benchmark_%sbits" % bits,
132            read_label,
133            read_latency,
134            x_axis_label="Message Size (Bytes)",
135            y_axis_label="Average Latency (nanoseconds)")
136
137        self.web.AddProfilingDataLabeledVector(
138            "fmq_write_latency_benchmark_%sbits" % bits,
139            write_label,
140            write_latency,
141            x_axis_label="Message Size (Bytes)",
142            y_axis_label="Average Latency (nanoseconds)")
143
144        # Assertions to check the performance requirements
145        for label, value in zip(read_label, read_latency):
146            if label in self.THRESHOLD[bits]:
147                asserts.assertLess(
148                    value, self.THRESHOLD[bits][label],
149                    "%s ns for %s is longer than the threshold %s ns" % (
150                        value, label, self.THRESHOLD[bits][label]))
151
152        for label, value in zip(write_label, write_latency):
153            if label in self.THRESHOLD[bits]:
154                asserts.assertLess(
155                    value, self.THRESHOLD[bits][label],
156                    "%s ns for %s is longer than the threshold %s ns" % (
157                        value, label, self.THRESHOLD[bits][label]))
158
159if __name__ == "__main__":
160    test_runner.main()
161