summaryrefslogtreecommitdiff
path: root/Userland/Tests
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Tests')
-rw-r--r--Userland/Tests/Kernel/stress-writeread.cpp175
1 files changed, 175 insertions, 0 deletions
diff --git a/Userland/Tests/Kernel/stress-writeread.cpp b/Userland/Tests/Kernel/stress-writeread.cpp
new file mode 100644
index 0000000000..5a35e8ad06
--- /dev/null
+++ b/Userland/Tests/Kernel/stress-writeread.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2021, the SerenityOS developers.
+ * 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.
+ *
+ * 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 HOLDER 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.
+ */
+
+#include <AK/Random.h>
+#include <LibCore/ArgsParser.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
+bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer);
+
+bool verify_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
+{
+ auto offset = block * buffer.size();
+ auto rs = lseek(fd, offset, SEEK_SET);
+ if (rs < 0) {
+ fprintf(stderr, "Couldn't seek to block %lld (offset %lld) while verifying: %s\n", block, offset, strerror(errno));
+ return false;
+ }
+ auto rw = read(fd, buffer.data(), buffer.size());
+ if (rw != static_cast<int>(buffer.size())) {
+ fprintf(stderr, "Failure to read block %lld: %s\n", block, strerror(errno));
+ return false;
+ }
+ srand((seed + 1) * (block + 1));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ if (buffer[i] != rand() % 256) {
+ fprintf(stderr, "Discrepancy detected at block %lld offset %zd\n", block, i);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool write_block(int fd, int seed, off_t block, AK::ByteBuffer& buffer)
+{
+ auto offset = block * buffer.size();
+ auto rs = lseek(fd, offset, SEEK_SET);
+ if (rs < 0) {
+ fprintf(stderr, "Couldn't seek to block %lld (offset %lld) while verifying: %s\n", block, offset, strerror(errno));
+ return false;
+ }
+ srand((seed + 1) * (block + 1));
+ for (size_t i = 0; i < buffer.size(); i++)
+ buffer[i] = rand();
+ auto rw = write(fd, buffer.data(), buffer.size());
+ if (rw != static_cast<int>(buffer.size())) {
+ fprintf(stderr, "Failure to write block %lld: %s\n", block, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char** argv)
+{
+ const char* target = nullptr;
+ int min_block_offset = 0;
+ int block_length = 2048;
+ int block_size = 512;
+ int count = 1024;
+ int rng_seed = 0;
+ bool paranoid_mode = false;
+ bool random_mode = false;
+ bool stop_mode = false;
+ bool uninitialized_mode = false;
+
+ Core::ArgsParser args_parser;
+ args_parser.add_option(min_block_offset, "Minimum block offset to consider", "min-offset", 'o', "size");
+ args_parser.add_option(block_length, "Number of blocks to consider", "length", 's', "size");
+ args_parser.add_option(block_size, "Block size", "block-size", 'b', "size");
+ args_parser.add_option(count, "Number of write/read cycles to run", "number", 'n', "number");
+ args_parser.add_option(rng_seed, "Random number generator seed", "seed", 'S', "number");
+ args_parser.add_option(paranoid_mode, "Check entire range for consistency after each write", "paranoid", 'p');
+ args_parser.add_option(random_mode, "Write one block inside range at random", "random", 'r');
+ args_parser.add_option(stop_mode, "Stop after first error", "abort-on-error", 'a');
+ args_parser.add_option(uninitialized_mode, "Don't pre-initialize block range", "uninitialized", 'u');
+ args_parser.add_positional_argument(target, "Target device/file path", "target");
+ args_parser.parse(argc, argv);
+
+ auto buffer = AK::ByteBuffer::create_zeroed(block_size);
+
+ int fd = open(target, O_CREAT | O_RDWR, 0666);
+ if (fd < 0) {
+ perror("Couldn't create target file");
+ return EXIT_FAILURE;
+ }
+
+ if (!uninitialized_mode) {
+ int old_percent = -100;
+ for (int i = min_block_offset; i < min_block_offset + block_length; i++) {
+ int percent;
+ if (block_length <= 1)
+ percent = 100;
+ else
+ percent = 100 * (i - min_block_offset) / (block_length - 1);
+ if (old_percent != percent) {
+ printf("Pre-initializing entire block range (%3d%%)...\n", percent);
+ old_percent = percent;
+ }
+
+ if (!write_block(fd, rng_seed, i, buffer))
+ return EXIT_FAILURE;
+ }
+ }
+
+ int r = EXIT_SUCCESS;
+ for (int i = 0; i < count; i++) {
+ printf("(%d/%d)\tPass %d...\n", i + 1, count, i + 1);
+
+ for (int j = min_block_offset; j < min_block_offset + block_length; j++) {
+ off_t block;
+ if (random_mode)
+ while ((block = AK::get_random<off_t>()) < 0)
+ ;
+ else
+ block = j;
+ block = min_block_offset + block % block_length;
+
+ if (paranoid_mode) {
+ for (int k = min_block_offset; j < min_block_offset + block_length; j++) {
+ if (!verify_block(fd, rng_seed, k, buffer)) {
+ if (stop_mode)
+ return EXIT_FAILURE;
+ else
+ r = EXIT_FAILURE;
+ }
+ }
+ } else {
+ if (!verify_block(fd, rng_seed, block, buffer)) {
+ if (stop_mode)
+ return EXIT_FAILURE;
+ else
+ r = EXIT_FAILURE;
+ }
+ }
+
+ if (!write_block(fd, rng_seed, block, buffer)) {
+ if (stop_mode)
+ return EXIT_FAILURE;
+ else
+ r = EXIT_FAILURE;
+ }
+ }
+ }
+
+ close(fd);
+ return r;
+}