-// perftrace2.c
+// perftrace.c
+#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
static inline int parse_delay(const char *ptr, struct timeval *delay) {
long new_value, fractions = -1;
*delay = (struct timeval){ 0, 0 };
- do {
- if (*ptr == '.')
- fractions = MICRO_IN_SEC / 10;
- else if (fractions == -1) {
- new_value = 10 * delay->tv_sec + *ptr - '0';
- if (*ptr < '0' || new_value / 10 != delay->tv_sec)
- return -1;
- delay->tv_sec = new_value;
- } else if (fractions > 0 && *ptr >= '0' && *ptr <= '9') {
- delay->tv_usec += fractions * (*ptr - '0');
- fractions /= 10;
- } else
+ for (;;) {
+ new_value = 10 * delay->tv_sec + *ptr - '0';
+ if (*ptr < '0' || new_value / 10 != delay->tv_sec)
return -1;
- } while (*(++ptr) != '\0');
+ 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')
+ return -1;
+ delay->tv_usec += fractions * (*ptr - '0');
+ }
return 0;
}
-static inline int parse_pid(const char *ptr, pid_t *pid) {
- pid_t pid_new;
+static inline int parse_arg_delay(
+ int argc, char **argv, int i, struct args *a, long clock_ticks
+) {
+ switch (argv[i][7]) {
+ case '=':
+ if (parse_delay(argv[i] + 8, &a->delay) < 0)
+ goto error;
+ break;
+ case '\0':
+ if (++i >= argc || parse_delay(argv[i], &a->delay) < 0)
+ goto error;
+ break;
+ default:
+ goto error;
+ }
+ if (
+ (clock_ticks <= 1 && a->delay.tv_sec < 1) || (
+ a->delay.tv_sec < 1 && a->delay.tv_usec < MICRO_IN_SEC / clock_ticks
+ )
+ ) {
+ fprintf(stderr, "Error: delay too small!\n");
+ return -1;
+ }
+ return i;
+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)
+ pid_new = 10 * pid + *ptr - '0';
+ if (*ptr < '0' || pid_new / 10 != pid)
return -1;
- *pid = pid_new;
+ pid = pid_new;
} while (*(++ptr) != '\0');
- return 0;
+ return pid;
}
-static int page_size, alarm_set;
-static long clock_ticks;
+static inline int parse_arg_pid(int argc, char **argv, int i, struct args *a) {
+ if (a->pid != 0) {
+ fprintf(stderr, "Error: '--pid' already specified.\n");
+ return -1;
+ }
+ switch (argv[i][5]) {
+ case '=':
+ if ((a->pid = parse_pid(argv[i] + 6)) < 0)
+ break;
+ return i;
+ case '\0':
+ if (++i >= argc || (a->pid = parse_pid(argv[i])) < 0)
+ break;
+ return i;
+ }
+ fprintf(stderr, "Error: parsing '--pid'\n");
+ return -1;
+}
-int parse_args(int argc, char **argv, struct args *a) {
- int i;
- char pid_or_argv_set = 0, *arg;
- if (argc < 2) {
- fprintf(stderr, "Error: missing arguments.\n");
+static inline int parse_arg_argv(int argc, char **argv, int i, struct args *a) {
+ if (a->pid != 0) {
+ fprintf(stderr, "Error: '--pid' already specified.\n");
return -1;
}
+ if (++i >= argc) {
+ fprintf(stderr, "Error: arguments expected after '--'\n");
+ return -1;
+ }
+ a->argv = argv + i;
+ return argc;
+}
+
+int parse_args(int argc, char **argv, struct args *a, long clock_ticks) {
+ int i;
for (i = 1; i < argc; i++) {
- arg = argv[i];
- if (strncmp(arg, "--delay", 7) == 0) {
- switch (arg[7]) {
- case '=':
- if (parse_delay(arg + 8, &a->delay) < 0)
- goto error;
- break;
- case '\0':
- if (++i >= argc || parse_delay(arg, &a->delay) < 0)
- goto error;
- break;
- default:
- goto error;
- }
- if (
- a->delay.tv_sec < 1
- && a->delay.tv_usec < MICRO_IN_SEC / clock_ticks
- ) {
- fprintf(stderr, "Error: delay too small!\n");
- return -1;
- }
- } else if (strncmp(arg, "--pid", 5) == 0) {
- if (pid_or_argv_set != 0) {
- fprintf(stderr, "Error: --pid already specified.\n");
- return -1;
- }
- pid_or_argv_set = 1;
- switch(arg[5]) {
- case '=':
- if (parse_pid(arg + 6, &a->pid) < 0)
- goto error;
- break;
- case '\0':
- if (++i >= argc || parse_pid(arg, &a->pid) < 0)
- goto error;
- break;
- default:
- goto error;
- }
- } else if (strcmp(arg, "--") == 0) {
- if (pid_or_argv_set != 0) {
- fprintf(stderr, "Error: --pid already specified.\n");
- return -1;
- }
- pid_or_argv_set = 2;
- if (++i >= argc)
- goto error;
- a->pid = argc - i;
- a->argv = argv + i;
- break;
- } else {
+ if (strncmp(argv[i], "--delay", 7) == 0)
+ i = parse_arg_delay(argc, argv, i, a, clock_ticks);
+ else if (strncmp(argv[i], "--pid", 5) == 0)
+ i = parse_arg_pid(argc, argv, i, a);
+ else if (strcmp(argv[i], "--") == 0)
+ i = parse_arg_argv(argc, argv, i, a);
+ else {
fprintf(stderr, "Error: Invalid arguments.\n");
return -1;
}
+ if (i == -1)
+ return -1;
}
- if (pid_or_argv_set == 0) {
- fprintf(stderr, "Error: Must either specify -- or --attach.\n");
+ if (a->pid == 0 && a->argv == NULL) {
+ fprintf(stderr, "Error: Must either specify '--' or '--pid'.\n");
return -1;
}
return 0;
-error:
- fprintf(stderr, "Error: parsing '%s'\n", arg);
- return -1;
}
int run_proc(struct args *a) {
return result;
}
-void handle_alarm(int signal) {
- alarm_set |= signal == SIGALRM;
-}
-
struct file_buffer {
int fd;
- char buffer[4], size, consumed;
+ char buffer[128], size, consumed;
};
static inline int file_buffer_get_char(struct file_buffer *fb) {
return ptr;
}
-int read_stat(pid_t pid, unsigned long *vm_size, unsigned long *resident) {
+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 int read_statm(
+ pid_t pid, unsigned long *vm_size, unsigned long *resident
+) {
struct file_buffer fb = { 0, { 0 }, 0, 0 };
int ret = 0;
char filename[128];
memcpy(filename, "/proc/", 6);
memcpy(stringify_pid(filename + 6, pid), "/statm", 7);
- fb.fd = open(filename, O_RDONLY);
+ if ((fb.fd = open(filename, O_RDONLY)) < 0) {
+ if (errno == ESRCH)
+ return -2;
+ perror("open");
+ return -1;
+ }
if (
read_unsigned_long(&fb, vm_size, ' ') < 0
|| read_unsigned_long(&fb, resident, ' ') < 0
)
ret = -1;
- close(fb.fd);
+ // 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;
+ }
+ }
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;
+}
+
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;
+ 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;
- clock_ticks = sysconf(_SC_CLK_TCK);
- page_size = getpagesize();
- if (parse_args(argc, argv, &a) < 0)
+ int page_size = getpagesize(), ret = EXIT_FAILURE;
+ if (parse_args(argc, argv, &a, clock_ticks) < 0)
return EXIT_FAILURE;
sa.sa_handler = handle_alarm;
sigemptyset(&sa.sa_mask);
return EXIT_FAILURE;
if (clock_getcpuclockid(a.pid, &clock_id) < 0)
goto error;
- for (;;) {
- if (clock_gettime(CLOCK_MONOTONIC, current) < 0)
+ for (alarm_set = 0;; alarm_set = 0) {
+ if (a.argv != NULL && check_child(&a) < 0)
goto error;
- if (waitpid(a.pid, NULL, WNOHANG) != 0)
- break;
- if (clock_gettime(clock_id, utime) < 0)
+ if (clock_gettime(CLOCK_MONOTONIC, current) < 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]);
}
- if (read_stat(a.pid, &vm_size, &resident) < 0)
+ current[1] = current[0];
+ utime[1] = utime[0];
+ switch (read_statm(a.pid, &vm_size, &resident)) {
+ case -1:
goto error;
+ case -2:
+ goto done;
+ }
printf(
(
- "{\"ts\":%ld.%09ld,\"utime\":%ld.%09ld,"
- "\"vm_size\":%ld,\"resident\":%ld}\n"
+ "{\"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,
vm_size * page_size,
resident * page_size
);
- for (alarm_set = 0; !alarm_set;)
- sleep(a.delay.tv_sec + (a.delay.tv_sec < INT_MAX));
- current[1] = current[0];
- utime[1] = utime[0];
+ sleep(!alarm_set * UINT_MAX);
}
- return EXIT_SUCCESS;
+done:
+ ret = EXIT_SUCCESS;
error:
- if (a.argv != NULL)
- waitpid(a.pid, NULL, 0);
- return EXIT_FAILURE;
+ if (a.argv != NULL && waitpid(a.pid, NULL, 0) < 0)
+ perror("waitpid");
+ return ret;
}