summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRom Lemarchand <romlem@google.com>2014-11-10 10:32:49 -0800
committerRom Lemarchand <romlem@google.com>2014-11-11 13:55:36 -0800
commitde3942afaa4a84529f7c110fe302bb2b346fbe23 (patch)
tree427811035656f8360756aad8af0beb7501fb9c4e
parent946b1184aabba0f1c5809ed7e6b63738145ba421 (diff)
downloadextras-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.mk20
-rw-r--r--tests/pagingtest/mmap_test.c49
-rw-r--r--tests/pagingtest/pageinout_test.c90
-rw-r--r--tests/pagingtest/pagingtest.c170
-rw-r--r--tests/pagingtest/pagingtest.h20
-rw-r--r--tests/pagingtest/thrashing_test.c78
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;
+}