first commit

This commit is contained in:
Noah Swerhun 2024-05-24 16:54:17 -05:00
commit cfd0598791
11 changed files with 378 additions and 0 deletions

1
.clang-format Normal file
View file

@ -0,0 +1 @@
BasedOnStyle: "Google"

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
build/

10
README.md Normal file
View file

@ -0,0 +1,10 @@
# pss-total
sum proportional set size of every process for accurate memory usage statistics.
In non-pretty mode, the number is given in kibibytes.
### Summary
pss-total [OPTIONS]
### Options
- `-p`, `--pretty`: format output using sensible units: KiB, MiB, or GiB.
- `-h`, `--help`: print this help message.

85
build.ninja Normal file
View file

@ -0,0 +1,85 @@
## Generated by ngen
## Do not modify by hand
builddir = build
rule mkdir
command = mkdir -p $out
description = Creating directory $out
rule regen_ninjafile
command = ngen -c $in -o $out
generator = 1
description = Regenerating $out
rule regen_compile_commands
command = ngen -c $in --write-compile-commands $out
description = Regenerating $out
build $builddir: mkdir
build $builddir/compile_commands.json: regen_compile_commands ngen.toml || $builddir
pool = console
build build.ninja: regen_ninjafile ngen.toml || $builddir/compile_commands.json
pool = console
# BEGIN TARGET debug
rule cc_debug
deps = gcc
depfile = $dep
command = cc -I. -fsanitize=address -O0 -g -MD -MF $dep -o $out -c $in
description = Building $in -> $out
rule link_debug
command = cc -fsanitize=address -o $out $in
description = Linking $out
build $builddir/debug/obj: mkdir
build $builddir/debug/dep: mkdir
build $builddir/debug/obj/src-main.c.o: cc_debug src/main.c
dep = $builddir/debug/dep/src-main.c.o.d
build $builddir/debug/pss-total: link_debug $builddir/debug/obj/src-main.c.o | || $builddir/debug/obj $builddir/debug/dep
build debug: phony $builddir/debug/pss-total
# END TARGET debug
# BEGIN TARGET release
rule cc_release
deps = gcc
depfile = $dep
command = cc -I. -flto -O2 -MD -MF $dep -o $out -c $in
description = Building $in -> $out
rule link_release
command = cc -flto -o $out $in
description = Linking $out
build $builddir/release/obj: mkdir
build $builddir/release/dep: mkdir
build $builddir/release/obj/src-main.c.o: cc_release src/main.c
dep = $builddir/release/dep/src-main.c.o.d
build $builddir/release/pss-total: link_release $builddir/release/obj/src-main.c.o | || $builddir/release/obj $builddir/release/dep
build release: phony $builddir/release/pss-total
# END TARGET release
# BEGIN TARGET main
rule cc_main
deps = gcc
depfile = $dep
command = cc -I. -MD -MF $dep -o $out -c $in
description = Building $in -> $out
rule link_main
command = cc -o $out $in
description = Linking $out
build $builddir/main/obj: mkdir
build $builddir/main/dep: mkdir
build $builddir/main/obj/src-main.c.o: cc_main src/main.c
dep = $builddir/main/dep/src-main.c.o.d
build $builddir/main/pss-total: link_main $builddir/main/obj/src-main.c.o | || $builddir/main/obj $builddir/main/dep
build main: phony $builddir/main/pss-total
# END TARGET main

2
install.sh Normal file
View file

@ -0,0 +1,2 @@
mkdir -p /usr/local/bin
cp build/release/pss-total /usr/local/bin

View file

@ -0,0 +1,20 @@
#ifndef NS_GET_LINE_H
#define NS_GET_LINE_H
#include <stdio.h>
#include "ns_str.h"
static int ns_fget_line(FILE *stream, Str *str) {
char c;
while ((c = fgetc(stream)) != '\n') {
if (c == EOF) {
return EOF;
}
ns_str_push_char(str, c);
}
return 1;
}
#endif

60
lib/include/ns_str.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef NS_STR_H
#define NS_STR_H
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct Str_T {
char *arr;
size_t len;
size_t cap;
};
typedef struct Str_T Str;
static void ns_str_init_empty(Str *nstr, size_t capactiy) {
nstr->len = 0;
nstr->cap = capactiy;
nstr->arr = calloc(nstr->cap, sizeof(char));
}
static void ns_str_push_char(Str *nstr, char c) {
if (nstr->len + 2 > nstr->cap) {
nstr->cap += 8;
nstr->arr = realloc(nstr->arr, nstr->cap * sizeof(char));
assert(nstr->arr != NULL);
}
nstr->arr[nstr->len] = c;
nstr->len++;
nstr->arr[nstr->len] = '\0';
}
static void ns_str_push(Str *nstr, char *str) {
size_t strlen_str = strlen(str);
if (nstr->len + strlen_str + 1 > nstr->cap) {
nstr->cap = nstr->len + strlen_str + 8;
nstr->arr = realloc(nstr->arr, nstr->cap * sizeof(char));
assert(nstr->arr != NULL);
}
strncpy(nstr->arr + nstr->len, str, nstr->cap - nstr->len);
nstr->len = nstr->len + strlen_str;
}
static void ns_str_set(Str *nstr, char *str) {
size_t strlen_str = strlen(str);
nstr->cap = strlen_str + 1;
nstr->arr = realloc(nstr->arr, nstr->cap * sizeof(char));
assert(nstr->arr != NULL);
strncpy(nstr->arr, str, nstr->cap);
nstr->len = strlen_str;
}
static void ns_str_destroy(Str *nstr) {
free(nstr->arr);
nstr->arr = NULL;
nstr->len = 0;
nstr->cap = 0;
}
#endif

