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);
~AddIntsService()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
main(int argc,char * argv[])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
server(void)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
client(void)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, ¤t);
283
284 // Calculate how long this operation took and update the stats
285 struct timespec deltaTimespec = tsDelta(&start, ¤t);
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
AddIntsService(int cpu)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
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)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
bindCPU(unsigned int cpu)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
operator <<(ostream & stream,const String16 & str)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
operator <<(ostream & stream,const cpu_set_t & set)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 }
385