/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. * This program is free software, released under the GPL. * Based on code by Minchan Kim * * User program that tests low-memory notifications. * * Compile with -lpthread * for instance * i686-pc-linux-gnu-gcc low-mem-test.c -o low-mem-test -lpthread * * Run as: low-mem-test * * This program runs in two threads. One thread continuously allocates memory * in the given chunk size, waiting for the specified microsecond interval * between allocations. The other runs in a loop that waits for a low-memory * notification, then frees some of the memory that the first thread has * allocated. */ #include #include #include #include #include #include #include #include int memory_chunk_size = 10000000; int wait_time_us = 10000; int autotesting; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; struct node { void *memory; struct node *prev; struct node *next; }; struct node head, tail; void work(void) { int i; while (1) { struct node *new = malloc(sizeof(struct node)); if (new == NULL) { perror("allocating node"); exit(1); } new->memory = malloc(memory_chunk_size); if (new->memory == NULL) { perror("allocating chunk"); exit(1); } pthread_mutex_lock(&mutex); new->next = &head; new->prev = head.prev; new->prev->next = new; new->next->prev = new; for (i = 0; i < memory_chunk_size / 4096; i++) { /* touch page */ ((unsigned char *) new->memory)[i * 4096] = 1; } pthread_mutex_unlock(&mutex); if (!autotesting) { printf("+"); fflush(stdout); } usleep(wait_time_us); } } void free_memory(void) { struct node *old; pthread_mutex_lock(&mutex); old = tail.next; if (old == &head) { fprintf(stderr, "no memory left to free\n"); exit(1); } old->prev->next = old->next; old->next->prev = old->prev; free(old->memory); free(old); pthread_mutex_unlock(&mutex); if (!autotesting) { printf("-"); fflush(stdout); } } void *poll_thread(void *dummy) { struct pollfd pfd; int fd = open("/dev/chromeos-low-mem", O_RDONLY); if (fd == -1) { perror("/dev/chromeos-low-mem"); exit(1); } pfd.fd = fd; pfd.events = POLLIN; if (autotesting) { /* Check that there is no memory shortage yet. */ poll(&pfd, 1, 0); if (pfd.revents != 0) { exit(0); } else { fprintf(stderr, "expected no events but " "poll() returned 0x%x\n", pfd.revents); exit(1); } } while (1) { poll(&pfd, 1, -1); if (autotesting) { /* Free several chunks and check that the notification * is gone. */ free_memory(); free_memory(); free_memory(); free_memory(); free_memory(); poll(&pfd, 1, 0); if (pfd.revents == 0) { exit(0); } else { fprintf(stderr, "expected no events but " "poll() returned 0x%x\n", pfd.revents); exit(1); } } free_memory(); } } int main(int argc, char **argv) { pthread_t threadid; head.next = NULL; head.prev = &tail; tail.next = &head; tail.prev = NULL; if (argc != 3 && (argc != 2 || strcmp(argv[1], "autotesting"))) { fprintf(stderr, "usage: low-mem-test " "\n" "or: low-mem-test autotesting\n"); exit(1); } if (argc == 2) { autotesting = 1; } else { memory_chunk_size = atoi(argv[1]); wait_time_us = atoi(argv[2]); } if (pthread_create(&threadid, NULL, poll_thread, NULL)) { perror("pthread"); return 1; } work(); return 0; }