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(¤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
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