Skip to content

Instantly share code, notes, and snippets.

@ragne
Last active September 30, 2021 11:51
Show Gist options
  • Save ragne/60bcc00939f186860d3c65c0a8730a39 to your computer and use it in GitHub Desktop.
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.
#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