/*
- cc -I. -DKERNEL_PRIVATE -O -o latency latency.c
+ cc -I. -DKERNEL_PRIVATE -O -o latency latency.c -lncurses
*/
#include <mach/mach.h>
#include <libc.h>
#include <termios.h>
-#include <bsd/curses.h>
+#include <curses.h>
#include <sys/ioctl.h>
#ifndef KERNEL_PRIVATE
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mach_syscalls.h>
-#include <mach/clock.h>
#include <mach/clock_types.h>
+#include <mach/mach_time.h>
#include <libkern/OSTypes.h>
double divisor;
int gotSIGWINCH = 0;
int trace_enabled = 0;
+struct host_basic_info hi;
+
#define SAMPLE_SIZE 300000
char name[32];
} codes_tab[MAX_ENTRIES];
+/* If NUMPARMS changes from the kernel, then PATHLENGTH will also reflect the change */
+#define NUMPARMS 23
+#define PATHLENGTH (NUMPARMS*sizeof(long))
struct th_info {
int thread;
int type;
int child_thread;
- int vfslookup;
int arg1;
double stime;
- char pathname[32];
+ long *pathptr;
+ char pathname[PATHLENGTH + 1];
};
#define MAX_THREADS 512
int decrementer_val = 0; /* Value used to reset decrementer */
int set_remove_flag = 1; /* By default, remove trace buffer */
-/* raw read of the timebase register */
-void clock_get_uptime( register AbsoluteTime *result)
-{
-#ifdef __ppc__
-
- register UInt32 hic;
- do {
- asm volatile(" mftbu %0" : "=r" (result->hi));
- asm volatile(" mftb %0" : "=r" (result->lo));
- asm volatile(" mftbu %0" : "=r" (hic));
- } while (hic != result->hi);
-
-#else
- result->lo = 0;
- result->hi = 0;
-#endif /* __ppc__ */
-
-}
-
-typedef unsigned long long abstime_scalar_t;
-
-#define AbsoluteTime_to_scalar(x) \
- (*(abstime_scalar_t *)(x))
-
-/* t1 += t2 */
-#define ADD_ABSOLUTETIME(t1, t2) \
- (AbsoluteTime_to_scalar(t1) += \
- AbsoluteTime_to_scalar(t2))
-
-/* t1 -= t2 */
-#define SUB_ABSOLUTETIME(t1, t2) \
- (AbsoluteTime_to_scalar(t1) -= \
- AbsoluteTime_to_scalar(t2))
+kd_buf **last_decrementer_kd; /* last DECR_TRAP per cpu */
+#define MAX_LOG_COUNT 30 /* limits the number of entries dumped in log_decrementer */
int
quit(s)
set_enable(0);
set_pidexclude(getpid(), 0);
screen_update(log_fp);
+ endwin();
set_rtcdec(0);
set_remove();
exit(1);
}
-void sigquit()
-{
- set_enable(0);
- set_pidexclude(getpid(), 0);
- set_rtcdec(0);
- set_remove();
-
- exit(1);
-}
-
-void sigterm()
+void leave() /* exit under normal conditions -- signal handler */
{
set_enable(0);
set_pidexclude(getpid(), 0);
+ endwin();
set_rtcdec(0);
set_remove();
exit(1);
}
-
void
screen_update(FILE *fp)
{
char *argv[];
{
mach_timespec_t remain;
- unsigned long long start, stop;
- AbsoluteTime timestamp1;
- AbsoluteTime timestamp2;
- AbsoluteTime adeadline, adelay;
+ uint64_t start, stop;
+ uint64_t timestamp1;
+ uint64_t timestamp2;
+ uint64_t adeadline, adelay;
double fdelay;
int elapsed_usecs;
double nanosecs_to_sleep;
int loop_cnt, sample_sc_now;
int decrementer_usec = 0;
+ kern_return_t ret;
+ int size;
+ int i, count;
+ host_name_port_t host;
void getdivisor();
void sample_sc();
void init_code_file();
getdivisor();
decrementer_val = decrementer_usec * divisor;
+ /* get the cpu count for the DECR_TRAP array */
+ host = mach_host_self();
+ size = sizeof(hi)/sizeof(int);
+ ret = host_info(host, HOST_BASIC_INFO, (host_info_t)&hi, &size);
+ if (ret != KERN_SUCCESS) {
+ mach_error(argv[0], ret);
+ exit(EXIT_FAILURE);
+ }
+
+ if ((last_decrementer_kd = (kd_buf **)malloc(hi.avail_cpus * sizeof(kd_buf *))) == (kd_buf **)0)
+ quit("can't allocate memory for decrementer tracing info\n");
+
nanosecs_to_sleep = (double)(num_of_usecs_to_sleep * 1000);
fdelay = nanosecs_to_sleep * (divisor /1000);
- AbsoluteTime_to_scalar(&adelay) = (abstime_scalar_t)fdelay;
+ adelay = (uint64_t)fdelay;
init_code_file();
*/
set_rtcdec(decrementer_val);
- initscr();
+ if (initscr() == (WINDOW *) 0)
+ {
+ printf("Unrecognized TERM type, try vt100\n");
+ exit(1);
+ }
+
clear();
refresh();
signal(SIGWINCH, sigwinch);
signal(SIGINT, sigintr);
- signal(SIGQUIT, sigquit);
- signal(SIGTERM, sigterm);
+ signal(SIGQUIT, leave);
+ signal(SIGTERM, leave);
+ signal(SIGHUP, leave);
if ((my_buffer = malloc(SAMPLE_SIZE * sizeof(kd_buf))) == (char *)0)
refresh_time = curr_time + 1;
}
- clock_get_uptime(×tamp1);
- adeadline = timestamp1;
- ADD_ABSOLUTETIME(&adeadline, &adelay);
+ timestamp1 = mach_absolute_time();
+ adeadline = timestamp1 + adelay;
mk_wait_until(adeadline);
- clock_get_uptime(×tamp2);
+ timestamp2 = mach_absolute_time();
- start = (((unsigned long long)timestamp1.hi) << 32) |
- (unsigned long long)((unsigned int)(timestamp1.lo));
+ start = timestamp1;
- stop = (((unsigned long long)timestamp2.hi) << 32) |
- (unsigned long long)((unsigned int)(timestamp2.lo));
+ stop = timestamp2;
elapsed_usecs = (int)(((double)(stop - start)) / divisor);
}
}
if (gotSIGWINCH) {
- initscr();
+ /*
+ No need to check for initscr error return.
+ We won't get here if it fails on the first call.
+ */
+ endwin();
clear();
refresh();
gotSIGWINCH = 0;
}
}
- }
-
+}
void getdivisor()
{
+ mach_timebase_info_data_t info;
- unsigned int delta;
- unsigned int abs_to_ns_num;
- unsigned int abs_to_ns_denom;
- unsigned int proc_to_abs_num;
- unsigned int proc_to_abs_denom;
+ (void) mach_timebase_info (&info);
- (void)MKGetTimeBaseInfo (&delta, &abs_to_ns_num, &abs_to_ns_denom,
- &proc_to_abs_num, &proc_to_abs_denom);
+ divisor = ( (double)info.denom / (double)info.numer) * 1000;
- divisor = ((double)abs_to_ns_denom / (double)abs_to_ns_num) * 1000;
}
/* This is the realtime band */
#endif
map->valid = 1;
map->thread = thread;
- (void)strncpy (map->command, command, sizeof(map->command));
- map->command[sizeof(map->command)-1] = '\0';
+ /*
+ The trace entry that returns the command name will hold
+ at most, MAXCOMLEN chars, and in that case, is not
+ guaranteed to be null terminated.
+ */
+ (void)strncpy (map->command, command, MAXCOMLEN);
+ map->command[MAXCOMLEN] = '\0';
}
}
-void sample_sc(long long start, long long stop)
+void sample_sc(uint64_t start, uint64_t stop)
{
- kd_buf *kd, *last_mach_sched, *last_decrementer_kd, *start_kd, *end_of_sample;
- unsigned long long now;
- int count;
+ kd_buf *kd, *last_mach_sched, *start_kd, *end_of_sample;
+ uint64_t now;
+ int count, i;
int first_entry = 1;
char command[32];
double timestamp, last_timestamp, delta, start_bias;
count = needed;
if (bufinfo.flags & KDBG_WRAPPED) {
- int i;
-
for (i = 0; i < cur_max; i++) {
th_state[i].thread = 0;
th_state[i].type = -1;
- th_state[i].vfslookup = 0;
+ th_state[i].pathptr = (long *)0;
th_state[i].pathname[0] = 0;
}
cur_max = 0;
}
}
end_of_sample = &((kd_buf *)my_buffer)[count];
- last_decrementer_kd = (kd_buf *)my_buffer;
+
+ /* Always reinitialize the DECR_TRAP array */
+ for (i=0; i < hi.avail_cpus; i++)
+ last_decrementer_kd[i] = (kd_buf *)my_buffer;
+
last_mach_sched = (kd_buf *)0;
for (kd = (kd_buf *)my_buffer; kd < end_of_sample; kd++) {
int debugid, thread, cpunum;
int type, clen, mode;
+ int len;
char *p;
long *sargptr;
+ kd_buf *cur_kd;
double i_latency;
struct th_info *ti;
char command1[32];
if (type == DECR_TRAP)
i_latency = handle_decrementer(kd);
- now = (((unsigned long long)kd->timestamp.tv_sec) << 32) |
- (unsigned long long)((unsigned int)(kd->timestamp.tv_nsec));
+ now = kd->timestamp;
timestamp = ((double)now) / divisor;
else if (debugid & DBG_FUNC_END)
exit_syscall(log_fp, kd, thread, type, command, timestamp, delta, start_bias, 0);
else if (type == DECR_TRAP) {
+ cur_kd = kd;
if (log_fp && i_thresh_hold && (int)i_latency > i_thresh_hold) {
- start_kd = last_decrementer_kd;
+ start_kd = last_decrementer_kd[cpunum];
kd = log_decrementer(start_kd, kd, end_of_sample, i_latency);
-
if (kd >= end_of_sample)
break;
}
- last_decrementer_kd = kd;
+ if ((kd->debugid & DBG_FUNC_MASK) == DECR_TRAP)
+ {
+ cpunum = (kd->arg5 & KDBG_CPU_MASK) ? 1: 0;
+ last_decrementer_kd[cpunum] = kd;
+ }
+ else
+ last_decrementer_kd[cpunum] = cur_kd;
}
continue;
}
if (first_entry) {
double latency;
- char buf1[128];
- char buf2[128];
+ char buf1[132];
+ char buf2[132];
latency = (double)(stop - start) / divisor;
latency -= (double)num_of_usecs_to_sleep;
break;
case DECR_TRAP:
- last_decrementer_kd = kd;
+ last_decrementer_kd[cpunum] = kd;
if (i_thresh_hold && (int)i_latency > i_thresh_hold)
p = "*";
ti->thread = thread;
ti->type = -1;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
ti->child_thread = 0;
}
- if (ti->vfslookup == 0) {
- ti->vfslookup = 1;
+ while ( (kd < end_of_sample) && ((kd->debugid & DBG_FUNC_MASK) == VFS_LOOKUP))
+ {
+ if (!ti->pathptr) {
ti->arg1 = kd->arg1;
- memset(&ti->pathname[0], 0, 32);
+ memset(&ti->pathname[0], 0, (PATHLENGTH + 1));
sargptr = (long *)&ti->pathname[0];
*sargptr++ = kd->arg2;
*sargptr++ = kd->arg3;
*sargptr++ = kd->arg4;
+ ti->pathptr = sargptr;
+
+ } else {
+ sargptr = ti->pathptr;
+
+ /*
+ We don't want to overrun our pathname buffer if the
+ kernel sends us more VFS_LOOKUP entries than we can
+ handle.
+ */
+
+ if ((long *)sargptr >= (long *)&ti->pathname[PATHLENGTH])
+ {
+ kd++;
+ continue;
+ }
+
+ /*
+ We need to detect consecutive vfslookup entries.
+ So, if we get here and find a START entry,
+ fake the pathptr so we can bypass all further
+ vfslookup entries.
+ */
+
+ if (kd->debugid & DBG_FUNC_START)
+ {
+ (long *)ti->pathptr = (long *)&ti->pathname[PATHLENGTH];
+ }
+ else
+ {
+ *sargptr++ = kd->arg1;
+ *sargptr++ = kd->arg2;
+ *sargptr++ = kd->arg3;
+ *sargptr++ = kd->arg4;
+ ti->pathptr = sargptr;
+ }
+ }
+ kd++;
+ }
- } else if (ti->vfslookup == 1) {
- ti->vfslookup = 0;
-
- sargptr = (long *)&ti->pathname[12];
- *sargptr++ = kd->arg1;
- *sargptr++ = kd->arg2;
- *sargptr++ = kd->arg3;
- *sargptr++ = kd->arg4;
+ kd--;
+
+ /* print the tail end of the pathname */
+ len = strlen(ti->pathname);
+ if (len > 42)
+ len -= 42;
+ else
+ len = 0;
- if (log_fp) {
- fprintf(log_fp, "%9.1f %8.1f\t\t%-28.28s %-28s %-8x %-8x %d %s\n",
- timestamp - start_bias, delta, "VFS_LOOKUP",
- ti->pathname, ti->arg1, thread, cpunum, command);
- }
+ if (log_fp) {
+ fprintf(log_fp, "%9.1f %8.1f\t\t%-14.14s %-42s %-8x %-8x %d %s\n",
+ timestamp - start_bias, delta, "VFS_LOOKUP",
+ &ti->pathname[len], ti->arg1, thread, cpunum, command);
}
+
last_timestamp = timestamp;
break;
else
ti->type = -1;
ti->stime = timestamp;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
+
#if 0
if (print_info && fp)
fprintf(fp, "cur_max = %d, ti = %x, type = %x, thread = %x\n", cur_max, ti, ti->type, ti->thread);
ti->thread = thread;
ti->child_thread = 0;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
}
}
ti->type = -1;
ti->thread = thread;
ti->type = -1;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
}
ti->child_thread = kd->arg1;
return (1);
kd_buf *log_decrementer(kd_buf *kd_beg, kd_buf *kd_end, kd_buf *end_of_sample, double i_latency)
{
kd_buf *kd, *kd_start, *kd_stop;
+ int kd_count; /* Limit the boundary of kd_start */
double timestamp, last_timestamp, delta, start_bias;
int thread, cpunum;
int debugid, type, clen;
- unsigned long long now;
+ int len;
+ uint64_t now;
struct th_info *ti;
long *sargptr;
char *p;
fprintf(log_fp, "RelTime(Us) Delta debugid arg1 arg2 arg3 arg4 thread cpu command\n\n");
thread = kd_beg->arg5 & KDBG_THREAD_MASK;
+ cpunum = (kd_end->arg5 & KDBG_CPU_MASK) ? 1: 0;
+
+ for (kd_count = 0, kd_start = kd_beg - 1; (kd_start >= (kd_buf *)my_buffer); kd_start--, kd_count++) {
+ if (kd_count == MAX_LOG_COUNT)
+ break;
- for (kd_start = kd_beg - 1; (kd_start >= (kd_buf *)my_buffer) && (kd_start->arg5 & KDBG_THREAD_MASK) == thread; kd_start--) {
- if ((kd_start->debugid & DBG_FUNC_MASK) == DECR_TRAP)
+ if((kd_start->arg5 & KDBG_CPU_MASK) != cpunum)
+ continue;
+
+ if ((kd_start->debugid & DBG_FUNC_MASK) == DECR_TRAP)
+ break;
+
+ if((kd_start->arg5 & KDBG_THREAD_MASK) != thread)
break;
}
+
if (kd_start < (kd_buf *)my_buffer)
kd_start = (kd_buf *)my_buffer;
- for (kd_stop = kd_end + 1; kd_stop < end_of_sample && (kd_start->arg5 & KDBG_THREAD_MASK) == thread; kd_stop++) {
- if ((kd_stop->debugid & DBG_FUNC_MASK) == DECR_TRAP)
+ thread = kd_end->arg5 & KDBG_THREAD_MASK;
+
+ for (kd_stop = kd_end + 1; kd_stop < end_of_sample; kd_stop++) {
+
+ if ((kd_stop->debugid & DBG_FUNC_MASK) == DECR_TRAP)
+ break;
+
+ if((kd_stop->arg5 & KDBG_CPU_MASK) != cpunum)
+ continue;
+
+ if((kd_stop->arg5 & KDBG_THREAD_MASK) != thread)
break;
}
+
if (kd_stop >= end_of_sample)
kd_stop = end_of_sample - 1;
- now = (((unsigned long long)kd_start->timestamp.tv_sec) << 32) |
- (unsigned long long)((unsigned int)(kd_start->timestamp.tv_nsec));
+ now = kd_start->timestamp;
timestamp = ((double)now) / divisor;
for (kd = kd_start; kd <= kd_stop; kd++) {
debugid = kd->debugid;
type = kd->debugid & DBG_FUNC_MASK;
- now = (((unsigned long long)kd->timestamp.tv_sec) << 32) |
- (unsigned long long)((unsigned int)(kd->timestamp.tv_nsec));
+ now = kd->timestamp;
timestamp = ((double)now) / divisor;
ti->thread = thread;
ti->type = -1;
- ti->vfslookup = 0;
+ ti->pathptr = (long *)0;
ti->child_thread = 0;
}
- if (ti->vfslookup == 0) {
- ti->vfslookup = 1;
+
+ while ( (kd <= kd_stop) && (kd->debugid & DBG_FUNC_MASK) == VFS_LOOKUP)
+ {
+ if (!ti->pathptr) {
ti->arg1 = kd->arg1;
- memset(&ti->pathname[0], 0, 32);
+ memset(&ti->pathname[0], 0, (PATHLENGTH + 1));
sargptr = (long *)&ti->pathname[0];
*sargptr++ = kd->arg2;
*sargptr++ = kd->arg3;
*sargptr++ = kd->arg4;
-
- } else if (ti->vfslookup == 1) {
- ti->vfslookup = 0;
-
- sargptr = (long *)&ti->pathname[12];
- *sargptr++ = kd->arg1;
- *sargptr++ = kd->arg2;
- *sargptr++ = kd->arg3;
- *sargptr++ = kd->arg4;
-
- fprintf(log_fp, "%9.1f %8.1f\t\t%-28.28s %-28s %-8x %-8x %d %s\n",
- timestamp - start_bias, delta, "VFS_LOOKUP",
- ti->pathname, ti->arg1, thread, cpunum, command);
+ ti->pathptr = sargptr;
+
+ } else {
+ sargptr = ti->pathptr;
+
+ /*
+ We don't want to overrun our pathname buffer if the
+ kernel sends us more VFS_LOOKUP entries than we can
+ handle.
+ */
+
+ if ((long *)sargptr >= (long *)&ti->pathname[PATHLENGTH])
+ {
+ kd++;
+ continue;
+ }
+
+ /*
+ We need to detect consecutive vfslookup entries.
+ So, if we get here and find a START entry,
+ fake the pathptr so we can bypass all further
+ vfslookup entries.
+ */
+
+ if (kd->debugid & DBG_FUNC_START)
+ {
+ (long *)ti->pathptr = (long *)&ti->pathname[PATHLENGTH];
+ }
+ else
+ {
+ *sargptr++ = kd->arg1;
+ *sargptr++ = kd->arg2;
+ *sargptr++ = kd->arg3;
+ *sargptr++ = kd->arg4;
+ ti->pathptr = sargptr;
+ }
+ }
+ kd++;
}
+
+ kd--;
+ /* print the tail end of the pathname */
+ len = strlen(ti->pathname);
+ if (len > 42)
+ len -= 42;
+ else
+ len = 0;
+
+ fprintf(log_fp, "%9.1f %8.1f\t\t%-14.14s %-42s %-8x %-8x %d %s\n",
+ timestamp - start_bias, delta, "VFS_LOOKUP",
+ &ti->pathname[len], ti->arg1, thread, cpunum, command);
+
last_timestamp = timestamp;
break;