diff options
author | Rom Lemarchand <romlem@google.com> | 2014-11-10 10:32:49 -0800 |
---|---|---|
committer | Rom Lemarchand <romlem@google.com> | 2014-11-11 13:55:36 -0800 |
commit | de3942afaa4a84529f7c110fe302bb2b346fbe23 (patch) | |
tree | 427811035656f8360756aad8af0beb7501fb9c4e | |
parent | 946b1184aabba0f1c5809ed7e6b63738145ba421 (diff) | |
download | extras-de3942afaa4a84529f7c110fe302bb2b346fbe23.tar.gz |
Add paging test
Add a system paging test that measures the following things:
- mmap and munmap a file
- page a file and and out of memory
- thrash the page cache by bringing in more pages than the system can hold in RAM
Change-Id: I46dfb52a02e5030babd5b24c13a2abca779aeb02
-rw-r--r-- | tests/pagingtest/Android.mk | 20 | ||||
-rw-r--r-- | tests/pagingtest/mmap_test.c | 49 | ||||
-rw-r--r-- | tests/pagingtest/pageinout_test.c | 90 | ||||
-rw-r--r-- | tests/pagingtest/pagingtest.c | 170 | ||||
-rw-r--r-- | tests/pagingtest/pagingtest.h | 20 | ||||
-rw-r--r-- | tests/pagingtest/thrashing_test.c | 78 |
6 files changed, 427 insertions, 0 deletions
diff --git a/tests/pagingtest/Android.mk b/tests/pagingtest/Android.mk new file mode 100644 index 00000000..727e3b81 --- /dev/null +++ b/tests/pagingtest/Android.mk @@ -0,0 +1,20 @@ +local_target_dir := $(TARGET_OUT_DATA)/local/tmp +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + pagingtest.c \ + mmap_test.c \ + pageinout_test.c \ + thrashing_test.c + +LOCAL_MODULE:= pagingtest + +LOCAL_MODULE_TAGS := tests + +LOCAL_MODULE_PATH := $(local_target_dir) +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := pagingtest +LOCAL_MODULE_STEM_64 := pagingtest64 + +include $(BUILD_EXECUTABLE) diff --git a/tests/pagingtest/mmap_test.c b/tests/pagingtest/mmap_test.c new file mode 100644 index 00000000..07ba94b0 --- /dev/null +++ b/tests/pagingtest/mmap_test.c @@ -0,0 +1,49 @@ +#include <stdio.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <errno.h> +#include <string.h> +#include "pagingtest.h" + +int mmap_test(int test_runs, unsigned long long alloc_size) { + void *buf; + int ret = -1; + int rc; + int i; + struct timeval begin_time, end_time, elapsed_time; + struct timeval total_time_mmap, total_time_munmap, total_time_in, total_time_out; + + timerclear(&total_time_mmap); + timerclear(&total_time_munmap); + timerclear(&total_time_in); + timerclear(&total_time_out); + + for (i = 0; i < test_runs; i++) { + gettimeofday(&begin_time, NULL); + buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + gettimeofday(&end_time, NULL); + if (buf == ((void *)-1)) { + fprintf(stderr, "Failed to mmap anonymous memory: %s\n", strerror(errno)); + goto err_map; + } + timersub(&end_time, &begin_time, &elapsed_time); + timeradd(&total_time_mmap, &elapsed_time, &total_time_mmap); + + gettimeofday(&begin_time, NULL); + munmap(buf, alloc_size); + gettimeofday(&end_time, NULL); + timersub(&end_time, &begin_time, &elapsed_time); + timeradd(&total_time_mmap, &elapsed_time, &total_time_mmap); + } + + printf("mmap: %llu us\n", total_time_mmap.tv_sec * USEC_PER_SEC + total_time_mmap.tv_usec); + printf("munmap: %llu us\n", total_time_munmap.tv_sec * USEC_PER_SEC + total_time_munmap.tv_usec); + + ret = 0; + goto end; +err: + munmap(buf, alloc_size); +end: +err_map: + return ret; +} diff --git a/tests/pagingtest/pageinout_test.c b/tests/pagingtest/pageinout_test.c new file mode 100644 index 00000000..4c5a0e3d --- /dev/null +++ b/tests/pagingtest/pageinout_test.c @@ -0,0 +1,90 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sys/mman.h> +#include <fcntl.h> +#include "pagingtest.h" + +int pageinout_test(int test_runs, unsigned long long file_size) { + int fd; + char tmpname[] = "pageinoutXXXXXX"; + unsigned char *vec; + int i; + long long j; + volatile char *buf; + int ret = -1; + int rc; + struct timeval begin_time, end_time, elapsed_time, total_time_in, total_time_out; + long pagesize = sysconf(_SC_PAGE_SIZE); + + timerclear(&total_time_in); + timerclear(&total_time_out); + + fd = create_tmp_file(tmpname, file_size); + if (fd < 0) { + return -1; + } + + vec = alloc_mincore_vec(file_size); + if (vec == NULL) { + goto err_alloc; + } + + buf = mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buf == ((void *)-1)) { + fprintf(stderr, "Failed to mmap file: %s\n", strerror(errno)); + goto err_mmap; + } + + if (!check_caching((void *)buf, vec, file_size, false)) { + goto err; + } + + for (i = 0; i < test_runs; i++) { + gettimeofday(&begin_time, NULL); + //Read backwards to prevent mmap prefetching + for (j = ((file_size - 1) & ~(pagesize - 1)); j >= 0; j -= pagesize) { + buf[j]; + } + gettimeofday(&end_time, NULL); + + timersub(&end_time, &begin_time, &elapsed_time); + timeradd(&total_time_in, &elapsed_time, &total_time_in); + + if (!check_caching((void *)buf, vec, file_size, true)) { + goto err; + } + + gettimeofday(&begin_time, NULL); + rc = madvise((void *)buf, file_size, MADV_DONTNEED) || + posix_fadvise(fd, 0, file_size, POSIX_FADV_DONTNEED); + gettimeofday(&end_time, NULL); + if (rc) { + fprintf(stderr, "posix_fadvise/madvise DONTNEED failed\n"); + goto err; + } + + timersub(&end_time, &begin_time, &elapsed_time); + timeradd(&total_time_out, &elapsed_time, &total_time_out); + + if (!check_caching((void *)buf, vec, file_size, false)) { + goto err; + } + } + + printf("page-in: %llu MB/s\n", (file_size * test_runs * USEC_PER_SEC) / + (1024 * 1024 * (total_time_in.tv_sec * USEC_PER_SEC + total_time_in.tv_usec))); + printf("page-out (clean): %llu MB/s\n", (file_size * test_runs * USEC_PER_SEC) / + (1024 * 1024 * (total_time_out.tv_sec * USEC_PER_SEC + total_time_out.tv_usec))); + + ret = 0; + +err: + munmap((void *)buf, file_size); +err_mmap: + free(vec); +err_alloc: + close(fd); + return ret; +} diff --git a/tests/pagingtest/pagingtest.c b/tests/pagingtest/pagingtest.c new file mode 100644 index 00000000..04ef0ccf --- /dev/null +++ b/tests/pagingtest/pagingtest.c @@ -0,0 +1,170 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include "pagingtest.h" + +#define TEST_RUNS 10 +#define ALLOC_SIZE (10 * 1024 * 1024) +#define FILE_SIZE (10 * 1024 * 1024) + +int create_tmp_file(char *filename, off_t size) { + void *buf; + ssize_t rc; + int fd; + int urandom; + + fd = mkstemp(filename); + if (fd < 0) { + fprintf(stderr, "unable to create temp file: %s\n", strerror(errno)); + goto err_mkstemp; + } + + urandom = open("/dev/urandom", O_RDONLY); + if (urandom < 0) { + fprintf(stderr, "unable to open urandom: %s\n", strerror(errno)); + goto err_open; + } + + if (unlink(filename)) { + fprintf(stderr, "unable to unlink temp file: %s\n", strerror(errno)); + goto err_unlink; + } + + if (ftruncate(fd, size)) { + fprintf(stderr, "unable to allocate temp file: %s\n", strerror(errno)); + goto err_truncate; + } + + buf = mmap(NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); + if (buf == (void *)-1) { + fprintf(stderr, "unable to mmap temp file: %s\n", strerror(errno)); + goto err_mmap; + } + + rc = read(urandom, buf, size); + + if (rc < 0) { + fprintf(stderr, "write random data failed: %s\n", strerror(errno)); + goto err; + } + + if (rc != size) { + fprintf(stderr, "write random data incomplete\n"); + goto err; + } + + if (madvise(buf, size, MADV_DONTNEED)) { + fprintf(stderr, "madvise DONTNEED failed: %s\n", strerror(errno)); + goto err; + } + + if (fsync(fd) < 0) { + fprintf(stderr, "fsync failed: %s\n", strerror(errno)); + goto err; + } + + rc = posix_fadvise(fd, 0, size, POSIX_FADV_DONTNEED); + if (rc) { + fprintf(stderr, "fadvise DONTNEED failed: %s\n", strerror(errno)); + goto err; + } + + munmap(buf, size); + close(urandom); + return fd; + +err: + munmap(buf, size); +err_mmap: +err_truncate: +err_unlink: + close(urandom); +err_open: + close(fd); +err_mkstemp: + return -1; +} + +unsigned char *alloc_mincore_vec(size_t size) { + unsigned char *vec; + + vec = malloc(mincore_vec_len(size)); + if (vec == NULL) { + fprintf(stderr, "malloc failed\n"); + } + + return vec; +} + +bool check_caching(void *buf, unsigned char *vec, size_t size, bool is_cached) { + bool ret = true; + size_t i; + + if (mincore(buf, size, vec)) { + fprintf(stderr, "mincore failed: %s\n", strerror(errno)); + return false; + } + + if (is_cached) { + for (i = 0; i < mincore_vec_len(size); i++) { + if (!(vec[i] & 0x1)) { + fprintf(stderr, "found an uncached page at page offset %zd\n", i); + ret = false; + } + } + } else { + for (i = 0; i < mincore_vec_len(size); i++) { + if (vec[i] & 0x1) { + fprintf(stderr, "found a cached page at page offset %zd\n", i); + ret = false; + } + } + } + + return ret; +} + +int main(int argc, char **argv) { + unsigned long long alloc_size = 0ULL; + unsigned long long file_size = 0ULL; + int test_runs = 0; + int rc; + + //arguments: <program> [test_runs [alloc_size [file_size]]] + if (argc >= 2) { + test_runs = atoi(argv[1]); + } + if (test_runs <= 0) { + test_runs = TEST_RUNS; + } + if (argc >= 3) { + alloc_size = strtoull(argv[2], NULL, 10); + } + if (!alloc_size) { + alloc_size = ALLOC_SIZE; + } + if (argc >= 4) { + file_size = strtoull(argv[3], NULL, 10); + } + if (!file_size) { + file_size = FILE_SIZE; + } + + rc = mmap_test(test_runs, alloc_size); + if (rc) { + return rc; + } + rc = pageinout_test(test_runs, file_size); + if (rc) { + return rc; + } + rc = thrashing_test(test_runs); + + return rc; +} diff --git a/tests/pagingtest/pagingtest.h b/tests/pagingtest/pagingtest.h new file mode 100644 index 00000000..2da9818c --- /dev/null +++ b/tests/pagingtest/pagingtest.h @@ -0,0 +1,20 @@ +#include <unistd.h> +#include <stdbool.h> + +#ifndef __PAGINGTEST_H__ +#define __PAGINGTEST_H__ +#define USEC_PER_SEC 1000000ULL +#define mincore_vec_len(size) (((size) + sysconf(_SC_PAGE_SIZE) - 1) / sysconf(_SC_PAGE_SIZE)) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) + +//Helpers +int create_tmp_file(char *filename, off_t size); +unsigned char *alloc_mincore_vec(size_t size); +bool check_caching(void *buf, unsigned char *vec, size_t size, bool is_cached); + +//Tests +int mmap_test(int test_runs, unsigned long long alloc_size); +int pageinout_test(int test_runs, unsigned long long file_size); +int thrashing_test(int test_runs); + +#endif //__PAGINGTEST_H__ diff --git a/tests/pagingtest/thrashing_test.c b/tests/pagingtest/thrashing_test.c new file mode 100644 index 00000000..c1ce162c --- /dev/null +++ b/tests/pagingtest/thrashing_test.c @@ -0,0 +1,78 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <errno.h> +#include <fcntl.h> +#include "pagingtest.h" + +#define LINESIZE 32 + +int thrashing_test(int test_runs) { + int fds[4] = {-1, -1, -1, -1}; + char tmpnames[4][17] = { "thrashing1XXXXXX", "thrashing2XXXXXX", "thrashing3XXXXXX", "thrashing4XXXXXX" }; + volatile char *bufs[4] = {0}; + unsigned i, j; + long long k; + int ret = -1; + struct timeval begin_time, end_time, elapsed_time, total_time; + unsigned long long filesize; + long num_pages; + long pagesize; + + timerclear(&total_time); + + num_pages = sysconf(_SC_PHYS_PAGES); + pagesize = sysconf(_SC_PAGE_SIZE); + if (num_pages < 0) { + fprintf(stderr, "failed to get the number of pages\n"); + return -1; + } + + filesize = num_pages * pagesize / (ARRAY_SIZE(fds) - 1); + + for (i = 0; i < ARRAY_SIZE(fds); i++) { + fds[i] = create_tmp_file(tmpnames[i], filesize); + if (fds[i] < 0) { + goto err_fd; + } + } + + for (i = 0; i < ARRAY_SIZE(fds); i++) { + bufs[i] = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fds[i], 0); + if (bufs[i] == ((void *)-1)) { + fprintf(stderr, "Failed to mmap file: %s\n", strerror(errno)); + goto err; + } + } + + for (i = 0; i < test_runs; i++) { + for (j = 0; j < ARRAY_SIZE(fds); j++) { + gettimeofday(&begin_time, NULL); + //Unfortunately when under memory pressure, fadvise and madvise stop working... + //Read backwards to prevent mmap prefetching + for (k = ((filesize - 1) & ~(pagesize - 1)); k >= 0; k -= pagesize) { + bufs[j][k]; + } + gettimeofday(&end_time, NULL); + + timersub(&end_time, &begin_time, &elapsed_time); + timeradd(&total_time, &elapsed_time, &total_time); + } + } + + printf("thrashing: %llu MB/s\n", (filesize * ARRAY_SIZE(fds) * test_runs * USEC_PER_SEC) / + (1024 * 1024 * (total_time.tv_sec * USEC_PER_SEC + total_time.tv_usec))); + + ret = 0; + +err: + for (i = 0; i < ARRAY_SIZE(bufs) && bufs[i] != NULL; i++) { + munmap((void *)bufs[i], filesize); + } +err_fd: + for (i = 0; i < ARRAY_SIZE(fds) && fds[i] >= 0; i++) { + close(fds[i]); + } + return ret; +} |