OSDN Git Service

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