/* cc -I/System/Library/Frameworks/System.framework/Versions/B/PrivateHeaders -arch x86_64 -arch i386 -O -o trace trace.c */ /* * NOTE: There exists another copy of this file in the kernel_tools. Changes * made here may also need to be made there. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef KERNEL_PRIVATE #define KERNEL_PRIVATE #include #undef KERNEL_PRIVATE #else #include #endif /*KERNEL_PRIVATE*/ #include #include #include int nbufs = 0; int enable_flag=0; int execute_flag=0; int logRAW_flag=0; int LogRAW_flag=0; int readRAW_flag = 0; int disable_flag=0; int init_flag=0; int kval_flag=0; int remove_flag=0; int bufset_flag=0; int bufget_flag=0; int filter_flag=0; int filter_file_flag=0; int filter_alloced=0; int trace_flag=0; int nowrap_flag=0; int freerun_flag=0; int verbose_flag=0; int usage_flag=0; int pid_flag=0; int pid_exflag=0; int ppt_flag=0; int done_with_args=0; int no_default_codes_flag=0; unsigned int value1=0; unsigned int value2=0; unsigned int value3=0; unsigned int value4=0; pid_t pid=0; int reenable=0; int force_32bit_exec = 0; int frequency = 0; int mib[6]; size_t needed; char *logfile = (char *)0; /* This file is trace format */ char *RAW_file = (char *)0; FILE *output_file; int output_fd; extern char **environ; uint8_t* type_filter_bitmap; #define SIZE_4KB (4 * (1 << 10)) #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END) #define DBG_FUNC_MASK 0xfffffffc #define SHORT_HELP 1 #define LONG_HELP 0 #define CSC_MASK 0xffff0000 #define BSC_exit 0x040c0004 #define BSC_thread_terminate 0x040c05a4 #define MACH_SCHEDULED 0x01400000 #define MACH_MAKERUNNABLE 0x01400018 #define MACH_STKHANDOFF 0x01400008 #define EMPTYSTRING "" #define UNKNOWN "unknown" char tmpcommand[MAXCOMLEN]; int total_threads = 0; int nthreads = 0; kd_threadmap *mapptr = 0; kd_cpumap_header* cpumap_header = NULL; kd_cpumap* cpumap = NULL; /* If NUMPARMS changes from the kernel, then PATHLENGTH will also reflect the change This is for the vfslookup entries that return pathnames */ #define NUMPARMS 23 #define PATHLENGTH (NUMPARMS*sizeof(long)) #define US_TO_SLEEP 50000 #define BASE_EVENTS 500000 mach_timebase_info_data_t mach_timebase; double divisor; typedef struct { uint32_t debugid; char *debug_string; } code_type_t; code_type_t* codesc = 0; size_t codesc_idx = 0; // Index into first empty codesc entry typedef struct event *event_t; struct event { event_t ev_next; uint64_t ev_thread; uint32_t ev_debugid; uint64_t ev_timestamp; }; typedef struct lookup *lookup_t; struct lookup { lookup_t lk_next; uint64_t lk_thread; uint64_t lk_dvp; int64_t *lk_pathptr; int64_t lk_pathname[NUMPARMS + 1]; }; typedef struct threadmap *threadmap_t; struct threadmap { threadmap_t tm_next; uint64_t tm_thread; uint64_t tm_pthread; boolean_t tm_deleteme; char tm_command[MAXCOMLEN + 1]; }; #define HASH_SIZE 1024 #define HASH_MASK 1023 event_t event_hash[HASH_SIZE]; lookup_t lookup_hash[HASH_SIZE]; threadmap_t threadmap_hash[HASH_SIZE]; event_t event_freelist; lookup_t lookup_freelist; threadmap_t threadmap_freelist; threadmap_t threadmap_temp; #define SBUFFER_SIZE (128 * 4096) char sbuffer[SBUFFER_SIZE]; int secs_to_run = 0; int use_current_buf = 0; kbufinfo_t bufinfo = {0, 0, 0, 0}; int codenum = 0; int codeindx_cache = 0; static void quit(char *); static int match_debugid(unsigned int, char *, int *); static void usage(int short_help); static int argtoi(int flag, char *req, char *str, int base); static int parse_codefile(const char *filename); static void codesc_find_dupes(void); static int read_command_map(int, uint32_t); static void read_cpu_map(int); static void find_thread_command(kd_buf *, char **); static void create_map_entry(uint64_t, char *); static void getdivisor(); static unsigned long argtoul(); static void set_enable(int); static void set_remove(); static void set_nowrap(); static void set_pidcheck(int, int); static void set_pidexclude(int, int); static void set_numbufs(int); static void set_freerun(); static void get_bufinfo(kbufinfo_t *); static int get_ktrace_state(void); static void set_init(); static void set_kval_list(); static void readtrace(char *); static void log_trace(); static void Log_trace(); static void read_trace(); static void signal_handler(int); static void signal_handler_RAW(int); static void delete_thread_entry(uint64_t); static void find_and_insert_tmp_map_entry(uint64_t, char *); static void create_tmp_map_entry(uint64_t, uint64_t); static void find_thread_name(uint64_t, char **, boolean_t); static void execute_process(char * const argv[]); static int writetrace(int); static int write_command_map(int); static int debugid_compar(const void *, const void *); static threadmap_t find_thread_entry(uint64_t); static void saw_filter_class(uint8_t class); static void saw_filter_end_range(uint8_t end_class); static void saw_filter_subclass(uint8_t subclass); static void filter_done_parsing(void); static void set_filter(void); static void set_filter_class(uint8_t class); static void set_filter_range(uint8_t class, uint8_t end); static void set_filter_subclass(uint8_t class, uint8_t subclass); static void parse_filter_file(char *filename); static void quit_args(const char *fmt, ...) __printflike(1, 2); #ifndef KERN_KDWRITETR #define KERN_KDWRITETR 17 #endif #ifndef KERN_KDWRITEMAP #define KERN_KDWRITEMAP 18 #endif #ifndef F_FLUSH_DATA #define F_FLUSH_DATA 40 #endif #ifndef RAW_VERSION1 typedef struct { int version_no; int thread_count; uint64_t TOD_secs; uint32_t TOD_usecs; } RAW_header; #define RAW_VERSION0 0x55aa0000 #define RAW_VERSION1 0x55aa0101 #endif #define ARRAYSIZE(x) ((int)(sizeof(x) / sizeof(*x))) #define EXTRACT_CLASS_LOW(debugid) ( (uint8_t) ( ((debugid) & 0xFF00 ) >> 8 ) ) #define EXTRACT_SUBCLASS_LOW(debugid) ( (uint8_t) ( ((debugid) & 0xFF ) ) ) #define ENCODE_CSC_LOW(class, subclass) \ ( (uint16_t) ( ((class) & 0xff) << 8 ) | ((subclass) & 0xff) ) RAW_header raw_header; void set_enable(int val) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDENABLE; #ifdef KDEBUG_ENABLE_PPT if (ppt_flag && val) { mib[3] = KDEBUG_ENABLE_PPT; } else { mib[3] = val; } #else mib[3] = val; #endif mib[4] = 0; mib[5] = 0; if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) { if (errno == EINVAL) { quit_args("trace facility failure, KERN_KDENABLE: trace buffer is uninitialized\n"); } quit_args("trace facility failure, KERN_KDENABLE: %s\n", strerror(errno)); } } void set_remove(void) { extern int errno; errno = 0; mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDREMOVE; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, NULL, &needed, NULL, 0) < 0) { if (errno == EBUSY) quit("the trace facility is currently in use...\n fs_usage, sc_usage, trace, and latency use this feature.\n\n"); else quit_args("trace facility failure, KERN_KDREMOVE: %s\n", strerror(errno)); } } void set_numbufs(int nbufs) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDSETBUF; mib[3] = nbufs; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDSETBUF: %s\n", strerror(errno)); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDSETUP; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, NULL, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDSETUP: %s\n", strerror(errno)); } void set_nowrap(void) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDEFLAGS; mib[3] = KDBG_NOWRAP; mib[4] = 0; mib[5] = 0; /* no flags */ if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) quit_args("trace facility failure, KDBG_NOWRAP: %s\n", strerror(errno)); } void set_pidcheck(int pid, int on_off_flag) { kd_regtype kr; kr.type = KDBG_TYPENONE; kr.value1 = pid; kr.value2 = on_off_flag; needed = sizeof(kd_regtype); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDPIDTR; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0) { if (errno == EACCES) { quit_args("trace facility failure, setting pid filter: %s\n", strerror(errno)); } else if (on_off_flag == 1 && errno == ESRCH) { set_remove(); quit_args("trace facility failure, setting pid filter: " "pid %d does not exist\n", pid); } else { quit_args("trace facility failure, KERN_KDPIDTR: %s\n", strerror(errno)); } } } void set_pidexclude(int pid, int on_off_flag) { kd_regtype kr; kr.type = KDBG_TYPENONE; kr.value1 = pid; kr.value2 = on_off_flag; needed = sizeof(kd_regtype); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDPIDEX; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0) { if (on_off_flag == 1) { printf ("pid %d does not exist\n", pid); set_remove(); exit(2); } } } void set_freerun(void) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDEFLAGS; mib[3] = KDBG_FREERUN; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) quit_args("trace facility failure, KDBG_FREERUN: %s\n", strerror(errno)); } static int get_ktrace_state(void) { int state; size_t state_size = sizeof(state); int err = sysctlbyname("ktrace.state", &state, &state_size, NULL, 0); if (err) { fprintf(stderr, "error: could not query ktrace.state sysctl (%d: %s)\n", errno, strerror(errno)); exit(1); } return state; } void get_bufinfo(kbufinfo_t *val) { needed = sizeof (*val); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDGETBUF; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, val, &needed, 0, 0) < 0) quit_args("trace facility failure, KERN_KDGETBUF: %s\n", strerror(errno)); } void set_init(void) { kd_regtype kr; kr.type = KDBG_RANGETYPE; kr.value1 = 0; kr.value2 = -1; needed = sizeof(kd_regtype); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDSETREG; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDSETREG (rangetype): %s\n", strerror(errno)); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDSETUP; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, NULL, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDSETUP: %s\n", strerror(errno)); } static void set_filter(void) { errno = 0; int mib[] = { CTL_KERN, KERN_KDEBUG, KERN_KDSET_TYPEFILTER }; size_t needed = KDBG_TYPEFILTER_BITMAP_SIZE; if(sysctl(mib, ARRAYSIZE(mib), type_filter_bitmap, &needed, NULL, 0)) { quit_args("trace facility failure, KERN_KDSET_TYPEFILTER: %s\n", strerror(errno)); } } void set_kval_list(void) { kd_regtype kr; kr.type = KDBG_VALCHECK; kr.value1 = value1; kr.value2 = value2; kr.value3 = value3; kr.value4 = value4; needed = sizeof(kd_regtype); mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDSETREG; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDSETREG (valcheck): %s\n", strerror(errno)); } void readtrace(char *buffer) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDREADTR; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, buffer, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDREADTR: %s\n", strerror(errno)); } int writetrace(int fd) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDWRITETR; mib[3] = fd; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) return 1; return 0; } int write_command_map(int fd) { mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDWRITEMAP; mib[3] = fd; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0) { if (errno == ENODATA) { if (verbose_flag) { printf("Cannot write thread map -- this is not fatal\n"); } } else { return 1; } } return 0; } static lookup_t handle_lookup_event(uint64_t thread, int debugid, kd_buf *kdp) { lookup_t lkp; int hashid; boolean_t first_record = FALSE; hashid = thread & HASH_MASK; if (debugid & DBG_FUNC_START) first_record = TRUE; for (lkp = lookup_hash[hashid]; lkp; lkp = lkp->lk_next) { if (lkp->lk_thread == thread) break; } if (lkp == NULL) { if (first_record == FALSE) return (0); if ((lkp = lookup_freelist)) lookup_freelist = lkp->lk_next; else lkp = (lookup_t)malloc(sizeof(struct lookup)); lkp->lk_thread = thread; lkp->lk_next = lookup_hash[hashid]; lookup_hash[hashid] = lkp; } if (first_record == TRUE) { lkp->lk_pathptr = lkp->lk_pathname; lkp->lk_dvp = kdp->arg1; } else { if (lkp->lk_pathptr > &lkp->lk_pathname[NUMPARMS-4]) return (lkp); *lkp->lk_pathptr++ = kdp->arg1; } *lkp->lk_pathptr++ = kdp->arg2; *lkp->lk_pathptr++ = kdp->arg3; *lkp->lk_pathptr++ = kdp->arg4; *lkp->lk_pathptr = 0; return (lkp); } static void delete_lookup_event(uint64_t thread, lookup_t lkp_to_delete) { lookup_t lkp; lookup_t lkp_prev; int hashid; hashid = thread & HASH_MASK; if ((lkp = lookup_hash[hashid])) { if (lkp == lkp_to_delete) lookup_hash[hashid] = lkp->lk_next; else { lkp_prev = lkp; for (lkp = lkp->lk_next; lkp; lkp = lkp->lk_next) { if (lkp == lkp_to_delete) { lkp_prev->lk_next = lkp->lk_next; break; } lkp_prev = lkp; } } if (lkp) { lkp->lk_next = lookup_freelist; lookup_freelist = lkp; } } } static void insert_start_event(uint64_t thread, int debugid, uint64_t now) { event_t evp; int hashid; hashid = thread & HASH_MASK; for (evp = event_hash[hashid]; evp; evp = evp->ev_next) { if (evp->ev_thread == thread && evp->ev_debugid == debugid) break; } if (evp == NULL) { if ((evp = event_freelist)) event_freelist = evp->ev_next; else evp = (event_t)malloc(sizeof(struct event)); evp->ev_thread = thread; evp->ev_debugid = debugid; evp->ev_next = event_hash[hashid]; event_hash[hashid] = evp; } evp->ev_timestamp = now; } static uint64_t consume_start_event(uint64_t thread, int debugid, uint64_t now) { event_t evp; event_t evp_prev; int hashid; uint64_t elapsed = 0; hashid = thread & HASH_MASK; if ((evp = event_hash[hashid])) { if (evp->ev_thread == thread && evp->ev_debugid == debugid) event_hash[hashid] = evp->ev_next; else { evp_prev = evp; for (evp = evp->ev_next; evp; evp = evp->ev_next) { if (evp->ev_thread == thread && evp->ev_debugid == debugid) { evp_prev->ev_next = evp->ev_next; break; } evp_prev = evp; } } if (evp) { elapsed = now - evp->ev_timestamp; evp->ev_next = event_freelist; event_freelist = evp; } } return (elapsed); } void log_trace(void) { int fd = -1; int ret = 0; char *buffer; uint32_t buffer_size = 1000000 * sizeof(kd_buf); if (logfile[0] == '-' && logfile[1] == '\0') { fd = STDOUT_FILENO; } else { fd = open(logfile, O_TRUNC | O_WRONLY | O_CREAT, 0777); } if (fd == -1) { perror("Can't open logfile"); exit(1); } get_bufinfo(&bufinfo); if (bufinfo.nolog != 1) { reenable = 1; set_enable(0); /* disable logging*/ } get_bufinfo(&bufinfo); if (verbose_flag) { if (bufinfo.flags & KDBG_WRAPPED) printf("Buffer has wrapped\n"); else printf("Buffer has not wrapped\n"); } ret = write_command_map(fd); if (ret) { close(fd); perror("failed to write logfile"); exit(1); } buffer = malloc(buffer_size); if (buffer == NULL) { quit("can't allocate memory for events\n"); } for (;;) { needed = buffer_size; readtrace(buffer); if (needed == 0) { break; } write(fd, buffer, needed * sizeof(kd_buf)); } free(buffer); close(fd); } /* * Why does this function exist? * trace -L needs millisecond level wait times. * When this code is running remotely, the mach_timebase_info_t data may * be from a device with a different timebase. This code avoids using * mach_absolute_time(), so that time calculations come out correct both * locally and remotely. */ static uint64_t current_millis() { struct timeval time; gettimeofday(&time, NULL); return (time.tv_sec * 1000) + (time.tv_usec / 1000); } void Log_trace(void) { size_t len; int num_cpus = 0; int fd; uint64_t current_ms; uint64_t ending_ms = 0; uint64_t last_time_written; uint32_t ms_to_run; if ((fd = open(logfile, O_TRUNC|O_WRONLY|O_CREAT, 0777)) == -1) { perror("Can't open logfile"); exit(1); } if (use_current_buf == 0) { /* * grab the number of cpus and scale the buffer size */ mib[0] = CTL_HW; mib[1] = HW_NCPU; mib[2] = 0; len = sizeof(num_cpus); sysctl(mib, 2, &num_cpus, &len, NULL, 0); if (!bufset_flag) nbufs = BASE_EVENTS * num_cpus; set_remove(); set_numbufs(nbufs); set_init(); if (filter_flag) set_filter(); if (kval_flag) set_kval_list(); } if (use_current_buf == 0) set_enable(1); if (write_command_map(fd)) { quit("can't write tracefile header\n"); } last_time_written = current_millis(); if (secs_to_run) { ms_to_run = secs_to_run * 1000; ending_ms = last_time_written + ms_to_run; } else ms_to_run = 0; while (LogRAW_flag) { needed = ms_to_run; if (writetrace(fd)) { perror("KDWRITETR returned error"); /* Clean up and exit in case of write fail */ break; } if (needed) { current_ms = current_millis(); printf("wrote %d events - elapsed time = %.1f secs\n", (int)needed, (double)(current_ms - last_time_written) / 1000.0); last_time_written = current_ms; } if (secs_to_run) { current_ms = current_millis(); if (current_ms > ending_ms) break; ms_to_run = (uint32_t)(ending_ms - current_ms); if (ms_to_run == 0) break; } } set_enable(0); set_numbufs(0); set_remove(); close(fd); } void read_trace(void) { char *buffer; uint32_t buffer_size; kd_buf *kd; int fd; int firsttime = 1; int lines = 0; int io_lines = 0; uint64_t bias = 0; uint32_t count_of_names; double last_event_time = 0.0; time_t trace_time; if (!readRAW_flag) { get_bufinfo(&bufinfo); if (bufinfo.nolog != 1) { reenable = 1; set_enable(0); /* disable logging*/ } if (verbose_flag) { if (bufinfo.flags & KDBG_WRAPPED) printf("Buffer has wrapped\n"); else printf("Buffer has not wrapped\n"); } fd = 0; count_of_names = 0; } else { fd = open(RAW_file, O_RDONLY); if (fd < 0) { perror("Can't open file"); exit(1); } if (read(fd, &raw_header, sizeof(RAW_header)) != sizeof(RAW_header)) { perror("read failed"); exit(2); } if (raw_header.version_no != RAW_VERSION1) { raw_header.version_no = RAW_VERSION0; raw_header.TOD_secs = time((long *)0); raw_header.TOD_usecs = 0; lseek(fd, (off_t)0, SEEK_SET); if (read(fd, &raw_header.thread_count, sizeof(int)) != sizeof(int)) { perror("read failed"); exit(2); } } else if (raw_header.version_no == RAW_VERSION1) { #if defined(__ILP32__) /* * If the raw trace file was written by armv7k, the 64-bit alignment * of TOD_secs causes RAW_header to be 24 bytes. If we only read 20 * bytes, the next 4 bytes might be a legitimate thread_id, but it might * also be 0 or a leaked kernel pointer from an armv7k trace file. For * both those cases, consume the 4 bytes and look for the thread map * after it. */ if (sizeof(raw_header) == 20) { uint32_t alignment_garbage; if (read(fd, &alignment_garbage, sizeof(alignment_garbage)) != sizeof(alignment_garbage)) { perror("read failed"); exit(2); } if ((alignment_garbage == 0) || (alignment_garbage >= 0x80000000)) { if (verbose_flag) { printf("Skipping 4 bytes to find valid thread map\n"); } } else { /* oops, go back to where we were */ lseek(fd, -(off_t)sizeof(alignment_garbage), SEEK_CUR); } } #endif } count_of_names = raw_header.thread_count; trace_time = (time_t) (raw_header.TOD_secs); printf("%s\n", ctime(&trace_time)); } buffer_size = 1000000 * sizeof(kd_buf); buffer = malloc(buffer_size); if (buffer == (char *) 0) quit("can't allocate memory for tracing info\n"); kd = (kd_buf *)(uintptr_t)buffer; read_command_map(fd, count_of_names); read_cpu_map(fd); for (;;) { uint32_t count; uint64_t now = 0; uint64_t prev; uint64_t prevdelta = 0; uint32_t cpunum = 0; uint64_t thread; double x = 0.0; double y = 0.0; double event_elapsed_time = 0; kd_buf *kdp; lookup_t lkp; boolean_t ending_event; int i; int debugid; int debugid_base; int dmsgindex; char dbgmessge[80]; char outbuf[32]; char *command; if (!readRAW_flag) { needed = buffer_size; mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDREADTR; mib[3] = 0; mib[4] = 0; mib[5] = 0; if (sysctl(mib, 3, buffer, &needed, NULL, 0) < 0) quit_args("trace facility failure, KERN_KDREADTR: %s\n", strerror(errno)); if (needed == 0) break; count = (uint32_t)needed; } else { uint32_t bytes_read; bytes_read = (uint32_t)read(fd, buffer, buffer_size); if (bytes_read == -1) { perror("read failed"); exit(2); } count = bytes_read / sizeof(kd_buf); if (count == 0) break; } for (kdp = &kd[0], i = 0; i < count; i++, kdp++) { prev = now; debugid = kdp->debugid; debugid_base = debugid & DBG_FUNC_MASK; now = kdp->timestamp & KDBG_TIMESTAMP_MASK; cpunum = kdbg_get_cpu(kdp); /* * Is this event from an IOP? If so, there will be no * thread command, label it with the symbolic IOP name */ if (cpumap && (cpunum < cpumap_header->cpu_count) && (cpumap[cpunum].flags & KDBG_CPUMAP_IS_IOP)) { command = cpumap[cpunum].name; } else { find_thread_command(kdp, &command); } /* * The internal use TRACE points clutter the output. * Print them only if in verbose mode. */ if (!verbose_flag) { /* Is this entry of Class DBG_TRACE */ if ((debugid >> 24) == DBG_TRACE) { if (((debugid >> 16) & 0xff) != DBG_TRACE_INFO) continue; } } if (firsttime) bias = now; now -= bias; thread = kdp->arg5; if (lines == 64 || firsttime) { prevdelta = now - prevdelta; if (firsttime) firsttime = 0; else { x = (double)prevdelta; x /= divisor; fprintf(output_file, "\n\nNumber of microsecs since in last page %8.1f\n", x); } prevdelta = now; /* * Output description row to output file (make sure to format correctly for 32-bit and 64-bit) */ fprintf(output_file, #ifdef __LP64__ " AbsTime(Us) Delta debugid arg1 arg2 arg3 arg4 thread cpu# command\n\n" #else " AbsTime(Us) Delta debugid arg1 arg2 arg3 arg4 thread cpu# command\n\n" #endif ); lines = 0; if (io_lines > 15000) { fcntl(output_fd, F_FLUSH_DATA, 0); io_lines = 0; } } lkp = 0; if (debugid_base == VFS_LOOKUP) { lkp = handle_lookup_event(thread, debugid, kdp); if ( !lkp || !(debugid & DBG_FUNC_END)) continue; } x = (double)now; x /= divisor; if (last_event_time) y = x - last_event_time; else y = x; last_event_time = x; ending_event = FALSE; if ( !lkp) { int t_debugid; uint64_t t_thread; if ((debugid & DBG_FUNC_START) || debugid == MACH_MAKERUNNABLE) { if (debugid_base != BSC_thread_terminate && debugid_base != BSC_exit) { if (debugid == MACH_MAKERUNNABLE) t_thread = kdp->arg1; else t_thread = thread; insert_start_event(t_thread, debugid_base, now); } } else if ((debugid & DBG_FUNC_END) || debugid == MACH_STKHANDOFF || debugid == MACH_SCHEDULED) { if (debugid == MACH_STKHANDOFF || debugid == MACH_SCHEDULED) { t_debugid = MACH_MAKERUNNABLE; t_thread = kdp->arg2; } else { t_debugid = debugid_base; t_thread = thread; } event_elapsed_time = (double)consume_start_event(t_thread, t_debugid, now); event_elapsed_time /= divisor; ending_event = TRUE; if (event_elapsed_time == 0 && (debugid == MACH_STKHANDOFF || debugid == MACH_SCHEDULED)) ending_event = FALSE; } } if (ending_event) { char *ch; sprintf(&outbuf[0], "(%-10.1f)", event_elapsed_time); /* * fix that right paren */ ch = &outbuf[11]; if (*ch != ')') { ch = strchr (&outbuf[0], ')'); } if (ch) { *ch = ' '; --ch; while (ch != &outbuf[0]) { if (*ch == ' ') --ch; else { *(++ch) = ')'; break; } } } } if (match_debugid(debugid_base, dbgmessge, &dmsgindex)) { if (ending_event) fprintf(output_file, "%13.1f %10.1f%s %-28x ", x, y, outbuf, debugid_base); else fprintf(output_file, "%13.1f %10.1f %-28x ", x, y, debugid_base); } else { if (ending_event) fprintf(output_file, "%13.1f %10.1f%s %-28.28s ", x, y, outbuf, dbgmessge); else fprintf(output_file, "%13.1f %10.1f %-28.28s ", x, y, dbgmessge); } if (lkp) { char *strptr; int len; strptr = (char *)lkp->lk_pathname; /* * print the tail end of the pathname */ len = (int)strlen(strptr); if (len > 51) len -= 51; else len = 0; #if defined(__LP64__) || defined(__arm64__) fprintf(output_file, "%-16llx %-51s %-16" PRIx64 " %-2d %s\n", (uint64_t)lkp->lk_dvp, &strptr[len], thread, cpunum, command); #else fprintf(output_file, "%-8x %-51s %-8" PRIx64 " %-2d %s\n", (unsigned int)lkp->lk_dvp, &strptr[len], thread, cpunum, command); #endif delete_lookup_event(thread, lkp); } else if (debugid == TRACE_INFO_STRING) { #if defined(__LP64__) || defined(__arm64__) fprintf(output_file, "%-32s%-36s %-16" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command); #else fprintf(output_file, "%-16s%-46s %-8" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command); #endif } else { #if defined(__LP64__) || defined(__arm64__) fprintf(output_file, "%-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-16" PRIx64 " %-2d %s\n", (uint64_t)kdp->arg1, (uint64_t)kdp->arg2, (uint64_t)kdp->arg3, (uint64_t)kdp->arg4, thread, cpunum, command); #else fprintf(output_file, "%-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-8" PRIx64 " %-2d %s\n", (uint64_t)kdp->arg1, (uint64_t)kdp->arg2, (uint64_t)kdp->arg3, (uint64_t)kdp->arg4, thread, cpunum, command); #endif } lines++; io_lines++; } } if (reenable == 1) set_enable(1); /* re-enable kernel logging */ } void signal_handler(int sig) { ptrace(PT_KILL, pid, (caddr_t)0, 0); /* * child is gone; no need to disable the pid */ exit(2); } void signal_handler_RAW(int sig) { LogRAW_flag = 0; } int main (int argc, char* argv[], char *envp[]) { extern char *optarg; extern int optind; int ch; int i; char *output_filename = NULL; unsigned int parsed_arg; for (i = 1; i < argc; i++) { if (strcmp("-X", argv[i]) == 0) { force_32bit_exec = 1; break; } } if (force_32bit_exec) { if (0 != reexec_to_match_lp64ness(FALSE)) { fprintf(stderr, "Could not re-execute: %d\n", errno); exit(1); } } #if !defined(__arm64__) else { if (0 != reexec_to_match_kernel()) { fprintf(stderr, "Could not re-execute: %d\n", errno); exit(1); } } #endif if (setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_PASSIVE) < 0) { printf("setiopolicy failed\n"); exit(1); } output_file = stdout; output_fd = 1; while ((ch = getopt(argc, argv, "hedEk:irb:gc:p:s:tR:L:l:S:F:a:x:Xnfvo:PT:N")) != EOF) { switch(ch) { case 'h': /* help */ usage_flag=1; break; case 'S': secs_to_run = argtoi('S', "decimal number", optarg, 10); break; case 'a': /* set tracing on a pid */ pid_flag=1; pid = argtoi('a', "decimal number", optarg, 10); break; case 'x': /* exclude a pid from tracing */ pid_exflag=1; pid = argtoi('x', "decimal number", optarg, 10); break; case 'v': verbose_flag=1; break; case 'l': logRAW_flag = 1; logfile = optarg; break; case 'L': LogRAW_flag = 1; logfile = optarg; signal(SIGINT, signal_handler_RAW); break; case 'e': enable_flag = 1; break; case 'i': init_flag = 1; break; case 'E': execute_flag = 1; break; case 'd': disable_flag = 1; break; case 'k': if (kval_flag == 0) value1 = (unsigned int) argtoul('k', "hex number", optarg, 16); else if (kval_flag == 1) value2 = (unsigned int) argtoul('k', "hex number", optarg, 16); else if (kval_flag == 2) value3 = (unsigned int) argtoul('k', "hex number", optarg, 16); else if (kval_flag == 3) value4 = (unsigned int) argtoul('k', "hex number", optarg, 16); else { fprintf(stderr, "A maximum of four values can be specified with -k\n"); usage(SHORT_HELP); } kval_flag++; break; case 'r': remove_flag = 1; break; case 'g': bufget_flag = 1; break; case 't': trace_flag = 1; break; case 'R': readRAW_flag = 1; RAW_file = optarg; break; case 'n': nowrap_flag = 1; break; case 'f': freerun_flag = 1; break; case 'b': bufset_flag = 1; nbufs = argtoi('b', "decimal number", optarg, 10); break; case 'c': filter_flag = 1; parsed_arg = argtoi('c', "decimal, hex, or octal number", optarg, 0); if (parsed_arg > 0xFF) quit_args("argument '-c %s' parsed as %u, " "class value must be 0-255\n", optarg, parsed_arg); saw_filter_class(parsed_arg); break; case 's': filter_flag = 1; parsed_arg = argtoi('s', "decimal, hex, or octal number", optarg, 0); if (parsed_arg > 0xFF) quit_args("argument '-s %s' parsed as %u, " "subclass value must be 0-255\n", optarg, parsed_arg); saw_filter_subclass(parsed_arg); break; case 'p': filter_flag = 1; parsed_arg = argtoi('p', "decimal, hex, or octal number", optarg, 0); if (parsed_arg > 0xFF) quit_args("argument '-p %s' parsed as %u, " "end range value must be 0-255\n", optarg, parsed_arg); saw_filter_end_range(parsed_arg); break; case 'P': ppt_flag = 1; break; case 'o': output_filename = optarg; break; case 'F': frequency = argtoi('F', "decimal number", optarg, 10); break; case 'X': break; case 'N': no_default_codes_flag = 1; break; case 'T': filter_flag = 1; // Flush out any unclosed -c argument filter_done_parsing(); parse_filter_file(optarg); break; default: usage(SHORT_HELP); } } argc -= optind; if (!no_default_codes_flag) { if (verbose_flag) printf("Adding default code file /usr/share/misc/trace.codes. Use '-N' to skip this.\n"); parse_codefile("/usr/share/misc/trace.codes"); } if (argc) { if (!execute_flag) { while (argc--) { const char *cfile = argv[optind++]; if (verbose_flag) printf("Adding code file %s \n", cfile); parse_codefile(cfile); } } } else { if (execute_flag) quit_args("-E flag needs an executable to launch\n"); } if (usage_flag) usage(LONG_HELP); getdivisor(); if (pid_flag && pid_exflag) quit_args("Can't use both -a and -x flag together\n"); if (kval_flag && filter_flag) quit_args("Cannot use -k flag with -c, -s, or -p\n"); if (output_filename && !trace_flag && !readRAW_flag) quit_args("When using 'o' option, must use the 't' or 'R' option too\n"); filter_done_parsing(); done_with_args = 1; if (LogRAW_flag) { get_bufinfo(&bufinfo); int ktrace_state = get_ktrace_state(); /* * Only use the current kdebug configuration when foreground * tracing is enabled. Both checks are necessary because the * background tool might have enabled tracing, but as soon as we * try to write a header, that configuration is removed for us. */ if ((ktrace_state == 1) && (bufinfo.nolog == 0)) { use_current_buf = 1; } } if (disable_flag) { if (pid_flag) { set_pidcheck(pid, 0); /* disable pid check for given pid */ exit(0); } else if (pid_exflag) { set_pidexclude(pid, 0); /* disable pid exclusion for given pid */ exit(0); } set_enable(0); exit(0); } if (remove_flag) { set_remove(); exit(0); } if (bufset_flag ) { if (!init_flag && !LogRAW_flag) { fprintf(stderr,"The -b flag must be used with the -i flag\n"); exit(1); } set_numbufs(nbufs); } if (nowrap_flag) set_nowrap(); if (freerun_flag) set_freerun(); if (bufget_flag) { printf("The kernel tracing settings are:\n"); /* determine the state of ktrace */ int state = get_ktrace_state(); /* get the name of the last process to configure ktrace */ char execname[20] = { 0 }; size_t execname_size = sizeof(execname); int err = sysctlbyname("ktrace.configured_by", &execname, &execname_size, NULL, 0); if (err) { fprintf(stderr, "error: could not query ktrace.configured_by sysctl (%d: %s)\n", errno, strerror(errno)); exit(1); } printf("\tTracing is "); switch (state) { case 0: printf("off"); break; case 1: printf("active (foreground)"); break; case 2: printf("active (background)"); break; default: printf("in an invalid state"); break; } printf("\n"); printf("\tLast configured by \"%s\"\n", execname[0] == '\0' ? "" : execname); /* get kdebug info */ get_bufinfo(&bufinfo); printf("The kernel buffer settings are:\n"); if (bufinfo.flags & KDBG_BUFINIT) printf("\tKernel buffer is initialized\n"); else printf("\tKernel buffer is not initialized\n"); printf("\t number of buf entries = %d\n", bufinfo.nkdbufs); if (verbose_flag) { if (bufinfo.flags & KDBG_MAPINIT) printf("\tKernel thread map is initialized\n"); else printf("\tKernel thread map is not initialized\n"); printf("\t number of thread entries = %d\n", bufinfo.nkdthreads); } if (bufinfo.nolog) printf("\tBuffer logging is disabled\n"); else printf("\tBuffer logging is enabled\n"); if (verbose_flag) printf("\tkernel flags = 0x%x\n", bufinfo.flags); if (bufinfo.flags & KDBG_NOWRAP) printf("\tKernel buffer wrap is disabled\n"); else printf("\tKernel buffer wrap is enabled\n"); if (bufinfo.flags & KDBG_RANGECHECK) printf("\tCollection within a range is enabled\n"); else printf("\tCollection within a range is disabled\n"); if (bufinfo.flags & KDBG_VALCHECK) printf("\tCollecting specific code values is enabled\n"); else printf("\tCollecting specific code values is disabled\n"); if (bufinfo.flags & KDBG_TYPEFILTER_CHECK) printf("\tCollection based on a filter is enabled\n"); else printf("\tCollection based on a filter is disabled\n"); if (bufinfo.flags & KDBG_PIDCHECK) printf("\tCollection based on pid is enabled\n"); else printf("\tCollection based on pid is disabled\n"); if (bufinfo.flags & KDBG_PIDEXCLUDE) printf("\tCollection based on pid exclusion is enabled\n"); else printf("\tCollection based on pid exclusion is disabled\n"); if (bufinfo.bufid == -1) printf("\tKernel buffer is not controlled by any process.\n"); else printf("\tKernel buffer is controlled by proc id [%d]\n", bufinfo.bufid); if (bufinfo.flags & KDBG_TYPEFILTER_CHECK) { if (verbose_flag) { bool (^should_print)(uint8_t*) = ^bool(uint8_t* ptr) { for (uint32_t i=0; i<32; ++i) { if (ptr[i] > 0) return true; } return false; }; uint8_t* typefilter = (uint8_t*)kdebug_typefilter(); if (typefilter) { bool header = false; // Reduce noise, only print lines that are allowing events. for (uint32_t tclass = 0; tclass < 0x100; ++tclass) { uint8_t* base = &typefilter[tclass * 32]; if (should_print(base)) { if (!header) { header = true; printf("\tTypefilter:\n"); printf("%18s ",""); for (uint32_t tsubclass=0; tsubclass<32; ++tsubclass) { printf("%02x ", tsubclass * 8); } printf("\n"); printf("%18s ",""); for (uint32_t tsubclass=0; tsubclass<32; ++tsubclass) { printf("---"); } printf("\n"); } printf("%16s%02x: ", "", tclass); for (uint32_t tsubclass=0; tsubclass<32; ++tsubclass) { printf("%02X ", typefilter[(tclass * 32) + tsubclass]); } printf("\n"); } } } } } } if (init_flag) set_init(); if (filter_flag) set_filter(); if (kval_flag) set_kval_list(); if (execute_flag) { fprintf(stderr, "Starting program: %s\n", argv[optind]); fflush(stdout); fflush(stderr); execute_process(&(argv[optind])); exit(0); } else if (enable_flag) { if (pid_flag) set_pidcheck(pid, 1); else if (pid_exflag) set_pidexclude(pid, 1); set_enable(1); } if (output_filename) { if (((output_fd = open(output_filename, O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644)) < 0 ) || !(output_file = fdopen(output_fd, "w"))) { fprintf(stderr, "Cannot open file \"%s\" for writing.\n", output_filename); usage(SHORT_HELP); } setbuffer(output_file, &sbuffer[0], SBUFFER_SIZE); if (fcntl(output_fd, F_NOCACHE, 1) < 0) { /* Not fatal */ fprintf(stderr, "Warning: setting F_NOCACHE on %s, failed\n", output_filename); } } if (!LogRAW_flag && !logRAW_flag) setbuffer(output_file, &sbuffer[0], SBUFFER_SIZE); if (trace_flag || readRAW_flag) read_trace(); else if (LogRAW_flag) Log_trace(); else if (logRAW_flag) log_trace(); exit(0); } /* end main */ static void execute_process(char * const argv[]) { int status = 0; int rc = 0; posix_spawnattr_t spawn_attrs; assert(argv); /* ensure that the process being spawned starts suspended */ rc = posix_spawnattr_init(&spawn_attrs); if (rc != 0) { quit_args("Failed to initialize spawn attrs: %s\n", strerror(rc)); } rc = posix_spawnattr_setflags(&spawn_attrs, POSIX_SPAWN_START_SUSPENDED); if (rc != 0) { quit_args("Unable to start process suspended: %s\n", strerror(rc)); } /* spawn the process with the rest of the arguments */ rc = posix_spawnp(&pid, argv[0], NULL, &spawn_attrs, argv, environ); if (rc != 0) { quit_args("Unabled to start process: %s\n", strerror(rc)); } signal(SIGINT, signal_handler); set_pidcheck(pid, 1); set_enable(1); /* start the child process */ rc = kill(pid, SIGCONT); if (rc != 0) { perror("Failed to continue child process:"); exit(EX_OSERR); } rc = waitpid(pid, &status, 0); if (rc == -1) { perror("Failed to wait for process: "); } } static void quit_args(const char *fmt, ...) { char buffer[1024]; if (reenable == 1) { reenable = 0; set_enable(1); /* re-enable kernel logging */ } va_list args; va_start (args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); fprintf(stderr, "trace error: %s", buffer); va_end(args); if (!done_with_args) usage(SHORT_HELP); exit(1); } void quit(char *s) { if (reenable == 1) { reenable = 0; set_enable(1); /* re-enable kernel logging */ } printf("trace: "); if (s) printf("%s", s); exit(1); } static void usage(int short_help) { if (short_help) { (void)fprintf(stderr, " usage: trace -h [-v]\n"); (void)fprintf(stderr, " usage: trace -i [-b numbufs]\n"); (void)fprintf(stderr, " usage: trace -g\n"); (void)fprintf(stderr, " usage: trace -d [-a pid | -x pid ]\n"); (void)fprintf(stderr, " usage: trace -r\n"); (void)fprintf(stderr, " usage: trace -n\n"); (void)fprintf(stderr, " usage: trace -e [ -c class [[-s subclass]... | -p class ]]... | \n"); (void)fprintf(stderr, " [-k code | -k code | -k code | -k code] [-P] [-T tracefilter] \n"); (void)fprintf(stderr, " [-a pid | -x pid] \n\n"); (void)fprintf(stderr, " usage: trace -E [ -c class [[-s subclass]... | -p class ]]... | \n"); (void)fprintf(stderr, " [-k code | -k code | -k code | -k code] [-P] [-T tracefilter] \n"); (void)fprintf(stderr, " executable_path [optional args to executable] \n\n"); (void)fprintf(stderr, " usage: trace -L RawFilename [-S SecsToRun]\n"); (void)fprintf(stderr, " usage: trace -l RawFilename\n"); (void)fprintf(stderr, " usage: trace -R RawFilename [-X] [-F frequency] [-o OutputFilename] [-N] [ExtraCodeFilename1 ExtraCodeFilename2 ...]\n"); (void)fprintf(stderr, " usage: trace -t [-o OutputFilename] [-N] [ExtraCodeFilename1 ExtraCodeFilename2 ...]\n"); (void)fprintf(stderr, " Trace will import /usr/share/misc/trace.codes as a default codefile unless -N is specified. Extra codefiles specified are used in addition to the default codefile.\n"); exit(1); } /* Only get here if printing long usage info */ (void)fprintf(stderr, "usage: trace -h [-v]\n"); (void)fprintf(stderr, "\tPrint this long command help.\n\n"); (void)fprintf(stderr, "\t -v Print extra information about tracefilter and code files.\n\n"); (void)fprintf(stderr, "usage: trace -i [-b numbufs]\n"); (void)fprintf(stderr, "\tInitialize the kernel trace buffer.\n\n"); (void)fprintf(stderr, "\t-b numbufs The number of trace elements the kernel buffer\n"); (void)fprintf(stderr, "\t can hold is set to numbufs. Use with the -i flag.\n"); (void)fprintf(stderr, "\t Enter a decimal value.\n\n"); (void)fprintf(stderr, "usage: trace -g\n"); (void)fprintf(stderr, "\tGet the kernel buffer settings.\n\n"); (void)fprintf(stderr, "usage: trace -d [-a pid | -x pid]\n"); (void)fprintf(stderr, "\tDisable/stop collection of kernel trace elements.\n\n"); (void)fprintf(stderr, "\t -a pid Disable/stop collection for this process only.\n\n"); (void)fprintf(stderr, "\t -x pid Disable/stop exclusion of this process only.\n\n"); (void)fprintf(stderr, "usage: trace -r\n"); (void)fprintf(stderr, "\tRemove the kernel trace buffer. Set controls to default.\n\n"); (void)fprintf(stderr, "usage: trace -n\n"); (void)fprintf(stderr, "\tDisables kernel buffer wrap around.\n\n"); (void)fprintf(stderr, "usage: trace -e [ -c class [[-s subclass]... | -p class ]]... |\n"); (void)fprintf(stderr, " [-k code | -k code | -k code | -k code] [-P] [-T tracefilter]\n"); (void) fprintf(stderr, " [-a pid | -x pid]\n\n"); (void)fprintf(stderr, "\t Enable/start collection of kernel trace elements. \n\n"); (void)fprintf(stderr, "\t By default, trace collects all tracepoints. \n"); (void)fprintf(stderr, "\t The following arguments may be used to restrict collection \n"); (void)fprintf(stderr, "\t to a limited set of tracepoints. \n\n"); (void)fprintf(stderr, "\t Multiple classes can be specified by repeating -c. \n"); (void)fprintf(stderr, "\t Multiple subclasses can be specified by repeating -s after -c. \n"); (void)fprintf(stderr, "\t Classes, subclasses, and class ranges can be entered \n"); (void)fprintf(stderr, "\t in hex (0xXX), decimal (XX), or octal (0XX). \n\n"); (void)fprintf(stderr, "\t -c class Restrict trace collection to given class. \n\n"); (void)fprintf(stderr, "\t -p class Restrict trace collection to given class range. \n"); (void)fprintf(stderr, "\t Must provide class with -c first. \n\n"); (void)fprintf(stderr, "\t -s subclass Restrict trace collection to given subclass. \n"); (void)fprintf(stderr, "\t Must provide class with -c first. \n\n"); (void)fprintf(stderr, "\t -a pid Restrict trace collection to the given process.\n\n"); (void)fprintf(stderr, "\t -x pid Exclude the given process from trace collection.\n\n"); (void)fprintf(stderr, "\t -k code Restrict trace collection up to four specific codes.\n"); (void)fprintf(stderr, "\t Enter codes in hex (0xXXXXXXXX). \n\n"); (void)fprintf(stderr, "\t -P Enable restricted PPT trace points only.\n\n"); (void)fprintf(stderr, "\t -T tracefilter Read class and subclass restrictions from a \n"); (void)fprintf(stderr, "\t tracefilter description file. \n"); (void)fprintf(stderr, "\t Run trace -h -v for more info on this file. \n\n"); (void)fprintf(stderr, "usage: trace -E [ -c class [[-s subclass]... | -p class ]]... |\n"); (void)fprintf(stderr, " [-k code | -k code | -k code | -k code] [-P] [-T tracefilter]\n"); (void)fprintf(stderr, " executable_path [optional args to executable] \n\n"); (void)fprintf(stderr, "\tLaunch the given executable and enable/start\n"); (void)fprintf(stderr, "\tcollection of kernel trace elements for that process.\n"); (void)fprintf(stderr, "\tSee -e(enable) flag for option descriptions.\n\n"); (void)fprintf(stderr, "usage: trace -t [-o OutputFilename] [-N] [ExtraCodeFilename1 ExtraCodeFilename2 ...] \n"); (void)fprintf(stderr, "\tCollect the kernel buffer trace data and print it.\n\n"); (void)fprintf(stderr, "\t -N Do not import /usr/share/misc/trace.codes (for raw hex tracing or supplying an alternate set of codefiles)\n"); (void)fprintf(stderr, "\t -o OutputFilename Print trace output to OutputFilename. Default is stdout.\n\n"); (void)fprintf(stderr, "usage: trace -R RawFilename [-X] [-F frequency] [-o OutputFilename] [-N] [ExtraCodeFilename1 ExtraCodeFilename2 ...] \n"); (void)fprintf(stderr, "\tRead raw trace file and print it.\n\n"); (void)fprintf(stderr, "\t -X Force trace to interpret trace data as 32 bit. \n"); (void)fprintf(stderr, "\t Default is to match the bit width of the current system. \n"); (void)fprintf(stderr, "\t -N Do not import /usr/share/misc/trace.codes (for raw hex tracing or supplying an alternate set of codefiles)\n"); (void)fprintf(stderr, "\t -F frequency Specify the frequency of the clock used to timestamp entries in RawFilename.\n\t Use command \"sysctl hw.tbfrequency\" on the target device, to get target frequency.\n"); (void)fprintf(stderr, "\t -o OutputFilename Print trace output to OutputFilename. Default is stdout.\n\n"); (void)fprintf(stderr, "usage: trace -L RawFilename [-S SecsToRun]\n"); (void)fprintf(stderr, "\tContinuously collect the kernel buffer trace data in the raw format \n"); (void)fprintf(stderr, "\tand write it to RawFilename. \n"); (void)fprintf(stderr, "\t-L implies -r -i if tracing isn't currently enabled.\n"); (void)fprintf(stderr, "\tOptions passed to -e(enable) are also accepted by -L. (except -a -x -P)\n\n"); (void)fprintf(stderr, "\t -S SecsToRun Specify the number of seconds to collect trace data.\n\n"); (void)fprintf(stderr, "usage: trace -l RawFilename\n"); (void)fprintf(stderr, "\tCollect the existing kernel buffer trace data in the raw format.\n\n"); if (verbose_flag) { (void)fprintf(stderr, "Code file: \n" "\t A code file consists of a list of tracepoints, one per line, \n" "\t with one tracepoint code in hex, followed by a tab, \n" "\t followed by the tracepoint's name. \n\n" "\t Example tracepoint: \n" "\t 0x010c007c\tMSC_mach_msg_trap \n" "\t This describes the tracepoint with the following info: \n" "\t Name: MSC_mach_msg_trap \n" "\t Class: 0x01 (Mach events) \n" "\t Subclass: 0x0c (Mach system calls) \n" "\t Code: 0x007c (Mach syscall number 31) \n\n" "\t See /usr/include/sys/kdebug.h for the currently defined \n" "\t class and subclass values. \n" "\t See /usr/share/misc/trace.codes for the currently allocated \n" "\t system tracepoints in trace code file format. \n" "\t This codefile is useful with the -R argument to trace. \n" "\n"); (void)fprintf(stderr, "Tracefilter description file: \n" "\t A tracefilter description file consists of a list of \n" "\t class and subclass filters in hex, one per line, \n" "\t which are applied as if they were passed with -c and -s. \n" "\t Pass -v to see what classes and subclasses are being set. \n\n" "\t File syntax: \n" "\t Class filter: \n" "\t C 0xXX \n" "\t Subclass filter (includes class): \n" "\t S 0xXXXX \n" "\t Comment: \n" "\t # This is a comment \n\n" "\t For example, to trace Mach events (class 1):\n" "\t C 0x01 \n" "\t or to trace Mach system calls (class 1 subclass 13): \n" "\t S 0x010C \n" "\n"); } exit(1); } static int argtoi(int flag, char *req, char *str, int base) { char *cp; int ret; ret = (int)strtol(str, &cp, base); if (cp == str || *cp) errx(EINVAL, "-%c flag requires a %s", flag, req); return (ret); } static unsigned long argtoul(int flag, char *req, char *str, int base) { char *cp; unsigned long ret; ret = (int)strtoul(str, &cp, base); if (cp == str || *cp) errx(EINVAL, "-%c flag requires a %s", flag, req); return (ret); } /* * comparison function for qsort * sort by debugid */ int debugid_compar(const void *p1, const void *p2) { const code_type_t *q1 = (const code_type_t *)p1; const code_type_t *q2 = (const code_type_t *)p2; if (q1->debugid > q2->debugid) return (1); else if (q1->debugid == q2->debugid) return (0); else return (-1); } /* * Filter args parsing state machine: * * Allowed args: * -c -p * -c -s (-s)* * -c (-c)* * every -c goes back to start * * Valid transitions: * start -> class (first -c) * class -> range (-c -p) * class -> sub (-c -s) * class -> class (-c -c) * range -> class (-c -p -c) * sub -> class (-c -s -c) * * -> start (on filter_done_parsing) * * Need to call filter_done_parsing after * calling saw_filter_* * to flush out any class flag waiting to see if * there is a -s flag coming up */ // What type of flag did I last see? enum { FILTER_MODE_START, FILTER_MODE_CLASS, FILTER_MODE_CLASS_RANGE, FILTER_MODE_SUBCLASS } filter_mode = FILTER_MODE_START; uint8_t filter_current_class = 0; uint8_t filter_current_subclass = 0; uint8_t filter_current_class_range = 0; static void saw_filter_class(uint8_t class) { switch(filter_mode) { case FILTER_MODE_START: case FILTER_MODE_CLASS_RANGE: case FILTER_MODE_SUBCLASS: filter_mode = FILTER_MODE_CLASS; filter_current_class = class; filter_current_subclass = 0; filter_current_class_range = 0; // the case of a lone -c is taken care of // by filter_done_parsing break; case FILTER_MODE_CLASS: filter_mode = FILTER_MODE_CLASS; // set old class, remember new one set_filter_class(filter_current_class); filter_current_class = class; filter_current_subclass = 0; filter_current_class_range = 0; break; default: quit_args("invalid case in saw_filter_class\n"); } } static void saw_filter_end_range(uint8_t end_class) { switch(filter_mode) { case FILTER_MODE_CLASS: filter_mode = FILTER_MODE_CLASS_RANGE; filter_current_class_range = end_class; set_filter_range(filter_current_class, filter_current_class_range); break; case FILTER_MODE_START: quit_args("must provide '-c class' before '-p 0x%x'\n", end_class); case FILTER_MODE_CLASS_RANGE: quit_args("extra range end '-p 0x%x'" " for class '-c 0x%x'\n", end_class, filter_current_class); case FILTER_MODE_SUBCLASS: quit_args("cannot provide both range end '-p 0x%x'" " and subclass '-s 0x%x'" " for class '-c 0x%x'\n", end_class, filter_current_subclass, filter_current_class); default: quit_args("invalid case in saw_filter_end_range\n"); } } static void saw_filter_subclass(uint8_t subclass) { switch(filter_mode) { case FILTER_MODE_CLASS: case FILTER_MODE_SUBCLASS: filter_mode = FILTER_MODE_SUBCLASS; filter_current_subclass = subclass; set_filter_subclass(filter_current_class, filter_current_subclass); break; case FILTER_MODE_START: quit_args("must provide '-c class'" " before subclass '-s 0x%x'\n", subclass); case FILTER_MODE_CLASS_RANGE: quit_args("cannot provide both range end '-p 0x%x'" " and subclass '-s 0x%x'" " for the same class '-c 0x%x'\n", filter_current_class_range, subclass, filter_current_class); default: quit_args("invalid case in saw_filter_subclass\n"); } } static void filter_done_parsing(void) { switch(filter_mode) { case FILTER_MODE_CLASS: // flush out the current class set_filter_class(filter_current_class); filter_mode = FILTER_MODE_START; filter_current_class = 0; filter_current_subclass = 0; filter_current_class_range = 0; break; case FILTER_MODE_SUBCLASS: case FILTER_MODE_START: case FILTER_MODE_CLASS_RANGE: filter_mode = FILTER_MODE_START; filter_current_class = 0; filter_current_subclass = 0; filter_current_class_range = 0; break; default: quit_args("invalid case in filter_done_parsing\n"); } } /* Tell set_filter_subclass not to print every. single. subclass. */ static boolean_t setting_class = FALSE; static boolean_t setting_range = FALSE; static void set_filter_subclass(uint8_t class, uint8_t subclass) { if (!filter_alloced) { type_filter_bitmap = (uint8_t *) calloc(1, KDBG_TYPEFILTER_BITMAP_SIZE); if (type_filter_bitmap == NULL) quit_args("Could not allocate type_filter_bitmap.\n"); filter_alloced = 1; } uint16_t csc = ENCODE_CSC_LOW(class, subclass); if (verbose_flag && !setting_class) printf("tracing subclass: 0x%4.4x\n", csc); if (verbose_flag && isset(type_filter_bitmap, csc)) printf("class %u (0x%2.2x), subclass %u (0x%2.2x) set twice.\n", class, class, subclass, subclass); setbit(type_filter_bitmap, csc); } static void set_filter_class(uint8_t class) { if (verbose_flag && !setting_range) printf("tracing class: 0x%2.2x\n", class); setting_class = TRUE; for (int i = 0; i < 256; i++) set_filter_subclass(class, i); setting_class = FALSE; } static void set_filter_range(uint8_t class, uint8_t end) { if (verbose_flag) printf("tracing range: 0x%2.2x - 0x%2.2x\n", class, end); setting_range = TRUE; for (int i = class; i <= end; i++) set_filter_class(i); setting_range = FALSE; } /* * Syntax of filter file: * Hexadecimal numbers only * Class: * C 0xXX * Subclass (includes class): * S 0xXXXX * Comment: * # * TBD: Class ranges? * TBD: K for -k flag? */ static void parse_filter_file(char *filename) { FILE* file; uint32_t current_line = 0; uint32_t parsed_arg = 0; int rval; char line[256]; if ( (file = fopen(filename, "r")) == NULL ) { quit_args("Failed to open filter description file %s: %s\n", filename, strerror(errno)); } if (verbose_flag) printf("Parsing typefilter file: %s\n", filename); while( fgets(line, sizeof(line), file) != NULL ) { current_line++; switch (line[0]) { case 'C': rval = sscanf(line, "C 0x%x\n", &parsed_arg); if (rval != 1) quit_args("invalid line %d of file %s: %s\n", current_line, filename, line); if (parsed_arg > 0xFF) quit_args("line %d of file %s: %s\n" "parsed as 0x%x, " "class value must be 0x0-0xFF\n", current_line, filename, line, parsed_arg); set_filter_class((uint8_t)parsed_arg); break; case 'S': rval = sscanf(line, "S 0x%x\n", &parsed_arg); if (rval != 1) quit_args("invalid line %d of file %s: %s\n", current_line, filename, line); if (parsed_arg > 0xFFFF) quit_args("line %d of file %s: %s\n" "parsed as 0x%x, " "value must be 0x0-0xFFFF\n", current_line, filename, line, parsed_arg); set_filter_subclass(EXTRACT_CLASS_LOW(parsed_arg), EXTRACT_SUBCLASS_LOW(parsed_arg)); break; case '#': // comment break; case '\n': // empty line break; case '\0': // end of file break; default: quit_args("Invalid filter description file: %s\n" "could not parse line %d: %s\n", filename, current_line, line); } } fclose(file); } /* * Find the debugid code in the list and return its index */ static int binary_search(code_type_t *list, int lowbound, int highbound, unsigned int code) { int low, high, mid; int tries = 0; low = lowbound; high = highbound; while (1) { mid = (low + high) / 2; tries++; if (low > high) return (-1); /* failed */ else if ( low + 1 >= high) { /* We have a match */ if (list[high].debugid == code) return(high); else if (list[low].debugid == code) return(low); else return(-1); /* search failed */ } else if (code < list[mid].debugid) high = mid; else low = mid; } } static int parse_codefile(const char *filename) { int fd; int j, line; size_t count; struct stat stat_buf; size_t file_size; char *file_addr, *endp; if ((fd = open(filename, O_RDONLY, 0)) == -1) { printf("Failed to open code description file %s\n",filename); return(-1); } if (fstat(fd, &stat_buf) == -1) { printf("Error: Can't fstat file: %s\n", filename); return(-1); } /* * For some reason mapping files with zero size fails * so it has to be handled specially. */ file_size = (size_t)stat_buf.st_size; if (stat_buf.st_size != 0) { file_addr = mmap(0, file_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0); if (file_addr == MAP_FAILED) { printf("Error: Can't map file: %s\n", filename); close(fd); return(-1); } } else { // Skip empty files close(fd); return(0); } close(fd); /* * If we get here, we have mapped the file * and we are ready to parse it. Count * the newlines to get total number of codes. */ for (count = 0, j=1; j < file_size; j++) { if (file_addr[j] == '\n') count++; } if (count == 0) { printf("Error: No codes in %s\n", filename); return(-1); } /* * Fudge the count to accomodate the last line in the file - * in case it doesn't end in a newline. */ count++; /* Grow the size of codesc to store new entries. */ size_t total_count = codesc_idx + count; code_type_t *new_codesc = (code_type_t *)realloc(codesc, (total_count) * sizeof(code_type_t)); if (new_codesc == NULL) { printf("Failed to grow/allocate buffer. Skipping file %s\n", filename); return (-1); } codesc = new_codesc; bzero((char *)(codesc + codesc_idx), count * sizeof(code_type_t)); for (line = 1, j = 0; j < file_size && codesc_idx < total_count; codesc_idx++) { /* Skip blank lines */ while (j < file_size && file_addr[j] == '\n') { j++; line++; } /* Skip leading whitespace */ while (file_addr[j] == ' ' || file_addr[j] == '\t') j++; /* Get the debugid code */ codesc[codesc_idx].debugid = (uint32_t)strtoul(file_addr + j, &endp, 16); j = (int)(endp - file_addr); if (codesc[codesc_idx].debugid == 0) { /* We didn't find a debugid code - skip this line */ if (verbose_flag) printf("Error: while parsing line %d, skip\n", line); while (j < file_size && file_addr[j] != '\n') j++; codesc_idx--; line++; continue; } /* Skip whitespace */ while (j < file_size && (file_addr[j] == ' ' || file_addr[j] == '\t')) { j++; } if (j >= file_size) { break; } /* Get around old file that had count at the beginning */ if (file_addr[j] == '\n') { /* missing debugid string - skip */ if (verbose_flag) { printf("Error: while parsing line %d, (0x%x) skip\n", line, codesc[codesc_idx].debugid); } j++; codesc_idx--; line++; continue; } if (j >= file_size) { break; } /* Next is the debugid string terminated by a newline */ codesc[codesc_idx].debug_string = &file_addr[j]; /* Null out the newline terminator */ while (j < file_size && file_addr[j] != '\n') { j++; } if (j >= file_size) { break; } file_addr[j] = '\0'; /* File must be read-write */ j++; line++; codenum++; /*Index into codesc is 0 to codenum-1 */ } if (verbose_flag) { printf("Parsed %d codes in %s\n", codenum, filename); printf("[%6d] 0x%8x %s\n", 0, codesc[0].debugid, codesc[0].debug_string); printf("[%6d] 0x%8x %s\n\n", codenum-1, codesc[codenum-1].debugid, codesc[codenum-1].debug_string); } /* sort */ qsort((void *)codesc, codesc_idx, sizeof(code_type_t), debugid_compar); if (verbose_flag) { printf("Sorted %zd codes in %s\n", codesc_idx, filename); printf("lowbound [%6d]: 0x%8x %s\n", 0, codesc[0].debugid, codesc[0].debug_string); printf("highbound [%6zd]: 0x%8x %s\n\n", codesc_idx - 1, codesc[codesc_idx - 1].debugid, codesc[codesc_idx - 1].debug_string); } codesc_find_dupes(); #if 0 /* Dump the codefile */ int i; for (i = 0; i < codesc_idx; i++) printf("[%d] 0x%x %s\n",i+1, codesc[i].debugid, codesc[i].debug_string); #endif return(0); } static void codesc_find_dupes(void) { boolean_t found_dupes = FALSE; if (codesc_idx == 0) { return; } uint32_t last_debugid = codesc[0].debugid; for(int i = 1; i < codesc_idx; i++) { if(codesc[i].debugid == last_debugid) { found_dupes = TRUE; if (verbose_flag) { fprintf(stderr, "WARNING: The debugid 0x%"PRIx32" (%s) has already been defined as '%s'.\n", codesc[i].debugid, codesc[i].debug_string, codesc[i - 1].debug_string); } } last_debugid = codesc[i].debugid; } if (found_dupes) { fprintf(stderr, "WARNING: One or more duplicate entries found in your codefiles, which will lead to unpredictable decoding behavior. Re-run with -v for more info\n"); } } int match_debugid(unsigned int xx, char * debugstr, int * yy) { int indx; if (codenum == 0) return(-1); if (codesc[codeindx_cache].debugid != xx) indx = binary_search(codesc, 0, (codenum-1), xx); else indx = codeindx_cache; if (indx == -1) return(indx); /* match failed */ else { bcopy(&codesc[indx].debug_string[0], debugstr,80); *yy = indx; codeindx_cache = indx; return(0); /* match success */ } } void read_cpu_map(int fd) { if (cpumap_header) { free(cpumap_header); cpumap_header = NULL; cpumap = NULL; } /* * To fit in the padding space of a VERSION1 file, the max possible * cpumap size is one page. */ cpumap_header = malloc(PAGE_SIZE); if (readRAW_flag) { /* * cpu maps exist in a RAW_VERSION1+ header only */ if (raw_header.version_no == RAW_VERSION1) { off_t cpumap_position = lseek(fd, 0, SEEK_CUR); /* cpumap is part of the last 4KB of padding in the preamble */ size_t padding_bytes = SIZE_4KB - (cpumap_position & (SIZE_4KB - 1)); if (read(fd, cpumap_header, padding_bytes) == padding_bytes) { if (cpumap_header->version_no == RAW_VERSION1) { cpumap = (kd_cpumap*)&cpumap_header[1]; } } } } else { int mib[3]; mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDCPUMAP; size_t temp = PAGE_SIZE; if (sysctl(mib, 3, cpumap_header, &temp, NULL, 0) == 0) { if (PAGE_SIZE >= temp) { if (cpumap_header->version_no == RAW_VERSION1) { cpumap = (kd_cpumap*)&cpumap_header[1]; } } } } if (!cpumap) { printf("Can't read the cpu map -- this is not fatal\n"); free(cpumap_header); cpumap_header = NULL; } else if (verbose_flag) { /* Dump the initial cpumap */ printf("\nCPU\tName\n"); for (int i = 0; i < cpumap_header->cpu_count; i++) { printf ("%2d\t%s\n", cpumap[i].cpu_id, cpumap[i].name); } printf("\n"); } } int read_command_map(int fd, uint32_t count) { int i; size_t size; int mib[6]; if (readRAW_flag) { total_threads = count; size = count * sizeof(kd_threadmap); } else { get_bufinfo(&bufinfo); total_threads = bufinfo.nkdthreads; size = bufinfo.nkdthreads * sizeof(kd_threadmap); } mapptr = 0; nthreads = total_threads * 2; if (verbose_flag) printf("Size of map table is %d, thus %d entries\n", (int)size, total_threads); if (size) { if ((mapptr = (kd_threadmap *) malloc(size))) bzero (mapptr, size); else { if (verbose_flag) printf("Thread map is not initialized -- this is not fatal\n"); return(0); } } if (readRAW_flag) { if (read(fd, mapptr, size) != size) { if (verbose_flag) printf("Can't read the thread map -- this is not fatal\n"); free(mapptr); mapptr = 0; return (int)size; } } else { /* Now read the threadmap */ mib[0] = CTL_KERN; mib[1] = KERN_KDEBUG; mib[2] = KERN_KDTHRMAP; mib[3] = 0; mib[4] = 0; mib[5] = 0; /* no flags */ if (sysctl(mib, 3, mapptr, &size, NULL, 0) < 0) { /* This is not fatal -- just means I cant map command strings */ if (verbose_flag) printf("Can't read the thread map -- this is not fatal\n"); free(mapptr); mapptr = 0; return(0); } } for (i = 0; i < total_threads; i++) { if (mapptr[i].thread) create_map_entry(mapptr[i].thread, &mapptr[i].command[0]); } if (verbose_flag) { /* Dump the initial map */ printf("Size of maptable returned is %ld, thus %ld entries\n", size, (size/sizeof(kd_threadmap))); printf("Thread Command\n"); for (i = 0; i < total_threads; i++) { printf ("0x%" PRIx64 " %s\n", (uint64_t)mapptr[i].thread, mapptr[i].command); } } return (int)size; } void create_map_entry(uint64_t thread, char *command) { threadmap_t tme; int hashid; if ((tme = threadmap_freelist)) threadmap_freelist = tme->tm_next; else tme = (threadmap_t)malloc(sizeof(struct threadmap)); tme->tm_thread = thread; tme->tm_deleteme = FALSE; (void)strncpy (tme->tm_command, command, MAXCOMLEN); tme->tm_command[MAXCOMLEN] = '\0'; hashid = thread & HASH_MASK; tme->tm_next = threadmap_hash[hashid]; threadmap_hash[hashid] = tme; } void delete_thread_entry(uint64_t thread) { threadmap_t tme = 0; threadmap_t tme_prev; int hashid; hashid = thread & HASH_MASK; if ((tme = threadmap_hash[hashid])) { if (tme->tm_thread == thread) threadmap_hash[hashid] = tme->tm_next; else { tme_prev = tme; for (tme = tme->tm_next; tme; tme = tme->tm_next) { if (tme->tm_thread == thread) { tme_prev->tm_next = tme->tm_next; break; } tme_prev = tme; } } if (tme) { tme->tm_next = threadmap_freelist; threadmap_freelist = tme; } } } void find_and_insert_tmp_map_entry(uint64_t pthread, char *command) { threadmap_t tme = 0; threadmap_t tme_prev; int hashid; if ((tme = threadmap_temp)) { if (tme->tm_pthread == pthread) threadmap_temp = tme->tm_next; else { tme_prev = tme; for (tme = tme->tm_next; tme; tme = tme->tm_next) { if (tme->tm_pthread == pthread) { tme_prev->tm_next = tme->tm_next; break; } tme_prev = tme; } } if (tme) { (void)strncpy (tme->tm_command, command, MAXCOMLEN); tme->tm_command[MAXCOMLEN] = '\0'; delete_thread_entry(tme->tm_thread); hashid = tme->tm_thread & HASH_MASK; tme->tm_next = threadmap_hash[hashid]; threadmap_hash[hashid] = tme; } } } void create_tmp_map_entry(uint64_t thread, uint64_t pthread) { threadmap_t tme; if ((tme = threadmap_freelist)) threadmap_freelist = tme->tm_next; else tme = (threadmap_t)malloc(sizeof(struct threadmap)); tme->tm_thread = thread; tme->tm_pthread = pthread; tme->tm_deleteme = FALSE; tme->tm_command[0] = '\0'; tme->tm_next = threadmap_temp; threadmap_temp = tme; } threadmap_t find_thread_entry(uint64_t thread) { threadmap_t tme; int hashid; hashid = thread & HASH_MASK; for (tme = threadmap_hash[hashid]; tme; tme = tme->tm_next) { if (tme->tm_thread == thread) return (tme); } return (0); } void find_thread_name(uint64_t thread, char **command, boolean_t deleteme) { threadmap_t tme; if ((tme = find_thread_entry(thread))) { *command = tme->tm_command; if (deleteme == TRUE) tme->tm_deleteme = deleteme; } else *command = EMPTYSTRING; } void find_thread_command(kd_buf *kbufp, char **command) { uint64_t thread; threadmap_t tme; int debugid_base; *command = EMPTYSTRING; thread = kbufp->arg5; debugid_base = kbufp->debugid & DBG_FUNC_MASK; if (debugid_base == BSC_exit || debugid_base == MACH_STKHANDOFF) { /* * Mark entry as invalid and return temp command pointer */ if ((tme = find_thread_entry(thread))) { strncpy(tmpcommand, tme->tm_command, MAXCOMLEN); *command = tmpcommand; if (debugid_base == BSC_exit || tme->tm_deleteme == TRUE) delete_thread_entry(thread); } } else if (debugid_base == TRACE_DATA_NEWTHREAD) { /* * Save the create thread data */ create_tmp_map_entry(kbufp->arg1, kbufp->arg5); } else if (debugid_base == TRACE_STRING_NEWTHREAD) { /* * process new map entry */ find_and_insert_tmp_map_entry(kbufp->arg5, (char *)&kbufp->arg1); } else if (debugid_base == TRACE_STRING_EXEC) { delete_thread_entry(kbufp->arg5); create_map_entry(kbufp->arg5, (char *)&kbufp->arg1); } else find_thread_name(thread, command, (debugid_base == BSC_thread_terminate)); } static void getdivisor(void) { (void) mach_timebase_info (&mach_timebase); if (frequency == 0) { divisor = ( (double)mach_timebase.denom / (double)mach_timebase.numer) * 1000; } else divisor = (double)frequency / 1000000; if (verbose_flag) printf("divisor = %g\n", divisor); }