// file-hot: mmap a file and keep touching its pages. Models the code and
// read-only data a set of processes actually executes and reads: shared
// libraries, JIT/class images, config, index files. With enough RAM the file
// sits in the page cache and touches cost zero disk I/O; under memory pressure
// they become disk reads (major faults).
//
//   file-hot <path> seq [pass_sleep_ms]        sequential pass, sleep, repeat
//   file-hot <path> rand                        uniform random page touches
//   file-hot <path> skew <hot_MiB> <hot_pct>    hot_pct% of touches land in the
//                                               first hot_MiB of the file, the
//                                               rest anywhere (program-text-like)
//
// Prints one line per "pass" (file_size/page touches) with elapsed ms.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

static double now(void) {
    struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t);
    return t.tv_sec + t.tv_nsec / 1e9;
}

int main(int argc, char **argv) {
    if (argc < 3) { fprintf(stderr, "usage: %s <path> seq|rand|skew ...\n", argv[0]); return 2; }
    const char *mode = argv[2];
    int seq = !strcmp(mode, "seq"), skew = !strcmp(mode, "skew");
    long sleep_ms = (seq && argc > 3) ? atol(argv[3]) : 200;
    size_t hot_mib = (skew && argc > 3) ? strtoul(argv[3], NULL, 10) : 32;
    int hot_pct = (skew && argc > 4) ? atoi(argv[4]) : 90;

    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) { perror("open"); return 1; }
    struct stat st; fstat(fd, &st);
    size_t len = st.st_size, page = sysconf(_SC_PAGESIZE), npages = len / page;
    size_t hot_pages = (hot_mib << 20) / page;
    if (hot_pages > npages) hot_pages = npages;

    // PROT_EXEC: the kernel treats executable file pages like program text.
    char *p = mmap(NULL, len, PROT_READ | PROT_EXEC, MAP_PRIVATE, fd, 0);
    if (p == MAP_FAILED) { perror("mmap"); return 1; }
    if (!seq) madvise(p, len, MADV_RANDOM);   // no readahead for random modes
    fprintf(stderr, "file-hot: %zu MiB mapped from %s, mode=%s", len >> 20, argv[1], mode);
    if (skew) fprintf(stderr, " hot=%zuMiB/%d%%", hot_mib, hot_pct);
    fprintf(stderr, "\n");

    struct timespec ts = { sleep_ms / 1000, (sleep_ms % 1000) * 1000000L };
    volatile char sink = 0;
    srand48(getpid());
    for (unsigned pass = 1;; pass++) {
        double t0 = now();
        if (seq) {
            for (size_t off = 0; off < len; off += page) sink += p[off];
        } else if (skew) {
            for (size_t i = 0; i < npages; i++) {
                size_t n = (drand48() * 100 < hot_pct) ? hot_pages : npages;
                sink += p[(size_t)(drand48() * n) * page];
            }
        } else {
            for (size_t i = 0; i < npages; i++) sink += p[(size_t)(drand48() * npages) * page];
        }
        printf("%ld pass=%u pass_ms=%.0f\n", (long)time(NULL), pass, (now() - t0) * 1000);
        fflush(stdout);
        if (seq) nanosleep(&ts, NULL);
    }
}
