X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/faa687fed273e36ee92ac51d06a94d786084a3b0..887d5eedc67e1d6b5933393070e765bc7739453c:/stackshot.tproj/stackshot.c?ds=inline diff --git a/stackshot.tproj/stackshot.c b/stackshot.tproj/stackshot.c new file mode 100644 index 0000000..7b0ddc8 --- /dev/null +++ b/stackshot.tproj/stackshot.c @@ -0,0 +1,233 @@ +/* Copyright (c) 2017 Apple Inc. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +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); +}