]> git.saurik.com Git - apple/system_cmds.git/blobdiff - stackshot.tproj/stackshot.c
system_cmds-790.tar.gz
[apple/system_cmds.git] / stackshot.tproj / stackshot.c
diff --git a/stackshot.tproj/stackshot.c b/stackshot.tproj/stackshot.c
new file mode 100644 (file)
index 0000000..7b0ddc8
--- /dev/null
@@ -0,0 +1,233 @@
+/* Copyright (c) 2017 Apple Inc. All rights reserved. */
+
+#include <stdio.h>
+#include <dispatch/dispatch.h>
+#include <sysexits.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <mach/mach_time.h>
+#include <sys/stackshot.h>
+#include <sys/types.h>
+#include <kern/debug.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <kern/kcdata.h>
+
+uint64_t
+stackshot_get_mach_absolute_time(void *buffer, uint32_t size)
+{
+    kcdata_iter_t iter = kcdata_iter_find_type(kcdata_iter(buffer, size), KCDATA_TYPE_MACH_ABSOLUTE_TIME);
+    if (!kcdata_iter_valid(iter) || kcdata_iter_size(iter) < sizeof(uint64_t)) {
+        fprintf(stderr, "bad kcdata\n");
+        exit(1);
+    }
+    return *(uint64_t *)kcdata_iter_payload(iter);
+}
+
+static void usage(char **argv)
+{
+    fprintf (stderr, "usage: %s [-d] [-t] >file\n", argv[0]);
+    fprintf (stderr, "    -d      : take delta stackshot\n");
+    fprintf (stderr, "    -b      : get bootprofile\n");
+    fprintf (stderr, "    -c      : get coalition data\n");
+    fprintf (stderr, "    -i      : get instructions and cycles\n");
+    fprintf (stderr, "    -t      : enable tailspin mode\n");
+    fprintf (stderr, "    -g      : get thread group data\n");
+    fprintf (stderr, "    -s      : fork a sleep process\n");
+    fprintf (stderr, "    -L      : disable loadinfo\n");
+    fprintf (stderr, "    -k      : active kernel threads only\n");
+    fprintf (stderr, "    -I      : disable io statistics\n");
+    fprintf (stderr, "    -S      : stress test: while(1) stackshot; \n");
+    fprintf (stderr, "    -p PID  : target a pid\n");
+    fprintf (stderr, "    -E      : grab existing kernel buffer\n");
+    exit(1);
+}
+
+void forksleep() {
+    pid_t pid = fork();
+    if (pid < 0) {
+        perror("fork");
+        exit(1);
+    }
+
+    if (pid == 0) {
+        execlp("sleep", "sleep", "30", NULL);
+        perror("execlp");
+        exit(1);
+    }
+}
+
+
+int main(int argc, char **argv) {
+
+    uint32_t iostats = 0;
+    uint32_t active_kernel_threads_only = 0;
+    uint32_t tailspin = 0;
+    uint32_t bootprofile = 0;
+    uint32_t thread_group = 0;
+    uint32_t coalition = 0;
+    uint32_t instrs_cycles = 0;
+    uint32_t flags = 0;
+    uint32_t loadinfo = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO;
+    boolean_t delta = FALSE;
+    boolean_t sleep = FALSE;
+    boolean_t stress = FALSE;
+    pid_t pid = -1;
+    int c;
+
+    while ((c = getopt(argc, argv, "SgIikbcLdtsp:E")) != EOF) {
+        switch(c) {
+        case 'I':
+            iostats |= STACKSHOT_NO_IO_STATS;
+            break;
+        case 'k':
+            active_kernel_threads_only |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY;
+            loadinfo &= ~STACKSHOT_SAVE_LOADINFO;
+            break;
+        case 'b':
+            bootprofile |= STACKSHOT_GET_BOOT_PROFILE;
+            break;
+        case 'c':
+            coalition |= STACKSHOT_SAVE_JETSAM_COALITIONS;
+            break;
+        case 'i':
+            instrs_cycles |= STACKSHOT_INSTRS_CYCLES;
+            break;
+        case 'L':
+            loadinfo = 0;
+            break;
+        case 'g':
+            thread_group |= STACKSHOT_THREAD_GROUP;
+            break;
+        case 't':
+            tailspin |= STACKSHOT_TAILSPIN;
+            break;
+        case 'd':
+            delta = TRUE;
+            break;
+        case 's':
+            sleep = TRUE;
+            break;
+        case 'p':
+            pid = atoi(optarg);
+            break;
+        case 'S':
+            stress = TRUE;
+            break;
+        case 'E':
+            flags = flags | STACKSHOT_RETRIEVE_EXISTING_BUFFER;
+            break;
+        case '?':
+        case 'h':
+        default:
+            usage(argv);
+            break;
+        }
+    }
+
+    if (thread_group && delta) {
+        fprintf(stderr, "stackshot does not support delta snapshots with thread groups\n");
+        return 1;
+    }
+
+    if (optind < argc) {
+        usage(argv);
+    }
+
+top:
+    ;
+
+    void * config = stackshot_config_create();
+    if (!config) {
+        perror("stackshot_config_create");
+        return 1;
+    }
+    flags =  flags | loadinfo | STACKSHOT_SAVE_IMP_DONATION_PIDS | STACKSHOT_GET_DQ | STACKSHOT_KCDATA_FORMAT |
+        tailspin | bootprofile | active_kernel_threads_only | iostats | thread_group | coalition | instrs_cycles;
+
+    int err = stackshot_config_set_flags(config, flags);
+    if (err != 0) {
+        perror("stackshot_config_set_flags");
+        return 1;
+    }
+
+    if (pid != -1) {
+        int err = stackshot_config_set_pid(config, pid);
+        if (err != 0) {
+            perror("stackshot_config_set_flags");
+            return 1;
+        }
+    }
+
+    err = stackshot_capture_with_config(config);
+    if (err != 0) {
+        perror("stackshot_capture_with_config");
+        return 1;
+    }
+
+    void *buf = stackshot_config_get_stackshot_buffer(config);
+    if (!buf) {
+        perror("stackshot_config_get_stackshot_buffer");
+        return 1;
+    }
+
+    uint32_t size = stackshot_config_get_stackshot_size(config);
+
+    if (delta) {
+        // output the original somewhere?
+
+        uint64_t time = stackshot_get_mach_absolute_time(buf, size);
+
+        err = stackshot_config_dealloc_buffer(config);
+        assert(!err);
+
+        flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT;
+        int err = stackshot_config_set_flags(config, flags);
+        if (err != 0) {
+            perror("stackshot_config_set_flags");
+            return 1;
+        }
+
+        err = stackshot_config_set_delta_timestamp(config, time);
+        if (err != 0) {
+            perror("stackshot_config_delta_timestamp");
+            return 1;
+        }
+
+        if (sleep) {
+            forksleep();
+        }
+        usleep(10000);
+
+        err = stackshot_capture_with_config(config);
+        if (err != 0) {
+            perror("stackshot_capture_with_config");
+            return 1;
+        }
+
+        buf = stackshot_config_get_stackshot_buffer(config);
+        if (!buf) {
+            perror("stackshot_config_get_stackshot_buffer");
+            return 1;
+        }
+
+        size = stackshot_config_get_stackshot_size(config);
+
+
+    }
+
+
+    if (stress) {
+        if (config) {
+            stackshot_config_dealloc(config);
+            config = NULL;
+        }
+        goto top;
+    }
+
+    fwrite(buf, size, 1, stdout);
+}