Created
April 23, 2026 00:03
-
-
Save edubart/cf149b0af834ff991415ac7b311abd70 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * 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