OSDN Git Service

Merge "Test util - add execCmd and full-range random"
[android-x86/system-extras.git] / tests / wifi / stress / wifiLoadScanAssoc_test.c
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 execCmd(const char *cmd);
105 static void randDelay(void);
106 static 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  */
125 int
126 main(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(&currentTime, NULL);
270         delta = tvDelta(&startTime, &currentTime);
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  */
386 void
387 init(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  */
451 void
452 execCmd(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  */
510 void 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(&current, NULL);
530
531         // How much time is left
532         delta = tvDelta(&start, &current);
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(&current) - tv2double(&start)));
542 }
543
544 static void
545 randBind(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 }