wifiLoadScanAssoc.c revision 87dd9e92610d5e7552f5cdb6ab2578035e2210f5
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.  There is also a default time in
43 * which the test executes, which is given by DEFAULT_DURATION and
44 * can be overriden through the use of the -t command-line option.
45 */
46
47#include <assert.h>
48#include <errno.h>
49#include <libgen.h>
50#include <math.h>
51#include <sched.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <time.h>
55#include <unistd.h>
56
57#include <sys/syscall.h>
58#include <sys/types.h>
59#include <sys/wait.h>
60
61#include <hardware_legacy/wifi.h>
62
63#define LOG_TAG "wifiLoadScanAssocTest"
64#include <utils/Log.h>
65#include <testUtil.h>
66
67#define DEFAULT_START_PASS     0
68#define DEFAULT_END_PASS     999
69#define DEFAULT_DURATION       FLT_MAX // A fairly long time, so that
70                                       // range of passes will have
71                                       // precedence
72#define DEFAULT_DELAY_MIN      0.0     // Min delay after start supplicant
73#define DEFAULT_DELAY_MAX     20.0     // Max delay after start supplicant
74#define DELAY_EXP            150.0     // Exponent which determines the
75                                       // amount by which values closer
76                                       // to DELAY_MIN are favored.
77
78#define CMD_STATUS           "wpa_cli status 2>&1"
79#define CMD_STOP_FRAMEWORK   "stop 2>&1"
80#define CMD_START_FRAMEWORK  "start 2>&1"
81
82#define MAXSTR      100
83#define MAXCMD      500
84
85typedef unsigned int bool_t;
86#define true (0 == 0)
87#define false (!true)
88
89// Local description of sched_setaffinity(2), sched_getaffinity(2), and
90// getcpu(2)
91#define CPU_SETSIZE 1024
92typedef struct {uint64_t bits[CPU_SETSIZE / 64]; } cpu_set_t;
93int sched_setaffinity(pid_t pid, unsigned int cpusetsize, const cpu_set_t *set);
94int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *set);
95struct getcpu_cache;
96int getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache);
97void CPU_CLR(int cpu, cpu_set_t *set);
98int CPU_ISSET(int cpu, const cpu_set_t *set);
99void CPU_SET(int cpu, cpu_set_t *set);
100void CPU_ZERO(cpu_set_t *set);
101
102// File scope variables
103cpu_set_t availCPU;
104unsigned int numAvailCPU;
105float delayMin = DEFAULT_DELAY_MIN;
106float delayMax = DEFAULT_DELAY_MAX;
107bool_t driverLoadedAtStart;
108
109// File scope prototypes
110static void init(void);
111static void execCmd(const char *cmd);
112static void randDelay(void);
113static void randBind(const cpu_set_t *availSet, int *chosenCPU);
114
115/*
116 * Main
117 *
118 * Performs the following high-level sequence of operations:
119 *
120 *   1. Command-line parsing
121 *
122 *   2. Initialization
123 *
124 *   3. Execute passes that repeatedly perform the WiFi load, scan,
125 *      associate, unload sequence.
126 *
127 *   4. Restore state of WiFi driver to state it was at the
128 *      start of the test.
129 *
130 *   5. Restart framework
131 */
132int
133main(int argc, char *argv[])
134{
135    FILE *fp;
136    int rv, opt;
137    int cpu;
138    char *chptr;
139    unsigned int pass;
140    char cmd[MAXCMD];
141    float duration = DEFAULT_DURATION;
142    unsigned int startPass = DEFAULT_START_PASS, endPass = DEFAULT_END_PASS;
143    struct timeval startTime, currentTime, delta;
144
145    testSetLogCatTag(LOG_TAG);
146
147    // Parse command line arguments
148    while ((opt = getopt(argc, argv, "d:D:s:e:t:?")) != -1) {
149        switch (opt) {
150        case 'd': // Minimum Delay
151            delayMin = strtod(optarg, &chptr);
152            if ((*chptr != '\0') || (delayMin < 0.0)) {
153                testPrintE("Invalid command-line specified minimum delay "
154                    "of: %s", optarg);
155                exit(1);
156            }
157            break;
158
159        case 'D': // Maximum Delay
160            delayMax = strtod(optarg, &chptr);
161            if ((*chptr != '\0') || (delayMax < 0.0)) {
162                testPrintE("Invalid command-line specified maximum delay "
163                    "of: %s", optarg);
164                exit(2);
165            }
166            break;
167
168        case 't': // Duration
169            duration = strtod(optarg, &chptr);
170            if ((*chptr != '\0') || (duration < 0.0)) {
171                testPrintE("Invalid command-line specified duration of: %s",
172                    optarg);
173                exit(3);
174            }
175            break;
176
177        case 's': // Starting Pass
178            startPass = strtoul(optarg, &chptr, 10);
179            if (*chptr != '\0') {
180                testPrintE("Invalid command-line specified starting pass "
181                    "of: %s", optarg);
182                exit(4);
183            }
184            break;
185
186        case 'e': // Ending Pass
187            endPass = strtoul(optarg, &chptr, 10);
188            if (*chptr != '\0') {
189                testPrintE("Invalid command-line specified ending pass "
190                    "of: %s", optarg);
191                exit(5);
192            }
193            break;
194
195        case '?':
196        default:
197            testPrintE("  %s [options]", basename(argv[0]));
198            testPrintE("    options:");
199            testPrintE("      -s Starting pass");
200            testPrintE("      -e Ending pass");
201            testPrintE("      -t Duration");
202            testPrintE("      -d Delay min");
203            testPrintE("      -D Delay max");
204            exit((opt == '?') ? 0 : 6);
205        }
206    }
207    if (delayMax < delayMin) {
208        testPrintE("Unexpected maximum delay less than minimum delay");
209        testPrintE("  delayMin: %f delayMax: %f", delayMin, delayMax);
210        exit(7);
211    }
212    if (endPass < startPass) {
213        testPrintE("Unexpected ending pass before starting pass");
214        testPrintE("  startPass: %u endPass: %u", startPass, endPass);
215        exit(8);
216    }
217    if (argc != optind) {
218        testPrintE("Unexpected command-line postional argument");
219        testPrintE("  %s [-s start_pass] [-e end_pass] [-d duration]",
220            basename(argv[0]));
221        exit(9);
222    }
223    testPrintI("duration: %g", duration);
224    testPrintI("startPass: %u", startPass);
225    testPrintI("endPass: %u", endPass);
226    testPrintI("delayMin: %f", delayMin);
227    testPrintI("delayMax: %f", delayMax);
228
229    init();
230
231    // For each pass
232    gettimeofday(&startTime, NULL);
233    for (pass = startPass; pass <= endPass; pass++) {
234        // Stop if duration of work has already been performed
235        gettimeofday(&currentTime, NULL);
236        delta = tvDelta(&startTime, &currentTime);
237        if (tv2double(&delta) > duration) { break; }
238
239        testPrintI("==== pass %u", pass);
240
241        // Use a pass dependent sequence of random numbers
242        srand48(pass);
243
244        // Load WiFi Driver
245        randBind(&availCPU, &cpu);
246        if ((rv = wifi_load_driver()) != 0) {
247            testPrintE("CPU: %i wifi_load_driver() failed, rv: %i\n",
248                cpu, rv);
249            exit(20);
250        }
251        testPrintI("CPU: %i wifi_load_driver succeeded", cpu);
252
253        // Start Supplicant
254        randBind(&availCPU, &cpu);
255        if ((rv = wifi_start_supplicant()) != 0) {
256            testPrintE("CPU: %i wifi_start_supplicant() failed, rv: %i\n",
257                cpu, rv);
258            exit(21);
259        }
260        testPrintI("CPU: %i wifi_start_supplicant succeeded", cpu);
261
262        // Sleep a random amount of time
263        randDelay();
264
265        /*
266         * Obtain WiFi Status
267         * Half the time skip this step, which helps increase the
268         * level of randomization.
269         */
270        if (testRandBool()) {
271            rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS);
272            if (rv >= (signed) sizeof(cmd) - 1) {
273                testPrintE("Command too long for: %s\n", CMD_STATUS);
274                exit(22);
275            }
276            execCmd(cmd);
277        }
278
279        // Stop Supplicant
280        randBind(&availCPU, &cpu);
281        if ((rv = wifi_stop_supplicant()) != 0) {
282            testPrintE("CPU: %i wifi_stop_supplicant() failed, rv: %i\n",
283                cpu, rv);
284            exit(23);
285        }
286        testPrintI("CPU: %i wifi_stop_supplicant succeeded", cpu);
287
288        // Unload WiFi Module
289        randBind(&availCPU, &cpu);
290        if ((rv = wifi_unload_driver()) != 0) {
291            testPrintE("CPU: %i wifi_unload_driver() failed, rv: %i\n",
292                cpu, rv);
293            exit(24);
294        }
295        testPrintI("CPU: %i wifi_unload_driver succeeded", cpu);
296    }
297
298    // If needed restore WiFi driver to state it was in at the
299    // start of the test.  It is assumed that it the driver
300    // was loaded, then the wpa_supplicant was also running.
301    if (driverLoadedAtStart) {
302        // Load driver
303        if ((rv = wifi_load_driver()) != 0) {
304            testPrintE("main load driver failed, rv: %i", rv);
305            exit(25);
306        }
307
308        // Start supplicant
309        if ((rv = wifi_start_supplicant()) != 0) {
310            testPrintE("main start supplicant failed, rv: %i", rv);
311            exit(26);
312        }
313
314        // Obtain WiFi Status
315        rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS);
316        if (rv >= (signed) sizeof(cmd) - 1) {
317            testPrintE("Command too long for: %s\n", CMD_STATUS);
318            exit(22);
319        }
320        execCmd(cmd);
321    }
322
323    // Start framework
324    rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK);
325    if (rv >= (signed) sizeof(cmd) - 1) {
326        testPrintE("Command too long for: %s\n", CMD_START_FRAMEWORK);
327        exit(27);
328    }
329    execCmd(cmd);
330
331    return 0;
332}
333
334/*
335 * Initialize
336 *
337 * Perform testcase initialization, which includes:
338 *
339 *   1. Determine which CPUs are available for use
340 *
341 *   2. Determine total number of available CPUs
342 *
343 *   3. Stop framework
344 *
345 *   4. Determine whether WiFi driver is loaded and if so
346 *      stop wpa_supplicant and unload WiFi driver.
347 */
348void
349init(void)
350{
351    int rv;
352    unsigned int n1;
353    char cmd[MAXCMD];
354
355    // Use whichever CPUs are available at start of test
356    rv = sched_getaffinity(0, sizeof(availCPU), &availCPU);
357    if (rv != 0) {
358        testPrintE("init sched_getaffinity failed, rv: %i errno: %i",
359            rv, errno);
360        exit(40);
361    }
362
363    // How many CPUs are available
364    numAvailCPU = 0;
365    for (n1 = 0; n1 < CPU_SETSIZE; n1++) {
366        if (CPU_ISSET(n1, &availCPU)) { numAvailCPU++; }
367    }
368    testPrintI("numAvailCPU: %u", numAvailCPU);
369
370    // Stop framework
371    rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK);
372    if (rv >= (signed) sizeof(cmd) - 1) {
373        testPrintE("Command too long for: %s\n", CMD_STOP_FRAMEWORK);
374        exit(41);
375    }
376    execCmd(cmd);
377
378    // Is WiFi driver loaded?
379    // If so stop the wpa_supplicant and unload the driver.
380    driverLoadedAtStart = is_wifi_driver_loaded();
381    testPrintI("driverLoadedAtStart: %u", driverLoadedAtStart);
382    if (driverLoadedAtStart) {
383        // Stop wpa_supplicant
384        // Might already be stopped, in which case request should
385        // return immediately with success.
386        if ((rv = wifi_stop_supplicant()) != 0) {
387            testPrintE("init stop supplicant failed, rv: %i", rv);
388            exit(42);
389        }
390        testPrintI("Stopped wpa_supplicant");
391
392        if ((rv = wifi_unload_driver()) != 0) {
393            testPrintE("init unload driver failed, rv: %i", rv);
394            exit(43);
395        }
396        testPrintI("WiFi driver unloaded");
397    }
398
399}
400
401/*
402 * Execute Command
403 *
404 * Executes the command pointed to by cmd.  Which CPU executes the
405 * command is randomly selected from the set of CPUs that were
406 * available during testcase initialization.  Output from the
407 * executed command is captured and sent to LogCat Info.  Once
408 * the command has finished execution, it's exit status is captured
409 * and checked for an exit status of zero.  Any other exit status
410 * causes diagnostic information to be printed and an immediate
411 * testcase failure.
412 */
413void
414execCmd(const char *cmd)
415{
416    FILE *fp;
417    int rv;
418    int status;
419    char str[MAXSTR];
420    int cpu;
421
422    // Randomly bind to one of the available CPUs
423    randBind(&availCPU, &cpu);
424
425    // Display CPU executing on and command to be executed
426    testPrintI("CPU: %u cmd: %s", cpu, cmd);
427
428    // Execute the command
429    fflush(stdout);
430    if ((fp = popen(cmd, "r")) == NULL) {
431        testPrintE("execCmd popen failed, errno: %i", errno);
432        exit(61);
433    }
434
435    // Obtain and display each line of output from the executed command
436    while (fgets(str, sizeof(str), fp) != NULL) {
437        if ((strlen(str) > 1) && (str[strlen(str) - 1] == '\n')) {
438            str[strlen(str) - 1] = '\0';
439        }
440        testPrintI(" out: %s", str);
441        delay(0.1);
442    }
443
444    // Obtain and check return status of executed command.
445    // Fail on non-zero exit status
446    status = pclose(fp);
447    if (!(WIFEXITED(status) && (WEXITSTATUS(status) == 0))) {
448        testPrintE("Unexpected command failure");
449        testPrintE("  status: %#x", status);
450        if (WIFEXITED(status)) {
451            testPrintE("WEXITSTATUS: %i", WEXITSTATUS(status));
452        }
453        if (WIFSIGNALED(status)) {
454            testPrintE("WTERMSIG: %i", WTERMSIG(status));
455        }
456        exit(62);
457    }
458}
459
460/*
461 * Random Delay
462 *
463 * Delays for a random amount of time within the range given
464 * by the file scope variables delayMin and delayMax.  The
465 * selected amount of delay can come from any part of the
466 * range, with a bias towards values closer to delayMin.
467 * The amount of bias is determined by the setting of DELAY_EXP.
468 * The setting of DELAY_EXP should always be > 1.0, with higher
469 * values causing a more significant bias toward the value
470 * of delayMin.
471 */
472void randDelay(void)
473{
474    const unsigned long nanosecspersec = 1000000000;
475    float            fract, biasedFract, amt;
476    struct timespec  remaining;
477    struct timeval   start, current, delta;
478
479    // Obtain start time
480    gettimeofday(&start, NULL);
481
482    // Determine random amount to sleep.
483    // Values closer to delayMin are prefered by an amount
484    // determined by the value of DELAY_EXP.
485    fract = (double) lrand48() / (double) (1UL << 31);
486    biasedFract = pow(DELAY_EXP, fract) / pow(DELAY_EXP, 1.0);
487    amt = delayMin + ((delayMax - delayMin) * biasedFract);
488
489    do {
490        // Get current time
491        gettimeofday(&current, NULL);
492
493        // How much time is left
494        delta = tvDelta(&start, &current);
495        if (tv2double(&delta) > amt) { break; }
496
497        // Request to sleep for the remaining time
498        remaining = double2ts(amt - tv2double(&delta));
499        (void) nanosleep(&remaining, NULL);
500    } while (true);
501
502    testPrintI("delay: %.2f",
503        (float) (tv2double(&current) - tv2double(&start)));
504}
505
506static void
507randBind(const cpu_set_t *availSet, int *chosenCPU)
508{
509    int rv;
510    cpu_set_t cpuset;
511    unsigned int chosenAvail, avail, cpu, currentCPU;
512
513    // Randomly bind to a CPU
514    // Lower 16 bits from random number generator thrown away,
515    // because the low-order bits tend to have the same sequence for
516    // different seed values.
517    chosenAvail = (lrand48() >> 16) % numAvailCPU;
518    CPU_ZERO(&cpuset);
519    avail = 0;
520    for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
521        if (CPU_ISSET(cpu, availSet)) {
522            if (chosenAvail == avail) {
523                CPU_SET(cpu, &cpuset);
524                break;
525            }
526            avail++;
527        }
528    }
529    assert(cpu < CPU_SETSIZE);
530    sched_setaffinity(0, sizeof(cpuset), &cpuset);
531
532    // Confirm executing on requested CPU
533    if ((rv = getcpu(&currentCPU, NULL, NULL)) != 0) {
534        testPrintE("randBind getcpu() failed, rv: %i errno: %i", rv, errno);
535        exit(80);
536
537    }
538    if (currentCPU != cpu) {
539        testPrintE("randBind executing on unexpected CPU %i, expected %i",
540            currentCPU, cpu);
541        exit(81);
542    }
543
544    // Let the caller know which CPU was chosen
545    *chosenCPU = cpu;
546}
547
548// Local implementation of sched_setaffinity(2), sched_getaffinity(2), and
549// getcpu(2)
550int
551sched_setaffinity(pid_t pid, unsigned int cpusetsize, const cpu_set_t *set)
552{
553    int rv;
554
555    rv = syscall(__NR_sched_setaffinity, pid, cpusetsize, set);
556
557    return rv;
558}
559
560int
561sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *set)
562{
563    int rv;
564
565    rv = syscall(__NR_sched_getaffinity, pid, cpusetsize, set);
566    if (rv < 0) { return rv; }
567
568    // Kernel implementation of sched_getaffinity() returns the number
569    // of bytes in the set that it set.  Set the rest of our set bits
570    // to 0.
571    memset(((char *) set) + rv, 0x00, sizeof(cpu_set_t) - rv);
572
573    return 0;
574}
575
576int
577getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *tcache)
578{
579    int rv;
580
581    rv = syscall(345, cpu, node, tcache);
582
583    return rv;
584}
585
586void
587CPU_CLR(int cpu, cpu_set_t *set)
588{
589    if (cpu < 0) { return; }
590    if ((unsigned) cpu >= (sizeof(cpu_set_t) * CHAR_BIT)) { return; }
591
592    *((uint64_t *)set + (cpu / 64)) &= ~(1ULL << (cpu % 64));
593}
594
595int
596CPU_ISSET(int cpu, const cpu_set_t *set)
597{
598    if (cpu < 0) { return 0; }
599    if ((unsigned) cpu >= (sizeof(cpu_set_t) * CHAR_BIT)) { return 0; }
600
601    if ((*((uint64_t *)set + (cpu / 64))) & (1ULL << (cpu % 64))) {
602        return true;
603    }
604
605    return false;
606}
607
608void
609CPU_SET(int cpu, cpu_set_t *set)
610{
611    if (cpu < 0) { return; }
612    if ((unsigned) cpu > (sizeof(cpu_set_t) * CHAR_BIT)) { return; }
613
614    *((uint64_t *)set + (cpu / 64)) |= 1ULL << (cpu % 64);
615}
616
617void
618CPU_ZERO(cpu_set_t *set)
619{
620    memset(set, 0x00, sizeof(cpu_set_t));
621}
622