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