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 <sys/param.h>
#include <sys/types.h>
#include <err.h>
#include <stdarg.h>
#include <inttypes.h>
+#include <spawn.h>
+#include <assert.h>
+#include <signal.h>
+#include <sysexits.h>
#include <libutil.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
-
int nbufs = 0;
int enable_flag=0;
int execute_flag=0;
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 CSC_MASK 0xffff0000
-#define VFS_LOOKUP 0x03010090
#define BSC_exit 0x040c0004
#define BSC_thread_terminate 0x040c05a4
-#define TRACE_DATA_NEWTHREAD 0x07000004
-#define TRACE_STRING_NEWTHREAD 0x07010004
-#define TRACE_STRING_EXEC 0x07010008
-#define TRACE_LOST_EVENTS 0x07020008
-#define TRACE_INFO_STRING 0x07020010
#define MACH_SCHEDULED 0x01400000
#define MACH_MAKERUNNABLE 0x01400018
#define MACH_STKHANDOFF 0x01400008
kd_cpumap_header* cpumap_header = NULL;
kd_cpumap* cpumap = NULL;
-/*
- If NUMPARMS changes from the kernel,
+/*
+ If NUMPARMS changes from the kernel,
then PATHLENGTH will also reflect the change
This is for the vfslookup entries that
return pathnames
#define US_TO_SLEEP 50000
#define BASE_EVENTS 500000
-
+mach_timebase_info_data_t mach_timebase;
double divisor;
typedef struct {
struct event {
event_t ev_next;
- uintptr_t ev_thread;
+ uint64_t ev_thread;
uint32_t ev_debugid;
uint64_t ev_timestamp;
};
struct lookup {
lookup_t lk_next;
-
- uintptr_t lk_thread;
- uintptr_t lk_dvp;
- long *lk_pathptr;
- long lk_pathname[NUMPARMS + 1];
+
+ 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;
-
- uintptr_t tm_thread;
- uintptr_t tm_pthread;
+
+ 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
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(uintptr_t, char *);
+static void create_map_entry(uint64_t, char *);
static void getdivisor();
static unsigned long argtoul();
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 read_trace();
static void signal_handler(int);
static void signal_handler_RAW(int);
-static void delete_thread_entry(uintptr_t);
-static void find_and_insert_tmp_map_entry(uintptr_t, char *);
-static void create_tmp_map_entry(uintptr_t, uintptr_t);
-static void find_thread_name(uintptr_t, char **, boolean_t);
+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(code_type_t *, code_type_t *);
+static int debugid_compar(const void *, const void *);
-static threadmap_t find_thread_entry(uintptr_t);
+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);
#endif
mib[4] = 0;
mib[5] = 0;
- if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 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 set_remove(void)
{
extern int errno;
-
+
errno = 0;
-
+
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
mib[2] = KERN_KDREMOVE;
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;
quit_args("trace facility failure, KERN_KDSETUP: %s\n", strerror(errno));
}
-void set_nowrap()
+void set_nowrap(void)
{
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
void set_pidcheck(int pid, int on_off_flag)
{
kd_regtype kr;
-
+
kr.type = KDBG_TYPENONE;
kr.value1 = pid;
kr.value2 = on_off_flag;
mib[5] = 0;
if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0)
{
- if (on_off_flag == 1)
+ if (errno == EACCES)
+ {
+ quit_args("trace facility failure, setting pid filter: %s\n",
+ strerror(errno));
+ }
+ else if (on_off_flag == 1 && errno == ESRCH)
{
- printf("trace facility failure, KERN_KDPIDTR,\n\tpid %d does not exist\n", pid);
set_remove();
- exit(2);
+ 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;
}
}
-void set_freerun()
+void set_freerun(void)
{
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
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);
quit_args("trace facility failure, KERN_KDGETBUF: %s\n", strerror(errno));
}
-void set_init()
+void set_init(void)
{
kd_regtype kr;
-
+
kr.type = KDBG_RANGETYPE;
kr.value1 = 0;
kr.value2 = -1;
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;
quit_args("trace facility failure, KERN_KDSETUP: %s\n", strerror(errno));
}
-
static void
set_filter(void)
{
}
}
-void set_kval_list()
+void set_kval_list(void)
{
kd_regtype kr;
-
+
kr.type = KDBG_VALCHECK;
kr.value1 = value1;
kr.value2 = value2;
{
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
- mib[2] = KERN_KDREADTR;
+ mib[2] = KERN_KDREADTR;
mib[3] = 0;
mib[4] = 0;
mib[5] = 0;
mib[4] = 0;
mib[5] = 0;
- if (sysctl(mib, 4, NULL, &needed, NULL, 0) < 0)
- return 1;
+ 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(uintptr_t thread, int debugid, kd_buf *kdp)
+lookup_t handle_lookup_event(uint64_t thread, int debugid, kd_buf *kdp)
{
lookup_t lkp;
int hashid;
static
-void delete_lookup_event(uintptr_t thread, lookup_t lkp_to_delete)
+void delete_lookup_event(uint64_t thread, lookup_t lkp_to_delete)
{
lookup_t lkp;
lookup_t lkp_prev;
static
-void insert_start_event(uintptr_t thread, int debugid, uint64_t now)
+void insert_start_event(uint64_t thread, int debugid, uint64_t now)
{
event_t evp;
int hashid;
static
-uint64_t consume_start_event(uintptr_t thread, int debugid, uint64_t now)
+uint64_t consume_start_event(uint64_t thread, int debugid, uint64_t now)
{
event_t evp;
event_t evp_prev;
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;
}
if (evp) {
elapsed = now - evp->ev_timestamp;
-
+
evp->ev_next = event_freelist;
event_freelist = evp;
}
return (elapsed);
}
-
void
-log_trace()
+log_trace(void)
{
- char *buffer;
- uint32_t buffer_size;
- int fd;
- int size;
- int pad_size;
- char pad_buf[4096];
+ 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 = open(logfile, O_TRUNC|O_WRONLY|O_CREAT, 0777)) == -1) {
+ if (fd == -1) {
perror("Can't open logfile");
exit(1);
}
else
printf("Buffer has not wrapped\n");
}
- buffer_size = 1000000 * sizeof(kd_buf);
- buffer = malloc(buffer_size);
-
- if (buffer == (char *) 0)
- quit("can't allocate memory for tracing info\n");
-
- read_command_map(0, 0);
- read_cpu_map(0);
-
- raw_header.version_no = RAW_VERSION1;
- raw_header.thread_count = total_threads;
- raw_header.TOD_secs = time((long *)0);
- raw_header.TOD_usecs = 0;
-
- write(fd, &raw_header, sizeof(RAW_header));
-
- size = total_threads * sizeof(kd_threadmap);
- write(fd, (char *)mapptr, size);
- pad_size = 4096 - ((sizeof(RAW_header) + size) & 4095);
-
- if (cpumap_header) {
- size_t cpumap_size = sizeof(kd_cpumap_header) + cpumap_header->cpu_count * sizeof(kd_cpumap);
- if (pad_size >= cpumap_size) {
- write(fd, (char *)cpumap_header, cpumap_size);
- pad_size -= cpumap_size;
- }
+ ret = write_command_map(fd);
+ if (ret) {
+ close(fd);
+ perror("failed to write logfile");
+ exit(1);
}
- memset(pad_buf, 0, pad_size);
- write(fd, pad_buf, pad_size);
+ buffer = malloc(buffer_size);
+ if (buffer == NULL) {
+ quit("can't allocate memory for events\n");
+ }
for (;;) {
needed = buffer_size;
readtrace(buffer);
- if (needed == 0)
+ 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()
+Log_trace(void)
{
- int size;
- kd_buf kd_tmp;
size_t len;
- int num_cpus;
- int try_writetrace = 1;
+ int num_cpus = 0;
int fd;
- char *buffer;
- kd_buf *kd;
- uint64_t sample_window_abs;
- uint64_t next_window_begins;
- uint64_t current_abs;
- uint64_t ending_abstime;
+ uint64_t current_ms;
+ uint64_t ending_ms = 0;
uint64_t last_time_written;
- uint32_t us_to_sleep;
- uint32_t us_to_adjust;
uint32_t ms_to_run;
- memset(&kd_tmp, 0, sizeof(kd_tmp));
-
if ((fd = open(logfile, O_TRUNC|O_WRONLY|O_CREAT, 0777)) == -1) {
perror("Can't open logfile");
exit(1);
if (kval_flag)
set_kval_list();
}
- /* Get kernel buffer information */
- get_bufinfo(&bufinfo);
-
- buffer = malloc(bufinfo.nkdbufs * sizeof(kd_buf));
- if (buffer == (char *) 0)
- quit("can't allocate memory for tracing info\n");
- memset(buffer, 0, bufinfo.nkdbufs * sizeof(kd_buf));
if (use_current_buf == 0)
set_enable(1);
if (write_command_map(fd)) {
- int pad_size;
- char pad_buf[4096];
-
- read_command_map(0, 0);
- read_cpu_map(0);
-
- raw_header.version_no = RAW_VERSION1;
- raw_header.thread_count = total_threads;
- raw_header.TOD_secs = time((long *)0);
- raw_header.TOD_usecs = 0;
-
- write(fd, &raw_header, sizeof(RAW_header));
-
- size = total_threads * sizeof(kd_threadmap);
- write(fd, (char *)mapptr, size);
-
- pad_size = 4096 - ((sizeof(RAW_header) + size) & 4095);
-
- if (cpumap_header) {
- size_t cpumap_size = sizeof(kd_cpumap_header) + cpumap_header->cpu_count * sizeof(kd_cpumap);
- if (pad_size >= cpumap_size) {
- write(fd, (char *)cpumap_header, cpumap_size);
- pad_size -= cpumap_size;
- }
- }
-
- memset(pad_buf, 0, pad_size);
- write(fd, pad_buf, pad_size);
+ quit("can't write tracefile header\n");
}
- sample_window_abs = (uint64_t)((double)US_TO_SLEEP * divisor);
- next_window_begins = mach_absolute_time() + sample_window_abs;
+ last_time_written = current_millis();
if (secs_to_run) {
- ending_abstime = mach_absolute_time() + (uint64_t)((double)secs_to_run * (double)1000000 * divisor);
ms_to_run = secs_to_run * 1000;
+ ending_ms = last_time_written + ms_to_run;
} else
ms_to_run = 0;
- last_time_written = mach_absolute_time();
while (LogRAW_flag) {
- current_abs = mach_absolute_time();
-
- if (try_writetrace) {
- needed = ms_to_run;
-
- if (writetrace(fd))
- try_writetrace = 0;
- else {
- if (needed) {
- current_abs = mach_absolute_time();
+ needed = ms_to_run;
- printf("wrote %d events - elapsed time = %.1f secs\n",
- (int)needed, ((double)(current_abs - last_time_written) / divisor) / 1000000);
+ if (writetrace(fd)) {
+ perror("KDWRITETR returned error");
- last_time_written = current_abs;
- }
- }
+ /* Clean up and exit in case of write fail */
+ break;
}
- if (try_writetrace == 0) {
-
- if (next_window_begins > current_abs)
- us_to_adjust = US_TO_SLEEP - (uint32_t)((double)(next_window_begins - current_abs) / divisor);
- else
- us_to_adjust = US_TO_SLEEP;
-
- next_window_begins = current_abs + sample_window_abs;
-
- us_to_sleep = US_TO_SLEEP - us_to_adjust;
- next_window_begins = current_abs + (uint64_t)((double)(us_to_sleep + US_TO_SLEEP) * divisor);
+ if (needed) {
+ current_ms = current_millis();
- if (us_to_sleep)
- usleep(us_to_sleep);
+ printf("wrote %d events - elapsed time = %.1f secs\n",
+ (int)needed, (double)(current_ms - last_time_written) / 1000.0);
- get_bufinfo(&bufinfo);
-
- if (bufinfo.flags & KDBG_WRAPPED)
- printf("lost events\n");
-
- needed = bufinfo.nkdbufs * sizeof(kd_buf);
-
- readtrace(buffer);
-
- if (bufinfo.flags & KDBG_WRAPPED) {
-
- kd = (kd_buf *) buffer;
-
- kd_tmp.timestamp = kd[0].timestamp;
- kd_tmp.debugid = TRACE_LOST_EVENTS;
-
- write(fd, &kd_tmp, sizeof(kd_tmp));
- }
- write(fd, buffer, needed * sizeof(kd_buf));
-
- if (verbose_flag && needed > nbufs)
- printf("needed = %ld\n", needed);
+ last_time_written = current_ms;
}
+
if (secs_to_run) {
- current_abs = mach_absolute_time();
+ current_ms = current_millis();
- if (current_abs > ending_abstime)
+ if (current_ms > ending_ms)
break;
- ms_to_run = (ending_abstime - current_abs) / (1000 * 1000);
-
+
+ ms_to_run = (uint32_t)(ending_ms - current_ms);
+
if (ms_to_run == 0)
break;
}
}
-void read_trace()
+void read_trace(void)
{
char *buffer;
uint32_t buffer_size;
kd_buf *kd;
int fd;
- int firsttime = 1;
+ int firsttime = 1;
int lines = 0;
int io_lines = 0;
uint64_t bias = 0;
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);
if (buffer == (char *) 0)
quit("can't allocate memory for tracing info\n");
- kd = (kd_buf *)buffer;
+ kd = (kd_buf *)(uintptr_t)buffer;
read_command_map(fd, count_of_names);
read_cpu_map(fd);
uint32_t count;
uint64_t now = 0;
uint64_t prev;
- uint64_t prevdelta;
- uint32_t cpunum;
- uintptr_t thread;
+ uint64_t prevdelta = 0;
+ uint32_t cpunum = 0;
+ uint64_t thread;
double x = 0.0;
double y = 0.0;
- double event_elapsed_time;
+ double event_elapsed_time = 0;
kd_buf *kdp;
lookup_t lkp;
boolean_t ending_event;
mib[0] = CTL_KERN;
mib[1] = KERN_KDEBUG;
- mib[2] = KERN_KDREADTR;
+ mib[2] = KERN_KDREADTR;
mib[3] = 0;
mib[4] = 0;
mib[5] = 0;
if (needed == 0)
break;
- count = needed;
+ count = (uint32_t)needed;
} else {
uint32_t bytes_read;
- bytes_read = read(fd, buffer, buffer_size);
+ bytes_read = (uint32_t)read(fd, buffer, buffer_size);
if (bytes_read == -1) {
perror("read failed");
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;
- cpunum = kdbg_get_cpu(kdp);
thread = kdp->arg5;
if (lines == 64 || firsttime)
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,
" 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);
if ( !lkp || !(debugid & DBG_FUNC_END))
continue;
}
+
x = (double)now;
x /= divisor;
last_event_time = x;
ending_event = FALSE;
- /*
- * 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 ( !lkp) {
int t_debugid;
- int t_thread;
+ uint64_t t_thread;
if ((debugid & DBG_FUNC_START) || debugid == MACH_MAKERUNNABLE) {
}
} 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;
/*
* print the tail end of the pathname
*/
- len = strlen(strptr);
+ len = (int)strlen(strptr);
if (len > 51)
len -= 51;
else
len = 0;
-#ifdef __LP64__
+#if defined(__LP64__) || defined(__arm64__)
- fprintf(output_file, "%-16lx %-51s %-16lx %-2d %s\n", lkp->lk_dvp, &strptr[len], thread, cpunum, command);
+ 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 %-8lx %-2d %s\n", (unsigned int)lkp->lk_dvp, &strptr[len], thread, cpunum, command);
+ 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) {
-#ifdef __LP64__
- fprintf(output_file, "%-32s%-36s %-16lx %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
+#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 %-8lx %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
+ fprintf(output_file, "%-16s%-46s %-8" PRIx64 " %-2d %s\n", (char *) &kdp->arg1, "", thread, cpunum, command);
#endif
} else {
-#ifdef __LP64__
- fprintf(output_file, "%-16lx %-16lx %-16lx %-16lx %-16lx %-2d %s\n", kdp->arg1, kdp->arg2, kdp->arg3, kdp->arg4, thread, cpunum, command);
+#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, "%-8lx %-8lx %-8lx %-8lx %-8lx %-2d %s\n", kdp->arg1, kdp->arg2, kdp->arg3, kdp->arg4, thread, cpunum, command);
+ 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++;
-void signal_handler(int sig)
+void signal_handler(int sig)
{
ptrace(PT_KILL, pid, (caddr_t)0, 0);
/*
}
-
-int main(argc, argv, env)
-int argc;
-char **argv;
-char **env;
+int main (int argc, char* argv[], char *envp[])
{
extern char *optarg;
extern int optind;
- int status;
int ch;
int i;
char *output_filename = NULL;
- char *filter_filename = NULL;
unsigned int parsed_arg;
for (i = 1; i < argc; i++) {
fprintf(stderr, "Could not re-execute: %d\n", errno);
exit(1);
}
- } else {
+ }
+#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);
case 'p':
filter_flag = 1;
parsed_arg = argtoi('p', "decimal, hex, or octal number", optarg, 0);
- if (parsed_arg > 0xFF)
+ 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);
if (LogRAW_flag) {
get_bufinfo(&bufinfo);
+ int ktrace_state = get_ktrace_state();
- if (bufinfo.nolog == 0)
+ /*
+ * 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(1);
+ exit(0);
}
else if (pid_exflag)
{
set_pidexclude(pid, 0); /* disable pid exclusion for given pid */
- exit(1);
+ exit(0);
}
set_enable(0);
- exit(1);
+ exit(0);
}
if (remove_flag)
{
set_remove();
- exit(1);
+ exit(0);
}
-
+
if (bufset_flag )
{
if (!init_flag && !LogRAW_flag)
}
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' ? "<unknown>" : execname);
+
+ /* get kdebug info */
+
get_bufinfo(&bufinfo);
printf("The kernel buffer settings are:\n");
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("\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)
fflush(stdout);
fflush(stderr);
- switch ((pid = vfork()))
- {
- case -1:
- perror("vfork: ");
- exit(1);
- case 0: /* child */
- setsid();
- ptrace(PT_TRACE_ME, 0, (caddr_t)0, 0);
- execve(argv[optind], &argv[optind], environ);
- perror("execve:");
- exit(1);
- }
- sleep(1);
+ execute_process(&(argv[optind]));
- signal(SIGINT, signal_handler);
- set_pidcheck(pid, 1);
- set_enable(1);
- ptrace(PT_CONTINUE, pid, (caddr_t)1, 0);
- waitpid(pid, &status, 0);
- /* child is gone; no need to disable the pid */
exit(0);
}
else if (enable_flag)
usage(SHORT_HELP);
}
setbuffer(output_file, &sbuffer[0], SBUFFER_SIZE);
-
+
if (fcntl(output_fd, F_NOCACHE, 1) < 0)
{
/* Not fatal */
} /* end main */
static void
-quit_args(const char *fmt, ...)
+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];
printf("trace: ");
if (s)
- printf("%s ", s);
+ printf("%s", s);
exit(1);
}
(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, "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 -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,
(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 -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");
static int
-argtoi(flag, req, str, base)
-int flag;
-char *req, *str;
-int base;
+argtoi(int flag, char *req, char *str, int base)
{
char *cp;
int ret;
return (ret);
}
-
static unsigned long
-argtoul(flag, req, str, base)
-int flag;
-char *req, *str;
-int base;
+argtoul(int flag, char *req, char *str, int base)
{
char *cp;
unsigned long ret;
return (ret);
}
-
/*
* comparison function for qsort
* sort by debugid
*/
-int debugid_compar(p1, p2)
- code_type_t *p1;
- code_type_t *p2;
+int
+debugid_compar(const void *p1, const void *p2)
{
- if (p1->debugid > p2->debugid)
- return(1);
- else if (p1->debugid == p2->debugid)
- return(0);
+ 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);
+ return (-1);
}
-
/*
* Filter args parsing state machine:
*
uint16_t csc = ENCODE_CSC_LOW(class, subclass);
- if (verbose_flag && !setting_class)
+ if (verbose_flag && !setting_class)
printf("tracing subclass: 0x%4.4x\n", csc);
if (verbose_flag && isset(type_filter_bitmap, csc))
*/
static void
-parse_filter_file(char *filename) {
+parse_filter_file(char *filename)
+{
FILE* file;
uint32_t current_line = 0;
uint32_t parsed_arg = 0;
while( fgets(line, sizeof(line), file) != NULL ) {
current_line++;
-
+
switch (line[0]) {
case 'C':
rval = sscanf(line, "C 0x%x\n", &parsed_arg);
fclose(file);
}
-
/*
* Find the debugid code in the list and return its index
*/
-static int binary_search(list, lowbound, highbound, code)
- code_type_t *list;
- int lowbound, highbound;
- unsigned int code;
+static int
+binary_search(code_type_t *list, int lowbound, int highbound, unsigned int code)
{
int low, high, mid;
int tries = 0;
parse_codefile(const char *filename)
{
int fd;
- int i, j, line;
+ int j, line;
size_t count;
struct stat stat_buf;
- unsigned long file_size;
+ size_t file_size;
char *file_addr, *endp;
if ((fd = open(filename, O_RDONLY, 0)) == -1)
* For some reason mapping files with zero size fails
* so it has to be handled specially.
*/
- file_size = stat_buf.st_size;
+ file_size = (size_t)stat_buf.st_size;
if (stat_buf.st_size != 0)
{
- if ((file_addr = mmap(0, stat_buf.st_size, PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_FILE, fd, 0)) == (char*) -1)
+ 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);
*/
count++;
- // Grow the size of codesc to store new entries.
+ /* 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));
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++)
+ for (line = 1, j = 0; j < file_size && codesc_idx < total_count;
+ codesc_idx++)
{
/* Skip blank lines */
- while (file_addr[j] == '\n')
+ 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 = strtoul(file_addr + j, &endp, 16);
- j = endp - file_addr;
+ codesc[codesc_idx].debugid = (uint32_t)strtoul(file_addr + j, &endp, 16);
+ j = (int)(endp - file_addr);
- if (codesc[codesc_idx].debugid == 0)
+ 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 (file_addr[j] != '\n' && j < file_size)
+ while (j < file_size && file_addr[j] != '\n')
j++;
- codesc_idx--;
+ codesc_idx--;
line++;
continue;
}
/* Skip whitespace */
- while (file_addr[j] == ' ' || file_addr[j] == '\t')
+ 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);
+ {
+ printf("Error: while parsing line %d, (0x%x) skip\n", line,
+ codesc[codesc_idx].debugid);
+ }
j++;
- codesc_idx--;
+ 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];
+ codesc[codesc_idx].debug_string = &file_addr[j];
/* Null out the newline terminator */
- while ((j < file_size) && (file_addr[j] != '\n'))
+ 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++;
}
/* sort */
- qsort((void *)codesc, codesc_idx, sizeof(code_type_t), debugid_compar);
+ 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("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);
+ 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)
+static void
+codesc_find_dupes(void)
{
boolean_t found_dupes = FALSE;
if (codesc_idx == 0)
}
}
-
-int match_debugid(unsigned int xx, char * debugstr, int * yy)
+int
+match_debugid(unsigned int xx, char * debugstr, int * yy)
{
int indx;
* 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 base_offset = lseek(fd, (off_t)0, SEEK_CUR);
- off_t aligned_offset = (base_offset + (4095)) & ~4095; /* <rdar://problem/13500105> */
-
- size_t padding_bytes = (size_t)(aligned_offset - base_offset);
-
+ 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 (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%lx %s\n",
- mapptr[i].thread,
+ printf ("0x%" PRIx64 " %s\n",
+ (uint64_t)mapptr[i].thread,
mapptr[i].command);
}
}
+
return (int)size;
}
-
-void create_map_entry(uintptr_t thread, char *command)
+void
+create_map_entry(uint64_t thread, char *command)
{
threadmap_t tme;
int hashid;
threadmap_hash[hashid] = tme;
}
-
-void delete_thread_entry(uintptr_t thread)
+void
+delete_thread_entry(uint64_t thread)
{
threadmap_t tme = 0;
threadmap_t tme_prev;
}
}
-
-void find_and_insert_tmp_map_entry(uintptr_t pthread, char *command)
+void
+find_and_insert_tmp_map_entry(uint64_t pthread, char *command)
{
threadmap_t tme = 0;
threadmap_t tme_prev;
}
}
-
-void create_tmp_map_entry(uintptr_t thread, uintptr_t pthread)
+void
+create_tmp_map_entry(uint64_t thread, uint64_t pthread)
{
threadmap_t tme;
threadmap_t
-find_thread_entry(uintptr_t thread)
+find_thread_entry(uint64_t thread)
{
threadmap_t tme;
int hashid;
return (0);
}
-
-void find_thread_name(uintptr_t thread, char **command, boolean_t deleteme)
+void
+find_thread_name(uint64_t thread, char **command, boolean_t deleteme)
{
threadmap_t tme;
*command = EMPTYSTRING;
}
-
-void find_thread_command(kd_buf *kbufp, char **command)
+void
+find_thread_command(kd_buf *kbufp, char **command)
{
- uintptr_t thread;
+ uint64_t thread;
threadmap_t tme;
int debugid_base;
find_thread_name(thread, command, (debugid_base == BSC_thread_terminate));
}
-
-static
-void getdivisor()
+static void
+getdivisor(void)
{
- mach_timebase_info_data_t info;
+ (void) mach_timebase_info (&mach_timebase);
if (frequency == 0) {
- (void) mach_timebase_info (&info);
-
- divisor = ( (double)info.denom / (double)info.numer) * 1000;
+ divisor = ( (double)mach_timebase.denom / (double)mach_timebase.numer) * 1000;
} else
divisor = (double)frequency / 1000000;