// anon-hog: allocate N MiB of anonymous memory, then keep it "hot" by
// touching one byte per page every INTERVAL_MS. Models a process heap
// (JVM heap, Go heap, Python objects) that is in active use.
//
//   anon-hog <MiB> [interval_ms] [fill]
//
// fill: a5 (default) writes a constant byte; rand fills from /dev/urandom
// (incompressible); half alternates 64 random and 64 constant bytes. The fill
// only matters when the heap can be swapped to compressed swap (zram, zswap).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>

int main(int argc, char **argv) {
    if (argc < 2) { fprintf(stderr, "usage: %s <MiB> [interval_ms] [a5|rand|half]\n", argv[0]); return 2; }
    size_t mib = strtoul(argv[1], NULL, 10);
    long interval_ms = argc > 2 ? atol(argv[2]) : 1000;
    size_t len = mib << 20;
    long page = sysconf(_SC_PAGESIZE);

    char *p = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (p == MAP_FAILED) { perror("mmap"); return 1; }
    const char *fill = argc > 3 ? argv[3] : "a5";
    if (!strcmp(fill, "a5")) {
        memset(p, 0xA5, len);             // fault everything in
    } else {
        FILE *u = fopen("/dev/urandom", "r");
        if (!u || fread(p, 1, len, u) != len) { perror("urandom"); return 1; }
        fclose(u);
        if (!strcmp(fill, "half"))
            for (size_t off = 0; off < len; off += 128) memset(p + off + 64, 0x41, 64);
    }
    fprintf(stderr, "anon-hog: %zu MiB resident, touching every %ld ms\n", mib, interval_ms);

    struct timespec ts = { interval_ms / 1000, (interval_ms % 1000) * 1000000L };
    volatile char sink = 0;
    for (;;) {
        for (size_t off = 0; off < len; off += page) sink += p[off];
        nanosleep(&ts, NULL);
    }
}
