diff options
author | Andrew Vagin <avagin@virtuozzo.com> | 2016-02-20 19:23:00 +0300 |
---|---|---|
committer | Pavel Emelyanov <xemul@virtuozzo.com> | 2016-02-24 13:09:17 +0300 |
commit | 88aaae3ace1eace0399244a753895334c727c708 (patch) | |
tree | 32a9a21b4a4b91f05cd0be839e9ecfe775fc60fc /test/others/bers | |
parent | a47d2917a50273770103abdb132186f09c466b5d (diff) |
tests: move non-zdtm tests to tests/others/
Signed-off-by: Andrew Vagin <avagin@virtuozzo.com>
Signed-off-by: Pavel Emelyanov <xemul@virtuozzo.com>
Diffstat (limited to 'test/others/bers')
-rw-r--r-- | test/others/bers/Makefile | 47 | ||||
-rw-r--r-- | test/others/bers/bers.c | 420 | ||||
-rw-r--r-- | test/others/bers/bers.txt | 74 |
3 files changed, 541 insertions, 0 deletions
diff --git a/test/others/bers/Makefile b/test/others/bers/Makefile new file mode 100644 index 000000000..74b6142e7 --- /dev/null +++ b/test/others/bers/Makefile @@ -0,0 +1,47 @@ +ifeq ($(strip $(V)),) + E = @echo + Q = @ +else + E = @\# + Q = +endif + +export E Q + +ASCIIDOC := asciidoc +A2X := a2x +XMLTO := xmlto + +SRC += bers.txt +XMLS := $(patsubst %.txt,%.xml,$(SRC)) +MANS := $(patsubst %.txt,%.8,$(SRC)) + +%.8: %.txt + $(E) " GEN " $@ + $(Q) $(ASCIIDOC) -b docbook -d manpage -o $(patsubst %.8,%.xml,$@) $< + $(Q) $(XMLTO) man --skip-validation $(patsubst %.8,%.xml,$@) 2>/dev/null + +docs: $(MANS) + @true + +CFLAGS := -O0 -ggdb3 +LIBS := -lpthread + +%.o: %.c + $(E) " CC " $@ + $(Q) $(CC) -c -o $@ $(CFLAGS) $^ + +bers: bers.o + $(E) " LINK " $@ + $(Q) $(CC) -o $@ $(CFLAGS) $(LIBS) $^ + +all: bers + @true + +clean: + $(E) " CLEAN " + $(Q) rm -f $(XMLS) $(MANS) + $(Q) rm -f bers.o + $(Q) rm -f bers + +.PHONY: all docs clean diff --git a/test/others/bers/bers.c b/test/others/bers/bers.c new file mode 100644 index 000000000..208325c67 --- /dev/null +++ b/test/others/bers/bers.c @@ -0,0 +1,420 @@ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <limits.h> +#include <stdbool.h> + +#include <pthread.h> +#include <sys/mman.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <syscall.h> + +#define min(x, y) ({ \ + typeof(x) _min1 = (x); \ + typeof(y) _min2 = (y); \ + (void) (&_min1 == &_min2); \ + _min1 < _min2 ? _min1 : _min2; }) + +#define max(x, y) ({ \ + typeof(x) _max1 = (x); \ + typeof(y) _max2 = (y); \ + (void) (&_max1 == &_max2); \ + _max1 > _max2 ? _max1 : _max2; }) + +#define MAX_CHUNK 4096 +#define PAGE_SIZE 4096 + +#define pr_info(fmt, ...) \ + printf("%8d: " fmt, sys_gettid(), ##__VA_ARGS__) + +#define pr_err(fmt, ...) \ + printf("%8d: Error (%s:%d): " fmt, sys_gettid(),\ + __FILE__, __LINE__, ##__VA_ARGS__) + +#define pr_perror(fmt, ...) \ + pr_err(fmt ": %m\n", ##__VA_ARGS__) + +#define pr_msg(fmt, ...) \ + printf(fmt, ##__VA_ARGS__) + + +#define pr_trace(fmt, ...) \ + printf("%8d: %s: " fmt, sys_gettid(), __func__, \ + ##__VA_ARGS__) + +enum { + MEM_FILL_MODE_NONE = 0, + MEM_FILL_MODE_ALL = 1, + MEM_FILL_MODE_LIGHT = 2, + MEM_FILL_MODE_DIRTIFY = 3, +}; + +typedef struct { + pthread_mutex_t mutex; + pthread_mutexattr_t mutex_attr; + + size_t opt_tasks; + + size_t opt_files; + size_t opt_file_size; + int prev_fd[MAX_CHUNK]; + + size_t opt_mem; + size_t opt_mem_chunks; + size_t opt_mem_chunk_size; + int opt_mem_fill_mode; + int opt_mem_cycle_mode; + unsigned int opt_refresh_time; + + char *opt_work_dir; + int work_dir_fd; + DIR *work_dir; + + pid_t err_pid; + int err_no; + + unsigned long prev_map[MAX_CHUNK]; +} shared_data_t; + +static shared_data_t *shared; + +static int sys_gettid(void) +{ + return syscall(__NR_gettid); +} + +static void dirtify_memory(unsigned long *chunks, size_t nr_chunks, + size_t chunk_size, int mode, const size_t nr_pages) +{ + void *page; + size_t i; + + pr_trace("filling memory\n"); + switch (mode) { + case MEM_FILL_MODE_LIGHT: + *((unsigned long *)chunks[0]) = -1ul; + break; + case MEM_FILL_MODE_ALL: + for (i = 0; i < nr_chunks; i++) + memset((void *)chunks[i], (char)i, chunk_size); + break; + case MEM_FILL_MODE_DIRTIFY: + for (i = 0; i < nr_chunks; i++) + *((unsigned long *)chunks[i]) = -1ul; + break; + } +} + +static void dirtify_files(int *fd, size_t nr_files, size_t size) +{ + size_t buf[8192]; + size_t i, j, c; + + /* + * Note we don't write any _sane_ data here, the only + * important thing is I/O activity by self. + */ + + for (i = 0; i < nr_files; i++) { + size_t c = min(size, sizeof(buf)); + size_t left = size; + + while (left > 0) { + write(fd[i], buf, c); + left -= c; + c = min(left, sizeof(buf)); + } + } +} + +static int create_files(shared_data_t *shared, int *fd, size_t nr_files) +{ + char path[PATH_MAX]; + size_t i; + + memset(fd, 0xff, sizeof(fd)); + + pr_info("\tCreating %lu files\n", shared->opt_files); + + for (i = 0; i < shared->opt_files; i++) { + if (shared->prev_fd[i] != -1) { + close(shared->prev_fd[i]); + shared->prev_fd[i] = -1; + } + snprintf(path, sizeof(path), "%08d-%04d-temp", sys_gettid(), i); + fd[i] = openat(shared->work_dir_fd, path, O_RDWR | O_CREAT | O_TRUNC, 0666); + if (fd[i] < 0) { + pr_perror("Can't open %s/%s", shared->opt_work_dir, path); + shared->err_pid = sys_gettid(); + shared->err_no = -errno; + return -1; + } + shared->prev_fd[i] = fd[i]; + } + + return 0; +} + +static void work_on_fork(shared_data_t *shared) +{ + const size_t nr_pages = shared->opt_mem_chunk_size / PAGE_SIZE; + unsigned long chunks[MAX_CHUNK] = { }; + int fd[MAX_CHUNK]; + size_t i; + void *mem; + + pr_trace("locking\n"); + pthread_mutex_lock(&shared->mutex); + pr_trace("init\n"); + + pr_info("\tCreating %lu mmaps each %lu K\n", + shared->opt_mem_chunks, shared->opt_mem_chunk_size >> 10); + + for (i = 0; i < shared->opt_mem_chunks; i++) { + if (shared->prev_map[i]) { + munmap((void *)shared->prev_map[i], shared->opt_mem_chunk_size); + shared->prev_map[i] = 0; + } + + /* If we won't change proto here, the kernel might merge close areas */ + mem = mmap(NULL, shared->opt_mem_chunk_size, + PROT_READ | PROT_WRITE | ((i % 2) ? PROT_EXEC : 0), + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + + if (mem != (void *)MAP_FAILED) { + shared->prev_map[i] = (unsigned long)mem; + chunks[i] = (unsigned long)mem; + + pr_info("\t\tMap at %lx\n",(unsigned long)mem); + } else { + pr_info("\t\tCan't map\n"); + + shared->err_pid = sys_gettid(); + shared->err_no = -errno; + exit(1); + } + } + + if (shared->opt_mem_fill_mode) + dirtify_memory(chunks, shared->opt_mem_chunks, + shared->opt_mem_chunk_size, + shared->opt_mem_fill_mode, + nr_pages); + + if (create_files(shared, fd, shared->opt_files)) + exit(1); + + if (shared->opt_file_size) + dirtify_files(fd, shared->opt_files, shared->opt_file_size); + + pr_trace("releasing\n"); + pthread_mutex_unlock(&shared->mutex); + + while (1) { + sleep(shared->opt_refresh_time); + if (shared->opt_mem_cycle_mode) + dirtify_memory(chunks, shared->opt_mem_chunks, + shared->opt_mem_chunk_size, + shared->opt_mem_cycle_mode, + nr_pages); + if (shared->opt_file_size) + dirtify_files(fd, shared->opt_files, shared->opt_file_size); + } +} + +static int parse_mem_mode(int *mode, char *opt) +{ + if (!strcmp(opt, "all")) { + *mode = MEM_FILL_MODE_ALL; + } else if (!strcmp(opt, "light")) { + *mode = MEM_FILL_MODE_LIGHT; + } else if (!strcmp(opt, "dirtify")) { + *mode = MEM_FILL_MODE_DIRTIFY; + } else { + pr_err("Unrecognized option %s\n", opt); + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + /* a - 97, z - 122, A - 65, 90 */ + static const char short_opts[] = "t:d:f:m:c:h"; + static struct option long_opts[] = { + {"tasks", required_argument, 0, 't'}, + {"dir", required_argument, 0, 'd'}, + {"files", required_argument, 0, 'f'}, + {"memory", required_argument, 0, 'm'}, + {"mem-chunks", required_argument, 0, 'c'}, + {"help", no_argument, 0, 'h'}, + {"mem-fill", required_argument, 0, 10}, + {"mem-cycle", required_argument, 0, 11}, + {"refresh", required_argument, 0, 12}, + {"file-size", required_argument, 0, 13}, + { }, + }; + + char workdir[PATH_MAX]; + int opt, idx, pidfd; + char pidbuf[32]; + int status; + pid_t pid; + size_t i; + + shared = (void *)mmap(NULL, sizeof(*shared), PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_SHARED, -1, 0); + if ((void *)shared == MAP_FAILED) { + pr_err("Failed to setup shared data\n"); + exit(1); + } + + pthread_mutexattr_init(&shared->mutex_attr); + pthread_mutexattr_setpshared(&shared->mutex_attr, PTHREAD_PROCESS_SHARED); + pthread_mutex_init(&shared->mutex, &shared->mutex_attr); + + /* + * Default options. + */ + shared->opt_mem_chunks = 1; + shared->opt_refresh_time = 1; + shared->opt_tasks = 1; + shared->opt_mem = 1 << 20ul; + memset(shared->prev_fd, 0xff, sizeof(shared->prev_fd)); + + while (1) { + idx = -1; + opt = getopt_long(argc, argv, short_opts, long_opts, &idx); + if (opt == -1) + break; + + switch(opt) { + case 't': + shared->opt_tasks = (size_t)atol(optarg); + break; + case 'f': + shared->opt_files = (size_t)atol(optarg); + break; + case 'm': + /* In megabytes */ + shared->opt_mem = (size_t)atol(optarg) << 20ul; + break; + case 'c': + shared->opt_mem_chunks = (size_t)atol(optarg); + break; + case 'd': + shared->opt_work_dir = optarg; + break; + case 'h': + goto usage; + break; + case 10: + if (parse_mem_mode(&shared->opt_mem_fill_mode, optarg)) + goto usage; + case 11: + if (parse_mem_mode(&shared->opt_mem_cycle_mode, optarg)) + goto usage; + break; + case 12: + shared->opt_refresh_time = (unsigned int)atoi(optarg); + break; + case 13: + shared->opt_file_size = (size_t)atol(optarg); + } + } + + if (!shared->opt_work_dir) { + shared->opt_work_dir = getcwd(workdir, sizeof(workdir)); + if (!shared->opt_work_dir) { + pr_perror("Can't fetch current working dir"); + exit(1); + } + shared->opt_work_dir = workdir; + } + + if (shared->opt_mem_chunks > MAX_CHUNK) + shared->opt_mem_chunks = MAX_CHUNK; + + if (shared->opt_files > MAX_CHUNK) + shared->opt_files = MAX_CHUNK; + + shared->work_dir = opendir(shared->opt_work_dir); + if (!shared->work_dir) { + pr_perror("Can't open working dir `%s'", + shared->opt_work_dir); + exit(1); + } + shared->work_dir_fd = dirfd(shared->work_dir); + + shared->opt_mem_chunk_size = shared->opt_mem / shared->opt_mem_chunks; + + if (shared->opt_mem_chunk_size && + shared->opt_mem_chunk_size < PAGE_SIZE) { + pr_err("Memory chunk size is too small, provide at least %lu M of memory\n", + (shared->opt_mem_chunks * PAGE_SIZE) >> 20ul); + exit(1); + } + + for (i = 0; i < shared->opt_tasks; i++) { + if (shared->err_no) + goto err_child; + + pid = fork(); + if (pid < 0) { + printf("Can't create fork: %m\n"); + exit(1); + } else if (pid == 0) { + work_on_fork(shared); + } + } + + /* + * Once everything is done and we're in cycle, + * create pidfile and go to sleep... + */ + pid = sys_gettid(); + pidfd = openat(shared->work_dir_fd, "bers.pid", O_RDWR | O_CREAT | O_TRUNC, 0666); + if (pidfd < 0) { + pr_perror("Can't open pidfile"); + exit(1); + } + snprintf(pidbuf, sizeof(pidbuf), "%d", sys_gettid()); + write(pidfd, pidbuf, strlen(pidbuf)); + close(pidfd); + pidfd = -1; + + /* + * Endless! + */ + while (!shared->err_no) + sleep(1); + +err_child: + pr_err("Child %d exited with %d\n", + shared->err_pid, shared->err_no); + return shared->err_no; + +usage: + pr_msg("bers [options]\n"); + pr_msg(" -t|--tasks <num> create <num> of tasks\n"); + pr_msg(" -d|--dir <dir> use directory <dir> for temporary files\n"); + pr_msg(" -f|--files <num> create <num> files for each task\n"); + pr_msg(" -m|--memory <num> allocate <num> megabytes for each task\n"); + pr_msg(" --memory-chunks <num> split memory to <num> equal parts\n"); + pr_msg(" --mem-fill <mode> fill memory with data dependin on <mode>:\n"); + pr_msg(" all fill every byte of memory\n"); + pr_msg(" light fill first bytes of every page\n"); + pr_msg(" dirtify fill every page\n"); + pr_msg(" --mem-cycle <mode> same as --mem-fill but for cycling\n"); + pr_msg(" --refresh <second> refresh loading of every task each <second>\n"); + pr_msg(" --file-size <bytes> write <bytes> of data into each file on every refresh cycle\n"); + + return 1; +} diff --git a/test/others/bers/bers.txt b/test/others/bers/bers.txt new file mode 100644 index 000000000..17c0c0800 --- /dev/null +++ b/test/others/bers/bers.txt @@ -0,0 +1,74 @@ +bers(8) +======= +:doctype: manpage +:man source: bers +:man version: 0.0.1 +:man manual: bers manual + +NAME +---- +bers - go berserk and eat computer resources + +SYNOPSIS +-------- +*bers* ['options'] + +DESCRIPTION +----------- +*bers* is a command line utility aimed to eat resources of the computer it runs on. +Idea behind is to create a number of tasks which would trash computer resources +eating cpu and i/o time. + +OPTIONS +------- +*-t*, *--tasks* 'num':: + Create 'num' number of forks. + +*-d*, *--dir* 'dir':: + Path to 'dir' directory where temporary files will be created to load + I/O subsystem. + +*-f*, *--files* 'num':: + Create 'num' files in each task. + +*-m*, *--memory* 'num':: + Allocate 'num' megabytes of memory for every task. + +*--mem-chunks* 'num':: + Allocate memory for each task not as one slab but split + it into 'num' equal parts. + +*--mem-fill* 'mode':: + Touch (write) into allocated memory once task is created. The + 'mode' might be one of the following: 'all' -- write every + single byte of the memory, 'light' -- write into first bytes + of first page of the allocated memory chunk, 'dirtify' -- write + into every page of every allocated chunk. + +*--mem-cycle* 'mode':: + Same as *--mem-fill*, but 'mode' taken into account while + task is cycling. By default each cycle initiated per one second. + +*--refresh* 'second':: + Refresh load state of every task each 'second'. By refsresh + here means to dirtify memory and file contents. + +*--file-size* 'bytes':: + Write 'bytes' of data into each file on every refresh cycle. + +EXAMPLE +------- + +bers -d test/bers/dump -t 256 -m 54 -c 4 -f 200 --mem-fill dirtify --mem-cycle dirtify + +We generate 256 tasks wit each allocating 54 megabytes of memory splitted +equally into 4 memory areas. Each task opens 200 files. On creation and +cycling we touch every page of every memory area. + +AUTHOR +------ +OpenVZ team. + +COPYRIGHT +--------- +Copyright \(C) 2014, Parallels Inc. |