From f34f683163784a48ad638f2eb47532da1bdf85c3 Mon Sep 17 00:00:00 2001 From: mar77i Date: Sun, 22 Feb 2026 14:00:23 +0100 Subject: [PATCH] major improvements. break up parse_args --- perftrace.c | 283 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 172 insertions(+), 111 deletions(-) diff --git a/perftrace.c b/perftrace.c index 1fd11e9..14ac928 100644 --- a/perftrace.c +++ b/perftrace.c @@ -1,6 +1,7 @@ -// perftrace2.c +// perftrace.c +#include #include #include #include @@ -23,108 +24,120 @@ struct args { 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) { @@ -157,13 +170,9 @@ struct timespec timespec_diff(struct timespec tsa, struct timespec tsb) { 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) { @@ -219,32 +228,72 @@ static inline char *stringify_pid(char *buf, pid_t pid) { 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//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); @@ -264,24 +313,38 @@ int main(int argc, char *argv[]) { 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, @@ -289,14 +352,12 @@ int main(int argc, char *argv[]) { 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; } -- 2.53.0