]> git.mar77i.info Git - perftrace/commitdiff
major improvements. break up parse_args
authormar77i <mar77i@protonmail.ch>
Sun, 22 Feb 2026 13:00:23 +0000 (14:00 +0100)
committermar77i <mar77i@protonmail.ch>
Sun, 22 Feb 2026 13:00:23 +0000 (14:00 +0100)
perftrace.c

index 1fd11e975bf40b22e414bc971a2be0596f32f417..14ac9288b283359a4d484f31998091fabe55e126 100644 (file)
@@ -1,6 +1,7 @@
 
-// perftrace2.c
+// perftrace.c
 
+#include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <stdio.h>
@@ -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/<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);
@@ -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;
 }