binderAddInts.cpp revision d2447fd2505466a8c30cdca247325f79ba95be34
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
54using namespace android;
55using namespace std;
56
57const int unbound = -1; // Indicator for a thread not bound to a specific CPU
58
59String16 serviceName("test.binderAddInts");
60
61struct 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
73class 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// Workaround for missing sched_setaffinity(2) and getcpu(2)
92#define CPU_SETSIZE 1024
93struct getcpu_cache;
94typedef struct { uint64_t bits[CPU_SETSIZE / 64]; } cpu_set_t;
95static int sched_getaffinity(pid_t pid, unsigned int cpusetsize,
96                             cpu_set_t *set);
97static int sched_setaffinity(pid_t pid, unsigned int cpusetsize,
98                             cpu_set_t *mask);
99static int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache);
100static int CPU_ISSET(int cpu, const cpu_set_t *set);
101static void CPU_SET(int cpu, cpu_set_t *set);
102static void CPU_ZERO(cpu_set_t *set);
103
104// File scope function prototypes
105static void server(void);
106static void client(void);
107static void bindCPU(unsigned int cpu);
108static ostream &operator<<(ostream &stream, const String16& str);
109static ostream &operator<<(ostream &stream, const cpu_set_t& set);
110
111int main(int argc, char *argv[])
112{
113    int rv;
114
115    // Determine CPUs available for use.
116    // This testcase limits its self to using CPUs that were
117    // available at the start of the benchmark.
118    cpu_set_t availCPUs;
119    if ((rv = sched_getaffinity(0, sizeof(availCPUs), &availCPUs)) != 0) {
120        cerr << "sched_getaffinity failure, rv: " << rv
121            << " errno: " << errno << endl;
122        exit(1);
123    }
124
125    // Parse command line arguments
126    int opt;
127    while ((opt = getopt(argc, argv, "s:c:n:d:?")) != -1) {
128        char *chptr; // character pointer for command-line parsing
129
130        switch (opt) {
131        case 'c': // client CPU
132        case 's': { // server CPU
133            // Parse the CPU number
134            int cpu = strtoul(optarg, &chptr, 10);
135            if (*chptr != '\0') {
136                cerr << "Invalid cpu specified for -" << (char) opt
137                    << " option of: " << optarg << endl;
138                exit(2);
139            }
140
141            // Is the CPU available?
142            if (!CPU_ISSET(cpu, &availCPUs)) {
143                cerr << "CPU " << optarg << " not currently available" << endl;
144                cerr << "  Available CPUs: " << availCPUs << endl;
145                exit(3);
146            }
147
148            // Record the choice
149            *((opt == 'c') ? &options.clientCPU : &options.serverCPU) = cpu;
150            break;
151        }
152
153        case 'n': // iterations
154            options.iterations = strtoul(optarg, &chptr, 10);
155            if (*chptr != '\0') {
156                cerr << "Invalid iterations specified of: " << optarg << endl;
157                exit(4);
158            }
159            if (options.iterations < 1) {
160                cerr << "Less than 1 iteration specified by: "
161                    << optarg << endl;
162                exit(5);
163            }
164            break;
165
166        case 'd': // Delay between each iteration
167            options.iterDelay = strtod(optarg, &chptr);
168            if ((*chptr != '\0') || (options.iterDelay < 0.0)) {
169                cerr << "Invalid delay specified of: " << optarg << endl;
170                exit(6);
171            }
172            break;
173
174        case '?':
175        default:
176            cerr << basename(argv[0]) << " [options]" << endl;
177            cerr << "  options:" << endl;
178            cerr << "    -s cpu - server CPU number" << endl;
179            cerr << "    -c cpu - client CPU number" << endl;
180            cerr << "    -n num - iterations" << endl;
181            cerr << "    -d time - delay after operation in seconds" << endl;
182            exit(((optopt == 0) || (optopt == '?')) ? 0 : 7);
183        }
184    }
185
186    // Display selected options
187    cout << "serverCPU: ";
188    if (options.serverCPU == unbound) {
189        cout << " unbound";
190    } else {
191        cout << options.serverCPU;
192    }
193    cout << endl;
194    cout << "clientCPU: ";
195    if (options.clientCPU == unbound) {
196        cout << " unbound";
197    } else {
198        cout << options.clientCPU;
199    }
200    cout << endl;
201    cout << "iterations: " << options.iterations << endl;
202    cout << "iterDelay: " << options.iterDelay << endl;
203
204    // Fork client, use this process as server
205    fflush(stdout);
206    switch (pid_t pid = fork()) {
207    case 0: // Child
208        client();
209        return 0;
210
211    default: // Parent
212        server();
213
214        // Wait for all children to end
215        do {
216            int stat;
217            rv = wait(&stat);
218            if ((rv == -1) && (errno == ECHILD)) { break; }
219            if (rv == -1) {
220                cerr << "wait failed, rv: " << rv << " errno: "
221                    << errno << endl;
222                perror(NULL);
223                exit(8);
224            }
225        } while (1);
226        return 0;
227
228    case -1: // Error
229        exit(9);
230    }
231
232    return 0;
233}
234
235static void server(void)
236{
237    int rv;
238
239    // Add the service
240    sp<ProcessState> proc(ProcessState::self());
241    sp<IServiceManager> sm = defaultServiceManager();
242    if ((rv = sm->addService(serviceName,
243        new AddIntsService(options.serverCPU))) != 0) {
244        cerr << "addService " << serviceName << " failed, rv: " << rv
245            << " errno: " << errno << endl;
246    }
247
248    // Start threads to handle server work
249    proc->startThreadPool();
250}
251
252static void client(void)
253{
254    int rv;
255    sp<IServiceManager> sm = defaultServiceManager();
256    double min = FLT_MAX, max = 0.0, total = 0.0; // Time in seconds for all
257                                                  // the IPC calls.
258
259    // If needed bind to client CPU
260    if (options.clientCPU != unbound) { bindCPU(options.clientCPU); }
261
262    // Attach to service
263    sp<IBinder> binder;
264    do {
265        binder = sm->getService(serviceName);
266        if (binder != 0) break;
267        cout << serviceName << " not published, waiting..." << endl;
268        usleep(500000); // 0.5 s
269    } while(true);
270
271    // Perform the IPC operations
272    for (unsigned int iter = 0; iter < options.iterations; iter++) {
273        Parcel send, reply;
274
275        // Create parcel to be sent.  Will use the iteration cound
276        // and the iteration count + 3 as the two integer values
277        // to be sent.
278        int val1 = iter;
279        int val2 = iter + 3;
280        int expected = val1 + val2;  // Expect to get the sum back
281        send.writeInt32(val1);
282        send.writeInt32(val2);
283
284        // Send the parcel, while timing how long it takes for
285        // the answer to return.
286        struct timespec start;
287        clock_gettime(CLOCK_MONOTONIC, &start);
288        if ((rv = binder->transact(AddIntsService::ADD_INTS,
289            send, &reply)) != 0) {
290            cerr << "binder->transact failed, rv: " << rv
291                << " errno: " << errno << endl;
292            exit(10);
293        }
294        struct timespec current;
295        clock_gettime(CLOCK_MONOTONIC, &current);
296
297        // Calculate how long this operation took and update the stats
298        struct timespec deltaTimespec = tsDelta(&start, &current);
299        double delta = ts2double(&deltaTimespec);
300        min = (delta < min) ? delta : min;
301        max = (delta > max) ? delta : max;
302        total += delta;
303        int result = reply.readInt32();
304        if (result != (int) (iter + iter + 3)) {
305            cerr << "Unexpected result for iteration " << iter << endl;
306            cerr << "  result: " << result << endl;
307            cerr << "expected: " << expected << endl;
308        }
309
310        if (options.iterDelay > 0.0) { delay(options.iterDelay); }
311    }
312
313    // Display the results
314    cout << "Time per iteration min: " << min
315        << " avg: " << (total / options.iterations)
316        << " max: " << max
317        << endl;
318}
319
320AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
321    if (cpu != unbound) { bindCPU(cpu); }
322};
323
324// Server function that handles parcels received from the client
325status_t AddIntsService::onTransact(uint32_t code, const Parcel &data,
326                                    Parcel* reply, uint32_t flags) {
327    int val1, val2;
328    status_t rv(0);
329    int cpu;
330
331    // If server bound to a particular CPU, check that
332    // were executing on that CPU.
333    if (cpu_ != unbound) {
334        getcpu((unsigned int *) &cpu, NULL, NULL);
335        if (cpu != cpu_) {
336            cerr << "server onTransact on CPU " << cpu << " expected CPU "
337                  << cpu_ << endl;
338            exit(20);
339        }
340    }
341
342    // Perform the requested operation
343    switch (code) {
344    case ADD_INTS:
345        val1 = data.readInt32();
346        val2 = data.readInt32();
347        reply->writeInt32(val1 + val2);
348        break;
349
350    default:
351      cerr << "server onTransact unknown code, code: " << code << endl;
352      exit(21);
353    }
354
355    return rv;
356}
357
358static void bindCPU(unsigned int cpu)
359{
360    int rv;
361    cpu_set_t cpuset;
362
363    CPU_ZERO(&cpuset);
364    CPU_SET(cpu, &cpuset);
365    rv = sched_setaffinity(0, sizeof(cpuset), &cpuset);
366
367    if (rv != 0) {
368        cerr << "bindCPU failed, rv: " << rv << " errno: " << errno << endl;
369        perror(NULL);
370        exit(30);
371    }
372}
373
374static ostream &operator<<(ostream &stream, const String16& str)
375{
376    for (unsigned int n1 = 0; n1 < str.size(); n1++) {
377        if ((str[n1] > 0x20) && (str[n1] < 0x80)) {
378            stream << (char) str[n1];
379        } else {
380            stream << '~';
381        }
382    }
383
384    return stream;
385}
386
387static ostream &operator<<(ostream &stream, const cpu_set_t& set)
388{
389    for (unsigned int n1 = 0; n1 < CPU_SETSIZE; n1++) {
390        if (CPU_ISSET(n1, &set)) {
391            if (n1 != 0) { stream << ' '; }
392            stream << n1;
393        }
394    }
395
396    return stream;
397}
398
399// ======== Local implementation of system calls with missing bionic call stubs
400static int sched_getaffinity(pid_t pid, unsigned int cpusetsize,
401                             cpu_set_t *set)
402{
403    int rv;
404
405    rv = syscall(__NR_sched_getaffinity, pid, cpusetsize, set);
406    if (rv < 0) { return rv; }
407
408    // Kernel implementation of sched_getaffinity() returns the number
409    // of bytes in the set that it set.  Set the rest of our set bits
410    // to 0.
411    memset(((char *) set) + rv, 0x00, sizeof(cpu_set_t) - rv);
412
413    return 0;
414}
415
416static int sched_setaffinity(pid_t pid, unsigned int cpusetsize,
417                             cpu_set_t *mask)
418{
419    int rv;
420
421    rv = syscall(__NR_sched_setaffinity, pid, cpusetsize, mask);
422
423    return rv;
424}
425
426static int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
427{
428    int rv;
429
430    rv = syscall(345, cpu, node, tcache);
431
432    return rv;
433}
434
435static int CPU_ISSET(int cpu, const cpu_set_t *set)
436{
437    if (cpu < 0) { return 0; }
438    if ((unsigned) cpu >= (sizeof(cpu_set_t) * CHAR_BIT)) { return 0; }
439
440    if ((*((uint64_t *)set + (cpu / 64))) & (1ULL << (cpu % 64))) {
441        return true;
442    }
443
444    return false;
445}
446
447static void CPU_SET(int cpu, cpu_set_t *set)
448{
449    if (cpu < 0) { return; }
450    if ((unsigned) cpu > (sizeof(cpu_set_t) * CHAR_BIT)) { return; }
451
452    *((uint64_t *)set + (cpu / 64)) |= 1ULL << (cpu % 64);
453}
454
455static void CPU_ZERO(cpu_set_t *set)
456{
457    memset(set, 0x00, sizeof(cpu_set_t));
458}
459