• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
main(int argc,char * argv[])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(&currentTime, NULL);
269         delta = tvDelta(&startTime, &currentTime);
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
init(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  */
randDelay(void)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
randBind(const cpu_set_t * availSet,int * chosenCPU)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