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