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
87typedef unsigned int bool_t;
88#define true (0 == 0)
89#define false (!true)
90
91// File scope variables
92cpu_set_t availCPU;
93unsigned int numAvailCPU;
94float delayMin = DEFAULT_DELAY_MIN;
95float delayMax = DEFAULT_DELAY_MAX;
96bool_t driverLoadedAtStart;
97
98// Command-line mutual exclusion detection flags.
99// Corresponding flag set true once an option is used.
100bool_t eFlag, sFlag, pFlag;
101
102// File scope prototypes
103static void init(void);
104static void randDelay(void);
105static 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 */
124int
125main(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()) != 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 */
385void
386init(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()) != 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 */
450void 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
475static void
476randBind(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