34
lib/include/ns_vec.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef NS_VEC_H
#define NS_VEC_H
#include <assert.h>
#include <stdlib.h>
#define ns_vec_gen(type) \
struct Vec_##type##_T { \
type *arr; \
size_t len; \
size_t cap; \
}; \
typedef struct Vec_##type##_T Vec_##type;
#define ns_vec_init(type, vec, cap_) \
vec.len = 0; \
vec.cap = cap_; \
vec.arr = malloc(vec.cap * sizeof(type)); \
assert(vec.arr != NULL);
#define ns_vec_push(type, vec, obj) \
if (vec.len + 1 > vec.cap) { \
vec.cap += 8; \
vec.arr = realloc(vec.arr, vec.cap * sizeof(type)); \
assert(vec.arr != NULL); \
} \
vec.arr[vec.len] = obj; \
vec.len++;
#define ns_vec_destroy(vec) \
free(vec.arr); \
vec.arr = NULL;
#endif

17
ngen.toml Normal file
View file

@ -0,0 +1,17 @@
[config]
compile_commands = true
[targets.main]
outfile = "pss-total"
compiler_flags = ["-I."]
sources = [
"src/main.c",
]
[targets.debug]
compiler_flags = ["-fsanitize=address", "-O0", "-g"]
linker_flags = ["-fsanitize=address"]
[targets.release]
compiler_flags = ["-flto", "-O2"]
linker_flags = ["-flto"]

147
src/main.c Normal file
View file

@ -0,0 +1,147 @@
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lib/include/ns_fget_line.h"
#include "lib/include/ns_str.h"
double read_file(const char *filename) {
FILE *fp;
int status;
Str line = {0};
char *pss;
double sum = 0;
size_t line_pss = 0;
errno = 0;
fp = fopen(filename, "r");
if (fp == NULL) {
switch (errno) {
case EACCES:
return 0;
break;
default:
perror("cannot open file pointer");
assert(fp != NULL);
break;
}
}
while (1) {
ns_str_init_empty(&line, 256);
errno = 0;
status = ns_fget_line(fp, &line);
if (status == EOF) {
if (errno != 0) {
perror("error reading file");
ns_str_destroy(&line);
return 0;
} else {
ns_str_destroy(&line);
break;
}
}
status = sscanf(line.arr, "Pss: %lu", &line_pss);
if (status != 1) {
ns_str_destroy(&line);
continue;
}
sum += line_pss;
ns_str_destroy(&line);
}
fclose(fp);
return sum;
}
int main(int argc, char **argv) {
DIR *proc_dp;
struct dirent *proc_ent;
Str smaps_path = {0};
double total_KiB = 0;
errno = 0;
proc_dp = opendir("/proc");
if (proc_dp == NULL) {
perror("error opening /proc for enumeration");
assert(proc_dp != NULL);
}
while (1) {
errno = 0;
proc_ent = readdir(proc_dp);
if (proc_ent == NULL) {
if (errno != 0) {
perror("error enumerating /proc");
assert(errno == 0);
} else {
break;
}
}
// skip if not a PID
if (!isdigit(proc_ent->d_name[0])) continue;
ns_str_set(&smaps_path, "/proc/");
ns_str_push(&smaps_path, proc_ent->d_name);
ns_str_push(&smaps_path, "/smaps");
total_KiB += read_file(smaps_path.arr);
ns_str_destroy(&smaps_path);
}
closedir(proc_dp);
if (argc == 2) {
if ((strcmp(argv[1], "--pretty") == 0) || (strcmp(argv[1], "-p") == 0)) {
const char *prefixes[] = {"KiB", "MiB", "GiB"};
size_t i = 0;
while (total_KiB >= 1024 && i < 2) {
total_KiB /= 1024;
i++;
}
printf("%0.2f %s\n", total_KiB, prefixes[i]);
exit(0);
} else if ((strcmp(argv[1], "--help") == 0) ||
(strcmp(argv[1], "-h") == 0)) {
printf("DESCRIPTION\n");
printf(
" pss-total: sum proportional set size of every process for "
"accurate memory\n");
printf(
" usage statistics. In non-pretty mode, the number is given in "
"kibibytes.\n");
printf("\n");
printf("SUMMARY\n");
printf(" pss-total [OPTIONS]\n");
printf("\n");
printf("OPTIONS\n");
printf(" -p, --pretty\n");
printf(" format output using sensible units: KiB, MiB, or GiB.\n");
printf("\n");
printf(" -h, --help\n");
printf(" print this help message.\n");
exit(0);
}
}
printf("%.0f\n", total_KiB);
return 0;
}

1
uninstall.sh Normal file
View file

@ -0,0 +1 @@
rm /usr/local/bin/pss-total