OSDN Git Service

Merge "Simpleperf: remove unnecessary exit()." am: dd7f62ed57 am: 5345ea0e4f
[android-x86/system-extras.git] / tests / binder / benchmarks / binderAddInts.cpp
1 /*
2  * Copyright (C) 2010 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
18 /*
19  * Binder add integers benchmark
20  *
21  * Measures the rate at which a short binder IPC operation can be
22  * performed.  The operation consists of the client sending a parcel
23  * that contains two integers.  For each parcel that the server
24  * receives, it adds the two integers and sends the sum back to
25  * the client.
26  *
27  * This benchmark supports the following command-line options:
28  *
29  *   -c cpu - bind client to specified cpu (default: unbound)
30  *   -s cpu - bind server to specified cpu (default: unbound)
31  *   -n num - perform IPC operation num times (default: 1000)
32  *   -d time - delay specified amount of seconds after each
33  *             IPC operation. (default 1e-3)
34  */
35
36 #include <cerrno>
37 #include <grp.h>
38 #include <iostream>
39 #include <iomanip>
40 #include <libgen.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #include <sys/syscall.h>
45 #include <sys/time.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48
49 #include <binder/IPCThreadState.h>
50 #include <binder/ProcessState.h>
51 #include <binder/IServiceManager.h>
52 #include <utils/Log.h>
53 #include <testUtil.h>
54
55 using namespace android;
56 using namespace std;
57
58 const int unbound = -1; // Indicator for a thread not bound to a specific CPU
59
60 String16 serviceName("test.binderAddInts");
61
62 struct options {
63     int serverCPU;
64     int clientCPU;
65     unsigned int iterations;
66     float        iterDelay; // End of iteration delay in seconds
67 } options = { // Set defaults
68     unbound, // Server CPU
69     unbound, // Client CPU
70     1000,    // Iterations
71     1e-3,    // End of iteration delay
72 };
73
74 class AddIntsService : public BBinder
75 {
76   public:
77     AddIntsService(int cpu = unbound);
78     virtual ~AddIntsService() {}
79
80     enum command {
81         ADD_INTS = 0x120,
82     };
83
84     virtual status_t onTransact(uint32_t code,
85                                 const Parcel& data, Parcel* reply,
86                                 uint32_t flags = 0);
87
88   private:
89     int cpu_;
90 };
91
92 struct Duration {
93     double value;
94     friend std::ostream& operator<<(std::ostream& stream, const Duration& d) {
95         static const char *SUFFIXES[] = {"s", "ms", "us", "ns"};
96         size_t suffix = 0;
97         double temp = d.value;
98         while (temp < .1 && suffix < 3) {
99             temp *= 1000;
100             suffix++;
101         }
102         stream << temp << SUFFIXES[suffix];
103         return stream;
104     }
105 };
106
107 // File scope function prototypes
108 static bool server(void);
109 static void client(void);
110 static void bindCPU(unsigned int cpu);
111 static ostream &operator<<(ostream &stream, const String16& str);
112 static ostream &operator<<(ostream &stream, const cpu_set_t& set);
113
114 int main(int argc, char *argv[])
115 {
116     int rv;
117
118     // Determine CPUs available for use.
119     // This testcase limits its self to using CPUs that were
120     // available at the start of the benchmark.
121     cpu_set_t availCPUs;
122     if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
123         cerr << "sched_getaffinity failure, rv: " << rv
124             << " errno: " << errno << endl;
125         exit(1);
126     }
127
128     // Parse command line arguments
129     int opt;
130     while ((opt = getopt(argc, argv, "s:c:n:d:?")) != -1) {
131         char *chptr; // character pointer for command-line parsing
132
133         switch (opt) {
134         case 'c': // client CPU
135         case 's': { // server CPU
136             // Parse the CPU number
137             int cpu = strtoul(optarg, &chptr, 10);
138             if (*chptr != '\0') {
139                 cerr << "Invalid cpu specified for -" << (char) opt
140                     << " option of: " << optarg << endl;
141                 exit(2);
142             }
143
144             // Is the CPU available?
145             if (!CPU_ISSET(cpu, &availCPUs)) {
146                 cerr << "CPU " << optarg << " not currently available" << endl;
147                 cerr << "  Available CPUs: " << availCPUs << endl;
148                 exit(3);
149             }
150
151             // Record the choice
152             *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
153             break;
154         }
155
156         case 'n': // iterations
157             options.iterations = strtoul(optarg, &chptr, 10);
158             if (*chptr != '\0') {
159                 cerr << "Invalid iterations specified of: " << optarg << endl;
160                 exit(4);
161             }
162             if (options.iterations < 1) {
163                 cerr << "Less than 1 iteration specified by: "
164                     << optarg << endl;
165                 exit(5);
166             }
167             break;
168
169         case 'd': // Delay between each iteration
170             options.iterDelay = strtod(optarg, &chptr);
171             if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
172                 cerr << "Invalid delay specified of: " << optarg << endl;
173                 exit(6);
174             }
175             break;
176
177         case '?':
178         default:
179             cerr << basename(argv[0]) << " [options]" << endl;
180             cerr << "  options:" << endl;
181             cerr << "    -s cpu - server CPU number" << endl;
182             cerr << "    -c cpu - client CPU number" << endl;
183             cerr << "    -n num - iterations" << endl;
184             cerr << "    -d time - delay after operation in seconds" << endl;
185             exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
186         }
187     }
188
189     // Display selected options
190     cout << "serverCPU: ";
191     if (options.serverCPU == unbound) {
192         cout << " unbound";
193     } else {
194         cout << options.serverCPU;
195     }
196     cout << endl;
197     cout << "clientCPU: ";
198     if (options.clientCPU == unbound) {
199         cout << " unbound";
200     } else {
201         cout << options.clientCPU;
202     }
203     cout << endl;
204     cout << "iterations: " << options.iterations << endl;
205     cout << "iterDelay: " << options.iterDelay << endl;
206
207     // Fork client, use this process as server
208     fflush(stdout);
209     switch (pid_t pid = fork()) {
210     case 0: // Child
211         client();
212         return 0;
213
214     default: // Parent
215         if (!server()) { break; }
216
217         // Wait for all children to end
218         do {
219             int stat;
220             rv = wait(&stat);
221             if ((rv == -1) && (errno == ECHILD)) { break; }
222             if (rv == -1) {
223                 cerr << "wait failed, rv: " << rv << " errno: "
224                     << errno << endl;
225                 perror(NULL);
226                 exit(8);
227             }
228         } while (1);
229         return 0;
230
231     case -1: // Error
232         exit(9);
233     }
234
235     return 0;
236 }
237
238 static bool server(void)
239 {
240     int rv;
241
242     // Add the service
243     sp<ProcessState> proc(ProcessState::self());
244     sp<IServiceManager> sm = defaultServiceManager();
245     if ((rv = sm->addService(serviceName,
246         new AddIntsService(options.serverCPU))) != 0) {
247         cerr << "addService " << serviceName << " failed, rv: " << rv
248             << " errno: " << errno << endl;
249         return false;
250     }
251
252     // Start threads to handle server work
253     proc->startThreadPool();
254     return true;
255 }
256
257 static void client(void)
258 {
259     int rv;
260     sp<IServiceManager> sm = defaultServiceManager();
261     double min = FLT_MAX, max = 0.0, total = 0.0; // Time in seconds for all
262                                                   // the IPC calls.
263
264     // If needed bind to client CPU
265     if (options.clientCPU != unbound) { bindCPU(options.clientCPU); }
266
267     // Attach to service
268     sp<IBinder> binder;
269     for (int i = 0; i < 3; i++) {
270         binder = sm->getService(serviceName);
271         if (binder != 0) break;
272         cout << serviceName << " not published, waiting..." << endl;
273         usleep(500000); // 0.5 s
274     }
275
276     if (binder == 0) {
277         cout << serviceName << " failed to publish, aborting" << endl;
278         return;
279     }
280
281     // Perform the IPC operations
282     for (unsigned int iter = 0; iter < options.iterations; iter++) {
283         Parcel send, reply;
284
285         // Create parcel to be sent.  Will use the iteration cound
286         // and the iteration count + 3 as the two integer values
287         // to be sent.
288         int val1 = iter;
289         int val2 = iter + 3;
290         int expected = val1 + val2;  // Expect to get the sum back
291         send.writeInt32(val1);
292         send.writeInt32(val2);
293
294         // Send the parcel, while timing how long it takes for
295         // the answer to return.
296         struct timespec start;
297         clock_gettime(CLOCK_MONOTONIC, &start);
298         if ((rv = binder->transact(AddIntsService::ADD_INTS,
299             send, &reply)) != 0) {
300             cerr << "binder->transact failed, rv: " << rv
301                 << " errno: " << errno << endl;
302             exit(10);
303         }
304         struct timespec current;
305         clock_gettime(CLOCK_MONOTONIC, &current);
306
307         // Calculate how long this operation took and update the stats
308         struct timespec deltaTimespec = tsDelta(&start, &current);
309         double delta = ts2double(&deltaTimespec);
310         min = (delta < min) ? delta : min;
311         max = (delta > max) ? delta : max;
312         total += delta;
313         int result = reply.readInt32();
314         if (result != (int) (iter + iter + 3)) {
315             cerr << "Unexpected result for iteration " << iter << endl;
316             cerr << "  result: " << result << endl;
317             cerr << "expected: " << expected << endl;
318         }
319
320         if (options.iterDelay > 0.0) { testDelaySpin(options.iterDelay); }
321     }
322
323     // Display the results
324     cout << fixed << setprecision(2)
325         << "Time per iteration min: " << Duration{min}
326         << " avg: " << Duration{total / options.iterations}
327         << " max: " << Duration{max}
328         << endl;
329 }
330
331 AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
332     if (cpu != unbound) { bindCPU(cpu); }
333 }
334
335 // Server function that handles parcels received from the client
336 status_t AddIntsService::onTransact(uint32_t code, const Parcel &data,
337                                     Parcel* reply, uint32_t /* flags */) {
338     int val1, val2;
339     status_t rv(0);
340     int cpu;
341
342     // If server bound to a particular CPU, check that
343     // were executing on that CPU.
344     if (cpu_ != unbound) {
345         cpu = sched_getcpu();
346         if (cpu != cpu_) {
347             cerr << "server onTransact on CPU " << cpu << " expected CPU "
348                   << cpu_ << endl;
349             exit(20);
350         }
351     }
352
353     // Perform the requested operation
354     switch (code) {
355     case ADD_INTS:
356         val1 = data.readInt32();
357         val2 = data.readInt32();
358         reply->writeInt32(val1 + val2);
359         break;
360
361     default:
362       cerr << "server onTransact unknown code, code: " << code << endl;
363       exit(21);
364     }
365
366     return rv;
367 }
368
369 static void bindCPU(unsigned int cpu)
370 {
371     int rv;
372     cpu_set_t cpuset;
373
374     CPU_ZERO(&cpuset);
375     CPU_SET(cpu, &cpuset);
376     rv = sched_setaffinity(0, sizeof(cpuset), &cpuset);
377
378     if (rv != 0) {
379         cerr << "bindCPU failed, rv: " << rv << " errno: " << errno << endl;
380         perror(NULL);
381         exit(30);
382     }
383 }
384
385 static ostream &operator<<(ostream &stream, const String16& str)
386 {
387     for (unsigned int n1 = 0; n1 < str.size(); n1++) {
388         if ((str[n1] > 0x20) && (str[n1] < 0x80)) {
389             stream << (char) str[n1];
390         } else {
391             stream << '~';
392         }
393     }
394
395     return stream;
396 }
397
398 static ostream &operator<<(ostream &stream, const cpu_set_t& set)
399 {
400     for (unsigned int n1 = 0; n1 < CPU_SETSIZE; n1++) {
401         if (CPU_ISSET(n1, &set)) {
402             if (n1 != 0) { stream << ' '; }
403             stream << n1;
404         }
405     }
406
407     return stream;
408 }