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