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, ¤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 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