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 87 typedef unsigned int bool_t; 88 #define true (0 == 0) 89 #define false (!true) 90 91 // File scope variables 92 cpu_set_t availCPU; 93 unsigned int numAvailCPU; 94 float delayMin = DEFAULT_DELAY_MIN; 95 float delayMax = DEFAULT_DELAY_MAX; 96 bool_t driverLoadedAtStart; 97 98 // Command-line mutual exclusion detection flags. 99 // Corresponding flag set true once an option is used. 100 bool_t eFlag, sFlag, pFlag; 101 102 // File scope prototypes 103 static void init(void); 104 static void randDelay(void); 105 static void randBind(const cpu_set_t *availSet, int *chosenCPU); 106 107 /* 108 * Main 109 * 110 * Performs the following high-level sequence of operations: 111 * 112 * 1. Command-line parsing 113 * 114 * 2. Initialization 115 * 116 * 3. Execute passes that repeatedly perform the WiFi load, scan, 117 * associate, unload sequence. 118 * 119 * 4. Restore state of WiFi driver to state it was at the 120 * start of the test. 121 * 122 * 5. Restart framework 123 */ 124 int 125 main(int argc, char *argv[]) 126 { 127 FILE *fp; 128 int rv, opt; 129 int cpu; 130 char *chptr; 131 unsigned int pass; 132 char cmd[MAXCMD]; 133 float duration = DEFAULT_DURATION; 134 unsigned int startPass = DEFAULT_START_PASS, endPass = DEFAULT_END_PASS; 135 struct timeval startTime, currentTime, delta; 136 137 testSetLogCatTag(LOG_TAG); 138 139 // Parse command line arguments 140 while ((opt = getopt(argc, argv, "d:D:s:e:p:t:?")) != -1) { 141 switch (opt) { 142 case 'd': // Minimum Delay 143 delayMin = strtod(optarg, &chptr); 144 if ((*chptr != '\0') || (delayMin < 0.0)) { 145 testPrintE("Invalid command-line specified minimum delay " 146 "of: %s", optarg); 147 exit(1); 148 } 149 break; 150 151 case 'D': // Maximum Delay 152 delayMax = strtod(optarg, &chptr); 153 if ((*chptr != '\0') || (delayMax < 0.0)) { 154 testPrintE("Invalid command-line specified maximum delay " 155 "of: %s", optarg); 156 exit(2); 157 } 158 break; 159 160 case 't': // Duration 161 duration = strtod(optarg, &chptr); 162 if ((*chptr != '\0') || (duration < 0.0)) { 163 testPrintE("Invalid command-line specified duration of: %s", 164 optarg); 165 exit(3); 166 } 167 break; 168 169 case 's': // Starting Pass 170 if (sFlag || pFlag) { 171 testPrintE("Invalid combination of command-line options,"); 172 if (sFlag) { 173 testPrintE(" -s flag specified multiple times."); 174 } else { 175 testPrintE(" -s and -p flags are mutually exclusive."); 176 } 177 exit(10); 178 } 179 sFlag = true; 180 startPass = strtoul(optarg, &chptr, 10); 181 if (*chptr != '\0') { 182 testPrintE("Invalid command-line specified starting pass " 183 "of: %s", optarg); 184 exit(4); 185 } 186 break; 187 188 case 'e': // Ending Pass 189 if (eFlag || pFlag) { 190 testPrintE("Invalid combination of command-line options,"); 191 if (sFlag) { 192 testPrintE(" -e flag specified multiple times."); 193 } else { 194 testPrintE(" -e and -p flags are mutually exclusive."); 195 } 196 exit(11); 197 } 198 eFlag = true; 199 endPass = strtoul(optarg, &chptr, 10); 200 if (*chptr != '\0') { 201 testPrintE("Invalid command-line specified ending pass " 202 "of: %s", optarg); 203 exit(5); 204 } 205 break; 206 207 case 'p': // Single Specific Pass 208 if (pFlag || sFlag || eFlag) { 209 testPrintE("Invalid combination of command-line options,"); 210 if (pFlag) { 211 testPrintE(" -p flag specified multiple times."); 212 } else { 213 testPrintE(" -p and -%c flags are mutually exclusive.", 214 (sFlag) ? 's' : 'e'); 215 } 216 exit(12); 217 } 218 pFlag = true; 219 endPass = startPass = strtoul(optarg, &chptr, 10); 220 if (*chptr != '\0') { 221 testPrintE("Invalid command-line specified pass " 222 "of: %s", optarg); 223 exit(13); 224 } 225 break; 226 227 case '?': 228 default: 229 testPrintE(" %s [options]", basename(argv[0])); 230 testPrintE(" options:"); 231 testPrintE(" -s Starting pass"); 232 testPrintE(" -e Ending pass"); 233 testPrintE(" -p Specific single pass"); 234 testPrintE(" -t Duration"); 235 testPrintE(" -d Delay min"); 236 testPrintE(" -D Delay max"); 237 exit(((optopt == 0) || (optopt == '?')) ? 0 : 6); 238 } 239 } 240 if (delayMax < delayMin) { 241 testPrintE("Unexpected maximum delay less than minimum delay"); 242 testPrintE(" delayMin: %f delayMax: %f", delayMin, delayMax); 243 exit(7); 244 } 245 if (endPass < startPass) { 246 testPrintE("Unexpected ending pass before starting pass"); 247 testPrintE(" startPass: %u endPass: %u", startPass, endPass); 248 exit(8); 249 } 250 if (argc != optind) { 251 testPrintE("Unexpected command-line postional argument"); 252 testPrintE(" %s [-s start_pass] [-e end_pass] [-d duration]", 253 basename(argv[0])); 254 exit(9); 255 } 256 testPrintI("duration: %g", duration); 257 testPrintI("startPass: %u", startPass); 258 testPrintI("endPass: %u", endPass); 259 testPrintI("delayMin: %f", delayMin); 260 testPrintI("delayMax: %f", delayMax); 261 262 init(); 263 264 // For each pass 265 gettimeofday(&startTime, NULL); 266 for (pass = startPass; pass <= endPass; pass++) { 267 // Stop if duration of work has already been performed 268 gettimeofday(¤tTime, NULL); 269 delta = tvDelta(&startTime, ¤tTime); 270 if (tv2double(&delta) > duration) { break; } 271 272 testPrintI("==== Starting pass: %u", pass); 273 274 // Use a pass dependent sequence of random numbers 275 srand48(pass); 276 277 // Load WiFi Driver 278 randBind(&availCPU, &cpu); 279 if ((rv = wifi_load_driver()) != 0) { 280 testPrintE("CPU: %i wifi_load_driver() failed, rv: %i\n", 281 cpu, rv); 282 exit(20); 283 } 284 testPrintI("CPU: %i wifi_load_driver succeeded", cpu); 285 286 // Start Supplicant 287 randBind(&availCPU, &cpu); 288 if ((rv = wifi_start_supplicant(false)) != 0) { 289 testPrintE("CPU: %i wifi_start_supplicant() failed, rv: %i\n", 290 cpu, rv); 291 exit(21); 292 } 293 testPrintI("CPU: %i wifi_start_supplicant succeeded", cpu); 294 295 // Sleep a random amount of time 296 randDelay(); 297 298 /* 299 * Obtain WiFi Status 300 * Half the time skip this step, which helps increase the 301 * level of randomization. 302 */ 303 if (testRandBool()) { 304 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS); 305 if (rv >= (signed) sizeof(cmd) - 1) { 306 testPrintE("Command too long for: %s\n", CMD_STATUS); 307 exit(22); 308 } 309 testExecCmd(cmd); 310 } 311 312 // Stop Supplicant 313 randBind(&availCPU, &cpu); 314 if ((rv = wifi_stop_supplicant(false)) != 0) { 315 testPrintE("CPU: %i wifi_stop_supplicant() failed, rv: %i\n", 316 cpu, rv); 317 exit(23); 318 } 319 testPrintI("CPU: %i wifi_stop_supplicant succeeded", cpu); 320 321 // Unload WiFi Module 322 randBind(&availCPU, &cpu); 323 if ((rv = wifi_unload_driver()) != 0) { 324 testPrintE("CPU: %i wifi_unload_driver() failed, rv: %i\n", 325 cpu, rv); 326 exit(24); 327 } 328 testPrintI("CPU: %i wifi_unload_driver succeeded", cpu); 329 330 testPrintI("==== Completed pass: %u", pass); 331 } 332 333 // If needed restore WiFi driver to state it was in at the 334 // start of the test. It is assumed that it the driver 335 // was loaded, then the wpa_supplicant was also running. 336 if (driverLoadedAtStart) { 337 // Load driver 338 if ((rv = wifi_load_driver()) != 0) { 339 testPrintE("main load driver failed, rv: %i", rv); 340 exit(25); 341 } 342 343 // Start supplicant 344 if ((rv = wifi_start_supplicant(false)) != 0) { 345 testPrintE("main start supplicant failed, rv: %i", rv); 346 exit(26); 347 } 348 349 // Obtain WiFi Status 350 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS); 351 if (rv >= (signed) sizeof(cmd) - 1) { 352 testPrintE("Command too long for: %s\n", CMD_STATUS); 353 exit(22); 354 } 355 testExecCmd(cmd); 356 } 357 358 // Start framework 359 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK); 360 if (rv >= (signed) sizeof(cmd) - 1) { 361 testPrintE("Command too long for: %s\n", CMD_START_FRAMEWORK); 362 exit(27); 363 } 364 testExecCmd(cmd); 365 366 testPrintI("Successfully completed %u passes", pass - startPass); 367 368 return 0; 369 } 370 371 /* 372 * Initialize 373 * 374 * Perform testcase initialization, which includes: 375 * 376 * 1. Determine which CPUs are available for use 377 * 378 * 2. Determine total number of available CPUs 379 * 380 * 3. Stop framework 381 * 382 * 4. Determine whether WiFi driver is loaded and if so 383 * stop wpa_supplicant and unload WiFi driver. 384 */ 385 void 386 init(void) 387 { 388 int rv; 389 unsigned int n1; 390 char cmd[MAXCMD]; 391 392 // Use whichever CPUs are available at start of test 393 rv = sched_getaffinity(0, sizeof(availCPU), &availCPU); 394 if (rv != 0) { 395 testPrintE("init sched_getaffinity failed, rv: %i errno: %i", 396 rv, errno); 397 exit(40); 398 } 399 400 // How many CPUs are available 401 numAvailCPU = 0; 402 for (n1 = 0; n1 < CPU_SETSIZE; n1++) { 403 if (CPU_ISSET(n1, &availCPU)) { numAvailCPU++; } 404 } 405 testPrintI("numAvailCPU: %u", numAvailCPU); 406 407 // Stop framework 408 rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK); 409 if (rv >= (signed) sizeof(cmd) - 1) { 410 testPrintE("Command too long for: %s\n", CMD_STOP_FRAMEWORK); 411 exit(41); 412 } 413 testExecCmd(cmd); 414 415 // Is WiFi driver loaded? 416 // If so stop the wpa_supplicant and unload the driver. 417 driverLoadedAtStart = is_wifi_driver_loaded(); 418 testPrintI("driverLoadedAtStart: %u", driverLoadedAtStart); 419 if (driverLoadedAtStart) { 420 // Stop wpa_supplicant 421 // Might already be stopped, in which case request should 422 // return immediately with success. 423 if ((rv = wifi_stop_supplicant(false)) != 0) { 424 testPrintE("init stop supplicant failed, rv: %i", rv); 425 exit(42); 426 } 427 testPrintI("Stopped wpa_supplicant"); 428 429 if ((rv = wifi_unload_driver()) != 0) { 430 testPrintE("init unload driver failed, rv: %i", rv); 431 exit(43); 432 } 433 testPrintI("WiFi driver unloaded"); 434 } 435 436 } 437 438 /* 439 * Random Delay 440 * 441 * Delays for a random amount of time within the range given 442 * by the file scope variables delayMin and delayMax. The 443 * selected amount of delay can come from any part of the 444 * range, with a bias towards values closer to delayMin. 445 * The amount of bias is determined by the setting of DELAY_EXP. 446 * The setting of DELAY_EXP should always be > 1.0, with higher 447 * values causing a more significant bias toward the value 448 * of delayMin. 449 */ 450 void randDelay(void) 451 { 452 const unsigned long nanosecspersec = 1000000000; 453 float fract, biasedFract, amt; 454 struct timeval startTime, endTime; 455 456 // Obtain start time 457 gettimeofday(&startTime, NULL); 458 459 // Determine random amount to sleep. 460 // Values closer to delayMin are prefered by an amount 461 // determined by the value of DELAY_EXP. 462 fract = testRandFract(); 463 biasedFract = pow(DELAY_EXP, fract) / pow(DELAY_EXP, 1.0); 464 amt = delayMin + ((delayMax - delayMin) * biasedFract); 465 466 // Delay 467 testDelay(amt); 468 469 // Obtain end time and display delta 470 gettimeofday(&endTime, NULL); 471 testPrintI("delay: %.2f", 472 (float) (tv2double(&endTime) - tv2double(&startTime))); 473 } 474 475 static void 476 randBind(const cpu_set_t *availSet, int *chosenCPU) 477 { 478 int rv; 479 cpu_set_t cpuset; 480 int chosenAvail, avail, cpu, currentCPU; 481 482 // Randomly bind to a CPU 483 // Lower 16 bits from random number generator thrown away, 484 // because the low-order bits tend to have the same sequence for 485 // different seed values. 486 chosenAvail = testRandMod(numAvailCPU); 487 CPU_ZERO(&cpuset); 488 avail = 0; 489 for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { 490 if (CPU_ISSET(cpu, availSet)) { 491 if (chosenAvail == avail) { 492 CPU_SET(cpu, &cpuset); 493 break; 494 } 495 avail++; 496 } 497 } 498 assert(cpu < CPU_SETSIZE); 499 sched_setaffinity(0, sizeof(cpuset), &cpuset); 500 501 // Confirm executing on requested CPU 502 if ((currentCPU = sched_getcpu()) < 0) { 503 testPrintE("randBind sched_getcpu() failed, rv: %i errno: %i", 504 currentCPU, errno); 505 exit(80); 506 507 } 508 if (currentCPU != cpu) { 509 testPrintE("randBind executing on unexpected CPU %i, expected %i", 510 currentCPU, cpu); 511 exit(81); 512 } 513 514 // Let the caller know which CPU was chosen 515 *chosenCPU = cpu; 516 } 517