Last active
September 30, 2021 11:51
-
-
Save ragne/60bcc00939f186860d3c65c0a8730a39 to your computer and use it in GitHub Desktop.
A basic test that will allocate memory based on passed options and display available memory stats.
This file contains 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
#define _GNU_SOURCE | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <unistd.h> | |
#include "sys/types.h" | |
#include <stdlib.h> | |
#include <unistd.h> | |
#include <stdbool.h> | |
#include <errno.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#include <sys/resource.h> | |
#ifndef DEBUG | |
#define DEBUG false | |
#endif | |
#define DBG_PRINT(...) \ | |
do \ | |
{ \ | |
if (DEBUG) \ | |
printf(__VA_ARGS__); \ | |
} while (0) | |
void die(const char *fmt, ...) | |
{ | |
va_list args; | |
va_start(args, fmt); | |
vfprintf(stderr, fmt, args); | |
va_end(args); | |
fprintf(stderr, "error: %s\n", strerror(errno)); | |
exit(1); | |
} | |
void bail(const char *fmt, ...) | |
{ | |
va_list args; | |
va_start(args, fmt); | |
vfprintf(stderr, fmt, args); | |
va_end(args); | |
exit(1); | |
} | |
int max(int a, int b) | |
{ | |
return (((a) > (b)) ? (a) : (b)); | |
} | |
int min(int a, int b) | |
{ | |
return (((a) < (b)) ? (a) : (b)); | |
} | |
bool is_overcommit_enabled() | |
{ | |
const char *filename = "/proc/sys/vm/overcommit_memory"; | |
FILE *fp = fopen(filename, "r"); | |
if (fp == NULL) | |
die("cannot open %s! ", filename); | |
char buf[8]; | |
bool result = false; | |
while (fgets(buf, sizeof(buf), fp)) | |
{ | |
DBG_PRINT("dbg: %s", buf); | |
long res = strtol(buf, NULL, 0); | |
if (errno && errno == ERANGE) | |
{ | |
perror("Cannot parse buf!"); | |
result = false; | |
break; | |
} | |
if (res > 0 && res < 3) | |
{ | |
result = true; | |
} | |
else | |
{ | |
result = false; | |
} | |
} | |
fclose(fp); | |
return result; | |
} | |
uint32_t get_avail_mem() | |
{ | |
const char *filename = "/proc/meminfo"; | |
FILE *fp = fopen(filename, "r"); | |
if (fp == NULL) | |
die("cannot open %s! ", filename); | |
char buf[256]; | |
uint32_t total = 0; | |
uint32_t result = 0; | |
bool overcommit = is_overcommit_enabled(); | |
char *needle = overcommit ? "Committed_AS: %d kB" : "MemAvailable: %d kB"; | |
while (fgets(buf, sizeof(buf), fp)) | |
{ | |
DBG_PRINT("dbg: %s", buf); | |
if (overcommit) | |
{ | |
sscanf(buf, "CommitLimit: %d kB", &total); | |
} | |
if (sscanf(buf, needle, &result) == 1) | |
{ | |
break; | |
} | |
} | |
fclose(fp); | |
if (!overcommit) | |
{ | |
return result; | |
} | |
else | |
{ | |
return total - result; | |
} | |
} | |
int do_alloc(char *buf[], int len, int inc_in_mb) | |
{ | |
uint32_t acc; | |
acc = 0; | |
int i = 0; | |
while (i < len) | |
{ | |
int inc = inc_in_mb * 1024 * 1024 * sizeof(char); | |
char *volatile p = calloc(1, inc); // it's not necessary to mark this as volatile as next loop | |
// is actually touching those pages, but otherwise compiler might optimize | |
// it out | |
if (!p) | |
{ | |
DBG_PRINT("acc: %d\n", acc); | |
break; | |
} | |
else | |
{ | |
acc = acc + inc; | |
} | |
for (int i = 0; i < inc; i = i + getpagesize() - 1) | |
{ | |
p[i] = 0xab; | |
} | |
buf[i] = p; | |
if (p != 0) | |
DBG_PRINT("buf[%d]=%p\n", i, buf[i]); | |
i++; | |
} | |
return acc; | |
} | |
struct options | |
{ | |
int inc_size_mb; | |
int num_freed_buffers; | |
int count; | |
}; | |
void usage(const char *progname) | |
{ | |
bail("Usage: %s -c <count> -s <increment_size_in_megs> -n <num_Freed_buffers>\n", progname); | |
} | |
typedef struct options options; | |
options parse_opts(int argc, char **argv) | |
{ | |
options options; | |
int opt; | |
long inc_size, count, num_freed; | |
struct rlimit limit; | |
getrlimit(RLIMIT_STACK, &limit); | |
int clamp_size_to = (limit.rlim_cur / sizeof(char *)) - 0x100; | |
while ((opt = getopt(argc, argv, "n:s:c:h")) != -1) | |
{ | |
switch (opt) | |
{ | |
case 's': | |
inc_size = strtol(optarg, NULL, 0); | |
if (errno == ERANGE) | |
die("Cannot parse %s as long", optarg); | |
options.inc_size_mb = inc_size; | |
break; | |
case 'c': | |
count = strtol(optarg, NULL, 0); | |
if (errno == ERANGE) | |
die("Cannot parse %s as long", optarg); | |
options.count = min(count, clamp_size_to); | |
break; | |
case 'n': | |
num_freed = strtol(optarg, NULL, 0); | |
if (errno == ERANGE) | |
die("Cannot parse %s as long", optarg); | |
options.num_freed_buffers = num_freed; | |
break; | |
case 'h': | |
default: /* '?' */ | |
usage(argv[0]); | |
} | |
} | |
if (options.count <= 0 || options.inc_size_mb <= 0) | |
bail("Both -c and -s should be provided and greater than zero!\n"); | |
return options; | |
} | |
int main(int argc, char **argv) | |
{ | |
options o = parse_opts(argc, argv); | |
char *buf[o.count]; | |
uint buf_size = sizeof(buf) / sizeof *buf; | |
printf("overcommit: %s\n", is_overcommit_enabled() ? "true" : "false"); | |
int num_freed = min(o.num_freed_buffers, buf_size); | |
int total_allocated = do_alloc(buf, buf_size, o.inc_size_mb); | |
printf("total allocated: %d kB\n", total_allocated / 1024); | |
printf("free first: %d buffers\n", num_freed); | |
for (int i = 0; i < num_freed; i++) | |
{ | |
free(buf[i]); | |
} | |
printf("Available memory stats (updated each second)\n"); | |
while (1) | |
{ | |
printf("\rget_avail_mem: %d kB", get_avail_mem()); | |
fflush(stdout); | |
sleep(1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment