diff options
Diffstat (limited to 'tests/fs-tests/stress/atoms/rndrm99.c')
-rw-r--r-- | tests/fs-tests/stress/atoms/rndrm99.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/tests/fs-tests/stress/atoms/rndrm99.c b/tests/fs-tests/stress/atoms/rndrm99.c new file mode 100644 index 0000000..7751839 --- /dev/null +++ b/tests/fs-tests/stress/atoms/rndrm99.c @@ -0,0 +1,431 @@ +/* + * Copyright (C) 2007 Nokia Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + * Author: Adrian Hunter + */ + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/time.h> +#include <time.h> +#include <sys/vfs.h> +#include <sys/statvfs.h> +#include <dirent.h> +#include <ctype.h> +#include <limits.h> + +#include "tests.h" + +uint32_t files_created = 0; +uint32_t files_removed = 0; +uint32_t dirs_created = 0; +uint32_t dirs_removed = 0; +int64_t *size_ptr = 0; + +void display_stats(void) +{ + printf( "\nrndrm99 stats:\n" + "\tNumber of files created = %u\n" + "\tNumber of files deleted = %u\n" + "\tNumber of directories created = %u\n" + "\tNumber of directories deleted = %u\n" + "\tCurrent net size of creates and deletes = %lld\n", + (unsigned) files_created, + (unsigned) files_removed, + (unsigned) dirs_created, + (unsigned) dirs_removed, + (long long) (size_ptr ? *size_ptr : 0)); + fflush(stdout); +} + +struct timeval tv_before; +struct timeval tv_after; + +void before(void) +{ + CHECK(gettimeofday(&tv_before, NULL) != -1); +} + +void after(const char *msg) +{ + time_t diff; + CHECK(gettimeofday(&tv_after, NULL) != -1); + diff = tv_after.tv_sec - tv_before.tv_sec; + if (diff >= 8) { + printf("\nrndrm99: the following fn took more than 8 seconds: %s (took %u secs)\n",msg,(unsigned) diff); + fflush(stdout); + display_stats(); + } +} + +#define WRITE_BUFFER_SIZE 32768 + +static char write_buffer[WRITE_BUFFER_SIZE]; + +static void init_write_buffer() +{ + static int init = 0; + + if (!init) { + int i, d; + uint64_t u; + + u = RAND_MAX; + u += 1; + u /= 256; + d = (int) u; + srand(1); + for (i = 0; i < WRITE_BUFFER_SIZE; ++i) + write_buffer[i] = rand() / d; + init = 1; + } +} + +/* Write size random bytes into file descriptor fd at the current position, + returning the number of bytes actually written */ +uint64_t fill_file(int fd, uint64_t size) +{ + ssize_t written; + size_t sz; + unsigned start = 0, length; + uint64_t remains; + uint64_t actual_size = 0; + + init_write_buffer(); + remains = size; + while (remains > 0) { + length = WRITE_BUFFER_SIZE - start; + if (remains > length) + sz = length; + else + sz = (size_t) remains; + before(); + written = write(fd, write_buffer + start, sz); + if (written <= 0) { + CHECK(errno == ENOSPC); /* File system full */ + errno = 0; + after("write"); + fprintf(stderr,"\nrndrm99: write failed with ENOSPC\n");fflush(stderr); + display_stats(); + break; + } + after("write"); + remains -= written; + actual_size += written; + if ((size_t) written == sz) + start = 0; + else + start += written; + } + return actual_size; +} + +/* Create a file of size file_size */ +uint64_t create_file(const char *file_name, uint64_t file_size) +{ + int fd; + int flags; + mode_t mode; + uint64_t actual_size; /* Less than size if the file system is full */ + + flags = O_CREAT | O_TRUNC | O_WRONLY; + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH; + before(); + fd = open(file_name, flags, mode); + if (fd == -1 && errno == ENOSPC) { + errno = 0; + after("open"); + fprintf(stderr,"\nrndrm99: open failed with ENOSPC\n");fflush(stderr); + display_stats(); + return 0; /* File system full */ + } + CHECK(fd != -1); + after("open"); + actual_size = fill_file(fd, file_size); + before(); + CHECK(close(fd) != -1); + after("close"); + if (file_size != 0 && actual_size == 0) { + printf("\nrndrm99: unlinking zero size file\n");fflush(stdout); + before(); + CHECK(unlink(file_name) != -1); + after("unlink (create_file)"); + } + return actual_size; +} + +/* Create an empty sub-directory or small file in the current directory */ +int64_t create_entry(char *return_name) +{ + int fd; + char name[256]; + int64_t res; + + for (;;) { + sprintf(name, "%u", (unsigned) tests_random_no(10000000)); + before(); + fd = open(name, O_RDONLY); + after("open (create_entry)"); + if (fd == -1) + break; + before(); + close(fd); + after("close (create_entry)"); + } + if (return_name) + strcpy(return_name, name); + if (tests_random_no(2)) { + res = create_file(name, tests_random_no(4096)); + if (res > 0) + files_created += 1; + return res; + } else { + before(); + if (mkdir(name, 0777) == -1) { + CHECK(errno == ENOSPC); + after("mkdir"); + errno = 0; + fprintf(stderr,"\nrndrm99: mkdir failed with ENOSPC\n");fflush(stderr); + display_stats(); + return 0; + } + after("mkdir"); + dirs_created += 1; + return TESTS_EMPTY_DIR_SIZE; + } +} + +/* Remove a random file of empty sub-directory from the current directory */ +int64_t remove_entry(void) +{ + DIR *dir; + struct dirent *entry; + unsigned count = 0, pos; + int64_t result = 0; + + before(); + dir = opendir("."); + CHECK(dir != NULL); + after("opendir"); + for (;;) { + errno = 0; + before(); + entry = readdir(dir); + if (entry) { + after("readdir 1"); + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) + ++count; + } else { + CHECK(errno == 0); + after("readdir 1"); + break; + } + } + pos = tests_random_no(count); + count = 0; + before(); + rewinddir(dir); + after("rewinddir"); + for (;;) { + errno = 0; + before(); + entry = readdir(dir); + if (!entry) { + CHECK(errno == 0); + after("readdir 2"); + break; + } + after("readdir 2"); + if (strcmp(".",entry->d_name) != 0 && + strcmp("..",entry->d_name) != 0) { + if (count == pos) { + if (entry->d_type == DT_DIR) { + before(); + tests_clear_dir(entry->d_name); + after("tests_clear_dir"); + before(); + CHECK(rmdir(entry->d_name) != -1); + after("rmdir"); + result = TESTS_EMPTY_DIR_SIZE; + dirs_removed += 1; + } else { + struct stat st; + before(); + CHECK(stat(entry->d_name, &st) != -1); + after("stat"); + result = st.st_size; + before(); + CHECK(unlink(entry->d_name) != -1); + after("unlink"); + files_removed += 1; + } + } + ++count; + } + } + before(); + CHECK(closedir(dir) != -1); + after("closedir"); + return result; +} + +void rndrm99(void) +{ + int64_t repeat, loop_cnt; + int64_t size, this_size; + pid_t pid; + char dir_name[256]; + + size_ptr = &size; + /* Create a directory to test in */ + pid = getpid(); + tests_cat_pid(dir_name, "rndrm99_test_dir_", pid); + if (chdir(dir_name) == -1) + CHECK(mkdir(dir_name, 0777) != -1); + CHECK(chdir(dir_name) != -1); + /* Repeat loop */ + repeat = tests_repeat_parameter; + size = 0; + for (;;) { + /* Create and remove sub-dirs and small files, */ + /* but tending to grow */ + printf("\nrndrm99: growing\n");fflush(stdout); + loop_cnt = 0; + do { + if (loop_cnt++ % 2000 == 0) + display_stats(); + if (tests_random_no(3)) { + this_size = create_entry(NULL); + if (!this_size) + break; + size += this_size; + } else { + this_size = remove_entry(); + size -= this_size; + if (size < 0) + size = 0; + if (!this_size) + this_size = 1; + } + } while (this_size && + (tests_size_parameter == 0 || + size < tests_size_parameter)); + /* Create and remove sub-dirs and small files, but */ + /* but tending to shrink */ + printf("\nrndrm99: shrinking\n");fflush(stdout); + loop_cnt = 0; + do { + if (loop_cnt++ % 2000 == 0) + display_stats(); + if (!tests_random_no(3)) { + this_size = create_entry(NULL); + size += this_size; + } else { + this_size = remove_entry(); + size -= this_size; + if (size < 0) + size = 0; + } + } while ((tests_size_parameter != 0 && + size > tests_size_parameter / 10) || + (tests_size_parameter == 0 && size > 100000)); + /* Break if repeat count exceeded */ + if (tests_repeat_parameter > 0 && --repeat <= 0) + break; + /* Sleep */ + if (tests_sleep_parameter > 0) { + unsigned us = tests_sleep_parameter * 1000; + unsigned rand_divisor = RAND_MAX / us; + unsigned s = (us / 2) + (rand() / rand_divisor); + printf("\nrndrm99: sleeping\n");fflush(stdout); + usleep(s); + } + } + printf("\nrndrm99: tidying\n");fflush(stdout); + display_stats(); + /* Tidy up by removing everything */ + tests_clear_dir("."); + CHECK(chdir("..") != -1); + CHECK(rmdir(dir_name) != -1); + size_ptr = 0; +} + +/* Title of this test */ + +const char *rndrm99_get_title(void) +{ + return "Randomly create and remove directories and files"; +} + +/* Description of this test */ + +const char *rndrm99_get_description(void) +{ + return + "Create a directory named rndrm99_test_dir_pid, where " \ + "pid is the process id. Within that directory, " \ + "randomly create and remove " \ + "a number of sub-directories and small files, " \ + "but do more creates than removes. " \ + "When the total size of all sub-directories and files " \ + "is greater than the size specified by the size parameter, " \ + "start to do more removes than creates. " \ + "The size parameter is given by the -z or --size option, " \ + "otherwise it defaults to 1000000. " \ + "A size of zero fills the file system until there is no " + "space left. " \ + "The task repeats, sleeping in between each iteration. " \ + "The repeat count is set by the -n or --repeat option, " \ + "otherwise it defaults to 1. " \ + "A repeat count of zero repeats forever. " \ + "The sleep value is given by the -p or --sleep option, " \ + "otherwise it defaults to 0. " + "Sleep is specified in milliseconds."; +} + +int main(int argc, char *argv[]) +{ + int run_test; + + /* Set default test size */ + tests_size_parameter = 1000000; + + /* Set default test repetition */ + tests_repeat_parameter = 1; + + /* Set default test sleep */ + tests_sleep_parameter = 0; + + /* Handle common arguments */ + run_test = tests_get_args(argc, argv, rndrm99_get_title(), + rndrm99_get_description(), "znp"); + if (!run_test) + return 1; + /* Change directory to the file system and check it is ok for testing */ + tests_check_test_file_system(); + /* Do the actual test */ + rndrm99(); + return 0; +} |