Created
January 10, 2024 02:02
-
-
Save skeeto/f92daacf86ed81e2d097ab668ed3117b to your computer and use it in GitHub Desktop.
/proc/meminfo parser
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
// Parse /proc/meminfo into a hash map | |
// This is free and unencumbered software released into the public domain. | |
#include <fcntl.h> | |
#include <stddef.h> | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#define S(s) (str){s, sizeof(s)-1} | |
#define new(a, t) (t *)alloc(a, sizeof(t)) | |
typedef struct { | |
char *beg, *end; | |
} arena; | |
static void *alloc(arena *a, ptrdiff_t size) | |
{ | |
if (size > a->end-a->beg) { | |
abort(); // out of memory | |
} | |
return memset(a->end -= size, 0, size); | |
} | |
typedef struct { | |
char *data; | |
ptrdiff_t len; | |
} str; | |
static _Bool equals(str a, str b) | |
{ | |
return a.len==b.len && (!a.len || !memcmp(a.data, b.data, a.len)); | |
} | |
static uint64_t hash64(str s) | |
{ | |
uint64_t h = 0x100; | |
for (ptrdiff_t i = 0; i < s.len; i++) { | |
h ^= s.data[i] & 255; | |
h *= 1111111111111111111u; | |
} | |
return h; | |
} | |
// Read entire file into a string. | |
static str readfile(char *path, arena *perm) | |
{ | |
str s = {0}; | |
int fd = open(path, O_RDONLY); | |
if (fd >= 0) { | |
s.data = perm->beg; | |
ptrdiff_t cap = perm->end - perm->beg; | |
while (s.len < cap) { | |
ptrdiff_t r = read(fd, s.data+s.len, cap-s.len); | |
if (r < 1) { | |
break; | |
} | |
s.len += r; | |
} | |
perm->beg += s.len; | |
close(fd); | |
} | |
return s; | |
} | |
typedef struct { | |
str head; | |
str tail; | |
} cut; | |
// Cut a string into two pieces around a delimiter. | |
static cut cutstr(str s, char delim) | |
{ | |
// skip leading matches | |
for (; s.len && *s.data==delim; s.data++, s.len--) {} | |
// find next delimiter, or end of string | |
ptrdiff_t len = 0; | |
for (; len<s.len && s.data[len]!=delim; len++) {} | |
cut r = {0}; | |
r.tail = r.head = s; | |
r.head.len = len; | |
len += len < s.len; // exclude delimiter | |
r.tail.data += len; | |
r.tail.len -= len; | |
return r; | |
} | |
typedef struct meminfo meminfo; | |
struct meminfo { | |
meminfo *child[2]; | |
str key; | |
int64_t value; | |
}; | |
meminfo *newmeminfo(arena *perm) | |
{ | |
cut line = {0}; | |
line.tail = readfile("/proc/meminfo", perm); | |
meminfo *mi = 0; | |
while (line.tail.len) { | |
cut token = {0}; | |
line = cutstr(line.tail, '\n'); | |
token.tail = line.head; | |
token = cutstr(token.tail, ' '); | |
str key = token.head; | |
token = cutstr(token.tail, ' '); | |
str value = token.head; | |
token = cutstr(token.tail, ' '); | |
str units = token.head; | |
if (!key.len) { | |
continue; // bad input? skip line | |
} | |
key.len--; // chop trailing ':' | |
uint64_t v = 0; // allow for overflow | |
for (ptrdiff_t i = 0; i < value.len; i++) { | |
v = v*10 + value.data[i] - '0'; | |
} | |
if (equals(units, S("kB"))) { | |
v *= 1024; | |
} | |
meminfo **m = &mi; | |
for (uint64_t h = hash64(key); *m; h <<= 1) { | |
m = &(*m)->child[h>>63]; | |
} | |
*m = new(perm, meminfo); | |
(*m)->key = key; | |
(*m)->value = v; | |
} | |
return mi; | |
} | |
int64_t lookup(meminfo *mi, str key) | |
{ | |
for (uint64_t h = hash64(key); mi; h <<= 1) { | |
if (equals(key, mi->key)) { | |
return mi->value; | |
} | |
mi = mi->child[h>>63]; | |
} | |
return -1; | |
} | |
static arena newarena(ptrdiff_t cap) | |
{ | |
arena a = {0}; | |
a.beg = malloc(cap); | |
a.end = a.beg ? a.beg+cap : a.beg; | |
return a; | |
} | |
int main(void) | |
{ | |
arena scratch = newarena(1<<20); | |
meminfo *mi = newmeminfo(&scratch); | |
int64_t swaptotal = lookup(mi, S("SwapTotal")); | |
printf("SwapTotal = %lld kB\n", (long long)swaptotal/1024); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment