// perftrace.c
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/time.h>
-#include <sys/wait.h>
+
+#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0]))
#define MICRO_IN_SEC 1000000
#define NANO_IN_SEC 1000000000
+#define TIMESPEC_TO_DOUBLE(t) \
+ ((double)(t).tv_sec + (double)(t).tv_nsec / NANO_IN_SEC)
+#define TIMESPEC_ISZERO(t) ((t).tv_sec == 0 && (t).tv_nsec == 0)
+
+struct list {
+ size_t len, allo;
+ void *data;
+};
+#define LIST_EMPTY ((struct list){ 0, 0, NULL })
+#define LIST_ITEMS(l, t) ((t*)(l).data)
+#define LIST_POS(p, l, t) (size_t)((p) - LIST_ITEMS((l), t))
+#define LIST_AT(l, i, t) (LIST_ITEMS((l), t) + i)
+
+static inline int list_maybe_grow(
+ struct list *l, size_t new_len, size_t item_size
+) {
+ void *np;
+ if (new_len <= l->allo)
+ return 0;
+ do
+ l->allo = l->allo ? l->allo * 2 : 8;
+ while (new_len > l->allo);
+ np = realloc(l->data, l->allo * item_size);
+ if (np == NULL) {
+ perror("realloc");
+ return -1;
+ }
+ l->data = np;
+ return 0;
+}
-struct args {
- struct timeval delay;
+struct arg_pid_item {
pid_t pid;
- char **argv;
+ char mode;
};
+static inline int parse_unsigned_long(
+ unsigned long *result, char c, char *label
+) {
+ register unsigned long new_value = 10 * *result + c - '0';
+ if (c >= '0' && new_value / 10 == *result) {
+ *result = new_value;
+ return 0;
+ }
+ if (label)
+ fprintf(
+ stderr,
+ "Error: %s: invalid character or overflow ('%c')\n",
+ label,
+ c
+ );
+ return -1;
+}
+
+static inline int parse_pid(const char *ptr, struct arg_pid_item *new_item) {
+ unsigned long pid = 0;
+ do {
+ if (parse_unsigned_long(&pid, *ptr, NULL) < 0)
+ return -1;
+ ptr++;
+ } while (*ptr != '\0' && *ptr != '+' && *ptr != '*');
+ *new_item = (struct arg_pid_item){ pid, *ptr };
+ if (ptr[0] != '\0' && ptr[1] != '\0')
+ return -1;
+ return 0;
+}
+
+static inline int parse_arg_pid(int argc, char ***argv, struct list *arg_pids) {
+ if (
+ list_maybe_grow(
+ arg_pids, ++arg_pids->len, sizeof (struct arg_pid_item)
+ ) < 0
+ )
+ return -1;
+ if ((**argv)[5] == '=') {
+ if (
+ parse_pid(
+ **argv + 6,
+ LIST_ITEMS(*arg_pids, struct arg_pid_item) + arg_pids->len - 1
+ ) < 0
+ )
+ return -1;
+ } else if ((**argv)[5] == '\0') {
+ if (
+ --argc == 0
+ || parse_pid(
+ *++(*argv),
+ LIST_ITEMS(*arg_pids, struct arg_pid_item) + arg_pids->len - 1
+ ) < 0
+ )
+ return -1;
+ } else
+ return -1;
+ return argc;
+}
+
static inline int parse_delay(const char *ptr, struct timeval *delay) {
- long new_value, fractions = -1;
+ register unsigned long fractions;
*delay = (struct timeval){ 0, 0 };
- for (;;) {
- new_value = 10 * delay->tv_sec + *ptr - '0';
- if (*ptr < '0' || new_value / 10 != delay->tv_sec)
+ for (; *ptr != '\0' && *ptr != '.'; ptr++)
+ if (parse_unsigned_long((unsigned long*)&delay->tv_sec, *ptr, NULL) < 0)
return -1;
- delay->tv_sec = new_value;
- ptr++;
- if (*ptr == '\0')
- return 0;
- else if (*ptr == '.')
- break;
- }
- for (fractions = MICRO_IN_SEC / 10; fractions > 0; fractions /= 10) {
- if (*(++ptr) == '\0')
- break;
- else if (*ptr < '0' || *ptr > '9')
+ ptr += *ptr != '\0';
+ for (
+ fractions = MICRO_IN_SEC / 10;
+ *ptr && fractions > 0;
+ ptr++, fractions /= 10
+ )
+ if (*ptr < '0' || *ptr > '9')
return -1;
- delay->tv_usec += fractions * (*ptr - '0');
- }
+ else
+ delay->tv_usec += fractions * (*ptr - '0');
return 0;
}
static inline int parse_arg_delay(
- int argc, char **argv, struct args *a, long clock_ticks
+ int argc, char ***argv, struct timeval *delay
) {
- switch (*argv[7]) {
- case '=':
- if (parse_delay(*argv + 8, &a->delay) < 0)
- goto error;
- break;
- case '\0':
- if (argc-- == 0 || parse_delay(*++argv, &a->delay) < 0)
- goto error;
- break;
- default:
- goto error;
- }
+ long clock_ticks;
+ if ((**argv)[7] == '=') {
+ if (parse_delay(**argv + 8, delay) < 0)
+ return -1;
+ } else if ((**argv)[7] == '\0') {
+ if (--argc == 0 || parse_delay(*++(*argv), delay) < 0)
+ return -1;
+ } else
+ return -1;
+ clock_ticks = sysconf(_SC_CLK_TCK);
if (
- (clock_ticks <= 1 && a->delay.tv_sec < 1) || (
- a->delay.tv_sec < 1 && a->delay.tv_usec < MICRO_IN_SEC / clock_ticks
+ (clock_ticks <= 1 && delay->tv_sec < 1) || (
+ delay->tv_sec < 1 && delay->tv_usec < MICRO_IN_SEC / clock_ticks
)
) {
fprintf(stderr, "Error: delay too small!\n");
return -1;
}
return argc;
-error:
- fprintf(stderr, "Error: parsing '--delay'\n");
- return -1;
}
-static inline pid_t parse_pid(const char *ptr) {
- pid_t pid = 0, pid_new;
- do {
- pid_new = 10 * pid + *ptr - '0';
- if (*ptr < '0' || pid_new / 10 != pid)
+int parse_args(
+ int argc,
+ char **argv,
+ struct list *pids,
+ struct timeval *delay,
+ int *humanize_mem
+) {
+ for (argc--, argv++; argc > 0; argc--, argv++) {
+ if (strncmp(*argv, "--pid", 5) == 0)
+ argc = parse_arg_pid(argc, &argv, pids);
+ else if (strncmp(*argv, "--delay", 7) == 0)
+ argc = parse_arg_delay(argc, &argv, delay);
+ else if (strcmp(*argv, "--humanize") == 0)
+ *humanize_mem = 1;
+ else
+ argc = -1;
+ if (argc == -1) {
+ fprintf(stderr, "Error: Could not process argument: '%s'\n", *argv);
return -1;
- pid = pid_new;
- } while (*(++ptr) != '\0');
- return pid;
+ }
+ }
+ return 0;
}
-static inline int parse_arg_pid(int argc, char **argv, struct args *a) {
- if (a->pid != 0) {
- fprintf(stderr, "Error: '--pid' already specified.\n");
- return -1;
- }
- switch (*argv[5]) {
- case '=':
- if ((a->pid = parse_pid(*argv + 6)) < 0)
- break;
- return argc;
- case '\0':
- if (argc-- == 0 || (a->pid = parse_pid(*++argv)) < 0)
+static int alarm_set, int_set, page_size;
+
+void handle_alarm(int signal) {
+ alarm_set |= signal == SIGALRM;
+}
+
+void handle_int(int signal) {
+ int_set |= signal == SIGINT;
+}
+
+static inline int alarm_await(void) {
+ if (int_set)
+ return 0;
+ while (!alarm_set)
+ sleep(UINT_MAX);
+ return alarm_set && !int_set;
+}
+
+struct pid_item {
+ pid_t pid, ppid;
+ struct timespec utime;
+ unsigned long vsize, rss;
+};
+
+static inline void get_field_labels(char digits, char *buffer) {
+ const char *s = "ppidvsizerss";
+ char *ptr = buffer;
+ for (digits &= 7; digits; digits >>= 1) {
+ if (!(digits & 1)) {
+ s += 4;
+ if (*s != 'u')
+ s++;
+ continue;
+ }
+ if (ptr > buffer) {
+ *ptr++ = ',';
+ *ptr++ = ' ';
+ }
+ *ptr++ = *s++;
+ *ptr++ = *s++;
+ *ptr++ = *s++;
+ if (!*s)
break;
- return argc;
+ *ptr++ = *s++;
+ if (*s != 'u')
+ *ptr++ = *s++;
}
- fprintf(stderr, "Error: parsing '--pid'\n");
- return -1;
+ *ptr = '\0';
}
-static inline int parse_arg_argv(int argc, char **argv, struct args *a) {
- if (a->pid != 0) {
- fprintf(stderr, "Error: '--pid' already specified.\n");
+static inline int perftrace_collect_details(pid_t pid, struct pid_item *item) {
+ struct timespec utime;
+ unsigned long ppid = 0;
+ clockid_t clock_id = 0;
+ int fd, read_bytes = 0, ret = -1;
+ char state = 0, buffer[64], digits = 0, *ptr = buffer;
+ snprintf(buffer, sizeof buffer, "/proc/%d/stat", pid);
+ if (clock_getcpuclockid(pid, &clock_id) < 0) {
+ perror("clock_getcpuclockid");
return -1;
}
- if (argc-- == 0) {
- fprintf(stderr, "Error: arguments expected after '--'\n");
+ if (clock_gettime(clock_id, &utime) < 0) {
+ if (errno == EINVAL)
+ utime = (struct timespec){ 0, 0 };
+ else {
+ perror("clock_gettime");
+ return -1;
+ }
+ }
+ fd = open(buffer, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
return -1;
}
- a->argv = ++argv;
- return argc;
+ for (;; ptr++) {
+ if (ptr - buffer >= read_bytes) {
+ ptr = buffer;
+ if ((read_bytes = read(fd, &buffer, sizeof buffer)) < 0) {
+ perror("read");
+ goto error;
+ } else if (read_bytes == 0)
+ break;
+ }
+ if (*ptr == ')') {
+ state = 2;
+ *item = (struct pid_item){ pid, 0, utime, 0, 0 };
+ } else if (state > 0 && *ptr == ' ')
+ state++;
+ else switch (state) {
+ case 4:
+ if (parse_unsigned_long(&ppid, *ptr, "ppid") < 0)
+ goto error;
+ item->ppid = ppid;
+ digits |= 1;
+ break;
+ case 23:
+ if (parse_unsigned_long(&item->vsize, *ptr, "vsize") < 0)
+ goto error;
+ digits |= 2;
+ break;
+ case 24:
+ if (parse_unsigned_long(&item->rss, *ptr, "rss") < 0)
+ goto error;
+ digits |= 4;
+ break;
+ }
+ }
+ if (digits == 7)
+ ret = 0;
+ else {
+ get_field_labels(digits ^ 7, buffer);
+ fprintf(stderr, "Error: missing state (%s)\n", buffer);
+ }
+error:
+ close(fd);
+ return ret;
}
-int parse_args(int argc, char **argv, struct args *a, long clock_ticks) {
- for (argv++, argc--; argc > 0; argv++, argc--) {
- if (strncmp(*argv, "--delay", 7) == 0)
- argc = parse_arg_delay(argc, argv, a, clock_ticks);
- else if (strncmp(*argv, "--pid", 5) == 0)
- argc = parse_arg_pid(argc, argv, a);
- else if (strcmp(*argv, "--") == 0)
- argc = parse_arg_argv(argc, argv, a);
- else {
- fprintf(stderr, "Error: Invalid argument: '%s'.\n", *argv);
+int perftrace_collect(struct list *pl) {
+ DIR *d = opendir("/proc");
+ struct dirent *ent;
+ unsigned long pid;
+ size_t old_len;
+ char *ptr;
+ while ((ent = readdir(d)) != NULL) {
+ pid = 0;
+ for (ptr = ent->d_name; *ptr; ptr++)
+ if (parse_unsigned_long(&pid, *ptr, NULL) < 0) {
+ pid = ULONG_MAX;
+ break;
+ }
+ if (pid == ULONG_MAX)
+ continue;
+ old_len = pl->len;
+ if (list_maybe_grow(pl, ++pl->len, sizeof (struct pid_item)) < 0)
return -1;
+ if (
+ perftrace_collect_details(
+ pid, LIST_ITEMS(*pl, struct pid_item) + old_len
+ ) < 0
+ ) {
+ pl->len--;
+ continue;
}
- if (argc == -1)
- return -1;
- }
- if (a->pid == 0 && a->argv == NULL) {
- fprintf(stderr, "Error: Must either specify '--' or '--pid'.\n");
- return -1;
}
+ closedir(d);
return 0;
}
-static inline int run_proc(struct args *a) {
- pid_t pid = fork();
- switch (pid) {
- case -1:
- perror("fork");
- return -1;
- break;
- case 0:
- execvp(*a->argv, a->argv);
- perror("execvp");
- exit(1);
- default:
- a->pid = pid;
- return 0;
+static inline struct pid_item *perftrace_find_pid_item(
+ struct list pl, pid_t pid
+) {
+ struct pid_item *item;
+ if (pid <= 0)
+ return NULL;
+ for (item = pl.data; LIST_POS(item, pl, struct pid_item) < pl.len; item++)
+ if (item->pid == pid)
+ return item;
+ return NULL;
+}
+
+static inline void swap_pid_items(
+ struct pid_item *a, struct pid_item *b
+) {
+ struct pid_item tmp;
+ tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+static inline void perftrace_invalidate_missing_pids(
+ struct list new, struct list *old
+) {
+ struct pid_item *old_item;
+ if (old->len == 0)
+ return;
+ for (
+ old_item = old->data;
+ LIST_POS(old_item, *old, struct pid_item) < old->len;
+ old_item++
+ ) {
+ if (perftrace_find_pid_item(new, old_item->pid) != NULL)
+ continue;
+ old_item->pid = -1;
+ if (LIST_POS(old_item, *old, struct pid_item) < --old->len)
+ swap_pid_items(
+ old_item--, LIST_AT(*old, old->len, struct pid_item)
+ );
}
}
static inline struct timespec timespec_diff(
struct timespec tsa, struct timespec tsb
) {
- struct timespec result = {
- .tv_sec = tsa.tv_sec - tsb.tv_sec,
- .tv_nsec = tsa.tv_nsec,
- };
+ struct timespec result = { 0, 0 };
+ if (TIMESPEC_ISZERO(tsa) || TIMESPEC_ISZERO(tsb))
+ return result;
+ result = (struct timespec){ tsa.tv_sec - tsb.tv_sec, tsa.tv_nsec };
if (tsb.tv_nsec > tsa.tv_nsec) {
result.tv_sec -= 1;
result.tv_nsec += NANO_IN_SEC;
return result;
}
-struct file_buffer {
- int fd;
- char buffer[128], size, consumed;
-};
-
-static inline int file_buffer_get_char(struct file_buffer *fb) {
- int ret;
- if (fb->consumed < fb->size)
- goto next;
- ret = read(fb->fd, fb->buffer, sizeof fb->buffer);
- switch (ret) {
- case -1:
- perror("read");
- return -1;
- case 0:
- return INT_MIN;
+static inline char humanize(double *value) {
+ const char *label = "BKMGTPEZY";
+ while (*value > 2048 && *label != 'Y') {
+ *value /= 1024;
+ label++;
}
- fb->size = ret;
- fb->consumed = 0;
-next:
- return fb->buffer[(int)fb->consumed++];
+ return *label;
}
-static inline int read_unsigned_long(
- struct file_buffer *fb, unsigned long *result, int end_char
+static inline void perftrace_humanize(double vsize, double rss) {
+ char rss_unit, vsize_unit;
+ vsize_unit = humanize(&vsize);
+ rss_unit = humanize(&rss);
+ printf(
+ (
+ " \"vsize\": \"%.2f%c\",\n"
+ " \"rss\": \"%.2f%c\"\n"
+ ),
+ vsize,
+ vsize_unit,
+ rss,
+ rss_unit
+ );
+}
+
+static inline int perftrace_pid(
+ struct pid_item item,
+ struct list *old,
+ struct timespec diff,
+ int humanize_mem,
+ int *json_state
) {
- unsigned long result_new;
- int c;
- *result = 0;
- for (;;) {
- if ((c = file_buffer_get_char(fb)) < 0)
- return c == INT_MIN && end_char == EOF ? 0 : -1;
- else if (c == end_char)
- return 0;
- result_new = 10 * *result + c - '0';
- if (c < '0' || result_new / 10 != *result) {
- fprintf(stderr, "Error: parsing stat value.\n");
+ struct pid_item *old_item = perftrace_find_pid_item(*old, item.pid);
+ struct timespec utime_diff;
+ double cpu = 0;
+ if (
+ !TIMESPEC_ISZERO(diff)
+ && old_item != NULL
+ && !TIMESPEC_ISZERO(
+ utime_diff = timespec_diff(item.utime, old_item->utime)
+ )
+ )
+ cpu = TIMESPEC_TO_DOUBLE(utime_diff) / TIMESPEC_TO_DOUBLE(diff) * 100;
+ printf(
+ (
+ "%s\n"
+ " {\n"
+ " \"pid\": %d,\n"
+ " \"ppid\": %d,\n"
+ " \"cpu%%\": %.03f,\n"
+ ),
+ *json_state == 2 ? "," : "",
+ item.pid,
+ item.ppid,
+ cpu
+ );
+ if (humanize_mem) {
+ perftrace_humanize(item.vsize, item.rss * page_size);
+ } else
+ printf(
+ (
+ " \"vsize\": %lu,\n"
+ " \"rss\": %lu\n"
+ ),
+ item.vsize,
+ item.rss * page_size
+ );
+ printf(" }");
+ *json_state = 2;
+ if (old_item == NULL) {
+ if (
+ list_maybe_grow(
+ old, ++old->len, sizeof (struct pid_item)
+ ) < 0
+ )
return -1;
- }
- *result = result_new;
+ old_item = LIST_AT(*old, old->len - 1, struct pid_item);
}
+ *old_item = item;
+ return 0;
}
-static inline char *stringify_pid(char *buf, pid_t pid) {
- ssize_t i;
- char *ptr = buf, tmp;
- do {
- *ptr++ = '0' + pid % 10;
- pid /= 10;
- } while (pid > 0);
- for (i = 0; i < (ptr - buf) / 2; i++) {
- tmp = buf[i];
- buf[i] = ptr[-i - 1];
- ptr[-i - 1] = tmp;
+static inline int perftrace_children(
+ struct pid_item item,
+ struct list new,
+ struct list *old,
+ struct timespec diff,
+ int humanize_mem,
+ int *json_state
+) {
+ struct pid_item *ptr;
+ for (ptr = new.data; LIST_POS(ptr, new, struct pid_item) < new.len; ptr++) {
+ if (ptr->ppid != item.pid)
+ continue;
+ if (perftrace_pid(*ptr, old, diff, humanize_mem, json_state) < 0)
+ return -1;
}
- return ptr;
+ return 0;
}
-static inline int read_utime(clockid_t clock_id, struct timespec *utime) {
- // maybe offer to record system time, too, from /proc/<pid>/stat
- if (clock_gettime(clock_id, utime) < 0) {
- if (errno == EINVAL)
- return -2;
- perror("clock_gettime");
- return -1;
- }
- return 0;
+static inline pid_t *perftrace_find_pid(struct list found_pids, pid_t pid) {
+ pid_t *ptr;
+ if (pid <= 0)
+ return NULL;
+ for (
+ ptr = found_pids.data;
+ LIST_POS(ptr, found_pids, pid_t) < found_pids.len;
+ ptr++
+ )
+ if (*ptr == pid)
+ return ptr;
+ return NULL;
}
-static inline int read_statm(
- pid_t pid, unsigned long *vm_size, unsigned long *resident
+static inline int perftrace_recursive(
+ struct pid_item item,
+ struct list new,
+ struct list *old,
+ struct timespec diff,
+ int humanize_mem,
+ int *json_state
) {
- struct file_buffer fb = { 0, { 0 }, 0, 0 };
+ struct list found_pids = LIST_EMPTY;
+ struct pid_item *pip;
+ size_t found = 0, last_found;
+ pid_t *ptr;
int ret = 0;
- char filename[128];
- memcpy(filename, "/proc/", 6);
- memcpy(stringify_pid(filename + 6, pid), "/statm", 7);
- if ((fb.fd = open(filename, O_RDONLY)) < 0) {
- if (errno == ESRCH)
- return -2;
- perror("open");
+ if (list_maybe_grow(&found_pids, ++found_pids.len, sizeof (pid_t)) < 0)
return -1;
- }
- if (
- read_unsigned_long(&fb, vm_size, ' ') < 0
- || read_unsigned_long(&fb, resident, ' ') < 0
- )
- ret = -1;
- // POSIX.1-2024 close
- if (close(fb.fd) < 0) {
- perror("close");
- if (errno != EINTR)
- ret = -1;
- else if (close(fb.fd) < 0) {
- perror("close");
- ret = -1;
- }
- }
+ *LIST_AT(found_pids, 0, pid_t) = item.pid;
+ do {
+ last_found = found;
+ for (
+ ptr = LIST_AT(found_pids, last_found, pid_t);
+ LIST_POS(ptr, found_pids, pid_t) < found_pids.len;
+ ptr++
+ )
+ for (
+ pip = new.data;
+ LIST_POS(pip, new, struct pid_item) < new.len;
+ pip++
+ ) {
+ if (
+ pip->ppid != *ptr
+ // avoid repeating pids in found_pids
+ || perftrace_find_pid(found_pids, pip->pid) != NULL
+ )
+ continue;
+ if (
+ perftrace_pid(*pip, old, diff, humanize_mem, json_state) < 0
+ || list_maybe_grow(
+ &found_pids, ++found_pids.len, sizeof (pid_t)
+ ) < 0
+ )
+ goto error;
+ *LIST_AT(found_pids, found_pids.len - 1, pid_t) = pip->pid;
+ found++;
+ }
+ } while (found > last_found);
+ ret = 0;
+error:
+ free(found_pids.data);
return ret;
}
-static inline int check_child(struct args *a) {
- pid_t pid = waitpid(a->pid, NULL, WNOHANG);
- if (pid == a->pid)
- a->argv = NULL;
- return pid;
-}
-
-static int alarm_set;
-
-void handle_alarm(int signal) {
- alarm_set |= signal == SIGALRM;
+static inline int perftrace(
+ struct list pids,
+ struct list *old,
+ struct timespec current,
+ struct timespec prev,
+ int humanize_mem,
+ int *json_state
+) {
+ struct arg_pid_item *arg_pid;
+ struct pid_item *item;
+ struct list new = LIST_EMPTY;
+ struct timespec diff = timespec_diff(current, prev);
+ struct tm lt;
+ size_t old_len_before = old->len;
+ int ret = -1;
+ if (perftrace_collect(&new) < 0)
+ return -1;
+ perftrace_invalidate_missing_pids(new, old);
+ if (old_len_before > 0 && old->len == 0) {
+ ret = 0;
+ goto error;
+ }
+ localtime_r(¤t.tv_sec, <);
+ printf(
+ (
+ "%s"
+ " {\n"
+ " \"ts\": \"%04d-%02d-%02d %02d:%02d:%02d.%03ld\",\n"
+ " \"perftrace\": ["
+ ),
+ *json_state ? "\n ]\n },\n" : "",
+ lt.tm_year + 1900,
+ lt.tm_mon + 1,
+ lt.tm_mday,
+ lt.tm_hour,
+ lt.tm_min,
+ lt.tm_sec,
+ (current.tv_nsec + MICRO_IN_SEC / 2 - 1) / MICRO_IN_SEC
+ );
+ *json_state = 1;
+ for (
+ arg_pid = pids.data;
+ LIST_POS(arg_pid, pids, struct arg_pid_item) < pids.len;
+ arg_pid++
+ ) {
+ if ((item = perftrace_find_pid_item(new, arg_pid->pid)) == NULL)
+ continue;
+ if (perftrace_pid(*item, old, diff, humanize_mem, json_state) < 0)
+ goto error;
+ if (
+ (
+ arg_pid->mode == '+'
+ && perftrace_children(
+ *item, new, old, diff, humanize_mem, json_state
+ ) < 0
+ ) || (
+ arg_pid->mode == '*'
+ && perftrace_recursive(
+ *item, new, old, diff, humanize_mem, json_state
+ ) < 0
+ )
+ )
+ goto error;
+ }
+ ret = 0;
+error:
+ free(new.data);
+ return ret;
}
-int main(int argc, char *argv[]) {
- struct timespec current[3] = { { 0, 0 }, { 0, 0 }, { 0, 0 } };
- struct timespec utime[3] = { { 0, 0 }, { 0, 0 }, { 0, 0 } };
- struct sigaction sa = { .sa_handler = handle_alarm, .sa_flags = 0 };
- struct args a = { { 0, MICRO_IN_SEC / 2 }, 0, NULL };
- unsigned long vm_size, resident;
- long clock_ticks = sysconf(_SC_CLK_TCK);
- clockid_t clock_id;
- int page_size = getpagesize(), ret = EXIT_FAILURE;
- if (parse_args(argc, argv, &a, clock_ticks) < 0)
- return EXIT_FAILURE;
- sa.sa_handler = handle_alarm;
+static inline int setup_signal_handler(int signum, void (*func)(int)) {
+ struct sigaction sa = { .sa_handler = func, .sa_flags = 0 };
sigemptyset(&sa.sa_mask);
- if (sigaction(SIGALRM, &sa, NULL) < 0) {
+ if (sigaction(signum, &sa, NULL) < 0) {
perror("sigaction");
- return EXIT_FAILURE;
+ return -1;
}
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ struct list pids = LIST_EMPTY, old = LIST_EMPTY;
+ struct timespec current, prev = { 0, 0 };
+ struct timeval delay = { 0, MICRO_IN_SEC / 2 };
+ int ret = EXIT_FAILURE, humanize_mem = 0, json_state = 0;
+ page_size = getpagesize();
+ if (
+ setup_signal_handler(SIGALRM, handle_alarm) < 0
+ || setup_signal_handler(SIGINT, handle_int) < 0
+ )
+ goto error;
if (
setitimer(
- ITIMER_REAL, &(struct itimerval){ a.delay, a.delay }, NULL
+ ITIMER_REAL, &(struct itimerval){ delay, delay }, NULL
) < 0
) {
perror("setitimer");
- return EXIT_FAILURE;
+ goto error;
}
- if (a.argv != NULL && run_proc(&a) < 0)
+ if (parse_args(argc, argv, &pids, &delay, &humanize_mem) < 0)
return EXIT_FAILURE;
- if (clock_getcpuclockid(a.pid, &clock_id) < 0)
- goto error;
- for (alarm_set = 0;; alarm_set = 0) {
- if (a.argv != NULL && check_child(&a) < 0)
- goto error;
- if (clock_gettime(CLOCK_MONOTONIC, current) < 0) {
+ printf("[\n");
+ for (alarm_set = 0; alarm_await(); alarm_set = 0) {
+ if (clock_gettime(CLOCK_REALTIME, ¤t) < 0) {
perror("clock_gettime");
- return -1;
- }
- switch (read_utime(clock_id, utime)) {
- case -1:
goto error;
- case -2:
- goto done;
- }
- if (current[1].tv_sec | current[1].tv_nsec) {
- current[2] = timespec_diff(current[0], current[1]);
- utime[2] = timespec_diff(utime[0], utime[1]);
}
- current[1] = current[0];
- utime[1] = utime[0];
- switch (read_statm(a.pid, &vm_size, &resident)) {
- case -1:
+ if (perftrace(pids, &old, current, prev, humanize_mem, &json_state) < 0)
goto error;
- case -2:
- goto done;
- }
- printf(
- (
- "{\"ts\":%ld.%09ld,\"ts_diff\":%ld.%09ld,"
- "\"utime\":%ld.%09ld,\"vm_size\":%ld,\"resident\":%ld}\n"
- ),
- current[0].tv_sec,
- current[0].tv_nsec,
- current[2].tv_sec,
- current[2].tv_nsec,
- utime[2].tv_sec,
- utime[2].tv_nsec,
- vm_size * page_size,
- resident * page_size
- );
- sleep(!alarm_set * UINT_MAX);
+ if (old.len == 0)
+ break;
+ prev = current;
}
-done:
ret = EXIT_SUCCESS;
error:
- if (a.argv != NULL && waitpid(a.pid, NULL, 0) < 0)
- perror("waitpid");
+ if (json_state)
+ printf("\n ]\n }\n");
+ printf("]\n");
+ free(pids.data);
+ free(old.data);
return ret;
}