testing/sd_stress: Port sdstress testing utility from PX4-Autopilot.
Add port of the sdstress utility from PX4-Autopilot: https://github.com/PX4/PX4-Autopilot This tool is useful for verifying correct operation of SD, EMMC, or other mountpoints.
This commit is contained in:
parent
877509762c
commit
4576b3923c
32
LICENSE
32
LICENSE
@ -2215,3 +2215,35 @@ proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
|
||||
apps/testing/sd_stress
|
||||
===================
|
||||
|
||||
Copyright (c) 2016-2021 PX4 Development Team. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
3. Neither the name PX4 nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
35
testing/sd_stress/Kconfig
Normal file
35
testing/sd_stress/Kconfig
Normal file
@ -0,0 +1,35 @@
|
||||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config TESTING_SD_STRESS
|
||||
tristate "SD stress program"
|
||||
depends on ALLOW_BSD_COMPONENTS
|
||||
default n
|
||||
---help---
|
||||
SD stress application based on https://github.com/PX4/PX4-Autopilot/blob/main/src/systemcmds/sd_stress/sd_stress.cpp
|
||||
|
||||
if TESTING_SD_STRESS
|
||||
|
||||
config TESTING_SD_STRESS_PROGNAME
|
||||
string "Program name"
|
||||
default "sdstress"
|
||||
---help---
|
||||
This is the name of the program that will be used when the NSH ELF
|
||||
program is installed.
|
||||
|
||||
config TESTING_SD_STRESS_PRIORITY
|
||||
int "SD stress task priority"
|
||||
default 100
|
||||
|
||||
config TESTING_SD_STRESS_STACKSIZE
|
||||
int "SD stress stack size"
|
||||
default DEFAULT_TASK_STACKSIZE
|
||||
|
||||
config TESTING_SD_STRESS_DEVICE
|
||||
string "SD / MMC mount point"
|
||||
default "/mnt"
|
||||
|
||||
endif
|
||||
|
3
testing/sd_stress/Make.defs
Normal file
3
testing/sd_stress/Make.defs
Normal file
@ -0,0 +1,3 @@
|
||||
ifneq ($(CONFIG_TESTING_SD_STRESS),)
|
||||
CONFIGURED_APPS += $(APPDIR)/testing/sd_stress
|
||||
endif
|
30
testing/sd_stress/Makefile
Normal file
30
testing/sd_stress/Makefile
Normal file
@ -0,0 +1,30 @@
|
||||
############################################################################
|
||||
# apps/testing/sd_stress/Makefile
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you 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.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
PROGNAME = $(CONFIG_TESTING_SD_STRESS_PROGNAME)
|
||||
PRIORITY = $(CONFIG_TESTING_SD_STRESS_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_TESTING_SD_STRESS_STACKSIZE)
|
||||
MODULE = $(CONFIG_TESTING_SD_STRESS)
|
||||
|
||||
MAINSRC = sd_stress_main.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
404
testing/sd_stress/sd_stress_main.c
Normal file
404
testing/sd_stress/sd_stress_main.c
Normal file
@ -0,0 +1,404 @@
|
||||
/****************************************************************************
|
||||
* apps/testing/sd_stress/sd_stress_main.c
|
||||
*
|
||||
* Original Licence:
|
||||
*
|
||||
* Copyright (c) 2016-2021 PX4 Development Team. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* 3. Neither the name PX4 nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Originally ported from PX4 https://github.com/PX4/PX4-Autopilot,
|
||||
* with the following additions:
|
||||
*
|
||||
* - The number of files can be specified from the command line.
|
||||
* - Bytes are written and read back from created files to verify integrity.
|
||||
* - The bytes written are obtained from a static set,
|
||||
* rather than a constant 0xAA
|
||||
* - The results are reported as a floating point, millisecond value.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <nuttx/clock.h>
|
||||
|
||||
static const size_t MAX_PATH_LEN = 52;
|
||||
|
||||
static const char *TEMPDIR = CONFIG_TESTING_SD_STRESS_DEVICE"/stress";
|
||||
static const char *TEMPDIR2 = CONFIG_TESTING_SD_STRESS_DEVICE"/moved";
|
||||
static const char *TEMPFILE = "tmp";
|
||||
|
||||
const size_t max_runs = 10000;
|
||||
const size_t min_runs = 1;
|
||||
const size_t default_runs = 32;
|
||||
|
||||
const size_t max_bytes = 10000;
|
||||
const size_t min_bytes = 1;
|
||||
const size_t default_bytes = 4096;
|
||||
|
||||
const size_t max_files = 999;
|
||||
const size_t min_files = 1;
|
||||
const size_t default_files = 64;
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf("Stress test on a mount point\n");
|
||||
printf(CONFIG_TESTING_SD_STRESS_PROGNAME ": [-r] [-b] [-f]\n");
|
||||
printf(" -r Number of runs (%u-%u), default %u\n",
|
||||
min_runs, max_runs, default_runs);
|
||||
printf(" -b Number of bytes (%u-%u), default %u\n",
|
||||
min_bytes, max_bytes, default_bytes);
|
||||
printf(" -f Number of files (%u-%u), default %u\n",
|
||||
min_files, max_files, default_files);
|
||||
}
|
||||
|
||||
static bool create_dir(const char *path)
|
||||
{
|
||||
int ret = mkdir(TEMPDIR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("mkdir %s failed, ret: %d, errno: %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool remove_dir(const char *path)
|
||||
{
|
||||
int ret = rmdir(TEMPDIR2);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("rmdir %s failed, ret: %d, errno: %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool create_files(const char *dir, const char *name,
|
||||
size_t num_files, char *bytes, size_t num_bytes)
|
||||
{
|
||||
bool passed = true;
|
||||
if (num_files > 999)
|
||||
{
|
||||
printf("too many files\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char *read_bytes = (char *)malloc(num_bytes);
|
||||
|
||||
if (!read_bytes)
|
||||
{
|
||||
printf("malloc failed for read bytes bufffer\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t path_len = strlen(dir) + strlen(name);
|
||||
|
||||
if (path_len + 5 >= MAX_PATH_LEN)
|
||||
{
|
||||
printf("path name too long\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_files; ++i)
|
||||
{
|
||||
char path[MAX_PATH_LEN];
|
||||
snprintf(path, MAX_PATH_LEN, "%s/%s%03u", dir, name, i);
|
||||
|
||||
memset(read_bytes, 0x0, num_bytes);
|
||||
|
||||
/* Fill the file with a set of incrementing bytes */
|
||||
|
||||
for (size_t j = 0; j < num_bytes; j++)
|
||||
{
|
||||
bytes[j] = (bytes[j] + i) & 0xff;
|
||||
}
|
||||
|
||||
int fd = open(path, O_CREAT | O_RDWR);
|
||||
|
||||
if (fd < 0)
|
||||
{
|
||||
printf("open %s failed, errno: %d -> %s\n",
|
||||
path, errno, strerror(errno));
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
int ret = write(fd, bytes, num_bytes);
|
||||
|
||||
if (ret != (int)num_bytes)
|
||||
{
|
||||
printf("write %s failed, ret: %d, errno %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = lseek(fd, 0, SEEK_SET);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("lseek %s failed, ret: %d, errno %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = read(fd, read_bytes, num_bytes);
|
||||
|
||||
if (ret != (int)num_bytes)
|
||||
{
|
||||
printf("read %s failed, ret: %d, errno %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(read_bytes, bytes, num_bytes) != 0)
|
||||
{
|
||||
printf("read and write buffers are not the same\n");
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = close(fd);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("close %s failed, ret: %d, errno %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
passed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(read_bytes);
|
||||
return passed;
|
||||
}
|
||||
|
||||
static bool remove_files(const char *dir, const char *name,
|
||||
size_t num_files)
|
||||
{
|
||||
if (num_files > 999)
|
||||
{
|
||||
printf("too many files\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t path_len = strlen(dir) + strlen(name);
|
||||
|
||||
if (path_len + 5 >= MAX_PATH_LEN)
|
||||
{
|
||||
printf("path name too long\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_files; ++i)
|
||||
{
|
||||
char path[MAX_PATH_LEN];
|
||||
snprintf(path, MAX_PATH_LEN, "%s/%s%03u", dir, name, i);
|
||||
|
||||
int ret = unlink(path);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("unlink %s failed, ret: %d, errno %d -> %s\n",
|
||||
path, ret, errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool rename_dir(const char *old_dir, const char *new_dir)
|
||||
{
|
||||
int ret = rename(old_dir, new_dir);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("rename %s to %s failed, ret: %d, errno %d -> %s\n",
|
||||
old_dir, new_dir, ret, errno, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct timespec get_abs_time(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return ts;
|
||||
}
|
||||
|
||||
uint64_t get_time_delta(const struct timespec *start,
|
||||
const struct timespec *end)
|
||||
{
|
||||
uint64_t elapsed;
|
||||
elapsed = (((uint64_t)end->tv_sec * NSEC_PER_SEC) + end->tv_nsec);
|
||||
elapsed -= (((uint64_t)start->tv_sec * NSEC_PER_SEC) + start->tv_nsec);
|
||||
return elapsed / 1000;
|
||||
}
|
||||
|
||||
float get_elapsed_time_ms(const struct timespec *start)
|
||||
{
|
||||
struct timespec now = get_abs_time();
|
||||
return get_time_delta(start, &now) / (float)USEC_PER_MSEC;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ch;
|
||||
int ret;
|
||||
size_t num_runs = default_runs;
|
||||
size_t num_bytes = default_bytes;
|
||||
size_t num_files = default_files;
|
||||
float total_time;
|
||||
float elapsed_time;
|
||||
struct timespec start;
|
||||
char * bytes;
|
||||
|
||||
while ((ch = getopt(argc, argv, "r:b:f:")) != EOF)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'r':
|
||||
num_runs = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
num_bytes = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
num_files = strtol(optarg, NULL, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_bytes > max_bytes || num_bytes < min_bytes)
|
||||
{
|
||||
printf("Bytes outside allowable range\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (num_runs > max_runs || num_runs < min_runs)
|
||||
{
|
||||
printf("Runs outside allowable range\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (num_files > max_files || num_files < min_files)
|
||||
{
|
||||
printf("File count outside allowable range\n");
|
||||
usage();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
printf("Start stress test with %u files, %u bytes and %u iterations.\n",
|
||||
num_files, num_bytes, num_runs);
|
||||
|
||||
bytes = (char *)malloc(num_bytes);
|
||||
ret = 0;
|
||||
|
||||
if (!bytes)
|
||||
{
|
||||
printf("Failed to allocate byte buffer.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_bytes; i++)
|
||||
{
|
||||
bytes[i] = i & 0xff;
|
||||
}
|
||||
|
||||
total_time = 0;
|
||||
|
||||
for (size_t i = 0; i < num_runs; ++i)
|
||||
{
|
||||
start = get_abs_time();
|
||||
|
||||
const bool result =
|
||||
create_dir(TEMPDIR)
|
||||
&& create_files(TEMPDIR, TEMPFILE, num_files, bytes, num_bytes)
|
||||
&& rename_dir(TEMPDIR, TEMPDIR2)
|
||||
&& remove_files(TEMPDIR2, TEMPFILE, num_files)
|
||||
&& remove_dir(TEMPDIR2);
|
||||
|
||||
elapsed_time = get_elapsed_time_ms(&start);
|
||||
total_time += elapsed_time;
|
||||
printf("iteration %u took %.3f ms: %s\n", i,
|
||||
elapsed_time, result ? "OK" : "FAIL");
|
||||
|
||||
if (!result)
|
||||
{
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Test %s: Average time: %.3f ms\n",
|
||||
ret ? "FAIL" : "OK", total_time / num_runs);
|
||||
|
||||
free(bytes);
|
||||
return ret;
|
||||
}
|
Loading…
Reference in New Issue
Block a user