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, ¤t); 300 301 // Calculate how long this operation took and update the stats 302 struct timespec deltaTimespec = tsDelta(&start, ¤t); 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