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