517 lines
16 KiB
C
517 lines
16 KiB
C
/*
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* WiFi load, scan, associate, unload stress test
|
|
*
|
|
* Repeatedly executes the following sequence:
|
|
*
|
|
* 1. Load WiFi driver
|
|
* 2. Start supplicant
|
|
* 3. Random delay
|
|
* 4. Obtain supplicant status (optional)
|
|
* 5. Stop supplicant
|
|
* 6. Unload WiFi driver
|
|
*
|
|
* The "Obtain supplicant status" step is optional and is pseudo
|
|
* randomly performed 50% of the time. The default range of
|
|
* delay after start supplicant is intentionally selected such
|
|
* that the obtain supplicant status and stop supplicant steps
|
|
* may be performed while the WiFi driver is still performing a scan
|
|
* or associate. The default values are given by DEFAULT_DELAY_MIN
|
|
* and DEFAULT_DELAY_MAX. Other values can be specified through the
|
|
* use of the -d and -D command-line options.
|
|
*
|
|
* Each sequence is refered to as a pass and by default an unlimited
|
|
* number of passes are performed. An override of the range of passes
|
|
* to be executed is available through the use of the -s (start) and
|
|
* -e (end) command-line options. Can also specify a single specific
|
|
* pass via the -p option. There is also a default time in which the
|
|
* test executes, which is given by DEFAULT_DURATION and can be overriden
|
|
* through the use of the -t command-line option.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <libgen.h>
|
|
#include <math.h>
|
|
#define _GNU_SOURCE
|
|
#include <sched.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <sys/syscall.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <hardware_legacy/wifi.h>
|
|
|
|
#define LOG_TAG "wifiLoadScanAssocTest"
|
|
#include <utils/Log.h>
|
|
#include <testUtil.h>
|
|
|
|
#define DEFAULT_START_PASS 0
|
|
#define DEFAULT_END_PASS 999
|
|
#define DEFAULT_DURATION FLT_MAX // A fairly long time, so that
|
|
// range of passes will have
|
|
// precedence
|
|
#define DEFAULT_DELAY_MIN 0.0 // Min delay after start supplicant
|
|
#define DEFAULT_DELAY_MAX 20.0 // Max delay after start supplicant
|
|
#define DELAY_EXP 150.0 // Exponent which determines the
|
|
// amount by which values closer
|
|
// to DELAY_MIN are favored.
|
|
|
|
#define CMD_STATUS "wpa_cli status 2>&1"
|
|
#define CMD_STOP_FRAMEWORK "stop 2>&1"
|
|
#define CMD_START_FRAMEWORK "start 2>&1"
|
|
|
|
#define MAXSTR 100
|
|
#define MAXCMD 500
|
|
|
|
typedef unsigned int bool_t;
|
|
#define true (0 == 0)
|
|
#define false (!true)
|
|
|
|
// File scope variables
|
|
cpu_set_t availCPU;
|
|
unsigned int numAvailCPU;
|
|
float delayMin = DEFAULT_DELAY_MIN;
|
|
float delayMax = DEFAULT_DELAY_MAX;
|
|
bool_t driverLoadedAtStart;
|
|
|
|
// Command-line mutual exclusion detection flags.
|
|
// Corresponding flag set true once an option is used.
|
|
bool_t eFlag, sFlag, pFlag;
|
|
|
|
// File scope prototypes
|
|
static void init(void);
|
|
static void randDelay(void);
|
|
static void randBind(const cpu_set_t *availSet, int *chosenCPU);
|
|
|
|
/*
|
|
* Main
|
|
*
|
|
* Performs the following high-level sequence of operations:
|
|
*
|
|
* 1. Command-line parsing
|
|
*
|
|
* 2. Initialization
|
|
*
|
|
* 3. Execute passes that repeatedly perform the WiFi load, scan,
|
|
* associate, unload sequence.
|
|
*
|
|
* 4. Restore state of WiFi driver to state it was at the
|
|
* start of the test.
|
|
*
|
|
* 5. Restart framework
|
|
*/
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
FILE *fp;
|
|
int rv, opt;
|
|
int cpu;
|
|
char *chptr;
|
|
unsigned int pass;
|
|
char cmd[MAXCMD];
|
|
float duration = DEFAULT_DURATION;
|
|
unsigned int startPass = DEFAULT_START_PASS, endPass = DEFAULT_END_PASS;
|
|
struct timeval startTime, currentTime, delta;
|
|
|
|
testSetLogCatTag(LOG_TAG);
|
|
|
|
// Parse command line arguments
|
|
while ((opt = getopt(argc, argv, "d:D:s:e:p:t:?")) != -1) {
|
|
switch (opt) {
|
|
case 'd': // Minimum Delay
|
|
delayMin = strtod(optarg, &chptr);
|
|
if ((*chptr != '\0') || (delayMin < 0.0)) {
|
|
testPrintE("Invalid command-line specified minimum delay "
|
|
"of: %s", optarg);
|
|
exit(1);
|
|
}
|
|
break;
|
|
|
|
case 'D': // Maximum Delay
|
|
delayMax = strtod(optarg, &chptr);
|
|
if ((*chptr != '\0') || (delayMax < 0.0)) {
|
|
testPrintE("Invalid command-line specified maximum delay "
|
|
"of: %s", optarg);
|
|
exit(2);
|
|
}
|
|
break;
|
|
|
|
case 't': // Duration
|
|
duration = strtod(optarg, &chptr);
|
|
if ((*chptr != '\0') || (duration < 0.0)) {
|
|
testPrintE("Invalid command-line specified duration of: %s",
|
|
optarg);
|
|
exit(3);
|
|
}
|
|
break;
|
|
|
|
case 's': // Starting Pass
|
|
if (sFlag || pFlag) {
|
|
testPrintE("Invalid combination of command-line options,");
|
|
if (sFlag) {
|
|
testPrintE(" -s flag specified multiple times.");
|
|
} else {
|
|
testPrintE(" -s and -p flags are mutually exclusive.");
|
|
}
|
|
exit(10);
|
|
}
|
|
sFlag = true;
|
|
startPass = strtoul(optarg, &chptr, 10);
|
|
if (*chptr != '\0') {
|
|
testPrintE("Invalid command-line specified starting pass "
|
|
"of: %s", optarg);
|
|
exit(4);
|
|
}
|
|
break;
|
|
|
|
case 'e': // Ending Pass
|
|
if (eFlag || pFlag) {
|
|
testPrintE("Invalid combination of command-line options,");
|
|
if (sFlag) {
|
|
testPrintE(" -e flag specified multiple times.");
|
|
} else {
|
|
testPrintE(" -e and -p flags are mutually exclusive.");
|
|
}
|
|
exit(11);
|
|
}
|
|
eFlag = true;
|
|
endPass = strtoul(optarg, &chptr, 10);
|
|
if (*chptr != '\0') {
|
|
testPrintE("Invalid command-line specified ending pass "
|
|
"of: %s", optarg);
|
|
exit(5);
|
|
}
|
|
break;
|
|
|
|
case 'p': // Single Specific Pass
|
|
if (pFlag || sFlag || eFlag) {
|
|
testPrintE("Invalid combination of command-line options,");
|
|
if (pFlag) {
|
|
testPrintE(" -p flag specified multiple times.");
|
|
} else {
|
|
testPrintE(" -p and -%c flags are mutually exclusive.",
|
|
(sFlag) ? 's' : 'e');
|
|
}
|
|
exit(12);
|
|
}
|
|
pFlag = true;
|
|
endPass = startPass = strtoul(optarg, &chptr, 10);
|
|
if (*chptr != '\0') {
|
|
testPrintE("Invalid command-line specified pass "
|
|
"of: %s", optarg);
|
|
exit(13);
|
|
}
|
|
break;
|
|
|
|
case '?':
|
|
default:
|
|
testPrintE(" %s [options]", basename(argv[0]));
|
|
testPrintE(" options:");
|
|
testPrintE(" -s Starting pass");
|
|
testPrintE(" -e Ending pass");
|
|
testPrintE(" -p Specific single pass");
|
|
testPrintE(" -t Duration");
|
|
testPrintE(" -d Delay min");
|
|
testPrintE(" -D Delay max");
|
|
exit(((optopt == 0) || (optopt == '?')) ? 0 : 6);
|
|
}
|
|
}
|
|
if (delayMax < delayMin) {
|
|
testPrintE("Unexpected maximum delay less than minimum delay");
|
|
testPrintE(" delayMin: %f delayMax: %f", delayMin, delayMax);
|
|
exit(7);
|
|
}
|
|
if (endPass < startPass) {
|
|
testPrintE("Unexpected ending pass before starting pass");
|
|
testPrintE(" startPass: %u endPass: %u", startPass, endPass);
|
|
exit(8);
|
|
}
|
|
if (argc != optind) {
|
|
testPrintE("Unexpected command-line postional argument");
|
|
testPrintE(" %s [-s start_pass] [-e end_pass] [-d duration]",
|
|
basename(argv[0]));
|
|
exit(9);
|
|
}
|
|
testPrintI("duration: %g", duration);
|
|
testPrintI("startPass: %u", startPass);
|
|
testPrintI("endPass: %u", endPass);
|
|
testPrintI("delayMin: %f", delayMin);
|
|
testPrintI("delayMax: %f", delayMax);
|
|
|
|
init();
|
|
|
|
// For each pass
|
|
gettimeofday(&startTime, NULL);
|
|
for (pass = startPass; pass <= endPass; pass++) {
|
|
// Stop if duration of work has already been performed
|
|
gettimeofday(¤tTime, NULL);
|
|
delta = tvDelta(&startTime, ¤tTime);
|
|
if (tv2double(&delta) > duration) { break; }
|
|
|
|
testPrintI("==== Starting pass: %u", pass);
|
|
|
|
// Use a pass dependent sequence of random numbers
|
|
srand48(pass);
|
|
|
|
// Load WiFi Driver
|
|
randBind(&availCPU, &cpu);
|
|
if ((rv = wifi_load_driver()) != 0) {
|
|
testPrintE("CPU: %i wifi_load_driver() failed, rv: %i\n",
|
|
cpu, rv);
|
|
exit(20);
|
|
}
|
|
testPrintI("CPU: %i wifi_load_driver succeeded", cpu);
|
|
|
|
// Start Supplicant
|
|
randBind(&availCPU, &cpu);
|
|
if ((rv = wifi_start_supplicant(false)) != 0) {
|
|
testPrintE("CPU: %i wifi_start_supplicant() failed, rv: %i\n",
|
|
cpu, rv);
|
|
exit(21);
|
|
}
|
|
testPrintI("CPU: %i wifi_start_supplicant succeeded", cpu);
|
|
|
|
// Sleep a random amount of time
|
|
randDelay();
|
|
|
|
/*
|
|
* Obtain WiFi Status
|
|
* Half the time skip this step, which helps increase the
|
|
* level of randomization.
|
|
*/
|
|
if (testRandBool()) {
|
|
rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS);
|
|
if (rv >= (signed) sizeof(cmd) - 1) {
|
|
testPrintE("Command too long for: %s\n", CMD_STATUS);
|
|
exit(22);
|
|
}
|
|
testExecCmd(cmd);
|
|
}
|
|
|
|
// Stop Supplicant
|
|
randBind(&availCPU, &cpu);
|
|
if ((rv = wifi_stop_supplicant(false)) != 0) {
|
|
testPrintE("CPU: %i wifi_stop_supplicant() failed, rv: %i\n",
|
|
cpu, rv);
|
|
exit(23);
|
|
}
|
|
testPrintI("CPU: %i wifi_stop_supplicant succeeded", cpu);
|
|
|
|
// Unload WiFi Module
|
|
randBind(&availCPU, &cpu);
|
|
if ((rv = wifi_unload_driver()) != 0) {
|
|
testPrintE("CPU: %i wifi_unload_driver() failed, rv: %i\n",
|
|
cpu, rv);
|
|
exit(24);
|
|
}
|
|
testPrintI("CPU: %i wifi_unload_driver succeeded", cpu);
|
|
|
|
testPrintI("==== Completed pass: %u", pass);
|
|
}
|
|
|
|
// If needed restore WiFi driver to state it was in at the
|
|
// start of the test. It is assumed that it the driver
|
|
// was loaded, then the wpa_supplicant was also running.
|
|
if (driverLoadedAtStart) {
|
|
// Load driver
|
|
if ((rv = wifi_load_driver()) != 0) {
|
|
testPrintE("main load driver failed, rv: %i", rv);
|
|
exit(25);
|
|
}
|
|
|
|
// Start supplicant
|
|
if ((rv = wifi_start_supplicant(false)) != 0) {
|
|
testPrintE("main start supplicant failed, rv: %i", rv);
|
|
exit(26);
|
|
}
|
|
|
|
// Obtain WiFi Status
|
|
rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STATUS);
|
|
if (rv >= (signed) sizeof(cmd) - 1) {
|
|
testPrintE("Command too long for: %s\n", CMD_STATUS);
|
|
exit(22);
|
|
}
|
|
testExecCmd(cmd);
|
|
}
|
|
|
|
// Start framework
|
|
rv = snprintf(cmd, sizeof(cmd), "%s", CMD_START_FRAMEWORK);
|
|
if (rv >= (signed) sizeof(cmd) - 1) {
|
|
testPrintE("Command too long for: %s\n", CMD_START_FRAMEWORK);
|
|
exit(27);
|
|
}
|
|
testExecCmd(cmd);
|
|
|
|
testPrintI("Successfully completed %u passes", pass - startPass);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize
|
|
*
|
|
* Perform testcase initialization, which includes:
|
|
*
|
|
* 1. Determine which CPUs are available for use
|
|
*
|
|
* 2. Determine total number of available CPUs
|
|
*
|
|
* 3. Stop framework
|
|
*
|
|
* 4. Determine whether WiFi driver is loaded and if so
|
|
* stop wpa_supplicant and unload WiFi driver.
|
|
*/
|
|
void
|
|
init(void)
|
|
{
|
|
int rv;
|
|
unsigned int n1;
|
|
char cmd[MAXCMD];
|
|
|
|
// Use whichever CPUs are available at start of test
|
|
rv = sched_getaffinity(0, sizeof(availCPU), &availCPU);
|
|
if (rv != 0) {
|
|
testPrintE("init sched_getaffinity failed, rv: %i errno: %i",
|
|
rv, errno);
|
|
exit(40);
|
|
}
|
|
|
|
// How many CPUs are available
|
|
numAvailCPU = 0;
|
|
for (n1 = 0; n1 < CPU_SETSIZE; n1++) {
|
|
if (CPU_ISSET(n1, &availCPU)) { numAvailCPU++; }
|
|
}
|
|
testPrintI("numAvailCPU: %u", numAvailCPU);
|
|
|
|
// Stop framework
|
|
rv = snprintf(cmd, sizeof(cmd), "%s", CMD_STOP_FRAMEWORK);
|
|
if (rv >= (signed) sizeof(cmd) - 1) {
|
|
testPrintE("Command too long for: %s\n", CMD_STOP_FRAMEWORK);
|
|
exit(41);
|
|
}
|
|
testExecCmd(cmd);
|
|
|
|
// Is WiFi driver loaded?
|
|
// If so stop the wpa_supplicant and unload the driver.
|
|
driverLoadedAtStart = is_wifi_driver_loaded();
|
|
testPrintI("driverLoadedAtStart: %u", driverLoadedAtStart);
|
|
if (driverLoadedAtStart) {
|
|
// Stop wpa_supplicant
|
|
// Might already be stopped, in which case request should
|
|
// return immediately with success.
|
|
if ((rv = wifi_stop_supplicant(false)) != 0) {
|
|
testPrintE("init stop supplicant failed, rv: %i", rv);
|
|
exit(42);
|
|
}
|
|
testPrintI("Stopped wpa_supplicant");
|
|
|
|
if ((rv = wifi_unload_driver()) != 0) {
|
|
testPrintE("init unload driver failed, rv: %i", rv);
|
|
exit(43);
|
|
}
|
|
testPrintI("WiFi driver unloaded");
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Random Delay
|
|
*
|
|
* Delays for a random amount of time within the range given
|
|
* by the file scope variables delayMin and delayMax. The
|
|
* selected amount of delay can come from any part of the
|
|
* range, with a bias towards values closer to delayMin.
|
|
* The amount of bias is determined by the setting of DELAY_EXP.
|
|
* The setting of DELAY_EXP should always be > 1.0, with higher
|
|
* values causing a more significant bias toward the value
|
|
* of delayMin.
|
|
*/
|
|
void randDelay(void)
|
|
{
|
|
const unsigned long nanosecspersec = 1000000000;
|
|
float fract, biasedFract, amt;
|
|
struct timeval startTime, endTime;
|
|
|
|
// Obtain start time
|
|
gettimeofday(&startTime, NULL);
|
|
|
|
// Determine random amount to sleep.
|
|
// Values closer to delayMin are prefered by an amount
|
|
// determined by the value of DELAY_EXP.
|
|
fract = testRandFract();
|
|
biasedFract = pow(DELAY_EXP, fract) / pow(DELAY_EXP, 1.0);
|
|
amt = delayMin + ((delayMax - delayMin) * biasedFract);
|
|
|
|
// Delay
|
|
testDelay(amt);
|
|
|
|
// Obtain end time and display delta
|
|
gettimeofday(&endTime, NULL);
|
|
testPrintI("delay: %.2f",
|
|
(float) (tv2double(&endTime) - tv2double(&startTime)));
|
|
}
|
|
|
|
static void
|
|
randBind(const cpu_set_t *availSet, int *chosenCPU)
|
|
{
|
|
int rv;
|
|
cpu_set_t cpuset;
|
|
int chosenAvail, avail, cpu, currentCPU;
|
|
|
|
// Randomly bind to a CPU
|
|
// Lower 16 bits from random number generator thrown away,
|
|
// because the low-order bits tend to have the same sequence for
|
|
// different seed values.
|
|
chosenAvail = testRandMod(numAvailCPU);
|
|
CPU_ZERO(&cpuset);
|
|
avail = 0;
|
|
for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
|
|
if (CPU_ISSET(cpu, availSet)) {
|
|
if (chosenAvail == avail) {
|
|
CPU_SET(cpu, &cpuset);
|
|
break;
|
|
}
|
|
avail++;
|
|
}
|
|
}
|
|
assert(cpu < CPU_SETSIZE);
|
|
sched_setaffinity(0, sizeof(cpuset), &cpuset);
|
|
|
|
// Confirm executing on requested CPU
|
|
if ((currentCPU = sched_getcpu()) < 0) {
|
|
testPrintE("randBind sched_getcpu() failed, rv: %i errno: %i",
|
|
currentCPU, errno);
|
|
exit(80);
|
|
|
|
}
|
|
if (currentCPU != cpu) {
|
|
testPrintE("randBind executing on unexpected CPU %i, expected %i",
|
|
currentCPU, cpu);
|
|
exit(81);
|
|
}
|
|
|
|
// Let the caller know which CPU was chosen
|
|
*chosenCPU = cpu;
|
|
}
|