1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 
17 #include <android-base/logging.h>
18 
19 #include <gtest/gtest.h>
20 #include <utils/StrongPointer.h>
21 #include <chrono>
22 #include <iostream>
23 
24 #include <android/hardware/tests/msgq/1.0/IBenchmarkMsgQ.h>
25 #include <fmq/MessageQueue.h>
26 #include <hidl/ServiceManagement.h>
27 
28 // libutils:
29 using android::OK;
30 using android::sp;
31 using android::status_t;
32 
33 // generated
34 using android::hardware::tests::msgq::V1_0::IBenchmarkMsgQ;
35 using std::cerr;
36 using std::cout;
37 using std::endl;
38 
39 // libhidl
40 using android::hardware::kSynchronizedReadWrite;
41 using android::hardware::MQDescriptorSync;
42 using android::hardware::MessageQueue;
43 using android::hardware::details::waitForHwService;
44 
45 /*
46  * All the benchmark cases will be performed on an FMQ of size kQueueSize.
47  */
48 static const int32_t kQueueSize = 1024 * 16;
49 
50 /*
51  * The number of iterations for each experiment.
52  */
53 static const uint32_t kNumIterations = 1000;
54 
55 /*
56  * The various packet sizes used are as follows.
57  */
58 enum PacketSizes {
59     kPacketSize64 = 64,
60     kPacketSize128 = 128,
61     kPacketSize256 = 256,
62     kPacketSize512 = 512,
63     kPacketSize1024 = 1024
64 };
65 
66 class MQTestClient : public ::testing::Test {
67 protected:
TearDown()68     virtual void TearDown() {
69         delete mFmqInbox;
70         delete mFmqOutbox;
71     }
72 
SetUp()73     virtual void SetUp() {
74         // waitForHwService is required because IBenchmarkMsgQ is not in manifest.xml.
75         // "Real" HALs shouldn't be doing this.
76         waitForHwService(IBenchmarkMsgQ::descriptor, "default");
77         service = IBenchmarkMsgQ::getService();
78         ASSERT_NE(service, nullptr);
79         ASSERT_TRUE(service->isRemote());
80         /*
81          * Request service to configure the client inbox queue.
82          */
83         service->configureClientInboxSyncReadWrite([this](bool ret,
84                                                           const MQDescriptorSync<uint8_t>& in) {
85           ASSERT_TRUE(ret);
86           mFmqInbox = new (std::nothrow) MessageQueue<uint8_t, kSynchronizedReadWrite>(in);
87         });
88 
89         ASSERT_TRUE(mFmqInbox != nullptr);
90         ASSERT_TRUE(mFmqInbox->isValid());
91         /*
92          * Reqeust service to configure the client outbox queue.
93          */
94         service->configureClientOutboxSyncReadWrite([this](bool ret,
95                                                            const MQDescriptorSync<uint8_t>& out) {
96          ASSERT_TRUE(ret);
97           mFmqOutbox = new (std::nothrow) MessageQueue<uint8_t,
98                              kSynchronizedReadWrite>(out);
99         });
100 
101         ASSERT_TRUE(mFmqOutbox != nullptr);
102         ASSERT_TRUE(mFmqOutbox->isValid());
103     }
104 
105     sp<IBenchmarkMsgQ> service;
106     android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite>* mFmqInbox = nullptr;
107     android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite>* mFmqOutbox = nullptr;
108 };
109 
110 /*
111  * Client writes a 64 byte packet into the outbox queue, service reads the
112  * same and
113  * writes the packet into the client's inbox queue. Client reads the packet. The
114  * average time taken for the cycle is measured.
115  */
TEST_F(MQTestClient,BenchMarkMeasurePingPongTransfer)116 TEST_F(MQTestClient, BenchMarkMeasurePingPongTransfer) {
117     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
118     ASSERT_TRUE(data != nullptr);
119     int64_t accumulatedTime = 0;
120     size_t numRoundTrips = 0;
121 
122     /*
123      * This method requests the service to create a thread which reads
124      * from mFmqOutbox and writes into mFmqInbox.
125      */
126     service->benchmarkPingPong(kNumIterations);
127     std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
128             std::chrono::high_resolution_clock::now();
129     while (numRoundTrips < kNumIterations) {
130         while (mFmqOutbox->write(data, kPacketSize64) == 0) {
131         }
132 
133         while (mFmqInbox->read(data, kPacketSize64) == 0) {
134         }
135 
136         numRoundTrips++;
137     }
138     std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
139             std::chrono::high_resolution_clock::now();
140     accumulatedTime += static_cast<int64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
141             timeEnd - timeStart).count());
142     accumulatedTime /= kNumIterations;
143 
144     cout << "Round trip time for " << kPacketSize64 << "bytes: " <<
145          accumulatedTime << "ns" << endl;
146     delete[] data;
147 }
148 
149 /*
150  * Measure the average time taken to read 64 bytes from the queue.
151  */
TEST_F(MQTestClient,BenchMarkMeasureRead64Bytes)152 TEST_F(MQTestClient, BenchMarkMeasureRead64Bytes) {
153     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
154     ASSERT_TRUE(data != nullptr);
155 
156     uint32_t numLoops = kQueueSize / kPacketSize64;
157     uint64_t accumulatedTime = 0;
158     for (uint32_t i = 0; i < kNumIterations; i++) {
159         bool ret = service->requestWrite(kQueueSize);
160         ASSERT_TRUE(ret);
161         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
162                 std::chrono::high_resolution_clock::now();
163         /*
164          * The read() method returns true only if the the correct number of bytes
165          * were succesfully read from the queue.
166          */
167         for (uint32_t j = 0; j < numLoops; j++) {
168             ASSERT_TRUE(mFmqInbox->read(data, kPacketSize64));
169         }
170 
171         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
172                 std::chrono::high_resolution_clock::now();
173         accumulatedTime += (timeEnd - timeStart).count();
174     }
175 
176     accumulatedTime /= (numLoops * kNumIterations);
177     cout << "Average time to read" << kPacketSize64
178          << "bytes: " << accumulatedTime << "ns" << endl;
179     delete[] data;
180 }
181 
182 /*
183  * Measure the average time taken to read 128 bytes.
184  */
TEST_F(MQTestClient,BenchMarkMeasureRead128Bytes)185 TEST_F(MQTestClient, BenchMarkMeasureRead128Bytes) {
186     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize128];
187     ASSERT_TRUE(data != nullptr);
188 
189     uint32_t numLoops = kQueueSize / kPacketSize128;
190     uint64_t accumulatedTime = 0;
191 
192     for (uint32_t i = 0; i < kNumIterations; i++) {
193         bool ret = service->requestWrite(kQueueSize);
194         ASSERT_TRUE(ret);
195         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
196                 std::chrono::high_resolution_clock::now();
197 
198         /*
199          * The read() method returns true only if the the correct number of bytes
200          * were succesfully read from the queue.
201          */
202         for (uint32_t j = 0; j < numLoops; j++) {
203             ASSERT_TRUE(mFmqInbox->read(data, kPacketSize128));
204         }
205         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
206                 std::chrono::high_resolution_clock::now();
207         accumulatedTime += (timeEnd - timeStart).count();
208     }
209 
210     accumulatedTime /= (numLoops * kNumIterations);
211     cout << "Average time to read" << kPacketSize128
212          << "bytes: " << accumulatedTime << "ns" << endl;
213     delete[] data;
214 }
215 
216 /*
217  * Measure the average time taken to read 256 bytes from the queue.
218  */
TEST_F(MQTestClient,BenchMarkMeasureRead256Bytes)219 TEST_F(MQTestClient, BenchMarkMeasureRead256Bytes) {
220     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize256];
221     ASSERT_TRUE(data != nullptr);
222     uint32_t numLoops = kQueueSize / kPacketSize256;
223     uint64_t accumulatedTime = 0;
224 
225     for (uint32_t i = 0; i < kNumIterations; i++) {
226         bool ret = service->requestWrite(kQueueSize);
227         ASSERT_TRUE(ret);
228         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
229                 std::chrono::high_resolution_clock::now();
230         /*
231          * The read() method returns true only if the the correct number of bytes
232          * were succesfully read from the queue.
233          */
234         for (uint32_t j = 0; j < numLoops; j++) {
235             ASSERT_TRUE(mFmqInbox->read(data, kPacketSize256));
236         }
237 
238         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
239                 std::chrono::high_resolution_clock::now();
240         accumulatedTime += (timeEnd - timeStart).count();
241     }
242 
243     accumulatedTime /= (numLoops * kNumIterations);
244     cout << "Average time to read" << kPacketSize256
245          << "bytes: " << accumulatedTime << "ns" << endl;
246     delete[] data;
247 }
248 
249 /*
250  * Measure the average time taken to read 512 bytes from the queue.
251  */
TEST_F(MQTestClient,BenchMarkMeasureRead512Bytes)252 TEST_F(MQTestClient, BenchMarkMeasureRead512Bytes) {
253     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize512];
254     ASSERT_TRUE(data != nullptr);
255     uint32_t numLoops = kQueueSize / kPacketSize512;
256     uint64_t accumulatedTime = 0;
257     for (uint32_t i = 0; i < kNumIterations; i++) {
258         bool ret = service->requestWrite(kQueueSize);
259         ASSERT_TRUE(ret);
260         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
261                 std::chrono::high_resolution_clock::now();
262         /*
263          * The read() method returns true only if the the correct number of bytes
264          * were succesfully read from the queue.
265          */
266         for (uint32_t j = 0; j < numLoops; j++) {
267             ASSERT_TRUE(mFmqInbox->read(data, kPacketSize512));
268         }
269         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
270                 std::chrono::high_resolution_clock::now();
271         accumulatedTime += (timeEnd - timeStart).count();
272     }
273 
274     accumulatedTime /= (numLoops * kNumIterations);
275     cout << "Average time to read" << kPacketSize512
276          << "bytes: " << accumulatedTime << "ns" << endl;
277     delete[] data;
278 }
279 
280 /*
281  * Measure the average time taken to write 64 bytes into the queue.
282  */
TEST_F(MQTestClient,BenchMarkMeasureWrite64Bytes)283 TEST_F(MQTestClient, BenchMarkMeasureWrite64Bytes) {
284     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
285     ASSERT_TRUE(data != nullptr);
286     uint32_t numLoops = kQueueSize / kPacketSize64;
287     uint64_t accumulatedTime = 0;
288 
289     for (uint32_t i = 0; i < kNumIterations; i++) {
290         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
291                 std::chrono::high_resolution_clock::now();
292         /*
293          * Write until the queue is full and request service to empty the queue.
294          */
295         for (uint32_t j = 0; j < numLoops; j++) {
296             bool result = mFmqOutbox->write(data, kPacketSize64);
297             ASSERT_TRUE(result);
298         }
299 
300         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
301                 std::chrono::high_resolution_clock::now();
302         accumulatedTime += (timeEnd - timeStart).count();
303 
304         bool ret = service->requestRead(kQueueSize);
305         ASSERT_TRUE(ret);
306     }
307 
308     accumulatedTime /= (numLoops * kNumIterations);
309     cout << "Average time to write " << kPacketSize64
310          << "bytes: " << accumulatedTime << "ns" << endl;
311     delete[] data;
312 }
313 
314 /*
315  * Measure the average time taken to write 128 bytes into the queue.
316  */
TEST_F(MQTestClient,BenchMarkMeasureWrite128Bytes)317 TEST_F(MQTestClient, BenchMarkMeasureWrite128Bytes) {
318     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize128];
319     ASSERT_TRUE(data != nullptr);
320     uint32_t numLoops = kQueueSize / kPacketSize128;
321     uint64_t accumulatedTime = 0;
322 
323     for (uint32_t i = 0; i < kNumIterations; i++) {
324         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
325                 std::chrono::high_resolution_clock::now();
326         /*
327          * Write until the queue is full and request service to empty the queue.
328          */
329         for (uint32_t j = 0; j < numLoops; j++) {
330             ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize128));
331         }
332 
333         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
334                 std::chrono::high_resolution_clock::now();
335         accumulatedTime += (timeEnd - timeStart).count();
336 
337         bool ret = service->requestRead(kQueueSize);
338         ASSERT_TRUE(ret);
339     }
340 
341     accumulatedTime /= (numLoops * kNumIterations);
342     cout << "Average time to write " << kPacketSize128
343          << "bytes: " << accumulatedTime << "ns" << endl;
344     delete[] data;
345 }
346 
347 /*
348  * Measure the average time taken to write 256 bytes into the queue.
349  */
TEST_F(MQTestClient,BenchMarkMeasureWrite256Bytes)350 TEST_F(MQTestClient, BenchMarkMeasureWrite256Bytes) {
351     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize256];
352     ASSERT_TRUE(data != nullptr);
353     uint32_t numLoops = kQueueSize / kPacketSize256;
354     uint64_t accumulatedTime = 0;
355 
356     for (uint32_t i = 0; i < kNumIterations; i++) {
357         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
358                 std::chrono::high_resolution_clock::now();
359         /*
360          * Write until the queue is full and request service to empty the queue.
361          */
362         for (uint32_t j = 0; j < numLoops; j++) {
363             ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize256));
364         }
365         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
366                 std::chrono::high_resolution_clock::now();
367         accumulatedTime += (timeEnd - timeStart).count();
368 
369         bool ret = service->requestRead(kQueueSize);
370         ASSERT_TRUE(ret);
371     }
372 
373     accumulatedTime /= (numLoops * kNumIterations);
374     cout << "Average time to write " << kPacketSize256
375          << "bytes: " << accumulatedTime << "ns" << endl;
376     delete[] data;
377 }
378 
379 /*
380  * Measure the average time taken to write 512 bytes into the queue.
381  */
TEST_F(MQTestClient,BenchMarkMeasureWrite512Bytes)382 TEST_F(MQTestClient, BenchMarkMeasureWrite512Bytes) {
383     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize512];
384     ASSERT_TRUE(data != nullptr);
385     uint32_t numLoops = kQueueSize / kPacketSize512;
386     uint64_t accumulatedTime = 0;
387 
388     for (uint32_t i = 0; i < kNumIterations; i++) {
389         std::chrono::time_point<std::chrono::high_resolution_clock> timeStart =
390                 std::chrono::high_resolution_clock::now();
391 
392         /*
393          * Write until the queue is full and request service to empty the queue.
394          * The write() method returns true only if the specified number of bytes
395          * were succesfully written.
396          */
397         for (uint32_t j = 0; j < numLoops; j++) {
398             ASSERT_TRUE(mFmqOutbox->write(data, kPacketSize512));
399         }
400 
401         std::chrono::time_point<std::chrono::high_resolution_clock> timeEnd =
402                 std::chrono::high_resolution_clock::now();
403         accumulatedTime += (timeEnd - timeStart).count();
404 
405         bool ret = service->requestRead(kQueueSize);
406         ASSERT_TRUE(ret);
407     }
408 
409     accumulatedTime /= (numLoops * kNumIterations);
410     cout << "Average time to write " << kPacketSize512
411          << "bytes: " << accumulatedTime << "ns" << endl;
412     delete[] data;
413 }
414 
415 /*
416  * Service continuously writes a packet of 64 bytes into the client's inbox
417  * queue
418  * of size 16K. Client keeps reading from the inbox queue. The average write to
419  * read delay is calculated.
420  */
TEST_F(MQTestClient,BenchMarkMeasureServiceWriteClientRead)421 TEST_F(MQTestClient, BenchMarkMeasureServiceWriteClientRead) {
422     uint8_t* data = new (std::nothrow) uint8_t[kPacketSize64];
423     ASSERT_TRUE(data != nullptr);
424     /*
425      * This method causes the service to create a thread which writes
426      * into the mFmqInbox queue kNumIterations packets.
427      */
428     service->benchmarkServiceWriteClientRead(kNumIterations);
429     android::hardware::hidl_vec<int64_t> clientRcvTimeArray;
430     clientRcvTimeArray.resize(kNumIterations);
431     for (uint32_t i = 0; i < kNumIterations; i++) {
432         do {
433             clientRcvTimeArray[i] =
434                     std::chrono::high_resolution_clock::now().time_since_epoch().count();
435         } while (mFmqInbox->read(data, kPacketSize64) == 0);
436     }
437     service->sendTimeData(clientRcvTimeArray);
438     delete[] data;
439 }
440