first commit
This commit is contained in:
commit
cfd0598791
11 changed files with 378 additions and 0 deletions
1
.clang-format
Normal file
1
.clang-format
Normal file
|
@ -0,0 +1 @@
|
||||||
|
BasedOnStyle: "Google"
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
build/
|
10
README.md
Normal file
10
README.md
Normal 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
85
build.ninja
Normal 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
2
install.sh
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
mkdir -p /usr/local/bin
|
||||||
|
cp build/release/pss-total /usr/local/bin
|
20
lib/include/ns_fget_line.h
Normal file
20
lib/include/ns_fget_line.h
Normal 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
60
lib/include/ns_str.h
Normal 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
34
lib/include/ns_vec.h
Normal 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
17
ngen.toml
Normal 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
147
src/main.c
Normal 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
1
uninstall.sh
Normal file
|
@ -0,0 +1 @@
|
||||||
|
rm /usr/local/bin/pss-total
|
Loading…
Reference in a new issue