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// File scope function prototypes
92static void server(void);
93static void client(void);
94static void bindCPU(unsigned int cpu);
95static ostream &operator<<(ostream &stream, const String16& str);
96static ostream &operator<<(ostream &stream, const cpu_set_t& set);
97
98int 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
222static 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
239static 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
307AddIntsService::AddIntsService(int cpu): cpu_(cpu) {
308    if (cpu != unbound) { bindCPU(cpu); }
309};
310
311// Server function that handles parcels received from the client
312status_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
345static 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
361static 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
374static 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}
385