Home | History | Annotate | Download | only in stress
      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
    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
    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  */
    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
    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