wifiLoadScanAssoc.c revision 87dd9e92610d5e7552f5cdb6ab2578035e2210f5
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 * WiFi load, scan, associate, unload stress test 20 * 21 * Repeatedly executes the following sequence: 22 * 23 * 1. Load WiFi driver 24 * 2. Start supplicant 25 * 3. Random delay 26 * 4. Obtain supplicant status (optional) 27 * 5. Stop supplicant 28 * 6. Unload WiFi driver 29 * 30 * The "Obtain supplicant status" step is optional and is pseudo 31 * randomly performed 50% of the time. The default range of 32 * delay after start supplicant is intentionally selected such 33 * that the obtain supplicant status and stop supplicant steps 34 * may be performed while the WiFi driver is still performing a scan 35 * or associate. The default values are given by DEFAULT_DELAY_MIN 36 * and DEFAULT_DELAY_MAX. Other values can be specified through the 37 * use of the -d and -D command-line options. 38 * 39 * Each sequence is refered to as a pass and by default an unlimited 40 * number of passes are performed. An override of the range of passes 41 * to be executed is available through the use of the -s (start) and 42 * -e (end) command-line options. There is also a default time in 43 * which the test executes, which is given by DEFAULT_DURATION and 44 * can be overriden through the use of the -t command-line option. 45 */ 46 47#include <assert.h> 48#include <errno.h> 49#include <libgen.h> 50#include <math.h> 51#include <sched.h> 52#include <stdio.h> 53#include <stdlib.h> 54#include <time.h> 55#include <unistd.h> 56 57#include <sys/syscall.h> 58#include <sys/types.h> 59#include <sys/wait.h> 60 61#include <hardware_legacy/wifi.h> 62 63#define LOG_TAG "wifiLoadScanAssocTest" 64#include <utils/Log.h> 65#include <testUtil.h> 66 67#define DEFAULT_START_PASS 0 68#define DEFAULT_END_PASS 999 69#define DEFAULT_DURATION FLT_MAX // A fairly long time, so that 70 // range of passes will have 71 // precedence 72#define DEFAULT_DELAY_MIN 0.0 // Min delay after start supplicant 73#define DEFAULT_DELAY_MAX 20.0 // Max delay after start supplicant 74#define DELAY_EXP 150.0 // Exponent which determines the 75 // amount by which values closer 76 // to DELAY_MIN are favored. 77 78#define CMD_STATUS "wpa_cli status 2>&1" 79#define CMD_STOP_FRAMEWORK "stop 2>&1" 80#define CMD_START_FRAMEWORK "start 2>&1" 81 82#define MAXSTR 100 83#define MAXCMD 500 84 85typedef unsigned int bool_t; 86#define true (0 == 0) 87#define false (!true) 88 89// Local description of sched_setaffinity(2), sched_getaffinity(2), and 90// getcpu(2) 91#define CPU_SETSIZE 1024 92typedef struct {uint64_t bits[CPU_SETSIZE / 64]; } cpu_set_t; 93int sched_setaffinity(pid_t pid, unsigned int cpusetsize, const cpu_set_t *set); 94int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *set); 95struct getcpu_cache; 96int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache); 97void CPU_CLR(int cpu, cpu_set_t *set); 98int CPU_ISSET(int cpu, const cpu_set_t *set); 99void CPU_SET(int cpu, cpu_set_t *set); 100void CPU_ZERO(cpu_set_t *set); 101 102// File scope variables 103cpu_set_t availCPU; 104unsigned int numAvailCPU; 105float delayMin = DEFAULT_DELAY_MIN; 106float delayMax = DEFAULT_DELAY_MAX; 107bool_t driverLoadedAtStart; 108 109// File scope prototypes 110static void init(void); 111static void execCmd(const char *cmd); 112static void randDelay(void); 113static void randBind(const cpu_set_t *availSet, int *chosenCPU); 114 115/* 116 * Main 117 * 118 * Performs the following high-level sequence of operations: 119 * 120 * 1. Command-line parsing 121 * 122 * 2. Initialization 123 * 124 * 3. Execute passes that repeatedly perform the WiFi load, scan, 125 * associate, unload sequence. 126 * 127 * 4. Restore state of WiFi driver to state it was at the 128 * start of the test. 129 * 130 * 5. Restart framework 131 */ 132int 133main(int argc, char *argv[]) 134{ 135 FILE *fp; 136 int rv, opt; 137 int cpu; 138 char *chptr; 139 unsigned int pass; 140 char cmd[MAXCMD]; 141 float duration = DEFAULT_DURATION; 142 unsigned int startPass = DEFAULT_START_PASS, endPass = DEFAULT_END_PASS; 143 struct timeval startTime, currentTime, delta; 144 145 testSetLogCatTag(LOG_TAG); 146 147 // Parse command line arguments 148 while ((opt = getopt(argc, argv, "d:D:s:e:t:?")) != -1) { 149 switch (opt) { 150 case 'd': // Minimum Delay 151 delayMin = strtod(optarg, &chptr); 152 if ((*chptr != '\0') || (delayMin < 0.0)) { 153 testPrintE("Invalid command-line specified minimum delay " 154 "of: %s", optarg); 155 exit(1); 156 } 157 break; 158 159 case 'D': // Maximum Delay 160 delayMax = strtod(optarg, &chptr); 161 if ((*chptr != '\0') || (delayMax < 0.0)) { 162 testPrintE("Invalid command-line specified maximum delay " 163 "of: %s", optarg); 164 exit(2); 165 } 166 break; 167 168 case 't': // Duration 169 duration = strtod(optarg, &chptr); 170 if ((*chptr != '\0') || (duration < 0.0)) { 171 testPrintE("Invalid command-line specified duration of: %s", 172 optarg); 173 exit(3); 174 } 175 break; 176 177 case 's': // Starting Pass 178 startPass = strtoul(optarg, &chptr, 10); 179 if (*chptr != '\0') { 180 testPrintE("Invalid command-line specified starting pass " 181 "of: %s", optarg); 182 exit(4); 183 } 184 break; 185 186 case 'e': // Ending Pass 187 endPass = strtoul(optarg, &chptr, 10); 188 if (*chptr != '\0') { 189 testPrintE("Invalid command-line specified ending pass " 190 "of: %s", optarg); 191 exit(5); 192 } 193 break; 194 195 case '?': 196 default: 197 testPrintE(" %s [options]", basename(argv[0])); 198 testPrintE(" options:"); 199 testPrintE(" -s Starting pass"); 200 testPrintE(" -e Ending pass"); 201 testPrintE(" -t Duration"); 202 testPrintE(" -d Delay min"); 203 testPrintE(" -D Delay max"); 204 exit((opt == '?') ? 0 : 6); 205 } 206 } 207 if (delayMax < delayMin) { 208 testPrintE("Unexpected maximum delay less than minimum delay"); 209 testPrintE(" delayMin: %f delayMax: %f", delayMin, delayMax); 210 exit(7); 211 } 212 if (endPass < startPass) { 213 testPrintE("Unexpected ending pass before starting pass"); 214 testPrintE(" startPass: %u endPass: %u", startPass, endPass); 215 exit(8); 216 } 217 if (argc != optind) { 218 testPrintE("Unexpected command-line postional argument"); 219 testPrintE(" %s [-s start_pass] [-e end_pass] [-d duration]", 220 basename(argv[0])); 221 exit(9); 222 } 223 testPrintI("duration: %g", duration); 224 testPrintI("startPass: %u", startPass); 225 testPrintI("endPass: %u", endPass); 226 testPrintI("delayMin: %f", delayMin); 227 testPrintI("delayMax: %f", delayMax); 228 229 init(); 230 231 // For each pass 232 gettimeofday(&startTime, NULL); 233 for (pass = startPass; pass <= endPass; pass++) { 234 // Stop if duration of work has already been performed 235 gettimeofday(¤tTime, NULL); 236 delta = tvDelta(&startTime, ¤tTime); 237 if (tv2double(&delta) > duration) { break; } 238 239 testPrintI("==== pass %u", pass); 240 241 // Use a pass dependent sequence of random numbers 242 srand48(pass); 243 244 // Load WiFi Driver 245 randBind(&availCPU, &cpu); 246 if ((rv = wifi_load_driver()) != 0) { 247 testPrintE("CPU: %i wifi_load_driver() failed, rv: %i\n", 248 cpu, rv); 249 exit(20); 250 } 251 testPrintI("CPU: %i wifi_load_driver succeeded", cpu); 252 253 // Start Supplicant 254 randBind(&availCPU, &cpu); 255 if ((rv = wifi_start_supplicant()) != 0) { 256 testPrintE("CPU: %i wifi_start_supplicant() failed, rv: %i\n", 257 cpu, rv); 258 exit(21); 259 } 260 testPrintI("CPU: %i wifi_start_supplicant succeeded", cpu); 261 262 // Sleep a random amount of time 263 randDelay(); 264 265 /* 266 * Obtain WiFi Status 267 * Half the time skip this step, which helps increase the 268 * level of randomization. 269 */ 270 if (testRandBool()) { 271 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS); 272 if (rv >= (signed) sizeof(cmd) - 1) { 273 testPrintE("Command too long for: %s\n", CMD_STATUS); 274 exit(22); 275 } 276 execCmd(cmd); 277 } 278 279 // Stop Supplicant 280 randBind(&availCPU, &cpu); 281 if ((rv = wifi_stop_supplicant()) != 0) { 282 testPrintE("CPU: %i wifi_stop_supplicant() failed, rv: %i\n", 283 cpu, rv); 284 exit(23); 285 } 286 testPrintI("CPU: %i wifi_stop_supplicant succeeded", cpu); 287 288 // Unload WiFi Module 289 randBind(&availCPU, &cpu); 290 if ((rv = wifi_unload_driver()) != 0) { 291 testPrintE("CPU: %i wifi_unload_driver() failed, rv: %i\n", 292 cpu, rv); 293 exit(24); 294 } 295 testPrintI("CPU: %i wifi_unload_driver succeeded", cpu); 296 } 297 298 // If needed restore WiFi driver to state it was in at the 299 // start of the test. It is assumed that it the driver 300 // was loaded, then the wpa_supplicant was also running. 301 if (driverLoadedAtStart) { 302 // Load driver 303 if ((rv = wifi_load_driver()) != 0) { 304 testPrintE("main load driver failed, rv: %i", rv); 305 exit(25); 306 } 307 308 // Start supplicant 309 if ((rv = wifi_start_supplicant()) != 0) { 310 testPrintE("main start supplicant failed, rv: %i", rv); 311 exit(26); 312 } 313 314 // Obtain WiFi Status 315 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS); 316 if (rv >= (signed) sizeof(cmd) - 1) { 317 testPrintE("Command too long for: %s\n", CMD_STATUS); 318 exit(22); 319 } 320 execCmd(cmd); 321 } 322 323 // Start framework 324 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK); 325 if (rv >= (signed) sizeof(cmd) - 1) { 326 testPrintE("Command too long for: %s\n", CMD_START_FRAMEWORK); 327 exit(27); 328 } 329 execCmd(cmd); 330 331 return 0; 332} 333 334/* 335 * Initialize 336 * 337 * Perform testcase initialization, which includes: 338 * 339 * 1. Determine which CPUs are available for use 340 * 341 * 2. Determine total number of available CPUs 342 * 343 * 3. Stop framework 344 * 345 * 4. Determine whether WiFi driver is loaded and if so 346 * stop wpa_supplicant and unload WiFi driver. 347 */ 348void 349init(void) 350{ 351 int rv; 352 unsigned int n1; 353 char cmd[MAXCMD]; 354 355 // Use whichever CPUs are available at start of test 356 rv = sched_getaffinity(0, sizeof(availCPU), &availCPU); 357 if (rv != 0) { 358 testPrintE("init sched_getaffinity failed, rv: %i errno: %i", 359 rv, errno); 360 exit(40); 361 } 362 363 // How many CPUs are available 364 numAvailCPU = 0; 365 for (n1 = 0; n1 < CPU_SETSIZE; n1++) { 366 if (CPU_ISSET(n1, &availCPU)) { numAvailCPU++; } 367 } 368 testPrintI("numAvailCPU: %u", numAvailCPU); 369 370 // Stop framework 371 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK); 372 if (rv >= (signed) sizeof(cmd) - 1) { 373 testPrintE("Command too long for: %s\n", CMD_STOP_FRAMEWORK); 374 exit(41); 375 } 376 execCmd(cmd); 377 378 // Is WiFi driver loaded? 379 // If so stop the wpa_supplicant and unload the driver. 380 driverLoadedAtStart = is_wifi_driver_loaded(); 381 testPrintI("driverLoadedAtStart: %u", driverLoadedAtStart); 382 if (driverLoadedAtStart) { 383 // Stop wpa_supplicant 384 // Might already be stopped, in which case request should 385 // return immediately with success. 386 if ((rv = wifi_stop_supplicant()) != 0) { 387 testPrintE("init stop supplicant failed, rv: %i", rv); 388 exit(42); 389 } 390 testPrintI("Stopped wpa_supplicant"); 391 392 if ((rv = wifi_unload_driver()) != 0) { 393 testPrintE("init unload driver failed, rv: %i", rv); 394 exit(43); 395 } 396 testPrintI("WiFi driver unloaded"); 397 } 398 399} 400 401/* 402 * Execute Command 403 * 404 * Executes the command pointed to by cmd. Which CPU executes the 405 * command is randomly selected from the set of CPUs that were 406 * available during testcase initialization. Output from the 407 * executed command is captured and sent to LogCat Info. Once 408 * the command has finished execution, it's exit status is captured 409 * and checked for an exit status of zero. Any other exit status 410 * causes diagnostic information to be printed and an immediate 411 * testcase failure. 412 */ 413void 414execCmd(const char *cmd) 415{ 416 FILE *fp; 417 int rv; 418 int status; 419 char str[MAXSTR]; 420 int cpu; 421 422 // Randomly bind to one of the available CPUs 423 randBind(&availCPU, &cpu); 424 425 // Display CPU executing on and command to be executed 426 testPrintI("CPU: %u cmd: %s", cpu, cmd); 427 428 // Execute the command 429 fflush(stdout); 430 if ((fp = popen(cmd, "r")) == NULL) { 431 testPrintE("execCmd popen failed, errno: %i", errno); 432 exit(61); 433 } 434 435 // Obtain and display each line of output from the executed command 436 while (fgets(str, sizeof(str), fp) != NULL) { 437 if ((strlen(str) > 1) && (str[strlen(str) - 1] == '\n')) { 438 str[strlen(str) - 1] = '\0'; 439 } 440 testPrintI(" out: %s", str); 441 delay(0.1); 442 } 443 444 // Obtain and check return status of executed command. 445 // Fail on non-zero exit status 446 status = pclose(fp); 447 if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) { 448 testPrintE("Unexpected command failure"); 449 testPrintE(" status: %#x", status); 450 if (WIFEXITED(status)) { 451 testPrintE("WEXITSTATUS: %i", WEXITSTATUS(status)); 452 } 453 if (WIFSIGNALED(status)) { 454 testPrintE("WTERMSIG: %i", WTERMSIG(status)); 455 } 456 exit(62); 457 } 458} 459 460/* 461 * Random Delay 462 * 463 * Delays for a random amount of time within the range given 464 * by the file scope variables delayMin and delayMax. The 465 * selected amount of delay can come from any part of the 466 * range, with a bias towards values closer to delayMin. 467 * The amount of bias is determined by the setting of DELAY_EXP. 468 * The setting of DELAY_EXP should always be > 1.0, with higher 469 * values causing a more significant bias toward the value 470 * of delayMin. 471 */ 472void randDelay(void) 473{ 474 const unsigned long nanosecspersec = 1000000000; 475 float fract, biasedFract, amt; 476 struct timespec remaining; 477 struct timeval start, current, delta; 478 479 // Obtain start time 480 gettimeofday(&start, NULL); 481 482 // Determine random amount to sleep. 483 // Values closer to delayMin are prefered by an amount 484 // determined by the value of DELAY_EXP. 485 fract = (double) lrand48() / (double) (1UL << 31); 486 biasedFract = pow(DELAY_EXP, fract) / pow(DELAY_EXP, 1.0); 487 amt = delayMin + ((delayMax - delayMin) * biasedFract); 488 489 do { 490 // Get current time 491 gettimeofday(¤t, NULL); 492 493 // How much time is left 494 delta = tvDelta(&start, ¤t); 495 if (tv2double(&delta) > amt) { break; } 496 497 // Request to sleep for the remaining time 498 remaining = double2ts(amt - tv2double(&delta)); 499 (void) nanosleep(&remaining, NULL); 500 } while (true); 501 502 testPrintI("delay: %.2f", 503 (float) (tv2double(¤t) - tv2double(&start))); 504} 505 506static void 507randBind(const cpu_set_t *availSet, int *chosenCPU) 508{ 509 int rv; 510 cpu_set_t cpuset; 511 unsigned int chosenAvail, avail, cpu, currentCPU; 512 513 // Randomly bind to a CPU 514 // Lower 16 bits from random number generator thrown away, 515 // because the low-order bits tend to have the same sequence for 516 // different seed values. 517 chosenAvail = (lrand48() >> 16) % numAvailCPU; 518 CPU_ZERO(&cpuset); 519 avail = 0; 520 for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { 521 if (CPU_ISSET(cpu, availSet)) { 522 if (chosenAvail == avail) { 523 CPU_SET(cpu, &cpuset); 524 break; 525 } 526 avail++; 527 } 528 } 529 assert(cpu < CPU_SETSIZE); 530 sched_setaffinity(0, sizeof(cpuset), &cpuset); 531 532 // Confirm executing on requested CPU 533 if ((rv = getcpu(¤tCPU, NULL, NULL)) != 0) { 534 testPrintE("randBind getcpu() failed, rv: %i errno: %i", rv, errno); 535 exit(80); 536 537 } 538 if (currentCPU != cpu) { 539 testPrintE("randBind executing on unexpected CPU %i, expected %i", 540 currentCPU, cpu); 541 exit(81); 542 } 543 544 // Let the caller know which CPU was chosen 545 *chosenCPU = cpu; 546} 547 548// Local implementation of sched_setaffinity(2), sched_getaffinity(2), and 549// getcpu(2) 550int 551sched_setaffinity(pid_t pid, unsigned int cpusetsize, const cpu_set_t *set) 552{ 553 int rv; 554 555 rv = syscall(__NR_sched_setaffinity, pid, cpusetsize, set); 556 557 return rv; 558} 559 560int 561sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *set) 562{ 563 int rv; 564 565 rv = syscall(__NR_sched_getaffinity, pid, cpusetsize, set); 566 if (rv < 0) { return rv; } 567 568 // Kernel implementation of sched_getaffinity() returns the number 569 // of bytes in the set that it set. Set the rest of our set bits 570 // to 0. 571 memset(((char *) set) + rv, 0x00, sizeof(cpu_set_t) - rv); 572 573 return 0; 574} 575 576int 577getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache) 578{ 579 int rv; 580 581 rv = syscall(345, cpu, node, tcache); 582 583 return rv; 584} 585 586void 587CPU_CLR(int cpu, cpu_set_t *set) 588{ 589 if (cpu < 0) { return; } 590 if ((unsigned) cpu >= (sizeof(cpu_set_t) * CHAR_BIT)) { return; } 591 592 *((uint64_t *)set + (cpu / 64)) &= ~(1ULL << (cpu % 64)); 593} 594 595int 596CPU_ISSET(int cpu, const cpu_set_t *set) 597{ 598 if (cpu < 0) { return 0; } 599 if ((unsigned) cpu >= (sizeof(cpu_set_t) * CHAR_BIT)) { return 0; } 600 601 if ((*((uint64_t *)set + (cpu / 64))) & (1ULL << (cpu % 64))) { 602 return true; 603 } 604 605 return false; 606} 607 608void 609CPU_SET(int cpu, cpu_set_t *set) 610{ 611 if (cpu < 0) { return; } 612 if ((unsigned) cpu > (sizeof(cpu_set_t) * CHAR_BIT)) { return; } 613 614 *((uint64_t *)set + (cpu / 64)) |= 1ULL << (cpu % 64); 615} 616 617void 618CPU_ZERO(cpu_set_t *set) 619{ 620 memset(set, 0x00, sizeof(cpu_set_t)); 621} 622