#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>
#define STACKSHOT_TAILSPIN (0x80000)
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, " -t : enable tailspin mode\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, " -p PID : target a pid\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 loadinfo = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO;
boolean_t delta = FALSE;
boolean_t sleep = FALSE;
pid_t pid = -1;
int c;
while ((c = getopt(argc, argv, "IkbLdtsp:")) != 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 'L':
loadinfo = 0;
break;
case 't':
tailspin |= STACKSHOT_TAILSPIN;
break;
case 'd':
delta = TRUE;
break;
case 's':
sleep = TRUE;
break;
case 'p':
pid = atoi(optarg);
break;
case '?':
case 'h':
default:
usage(argv);
break;
}
}
if (optind < argc)
{
usage(argv);
}
void * config = stackshot_config_create();
if (!config) {
perror("stackshot_config_create");
return 1;
}
uint32_t flags = loadinfo | STACKSHOT_SAVE_IMP_DONATION_PIDS | STACKSHOT_GET_DQ | STACKSHOT_KCDATA_FORMAT |
tailspin | bootprofile | active_kernel_threads_only | iostats;
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) {
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);
}
fwrite(buf, size, 1, stdout);
}