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