aboutsummaryrefslogtreecommitdiff
path: root/tests/fs-tests/stress/atoms/rndrm99.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/fs-tests/stress/atoms/rndrm99.c')
-rw-r--r--tests/fs-tests/stress/atoms/rndrm99.c431
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;
+}