• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #define LOG_TAG "HwbinderThroughputTest"
17 
18 #include <unistd.h>
19 #include <sys/wait.h>
20 
21 #include <cstring>
22 #include <iostream>
23 #include <string>
24 #include <tuple>
25 #include <vector>
26 
27 #include <log/log.h>
28 
29 #include <android/hardware/tests/libhwbinder/1.0/IBenchmark.h>
30 #include <hidl/HidlSupport.h>
31 #include <hidl/ServiceManagement.h>
32 
33 using namespace std;
34 using namespace android;
35 using namespace android::hardware;
36 
37 // Generated HIDL files
38 using android::hardware::tests::libhwbinder::V1_0::IBenchmark;
39 
40 #define ASSERT_TRUE(cond) \
41 do { \
42     if (!(cond)) {\
43        cerr << __func__ << ":" << __LINE__ << " condition:" << #cond << " failed\n" << endl; \
44        exit(EXIT_FAILURE); \
45     } \
46 } while (0)
47 
48 class Pipe {
49     int m_readFd;
50     int m_writeFd;
Pipe(int readFd,int writeFd)51     Pipe(int readFd, int writeFd)
52             : m_readFd{readFd}, m_writeFd{writeFd} {
53     }
54     Pipe(const Pipe &) = delete;
55     Pipe& operator=(const Pipe &) = delete;
56     Pipe& operator=(const Pipe &&) = delete;
57  public:
Pipe(Pipe && rval)58     Pipe(Pipe&& rval) noexcept {
59         m_readFd = rval.m_readFd;
60         m_writeFd = rval.m_writeFd;
61         rval.m_readFd = 0;
62         rval.m_writeFd = 0;
63     }
~Pipe()64     ~Pipe() {
65         if (m_readFd)
66             close(m_readFd);
67         if (m_writeFd)
68             close(m_writeFd);
69     }
signal()70     void signal() {
71         bool val = true;
72         int error = write(m_writeFd, &val, sizeof(val));
73         ASSERT_TRUE(error >= 0);
74     }
wait()75     void wait() {
76         bool val = false;
77         int error = read(m_readFd, &val, sizeof(val));
78         ASSERT_TRUE(error >= 0);
79     }
send(const T & v)80     template<typename T> void send(const T& v) {
81         int error = write(m_writeFd, &v, sizeof(T));
82         ASSERT_TRUE(error >= 0);
83     }
recv(T & v)84     template<typename T> void recv(T& v) {
85         int error = read(m_readFd, &v, sizeof(T));
86         ASSERT_TRUE(error >= 0);
87     }
createPipePair()88     static tuple<Pipe, Pipe> createPipePair() {
89         int a[2];
90         int b[2];
91 
92         int error1 = pipe(a);
93         int error2 = pipe(b);
94         ASSERT_TRUE(error1 >= 0);
95         ASSERT_TRUE(error2 >= 0);
96 
97         return make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
98     }
99 };
100 
101 static const uint32_t num_buckets = 128;
102 static const uint64_t max_time_bucket = 50ull * 1000000;
103 static const uint64_t time_per_bucket = max_time_bucket / num_buckets;
104 static constexpr float time_per_bucket_ms = time_per_bucket / 1.0E6;
105 
106 struct ProcResults {
107     uint64_t m_best = max_time_bucket;
108     uint64_t m_worst = 0;
109     uint32_t m_buckets[num_buckets] = {0};
110     uint64_t m_transactions = 0;
111     uint64_t m_total_time = 0;
112 
113     // Add a new latency data point and update the aggregation info
114     // e.g. best/worst/total_time.
add_timeProcResults115     void add_time(uint64_t time) {
116         m_buckets[min(time, max_time_bucket - 1) / time_per_bucket] += 1;
117         m_best = min(time, m_best);
118         m_worst = max(time, m_worst);
119         m_transactions += 1;
120         m_total_time += time;
121     }
122     // Combine two sets of latency data points and update the aggregation info.
combineProcResults123     static ProcResults combine(const ProcResults& a, const ProcResults& b) {
124         ProcResults ret;
125         for (uint32_t i = 0; i < num_buckets; i++) {
126             ret.m_buckets[i] = a.m_buckets[i] + b.m_buckets[i];
127         }
128         ret.m_worst = max(a.m_worst, b.m_worst);
129         ret.m_best = min(a.m_best, b.m_best);
130         ret.m_transactions = a.m_transactions + b.m_transactions;
131         ret.m_total_time = a.m_total_time + b.m_total_time;
132         return ret;
133     }
134     // Calculate and report the final aggregated results.
dumpProcResults135     void dump() {
136         double best = (double) m_best / 1.0E6;
137         double worst = (double) m_worst / 1.0E6;
138         double average = (double) m_total_time / m_transactions / 1.0E6;
139         cout << "average:"
140              << average
141              << "ms worst:"
142              << worst
143              << "ms best:"
144              << best
145              << "ms"
146              << endl;
147 
148         uint64_t cur_total = 0;
149         for (uint32_t i = 0; i < num_buckets; i++) {
150             float cur_time = time_per_bucket_ms * i + 0.5f * time_per_bucket_ms;
151             if ((cur_total < 0.5f * m_transactions)
152                 && (cur_total + m_buckets[i] >= 0.5f * m_transactions)) {
153                 cout << "50%: " << cur_time << " ";
154             }
155             if ((cur_total < 0.9f * m_transactions)
156                 && (cur_total + m_buckets[i] >= 0.9f * m_transactions)) {
157                 cout << "90%: " << cur_time << " ";
158             }
159             if ((cur_total < 0.95f * m_transactions)
160                 && (cur_total + m_buckets[i] >= 0.95f * m_transactions)) {
161                 cout << "95%: " << cur_time << " ";
162             }
163             if ((cur_total < 0.99f * m_transactions)
164                 && (cur_total + m_buckets[i] >= 0.99f * m_transactions)) {
165                 cout << "99%: " << cur_time << " ";
166             }
167             cur_total += m_buckets[i];
168         }
169         cout << endl;
170 
171     }
172 };
173 
generateServiceName(int num)174 string generateServiceName(int num) {
175     string serviceName = "hwbinderService" + to_string(num);
176     return serviceName;
177 }
178 
service_fx(const string & serviceName,Pipe p)179 void service_fx(const string &serviceName, Pipe p) {
180     // Start service.
181     sp<IBenchmark> server = IBenchmark::getService(serviceName, true);
182     ALOGD("Registering %s", serviceName.c_str());
183     status_t status = server->registerAsService(serviceName);
184     if (status != ::android::OK) {
185         ALOGE("Failed to register service %s", serviceName.c_str());
186         exit(EXIT_FAILURE);
187     }
188 
189     ALOGD("Starting %s", serviceName.c_str());
190 
191     // Signal service started to master and wait to exit.
192     p.signal();
193     p.wait();
194     exit(EXIT_SUCCESS);
195 }
196 
worker_fx(int num,int iterations,int service_count,bool get_stub,Pipe p)197 void worker_fx(
198         int num,
199         int iterations,
200         int service_count,
201         bool get_stub,
202         Pipe p) {
203     srand(num);
204     p.signal();
205     p.wait();
206 
207     // Get references to test services.
208     vector<sp<IBenchmark>> workers;
209 
210     for (int i = 0; i < service_count; i++) {
211         sp<IBenchmark> service = IBenchmark::getService(
212                 generateServiceName(i), get_stub);
213         ASSERT_TRUE(service != NULL);
214         if (get_stub) {
215             ASSERT_TRUE(!service->isRemote());
216         } else {
217             ASSERT_TRUE(service->isRemote());
218         }
219         workers.push_back(service);
220     }
221 
222     ProcResults results;
223     chrono::time_point<chrono::high_resolution_clock> start, end;
224     // Prepare data to IPC
225     hidl_vec<uint8_t> data_vec;
226     data_vec.resize(16);
227     for (size_t i = 0; i < data_vec.size(); i++) {
228         data_vec[i] = i;
229     }
230     // Run the benchmark.
231     for (int i = 0; i < iterations; i++) {
232         // Randomly pick a service.
233         int target = rand() % service_count;
234 
235         start = chrono::high_resolution_clock::now();
236         Return<void> ret = workers[target]->sendVec(data_vec, [&](const auto &) {});
237         if (!ret.isOk()) {
238             cout << "thread " << num << " failed status: "
239                 << ret.description() << endl;
240             exit(EXIT_FAILURE);
241         }
242         end = chrono::high_resolution_clock::now();
243 
244         uint64_t cur_time = uint64_t(
245                chrono::duration_cast<chrono::nanoseconds>(end - start).count());
246         results.add_time(cur_time);
247     }
248     // Signal completion to master and wait.
249     p.signal();
250     p.wait();
251 
252     // Send results to master and wait for go to exit.
253     p.send(results);
254     p.wait();
255 
256     exit (EXIT_SUCCESS);
257 }
258 
make_service(string service_name)259 Pipe make_service(string service_name) {
260     auto pipe_pair = Pipe::createPipePair();
261     pid_t pid = fork();
262     if (pid) {
263         /* parent */
264         return move(get<0>(pipe_pair));
265     } else {
266         /* child */
267         service_fx(service_name, move(get<1>(pipe_pair)));
268         /* never get here */
269         return move(get<0>(pipe_pair));
270     }
271 }
272 
make_worker(int num,int iterations,int service_count,bool get_stub)273 Pipe make_worker(int num, int iterations, int service_count, bool get_stub) {
274     auto pipe_pair = Pipe::createPipePair();
275     pid_t pid = fork();
276     if (pid) {
277         /* parent */
278         return move(get<0>(pipe_pair));
279     } else {
280         /* child */
281         worker_fx(num, iterations, service_count, get_stub,
282                   move(get<1>(pipe_pair)));
283         /* never get here */
284         return move(get<0>(pipe_pair));
285     }
286 }
287 
wait_all(vector<Pipe> & v)288 void wait_all(vector<Pipe>& v) {
289     for (size_t i = 0; i < v.size(); i++) {
290         v[i].wait();
291     }
292 }
293 
signal_all(vector<Pipe> & v)294 void signal_all(vector<Pipe>& v) {
295     for (size_t i = 0; i < v.size(); i++) {
296         v[i].signal();
297     }
298 }
299 
main(int argc,char * argv[])300 int main(int argc, char *argv[]) {
301     android::hardware::details::setTrebleTestingOverride(true);
302 
303     enum HwBinderMode {
304         kBinderize = 0,
305         kPassthrough = 1,
306     };
307     HwBinderMode mode = HwBinderMode::kBinderize;
308 
309     // Num of workers.
310     int workers = 2;
311     // Num of services.
312     int services = -1;
313     int iterations = 10000;
314 
315     vector<Pipe> worker_pipes;
316     vector<Pipe> service_pipes;
317 
318     // Parse arguments.
319     for (int i = 1; i < argc; i++) {
320         if (string(argv[i]) == "-m") {
321             if (!strcmp(argv[i + 1], "PASSTHROUGH")) {
322                 mode = HwBinderMode::kPassthrough;
323             }
324             i++;
325             continue;
326         }
327         if (string(argv[i]) == "-w") {
328             workers = atoi(argv[i + 1]);
329             i++;
330             continue;
331         }
332         if (string(argv[i]) == "-i") {
333             iterations = atoi(argv[i + 1]);
334             i++;
335             continue;
336         }
337         if (string(argv[i]) == "-s") {
338             services = atoi(argv[i + 1]);
339             i++;
340             continue;
341         }
342     }
343     // If service number is not provided, set it the same as the worker number.
344     if (services == -1) {
345         services = workers;
346     }
347     if (mode == HwBinderMode::kBinderize) {
348         // Create services.
349         vector<pid_t> pIds;
350         for (int i = 0; i < services; i++) {
351             string serviceName = generateServiceName(i);
352             cout << "creating service: " << serviceName << endl;
353             service_pipes.push_back(make_service(serviceName));
354         }
355         // Wait until all services are up.
356         wait_all(service_pipes);
357     }
358 
359     // Create workers (test clients).
360     bool get_stub = mode == HwBinderMode::kBinderize ? false : true;
361     for (int i = 0; i < workers; i++) {
362         worker_pipes.push_back(make_worker(i, iterations, services, get_stub));
363     }
364     // Wait untill all workers are ready.
365     wait_all(worker_pipes);
366 
367     // Run the workers and wait for completion.
368     chrono::time_point<chrono::high_resolution_clock> start, end;
369     cout << "waiting for workers to complete" << endl;
370     start = chrono::high_resolution_clock::now();
371     signal_all(worker_pipes);
372     wait_all(worker_pipes);
373     end = chrono::high_resolution_clock::now();
374 
375     // Calculate overall throughput.
376     double iterations_per_sec = double(iterations * workers)
377         / (chrono::duration_cast < chrono::nanoseconds
378             > (end - start).count() / 1.0E9);
379     cout << "iterations per sec: " << iterations_per_sec << endl;
380 
381     // Collect all results from the workers.
382     cout << "collecting results" << endl;
383     signal_all(worker_pipes);
384     ProcResults tot_results;
385     for (int i = 0; i < workers; i++) {
386         ProcResults tmp_results;
387         worker_pipes[i].recv(tmp_results);
388         tot_results = ProcResults::combine(tot_results, tmp_results);
389     }
390     tot_results.dump();
391 
392     if (mode == HwBinderMode::kBinderize) {
393         // Kill all the services.
394         cout << "killing services" << endl;
395         signal_all(service_pipes);
396         for (int i = 0; i < services; i++) {
397             int status;
398             wait(&status);
399             if (status != 0) {
400                 cout << "nonzero child status" << status << endl;
401             }
402         }
403     }
404     // Kill all the workers.
405     cout << "killing workers" << endl;
406     signal_all(worker_pipes);
407     for (int i = 0; i < workers; i++) {
408         int status;
409         wait(&status);
410         if (status != 0) {
411             cout << "nonzero child status" << status << endl;
412         }
413     }
414     return 0;
415 }
416