Skip to content

Instantly share code, notes, and snippets.

@edubart
Created April 23, 2026 00:03
Show Gist options
  • Select an option

  • Save edubart/cf149b0af834ff991415ac7b311abd70 to your computer and use it in GitHub Desktop.

Select an option

Save edubart/cf149b0af834ff991415ac7b311abd70 to your computer and use it in GitHub Desktop.
/*
* mmap_stress.c — minimal mmap read/write stress tool.
*
* Build: cc -O2 -Wall -Wextra -o mmap_stress mmap_stress.c
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
static void die(const char *msg) { perror(msg); exit(EXIT_FAILURE); }
/* Accepts decimal/hex with optional K/M/G suffix. */
static size_t parse_size(const char *s) {
char *end;
unsigned long long v = strtoull(s, &end, 0);
if (end == s) { fprintf(stderr, "invalid number: %s\n", s); exit(EXIT_FAILURE); }
switch (*end) {
case 'k': case 'K': v <<= 10; end++; break;
case 'm': case 'M': v <<= 20; end++; break;
case 'g': case 'G': v <<= 30; end++; break;
}
if (*end) { fprintf(stderr, "invalid suffix in: %s\n", s); exit(EXIT_FAILURE); }
return (size_t)v;
}
/* xorshift64 — fast, plenty random for this stress pattern. */
static uint64_t rng_state = 0x9E3779B97F4A7C15ULL;
static inline uint64_t xrand(void) {
uint64_t x = rng_state;
x ^= x << 13; x ^= x >> 7; x ^= x << 17;
return rng_state = x;
}
static void usage(const char *p) {
fprintf(stderr,
"Usage: %s --mem SIZE [--file FILE] [--msync]\n"
" [--rw-iters N] [--block-size N] [--iters N]\n"
"Sizes accept K/M/G suffixes.\n", p);
exit(EXIT_FAILURE);
}
int main(int argc, char **argv) {
const char *file = NULL;
size_t mem_size = 0;
int do_msync = 0;
size_t rw_iters = 32;
size_t block = 4096;
size_t iters = 1024;
static const struct option opts[] = {
{ "file", required_argument, 0, 'f' },
{ "mem", required_argument, 0, 'm' },
{ "msync", no_argument, 0, 's' },
{ "rw-iters", required_argument, 0, 'r' },
{ "block-size", required_argument, 0, 'b' },
{ "iters", required_argument, 0, 'i' },
{ 0, 0, 0, 0 }
};
int c;
while ((c = getopt_long(argc, argv, "", opts, NULL)) != -1) {
switch (c) {
case 'f': file = optarg; break;
case 'm': mem_size = parse_size(optarg); break;
case 's': do_msync = 1; break;
case 'r': rw_iters = parse_size(optarg); break;
case 'b': block = parse_size(optarg); break;
case 'i': iters = parse_size(optarg); break;
default: usage(argv[0]);
}
}
if (mem_size == 0) usage(argv[0]); /* --mem is always required */
if (block == 0) { fprintf(stderr, "block size must be > 0\n"); return 1; }
void *map;
size_t map_size = mem_size;
int fd = -1;
if (file) {
fd = open(file, O_RDWR);
if (fd < 0) die("open");
map = mmap(NULL, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
} else {
map = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
}
if (map == MAP_FAILED) die("mmap");
if (map_size < block) {
fprintf(stderr, "region (%zu) smaller than block size (%zu)\n",
map_size, block);
return 1;
}
unsigned char *region = (unsigned char *)map;
unsigned char *buf = malloc(block);
if (!buf) die("malloc");
const size_t max_off = map_size - block;
for (size_t i = 0; i < iters; i++) {
for (size_t j = 0; j < rw_iters; j++) {
size_t off = (size_t)(xrand() % (max_off + 1));
/* read block bytes, discard */
memcpy(buf, region + off, block);
/* write block random bytes back, 8 bytes per PRNG step */
size_t k = 0;
for (; k + 8 <= block; k += 8) {
uint64_t v = xrand();
memcpy(region + off + k, &v, 8);
}
if (k < block) {
uint64_t v = xrand();
memcpy(region + off + k, &v, block - k);
}
}
if (do_msync && msync(map, map_size, MS_SYNC) < 0) die("msync");
}
free(buf);
if (munmap(map, map_size) < 0) die("munmap");
if (fd >= 0) close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment