2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 * WiFi load, scan, associate, unload stress test
21 * Repeatedly executes the following sequence:
26 * 4. Obtain supplicant status (optional)
28 * 6. Unload WiFi driver
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.
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.
59 #include <sys/syscall.h>
60 #include <sys/types.h>
63 #include <hardware_legacy/wifi.h>
65 #define LOG_TAG "wifiLoadScanAssocTest"
66 #include <utils/Log.h>
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
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.
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"
87 typedef unsigned int bool_t;
91 // File scope variables
93 unsigned int numAvailCPU;
94 float delayMin = DEFAULT_DELAY_MIN;
95 float delayMax = DEFAULT_DELAY_MAX;
96 bool_t driverLoadedAtStart;
98 // Command-line mutual exclusion detection flags.
99 // Corresponding flag set true once an option is used.
100 bool_t eFlag, sFlag, pFlag;
102 // File scope prototypes
103 static void init(void);
104 static void execCmd(const char *cmd);
105 static void randDelay(void);
106 static void randBind(const cpu_set_t *availSet, int *chosenCPU);
111 * Performs the following high-level sequence of operations:
113 * 1. Command-line parsing
117 * 3. Execute passes that repeatedly perform the WiFi load, scan,
118 * associate, unload sequence.
120 * 4. Restore state of WiFi driver to state it was at the
123 * 5. Restart framework
126 main(int argc, char *argv[])
134 float duration = DEFAULT_DURATION;
135 unsigned int startPass = DEFAULT_START_PASS, endPass = DEFAULT_END_PASS;
136 struct timeval startTime, currentTime, delta;
138 testSetLogCatTag(LOG_TAG);
140 // Parse command line arguments
141 while ((opt = getopt(argc, argv, "d:D:s:e:p:t:?")) != -1) {
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 "
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 "
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",
170 case 's': // Starting Pass
171 if (sFlag || pFlag) {
172 testPrintE("Invalid combination of command-line options,");
174 testPrintE(" -s flag specified multiple times.");
176 testPrintE(" -s and -p flags are mutually exclusive.");
181 startPass = strtoul(optarg, &chptr, 10);
182 if (*chptr != '\0') {
183 testPrintE("Invalid command-line specified starting pass "
189 case 'e': // Ending Pass
190 if (eFlag || pFlag) {
191 testPrintE("Invalid combination of command-line options,");
193 testPrintE(" -e flag specified multiple times.");
195 testPrintE(" -e and -p flags are mutually exclusive.");
200 endPass = strtoul(optarg, &chptr, 10);
201 if (*chptr != '\0') {
202 testPrintE("Invalid command-line specified ending pass "
208 case 'p': // Single Specific Pass
209 if (pFlag || sFlag || eFlag) {
210 testPrintE("Invalid combination of command-line options,");
212 testPrintE(" -p flag specified multiple times.");
214 testPrintE(" -p and -%c flags are mutually exclusive.",
215 (sFlag) ? 's' : 'e');
220 endPass = startPass = strtoul(optarg, &chptr, 10);
221 if (*chptr != '\0') {
222 testPrintE("Invalid command-line specified pass "
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);
241 if (delayMax < delayMin) {
242 testPrintE("Unexpected maximum delay less than minimum delay");
243 testPrintE(" delayMin: %f delayMax: %f", delayMin, delayMax);
246 if (endPass < startPass) {
247 testPrintE("Unexpected ending pass before starting pass");
248 testPrintE(" startPass: %u endPass: %u", startPass, endPass);
251 if (argc != optind) {
252 testPrintE("Unexpected command-line postional argument");
253 testPrintE(" %s [-s start_pass] [-e end_pass] [-d duration]",
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);
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; }
273 testPrintI("==== Starting pass: %u", pass);
275 // Use a pass dependent sequence of random numbers
279 randBind(&availCPU, &cpu);
280 if ((rv = wifi_load_driver()) != 0) {
281 testPrintE("CPU: %i wifi_load_driver() failed, rv: %i\n",
285 testPrintI("CPU: %i wifi_load_driver succeeded", cpu);
288 randBind(&availCPU, &cpu);
289 if ((rv = wifi_start_supplicant()) != 0) {
290 testPrintE("CPU: %i wifi_start_supplicant() failed, rv: %i\n",
294 testPrintI("CPU: %i wifi_start_supplicant succeeded", cpu);
296 // Sleep a random amount of time
301 * Half the time skip this step, which helps increase the
302 * level of randomization.
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);
314 randBind(&availCPU, &cpu);
315 if ((rv = wifi_stop_supplicant()) != 0) {
316 testPrintE("CPU: %i wifi_stop_supplicant() failed, rv: %i\n",
320 testPrintI("CPU: %i wifi_stop_supplicant succeeded", cpu);
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",
329 testPrintI("CPU: %i wifi_unload_driver succeeded", cpu);
331 testPrintI("==== Completed pass: %u", pass);
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) {
339 if ((rv = wifi_load_driver()) != 0) {
340 testPrintE("main load driver failed, rv: %i", rv);
345 if ((rv = wifi_start_supplicant()) != 0) {
346 testPrintE("main start supplicant failed, rv: %i", rv);
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);
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);
367 testPrintI("Successfully completed %u passes", pass - startPass);
375 * Perform testcase initialization, which includes:
377 * 1. Determine which CPUs are available for use
379 * 2. Determine total number of available CPUs
383 * 4. Determine whether WiFi driver is loaded and if so
384 * stop wpa_supplicant and unload WiFi driver.
393 // Use whichever CPUs are available at start of test
394 rv = sched_getaffinity(0, sizeof(availCPU), &availCPU);
396 testPrintE("init sched_getaffinity failed, rv: %i errno: %i",
401 // How many CPUs are available
403 for (n1 = 0; n1 < CPU_SETSIZE; n1++) {
404 if (CPU_ISSET(n1, &availCPU)) { numAvailCPU++; }
406 testPrintI("numAvailCPU: %u", numAvailCPU);
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);
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);
428 testPrintI("Stopped wpa_supplicant");
430 if ((rv = wifi_unload_driver()) != 0) {
431 testPrintE("init unload driver failed, rv: %i", rv);
434 testPrintI("WiFi driver unloaded");
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
452 execCmd(const char *cmd)
460 // Randomly bind to one of the available CPUs
461 randBind(&availCPU, &cpu);
463 // Display CPU executing on and command to be executed
464 testPrintI("CPU: %u cmd: %s", cpu, cmd);
466 // Execute the command
468 if ((fp = popen(cmd, "r")) == NULL) {
469 testPrintE("execCmd popen failed, errno: %i", errno);
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';
478 testPrintI(" out: %s", str);
482 // Obtain and check return status of executed command.
483 // Fail on non-zero exit status
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));
491 if (WIFSIGNALED(status)) {
492 testPrintE("WTERMSIG: %i", WTERMSIG(status));
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
512 const unsigned long nanosecspersec = 1000000000;
513 float fract, biasedFract, amt;
514 struct timespec remaining;
515 struct timeval start, current, delta;
518 gettimeofday(&start, NULL);
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);
529 gettimeofday(¤t, NULL);
531 // How much time is left
532 delta = tvDelta(&start, ¤t);
533 if (tv2double(&delta) > amt) { break; }
535 // Request to sleep for the remaining time
536 remaining = double2ts(amt - tv2double(&delta));
537 (void) nanosleep(&remaining, NULL);
540 testPrintI("delay: %.2f",
541 (float) (tv2double(¤t) - tv2double(&start)));
545 randBind(const cpu_set_t *availSet, int *chosenCPU)
549 int chosenAvail, avail, cpu, currentCPU;
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);
558 for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
559 if (CPU_ISSET(cpu, availSet)) {
560 if (chosenAvail == avail) {
561 CPU_SET(cpu, &cpuset);
567 assert(cpu < CPU_SETSIZE);
568 sched_setaffinity(0, sizeof(cpuset), &cpuset);
570 // Confirm executing on requested CPU
571 if ((currentCPU = sched_getcpu()) < 0) {
572 testPrintE("randBind sched_getcpu() failed, rv: %i errno: %i",
577 if (currentCPU != cpu) {
578 testPrintE("randBind executing on unexpected CPU %i, expected %i",
583 // Let the caller know which CPU was chosen