/* Expanded ashmem test -- flex input, but mostly -*-C-*- really */ %option noyywrap yylineno %{ #include #include #include #include #include #include #include #include #include #ifdef ANDROID #include #else #include "ashmem.h" #endif #ifndef _LINUX_ASHMEM_H struct ashmem_pin { uint32_t offset; uint32_t len; }; #endif typedef struct { int beg; int end; } range_t; int ashmemfd = -1; void *pages; int nrpages; int pagesize; int parse_number (char *); range_t * parse_range (char *); void getpages (int); void pinpages (range_t *); void unpinpages (range_t *); void shrink (void); void purgepages (void); void fatal (int, const char *, ...); %} %% getpages[ \t]+[0-9]+ { getpages (parse_number (yytext + 8)); } pin[ \t]+[0-9]+[ \t]+[0-9]+ { pinpages (parse_range (yytext + 3)); } unpin[ \t]+[0-9]+[ \t]+[0-9]+ { unpinpages (parse_range (yytext + 5)); } shrink { shrink (); } purge { purgepages (); } [ \t\n]+ /* spaces */ [ \t]*#.* /* comment starts from '#' */ . { fprintf (stderr, "Unrecognized input '%s' at line %d\n", yytext, yylineno); exit (1); } %% void fatal (int error, const char *fmt, ...) { va_list ap; va_start (ap, fmt); vfprintf (stderr, fmt, ap); if (error) fprintf (stderr, " (error %d [%s])", error, strerror (error)); va_end (ap); fprintf (stderr, "\n"); exit (error ? error : 1); } int parse_number (char *buf) { while (isspace (*buf)) buf++; return atoi (buf); } range_t * parse_range (char *buf) { char *p, *q; range_t *range; if (ashmemfd == -1) fatal (0, "pages not allocated (use getpages X first)"); range = (range_t *) malloc (sizeof (range_t)); if (!range) fatal (0, "cannot allocate memory"); for (p = buf; isspace (*p); p++); if (!isdigit (*p)) fatal (0, "unexpected parse error"); range->beg = strtol (p, &q, 10); for (; isspace (*q); q++); range->end = strtol (q, NULL, 10); if (range->beg > nrpages) fatal (0, "range begin %d is outside of allocated pages", range->beg); if (range->end > nrpages) fatal (0, "range end %d is outside of allocated pages", range->beg); if (range->end == range->beg) fatal (0, "zero-sized range at line %d", yylineno); return range; } void getpages (int npages) { size_t size = npages * pagesize; if (ashmemfd != -1) fatal (0, "pages already allocated"); if (npages < 1) fatal (0, "should allocate at least one page"); ashmemfd = open ("/dev/ashmem", O_RDWR); if (ashmemfd < 0) fatal (errno, "can't open /dev/ashmem"); if (ioctl (ashmemfd, ASHMEM_SET_SIZE, size) < 0) fatal (errno, "can't set size"); pages = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemfd, 0); if (pages == MAP_FAILED) fatal (errno, "can't map pages"); nrpages = npages; printf ("-> got %d pages\n", npages); } void pinpages (range_t *range) { struct ashmem_pin pin; int ret; if (ashmemfd == -1) fatal (0, "pages not allocated (use getpages X first)"); if (range->end > range->beg) { /* Normal order, pin all at once. */ pin.offset = range->beg * pagesize; pin.len = (range->end - range->beg) * pagesize; ret = ioctl (ashmemfd, ASHMEM_PIN, &pin); if (ret < 0) fatal (errno, "can't pin %d..%d", range->beg, range->end); printf ("-> pin %d..%d OK ret %d\n", range->beg, range->end, ret); } else { /* Reverse order, pin page-by-page from higher to lower. */ int pgno; for (pgno = range->beg - 1; pgno >= range->end; pgno--) { pin.offset = pgno * pagesize; pin.len = pagesize; ret = ioctl (ashmemfd, ASHMEM_PIN, &pin); if (ret < 0) fatal (errno, "can't pin page %d", pgno); } printf ("-> pin backward %d..%d OK\n", range->beg, range->end); } } void unpinpages (range_t *range) { struct ashmem_pin pin; int ret; if (ashmemfd == -1) fatal (0, "pages not allocated (use getpages X first)"); if (range->end > range->beg) { /* Normal order, unpin all at once. */ pin.offset = range->beg * pagesize; pin.len = (range->end - range->beg) * pagesize; ret = ioctl (ashmemfd, ASHMEM_UNPIN, &pin); if (ret < 0) fatal (errno, "can't unpin %d..%d", range->beg, range->end); printf ("-> unpin %d..%d OK ret %d\n", range->beg, range->end, ret); } else { /* Reverse order, unpin page-by-page from higher to lower. */ int pgno; for (pgno = range->beg - 1; pgno >= range->end; pgno--) { pin.offset = pgno * pagesize; pin.len = pagesize; ret = ioctl (ashmemfd, ASHMEM_UNPIN, &pin); if (ret < 0) fatal (errno, "can't unpin page %d", pgno); } printf ("-> unpin backward %d..%d OK\n", range->beg, range->end); } } /* Enforce the kernel to run cache shrinker */ void shrink (void) { int fd; char cmd[2]; fd = open ("/proc/sys/vm/drop_caches", O_WRONLY); if (fd < 0) fatal (errno, "can't open /proc/sys/vm/drop_caches (is /proc mounted?)"); cmd[0] = '3'; cmd[1] = '\n'; if (write (fd, cmd, sizeof cmd) != sizeof cmd) fatal (errno, "can't write to /proc/sys/vm/drop_caches"); } void purgepages (void) { int ret; if (ashmemfd == -1) fatal (0, "pages not allocated (use getpages X first)"); ret = ioctl (ashmemfd, ASHMEM_PURGE_ALL_CACHES); if (ret < 0) fatal (errno, "can't purge caches"); printf ("-> purge return %d pages OK\n", ret); } int main (int argc, char *argv[]) { pagesize = getpagesize (); if (argc == 2) { yyin = fopen (argv[1], "r"); if (!yyin) fatal (errno, "can't open '%s'", argv[1]); } else yyin = stdin; yylex (); return 0; }