X-Git-Url: https://git.saurik.com/apple/system_cmds.git/blobdiff_plain/1c51fdde6f257ffe2a1b92d1a783dab947211e95..d1ccfde8e7580e81fbe4bd45dc2621b45a9f2e6f:/fs_usage.tproj/fs_usage.c diff --git a/fs_usage.tproj/fs_usage.c b/fs_usage.tproj/fs_usage.c index 465d0dc..6f8b2f2 100644 --- a/fs_usage.tproj/fs_usage.c +++ b/fs_usage.tproj/fs_usage.c @@ -23,7 +23,7 @@ */ /* -cc -I. -DKERNEL_PRIVATE -O -o fs_usage fs_usage.c +cc -I. -DPRIVATE -D__APPLE_PRIVATE -O -o fs_usage fs_usage.c */ #define Default_DELAY 1 /* default delay interval */ @@ -35,6 +35,7 @@ cc -I. -DKERNEL_PRIVATE -O -o fs_usage fs_usage.c #include #include #include +#include #include #include @@ -42,7 +43,6 @@ cc -I. -DKERNEL_PRIVATE -O -o fs_usage fs_usage.c #include #include -#include #include #ifndef KERNEL_PRIVATE @@ -56,10 +56,28 @@ cc -I. -DKERNEL_PRIVATE -O -o fs_usage fs_usage.c #include #include #import +#import #include extern int errno; + + +#define MAXINDEX 2048 + +typedef struct LibraryInfo { + unsigned long address; + char *name; +} LibraryInfo; + +LibraryInfo frameworkInfo[MAXINDEX]; +int numFrameworks = 0; + +char seg_addr_table[256]="/AppleInternal/Developer/seg_addr_table"; + +char *lookup_name(); + + /* MAXCOLS controls when extra data kicks in. MAX_WIDE_MODE_COLS controls -w mode to get even wider data in path. @@ -74,6 +92,7 @@ extern int errno; struct th_info { int in_filemgr; int thread; + int pid; int type; int arg1; int arg2; @@ -94,32 +113,130 @@ int cur_max = 0; int need_new_map = 1; int bias_secs; int wideflag = 0; +int columns = 0; int select_pid_mode = 0; /* Flag set indicates that output is restricted to selected pids or commands */ int one_good_pid = 0; /* Used to fail gracefully when bad pids given */ +char *arguments = 0; +int argmax = 0; -#define DBG_ZERO_FILL_FAULT 1 -#define DBG_PAGEIN_FAULT 2 -#define DBG_COW_FAULT 3 -#define DBG_CACHE_HIT_FAULT 4 +/* + * Network only or filesystem only output filter + * Default of zero means report all activity - no filtering + */ +#define FILESYS_FILTER 0x01 +#define NETWORK_FILTER 0x02 +#define CACHEHIT_FILTER 0x04 +#define EXEC_FILTER 0x08 +#define DEFAULT_DO_NOT_FILTER 0x00 + +int filter_mode = CACHEHIT_FILTER; + +#define NFS_DEV -1 + +struct diskrec { + struct diskrec *next; + char *diskname; + int dev; +}; + +struct diskio { + struct diskio *next; + struct diskio *prev; + int type; + int bp; + int dev; + int blkno; + int iosize; + int io_errno; + int issuing_thread; + int completion_thread; + char issuing_command[MAXCOMLEN]; + double issued_time; + double completed_time; +}; + +struct diskrec *disk_list = NULL; +struct diskio *free_diskios = NULL; +struct diskio *busy_diskios = NULL; + +struct diskio *insert_diskio(); +struct diskio *complete_diskio(); +void free_diskio(); +void print_diskio(); +void format_print(); +char *find_disk_name(); +void cache_disk_names(); +int ReadSegAddrTable(); +void mark_thread_waited(int); +int check_filter_mode(struct th_info *, int, int, int, char *); +void fs_usage_fd_set(unsigned int, unsigned int); +int fs_usage_fd_isset(unsigned int, unsigned int); +void fs_usage_fd_clear(unsigned int, unsigned int); +void init_arguments_buffer(); +int get_real_command_name(int, char *, int); +void create_map_entry(int, int, char *); + +void enter_syscall(); +void exit_syscall(); +void extend_syscall(); +void kill_thread_map(); #define TRACE_DATA_NEWTHREAD 0x07000004 +#define TRACE_DATA_EXEC 0x07000008 #define TRACE_STRING_NEWTHREAD 0x07010004 #define TRACE_STRING_EXEC 0x07010008 #define MACH_vmfault 0x01300000 +#define MACH_pageout 0x01300004 #define MACH_sched 0x01400000 #define MACH_stkhandoff 0x01400008 #define VFS_LOOKUP 0x03010090 #define BSC_exit 0x040C0004 +#define P_WrData 0x03020000 +#define P_RdData 0x03020008 +#define P_WrMeta 0x03020020 +#define P_RdMeta 0x03020028 +#define P_PgOut 0x03020040 +#define P_PgIn 0x03020048 +#define P_WrDataAsync 0x03020010 +#define P_RdDataAsync 0x03020018 +#define P_WrMetaAsync 0x03020030 +#define P_RdMetaAsync 0x03020038 +#define P_PgOutAsync 0x03020050 +#define P_PgInAsync 0x03020058 + +#define P_WrDataDone 0x03020004 +#define P_RdDataDone 0x0302000C +#define P_WrMetaDone 0x03020024 +#define P_RdMetaDone 0x0302002C +#define P_PgOutDone 0x03020044 +#define P_PgInDone 0x0302004C +#define P_WrDataAsyncDone 0x03020014 +#define P_RdDataAsyncDone 0x0302001C +#define P_WrMetaAsyncDone 0x03020034 +#define P_RdMetaAsyncDone 0x0302003C +#define P_PgOutAsyncDone 0x03020054 +#define P_PgInAsyncDone 0x0302005C + + #define MSC_map_fd 0x010c00ac + +// Network related codes #define BSC_recvmsg 0x040C006C #define BSC_sendmsg 0x040C0070 #define BSC_recvfrom 0x040C0074 +#define BSC_accept 0x040C0078 +#define BSC_select 0x040C0174 +#define BSC_socket 0x040C0184 +#define BSC_connect 0x040C0188 +#define BSC_bind 0x040C01A0 +#define BSC_listen 0x040C01A8 #define BSC_sendto 0x040C0214 +#define BSC_socketpair 0x040C021C #define BSC_read 0x040C000C #define BSC_write 0x040C0010 @@ -127,23 +244,37 @@ int one_good_pid = 0; /* Used to fail gracefully when bad pids given */ #define BSC_close 0x040C0018 #define BSC_link 0x040C0024 #define BSC_unlink 0x040C0028 +#define BSC_chdir 0x040c0030 +#define BSC_fchdir 0x040c0034 #define BSC_mknod 0x040C0038 #define BSC_chmod 0x040C003C #define BSC_chown 0x040C0040 #define BSC_access 0x040C0084 #define BSC_chflags 0x040C0088 #define BSC_fchflags 0x040C008C -#define BSC_sync 0x040C0090 +#define BSC_sync 0x040C0090 +#define BSC_dup 0x040C00A4 +#define BSC_revoke 0x040C00E0 #define BSC_symlink 0x040C00E4 -#define BSC_readlink 0x040C00E8 +#define BSC_readlink 0x040C00E8 +#define BSC_execve 0x040C00EC +#define BSC_chroot 0x040C00F4 +#define BSC_dup2 0x040C0168 #define BSC_fsync 0x040C017C #define BSC_readv 0x040C01E0 #define BSC_writev 0x040C01E4 #define BSC_fchown 0x040C01EC #define BSC_fchmod 0x040C01F0 -#define BSC_rename 0x040C0200 +#define BSC_rename 0x040C0200 +#define BSC_mkfifo 0x040c0210 #define BSC_mkdir 0x040C0220 -#define BSC_rmdir 0x040C0224 +#define BSC_rmdir 0x040C0224 +#define BSC_utimes 0x040C0228 +#define BSC_futimes 0x040C022C +#define BSC_pread 0x040C0264 +#define BSC_pread_extended 0x040E0264 +#define BSC_pwrite 0x040C0268 +#define BSC_pwrite_extended 0x040E0268 #define BSC_statfs 0x040C0274 #define BSC_fstatfs 0x040C0278 #define BSC_stat 0x040C02F0 @@ -155,17 +286,40 @@ int one_good_pid = 0; /* Used to fail gracefully when bad pids given */ #define BSC_mmap 0x040c0314 #define BSC_lseek 0x040c031c #define BSC_truncate 0x040C0320 -#define BSC_ftruncate 0x040C0324 +#define BSC_ftruncate 0x040C0324 +#define BSC_undelete 0x040C0334 #define BSC_statv 0x040C0364 #define BSC_lstatv 0x040C0368 #define BSC_fstatv 0x040C036C -#define BSC_mkcomplex 0x040C0360 +#define BSC_mkcomplex 0x040C0360 #define BSC_getattrlist 0x040C0370 #define BSC_setattrlist 0x040C0374 #define BSC_getdirentriesattr 0x040C0378 #define BSC_exchangedata 0x040C037C #define BSC_checkuseraccess 0x040C0380 -#define BSC_searchfs 0x040C0384 +#define BSC_searchfs 0x040C0384 +#define BSC_delete 0x040C0388 +#define BSC_copyfile 0x040C038C +#define BSC_getxattr 0x040C03A8 +#define BSC_fgetxattr 0x040C03AC +#define BSC_setxattr 0x040C03B0 +#define BSC_fsetxattr 0x040C03B4 +#define BSC_removexattr 0x040C03B8 +#define BSC_fremovexattr 0x040C03BC +#define BSC_listxattr 0x040C03C0 +#define BSC_flistxattr 0x040C03C4 +#define BSC_fsctl 0x040C03C8 +#define BSC_open_extended 0x040C0454 +#define BSC_stat_extended 0x040C045C +#define BSC_lstat_extended 0x040C0460 +#define BSC_fstat_extended 0x040C0464 +#define BSC_chmod_extended 0x040C0468 +#define BSC_fchmod_extended 0x040C046C +#define BSC_access_extended 0x040C0470 +#define BSC_mkfifo_extended 0x040C048C +#define BSC_mkdir_extended 0x040C0490 +#define BSC_load_shared_file 0x040C04A0 +#define BSC_lchown 0x040C05B0 // Carbon File Manager support #define FILEMGR_PBGETCATALOGINFO 0x1e000020 @@ -259,18 +413,32 @@ int kp_nentries = 0; #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END) #define DBG_FUNC_MASK 0xfffffffc -/* Default divisor */ -#define DIVISOR 16.6666 /* Trace divisor converts to microseconds */ -double divisor = DIVISOR; +double divisor = 0.0; /* Trace divisor converts to microseconds */ int mib[6]; size_t needed; char *my_buffer; -kbufinfo_t bufinfo = {0, 0, 0, 0}; +kbufinfo_t bufinfo = {0, 0, 0, 0, 0}; int total_threads = 0; -kd_threadmap *mapptr = 0; +kd_threadmap *mapptr = 0; /* pointer to list of threads */ + +/* defines for tracking file descriptor state */ +#define FS_USAGE_FD_SETSIZE 256 /* Initial number of file descriptors per + thread that we will track */ + +#define FS_USAGE_NFDBITS (sizeof (unsigned long) * 8) +#define FS_USAGE_NFDBYTES(n) (((n) / FS_USAGE_NFDBITS) * sizeof (unsigned long)) + +typedef struct { + unsigned int fd_valid; /* set if this is a valid entry */ + unsigned int fd_thread; + unsigned int fd_setsize; /* this is a bit count */ + unsigned long *fd_setptr; /* file descripter bitmap */ +} fd_threadmap; + +fd_threadmap *fdmapptr = 0; /* pointer to list of threads for fd tracking */ int trace_enabled = 0; int set_remove_flag = 1; @@ -308,19 +476,37 @@ void leave() /* exit under normal conditions -- INT handler */ } +void get_screenwidth() +{ + struct winsize size; + + columns = MAXCOLS; + + if (isatty(1)) { + if (ioctl(1, TIOCGWINSZ, &size) != -1) + columns = size.ws_col; + } +} + + void sigwinch() { - if (!wideflag) - initscr(); + if (!wideflag) + get_screenwidth(); } int -exit_usage(myname) { +exit_usage(char *myname) { - fprintf(stderr, "Usage: %s [-e] [-w] [pid | cmd [pid | cmd]....]\n", myname); + fprintf(stderr, "Usage: %s [-e] [-w] [-f mode] [pid | cmd [pid | cmd]....]\n", myname); fprintf(stderr, " -e exclude the specified list of pids from the sample\n"); fprintf(stderr, " and exclude fs_usage by default\n"); fprintf(stderr, " -w force wider, detailed, output\n"); + fprintf(stderr, " -f Output is based on the mode provided\n"); + fprintf(stderr, " mode = \"network\" Show only network related output\n"); + fprintf(stderr, " mode = \"filesys\" Show only file system related output\n"); + fprintf(stderr, " mode = \"exec\" Show only execs\n"); + fprintf(stderr, " mode = \"cachehit\" In addition, show cachehits\n"); fprintf(stderr, " pid selects process(s) to sample\n"); fprintf(stderr, " cmd selects process(s) matching command string to sample\n"); fprintf(stderr, "\n%s will handle a maximum list of %d pids.\n\n", myname, MAX_PIDS); @@ -330,7 +516,7 @@ exit_usage(myname) { exit(1); } - +int main(argc, argv) int argc; char *argv[]; @@ -346,11 +532,10 @@ main(argc, argv) int quit(); if ( geteuid() != 0 ) { - printf("'fs_usage' must be run as root...\n"); + fprintf(stderr, "'fs_usage' must be run as root...\n"); exit(1); } - - initscr(); + get_screenwidth(); /* get our name */ if (argc > 0) { @@ -361,8 +546,9 @@ main(argc, argv) myname++; } } + - while ((ch = getopt(argc, argv, "ew")) != EOF) { + while ((ch = getopt(argc, argv, "ewf:")) != EOF) { switch(ch) { case 'e': exclude_pids = 1; @@ -370,9 +556,20 @@ main(argc, argv) break; case 'w': wideflag = 1; - if (COLS < MAX_WIDE_MODE_COLS) - COLS = MAX_WIDE_MODE_COLS; - break; + if ((uint)columns < MAX_WIDE_MODE_COLS) + columns = MAX_WIDE_MODE_COLS; + break; + case 'f': + if (!strcmp(optarg, "network")) + filter_mode |= NETWORK_FILTER; + else if (!strcmp(optarg, "filesys")) + filter_mode |= FILESYS_FILTER; + else if (!strcmp(optarg, "cachehit")) + filter_mode &= ~CACHEHIT_FILTER; /* turns on CACHE_HIT */ + else if (!strcmp(optarg, "exec")) + filter_mode |= EXEC_FILTER; + break; + default: exit_usage(myname); } @@ -397,6 +594,7 @@ main(argc, argv) { argtopid("Terminal"); argtopid("telnetd"); + argtopid("telnet"); argtopid("sshd"); argtopid("rlogind"); argtopid("tcsh"); @@ -417,21 +615,25 @@ main(argc, argv) for (i = 0; i < num_of_pids; i++) { if (exclude_pids) - printf("exclude pid %d\n", pids[i]); + fprintf(stderr, "exclude pid %d\n", pids[i]); else - printf("pid %d\n", pids[i]); + fprintf(stderr, "pid %d\n", pids[i]); } #endif /* set up signal handlers */ signal(SIGINT, leave); signal(SIGQUIT, leave); + signal(SIGHUP, leave); signal(SIGTERM, leave); signal(SIGWINCH, sigwinch); if ((my_buffer = malloc(SAMPLE_SIZE * sizeof(kd_buf))) == (char *)0) quit("can't allocate memory for tracing info\n"); + ReadSegAddrTable(); + cache_disk_names(); + set_remove(); set_numbufs(SAMPLE_SIZE); set_init(); @@ -456,12 +658,13 @@ main(argc, argv) set_enable(1); getdivisor(); + init_arguments_buffer(); /* main loop */ while (1) { - usleep(1000 * 50); + usleep(1000 * 20); sample_sc(); } @@ -470,7 +673,7 @@ main(argc, argv) void find_proc_names() { - size_t bufSize = 0; + size_t bufSize = 0; struct kinfo_proc *kp; int quit(); @@ -512,6 +715,19 @@ struct th_info *find_thread(int thread, int type) { return ((struct th_info *)0); } + +void +mark_thread_waited(int thread) { + struct th_info *ti; + + for (ti = th_state; ti < &th_state[cur_max]; ti++) { + if (ti->thread == thread) { + ti->waited = 1; + } + } +} + + void set_enable(int val) { @@ -570,7 +786,7 @@ set_pidcheck(int pid, int on_off) if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0) { if (on_off == 1) - printf("pid %d does not exist\n", pid); + fprintf(stderr, "pid %d does not exist\n", pid); } else { one_good_pid++; @@ -601,7 +817,7 @@ set_pidexclude(int pid, int on_off) if (sysctl(mib, 3, &kr, &needed, NULL, 0) < 0) { if (on_off == 1) - printf("pid %d does not exist\n", pid); + fprintf(stderr, "pid %d does not exist\n", pid); } } @@ -624,8 +840,6 @@ get_bufinfo(kbufinfo_t *val) void set_remove() { - extern int errno; - errno = 0; mib[0] = CTL_KERN; @@ -703,10 +917,11 @@ sample_sc() count = needed; if (bufinfo.flags & KDBG_WRAPPED) { - printf("buffer wrapped count = %d\n", count); + fprintf(stderr, "fs_usage: buffer overrun, events generated too quickly\n"); for (i = 0; i < cur_max; i++) { th_state[i].thread = 0; + th_state[i].pid = 0; th_state[i].pathptr = (long *)0; th_state[i].pathname[0] = 0; } @@ -718,26 +933,78 @@ sample_sc() } kd = (kd_buf *)my_buffer; #if 0 - printf("READTR returned %d items\n", count); + fprintf(stderr, "READTR returned %d items\n", count); #endif for (i = 0; i < count; i++) { int debugid, thread; int type, n; long *sargptr; - unsigned long long now; + uint64_t now; + long long l_usecs; + int secs; + long curr_time; struct th_info *ti; - void enter_syscall(); - void exit_syscall(); - void kill_thread_map(); + struct diskio *dio; + - thread = kd[i].arg5 & KDBG_THREAD_MASK; + thread = kd[i].arg5; debugid = kd[i].debugid; type = kd[i].debugid & DBG_FUNC_MASK; + now = kd[i].timestamp & KDBG_TIMESTAMP_MASK; + + if (i == 0) + { + /* + * Compute bias seconds after each trace buffer read. + * This helps resync timestamps with the system clock + * in the event of a system sleep. + */ + l_usecs = (long long)(now / divisor); + secs = l_usecs / 1000000; + curr_time = time((long *)0); + bias_secs = curr_time - secs; + } + + switch (type) { + case P_RdMeta: + case P_WrMeta: + case P_RdData: + case P_WrData: + case P_PgIn: + case P_PgOut: + case P_RdMetaAsync: + case P_WrMetaAsync: + case P_RdDataAsync: + case P_WrDataAsync: + case P_PgInAsync: + case P_PgOutAsync: + insert_diskio(type, kd[i].arg1, kd[i].arg2, kd[i].arg3, kd[i].arg4, thread, (double)now); + continue; + + case P_RdMetaDone: + case P_WrMetaDone: + case P_RdDataDone: + case P_WrDataDone: + case P_PgInDone: + case P_PgOutDone: + case P_RdMetaAsyncDone: + case P_WrMetaAsyncDone: + case P_RdDataAsyncDone: + case P_WrDataAsyncDone: + case P_PgInAsyncDone: + case P_PgOutAsyncDone: + if ((dio = complete_diskio(kd[i].arg1, kd[i].arg4, kd[i].arg3, thread, (double)now))) { + print_diskio(dio); + free_diskio(dio); + } + continue; + + case TRACE_DATA_NEWTHREAD: - + for (n = 0, ti = th_state; ti < &th_state[MAX_THREADS]; ti++, n++) { if (ti->thread == 0) break; @@ -749,6 +1016,7 @@ sample_sc() ti->thread = thread; ti->child_thread = kd[i].arg1; + ti->pid = kd[i].arg2; continue; case TRACE_STRING_NEWTHREAD: @@ -756,16 +1024,45 @@ sample_sc() continue; if (ti->child_thread == 0) continue; - create_map_entry(ti->child_thread, (char *)&kd[i].arg1); + create_map_entry(ti->child_thread, ti->pid, (char *)&kd[i].arg1); if (ti == &th_state[cur_max - 1]) cur_max--; ti->child_thread = 0; ti->thread = 0; + ti->pid = 0; continue; + + case TRACE_DATA_EXEC: + + for (n = 0, ti = th_state; ti < &th_state[MAX_THREADS]; ti++, n++) { + if (ti->thread == 0) + break; + } + if (ti == &th_state[MAX_THREADS]) + continue; + if (n >= cur_max) + cur_max = n + 1; + + ti->thread = thread; + ti->pid = kd[i].arg1; + continue; case TRACE_STRING_EXEC: - create_map_entry(thread, (char *)&kd[i].arg1); + if ((ti = find_thread(thread, 0)) == (struct th_info *)0) + { + /* this is for backwards compatibility */ + create_map_entry(thread, 0, (char *)&kd[i].arg1); + } + else + { + create_map_entry(thread, ti->pid, (char *)&kd[i].arg1); + + if (ti == &th_state[cur_max - 1]) + cur_max--; + ti->thread = 0; + ti->pid = 0; + } continue; case BSC_exit: @@ -774,8 +1071,7 @@ sample_sc() case MACH_sched: case MACH_stkhandoff: - if (ti = find_thread(thread, 0)) - ti->waited = 1; + mark_thread_waited(thread); continue; case VFS_LOOKUP: @@ -789,7 +1085,6 @@ sample_sc() *sargptr++ = kd[i].arg3; *sargptr++ = kd[i].arg4; ti->pathptr = sargptr; - } else { sargptr = ti->pathptr; @@ -799,8 +1094,20 @@ sample_sc() handle. */ - if ((long *)sargptr >= (long *)&ti->pathname[PATHLENGTH]) - continue; + if ((long *)sargptr >= (long *)&ti->pathname[PATHLENGTH]) { + 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 (debugid & DBG_FUNC_START) { + (long *)ti->pathptr = (long *)&ti->pathname[PATHLENGTH]; + continue; + } *sargptr++ = kd[i].arg1; *sargptr++ = kd[i].arg2; @@ -810,8 +1117,6 @@ sample_sc() } continue; } - now = (((unsigned long long)kd[i].timestamp.tv_sec) << 32) | - (unsigned long long)((unsigned int)(kd[i].timestamp.tv_nsec)); if (debugid & DBG_FUNC_START) { char *p; @@ -1023,13 +1328,27 @@ sample_sc() enter_syscall(thread, type, &kd[i], p, (double)now); continue; } + switch (type) { + + case BSC_pread_extended: + case BSC_pwrite_extended: + extend_syscall(thread, type, &kd[i], (double)now); + + case MACH_pageout: + if (kd[i].arg2) + exit_syscall("PAGE_OUT_D", thread, type, 0, kd[i].arg1, 0, 4, (double)now); + else + exit_syscall("PAGE_OUT_V", thread, type, 0, kd[i].arg1, 0, 4, (double)now); + break; case MACH_vmfault: if (kd[i].arg2 == DBG_PAGEIN_FAULT) - exit_syscall("PAGE_IN", thread, type, 0, kd[i].arg1, 0, 2, (double)now); + exit_syscall("PAGE_IN", thread, type, kd[i].arg4, kd[i].arg1, 0, 6, (double)now); + else if (kd[i].arg2 == DBG_CACHE_HIT_FAULT) + exit_syscall("CACHE_HIT", thread, type, 0, kd[i].arg1, 0, 2, (double)now); else { - if (ti = find_thread(thread, type)) { + if ((ti = find_thread(thread, type))) { if (ti == &th_state[cur_max - 1]) cur_max--; ti->thread = 0; @@ -1044,7 +1363,7 @@ sample_sc() case BSC_mmap: exit_syscall("mmap", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; - + case BSC_recvmsg: exit_syscall("recvmsg", thread, type, kd[i].arg1, kd[i].arg2, 1, 1, (double)now); break; @@ -1057,18 +1376,86 @@ sample_sc() exit_syscall("recvfrom", thread, type, kd[i].arg1, kd[i].arg2, 1, 1, (double)now); break; + case BSC_accept: + exit_syscall("accept", thread, type, kd[i].arg1, kd[i].arg2, 2, 0, (double)now); + break; + + case BSC_select: + exit_syscall("select", thread, type, kd[i].arg1, kd[i].arg2, 0, 8, (double)now); + break; + + case BSC_socket: + exit_syscall("socket", thread, type, kd[i].arg1, kd[i].arg2, 2, 0, (double)now); + break; + + case BSC_connect: + exit_syscall("connect", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + + case BSC_bind: + exit_syscall("bind", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + + case BSC_listen: + exit_syscall("listen", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + case BSC_sendto: exit_syscall("sendto", thread, type, kd[i].arg1, kd[i].arg2, 1, 1, (double)now); break; + case BSC_socketpair: + exit_syscall("socketpair", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_getxattr: + exit_syscall("getxattr", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_setxattr: + exit_syscall("setxattr", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_removexattr: + exit_syscall("removexattr", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_listxattr: + exit_syscall("listxattr", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + case BSC_stat: exit_syscall("stat", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + + case BSC_stat_extended: + exit_syscall("stat_extended", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_execve: + exit_syscall("execve", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_load_shared_file: + exit_syscall("load_sf", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; case BSC_open: exit_syscall("open", thread, type, kd[i].arg1, kd[i].arg2, 2, 0, (double)now); break; + case BSC_open_extended: + exit_syscall("open_extended", thread, type, kd[i].arg1, kd[i].arg2, 2, 0, (double)now); + break; + + case BSC_dup: + exit_syscall("dup", thread, type, kd[i].arg1, kd[i].arg2, 2, 0, (double)now); + break; + + case BSC_dup2: + exit_syscall("dup2", thread, type, kd[i].arg1, kd[i].arg2, 2, 0, (double)now); + break; + case BSC_close: exit_syscall("close", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); break; @@ -1081,14 +1468,38 @@ sample_sc() exit_syscall("write", thread, type, kd[i].arg1, kd[i].arg2, 1, 1, (double)now); break; + case BSC_fgetxattr: + exit_syscall("fgetxattr", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + + case BSC_fsetxattr: + exit_syscall("fsetxattr", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + + case BSC_fremovexattr: + exit_syscall("fremovexattr", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + + case BSC_flistxattr: + exit_syscall("flistxattr", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + case BSC_fstat: exit_syscall("fstat", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); break; + case BSC_fstat_extended: + exit_syscall("fstat_extended", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + case BSC_lstat: exit_syscall("lstat", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + case BSC_lstat_extended: + exit_syscall("lstat_extended", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + case BSC_link: exit_syscall("link", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; @@ -1105,21 +1516,69 @@ sample_sc() exit_syscall("chmod", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + case BSC_chmod_extended: + exit_syscall("chmod_extended", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + case BSC_chown: exit_syscall("chown", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + case BSC_lchown: + exit_syscall("lchown", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + case BSC_access: exit_syscall("access", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + case BSC_access_extended: + exit_syscall("access_extended", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_chdir: + exit_syscall("chdir", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_chroot: + exit_syscall("chroot", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_utimes: + exit_syscall("utimes", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_delete: + exit_syscall("delete", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_undelete: + exit_syscall("undelete", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_revoke: + exit_syscall("revoke", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_fsctl: + exit_syscall("fsctl", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + case BSC_chflags: exit_syscall("chflags", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; - + case BSC_fchflags: exit_syscall("fchflags", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); break; + + case BSC_fchdir: + exit_syscall("fchdir", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; + + case BSC_futimes: + exit_syscall("futimes", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); + break; case BSC_sync: exit_syscall("sync", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); @@ -1145,6 +1604,14 @@ sample_sc() exit_syscall("writev", thread, type, kd[i].arg1, kd[i].arg2, 1, 1, (double)now); break; + case BSC_pread: + exit_syscall("pread", thread, type, kd[i].arg1, kd[i].arg2, 1, 9, (double)now); + break; + + case BSC_pwrite: + exit_syscall("pwrite", thread, type, kd[i].arg1, kd[i].arg2, 1, 9, (double)now); + break; + case BSC_fchown: exit_syscall("fchown", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); break; @@ -1153,13 +1620,25 @@ sample_sc() exit_syscall("fchmod", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); break; - case BSC_rename: - exit_syscall("rename", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + case BSC_fchmod_extended: + exit_syscall("fchmod_extended", thread, type, kd[i].arg1, kd[i].arg2, 1, 0, (double)now); break; case BSC_mkdir: exit_syscall("mkdir", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + + case BSC_mkdir_extended: + exit_syscall("mkdir_extended", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_mkfifo: + exit_syscall("mkfifo", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_mkfifo_extended: + exit_syscall("mkfifo_extended", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; case BSC_rmdir: exit_syscall("rmdir", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); @@ -1225,9 +1704,19 @@ sample_sc() exit_syscall("getdirentriesattr", thread, type, kd[i].arg1, kd[i].arg2, 0, 1, (double)now); break; + case BSC_exchangedata: exit_syscall("exchangedata", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); break; + + case BSC_rename: + exit_syscall("rename", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + + case BSC_copyfile: + exit_syscall("copyfile", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); + break; + case BSC_checkuseraccess: exit_syscall("checkuseraccess", thread, type, kd[i].arg1, kd[i].arg2, 0, 0, (double)now); @@ -1461,15 +1950,44 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) switch (type) { + case MACH_pageout: case MACH_vmfault: case MSC_map_fd: case BSC_mmap: case BSC_recvmsg: case BSC_sendmsg: case BSC_recvfrom: + case BSC_accept: + case BSC_select: + case BSC_socket: + case BSC_connect: + case BSC_bind: + case BSC_listen: case BSC_sendto: + case BSC_socketpair: + case BSC_execve: + case BSC_getxattr: + case BSC_fgetxattr: + case BSC_setxattr: + case BSC_fsetxattr: + case BSC_removexattr: + case BSC_fremovexattr: + case BSC_listxattr: + case BSC_flistxattr: + case BSC_open_extended: + case BSC_stat_extended: + case BSC_lstat_extended: + case BSC_fstat_extended: + case BSC_chmod_extended: + case BSC_fchmod_extended: + case BSC_access_extended: + case BSC_mkfifo_extended: + case BSC_mkdir_extended: case BSC_stat: + case BSC_load_shared_file: case BSC_open: + case BSC_dup: + case BSC_dup2: case BSC_close: case BSC_read: case BSC_write: @@ -1480,19 +1998,33 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) case BSC_mknod: case BSC_chmod: case BSC_chown: + case BSC_lchown: case BSC_access: case BSC_chflags: case BSC_fchflags: + case BSC_fchdir: + case BSC_futimes: + case BSC_chdir: + case BSC_utimes: + case BSC_chroot: + case BSC_undelete: + case BSC_delete: + case BSC_revoke: + case BSC_fsctl: + case BSC_copyfile: case BSC_sync: case BSC_symlink: case BSC_readlink: case BSC_fsync: case BSC_readv: case BSC_writev: + case BSC_pread: + case BSC_pwrite: case BSC_fchown: case BSC_fchmod: case BSC_rename: case BSC_mkdir: + case BSC_mkfifo: case BSC_rmdir: case BSC_statfs: case BSC_fstatfs: @@ -1580,7 +2112,11 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) case FILEMGR_PBLOCKRANGE: case FILEMGR_PBUNLOCKRANGE: - + if ((ti = find_thread(thread, BSC_execve))) { + if (ti->pathptr) { + exit_syscall("execve", thread, BSC_execve, 0, 0, 0, 0, (double)now); + } + } for (i = 0, ti = th_state; ti < &th_state[MAX_THREADS]; ti++, i++) { if (ti->thread == 0) break; @@ -1589,35 +2125,31 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) return; if (i >= cur_max) cur_max = i + 1; - + if ((type >> 24) == FILEMGR_CLASS) { ti->in_filemgr = 1; l_usecs = (long long)(now / divisor); secs = l_usecs / 1000000; - - if (bias_secs == 0) { - curr_time = time((long *)0); - bias_secs = curr_time - secs; - } curr_time = bias_secs + secs; + sprintf(buf, "%-8.8s", &(ctime(&curr_time)[11])); tsclen = strlen(buf); - if (COLS > MAXCOLS || wideflag) { + if (columns > MAXCOLS || wideflag) { usecs = l_usecs - (long long)((long long)secs * 1000000); sprintf(&buf[tsclen], ".%03ld", (long)usecs / 1000); tsclen = strlen(buf); } /* Print timestamp column */ - printf(buf); + printf("%s", buf); map = find_thread_map(thread); if (map) { sprintf(buf, " %-25.25s ", name); nmclen = strlen(buf); - printf(buf); + printf("%s", buf); sprintf(buf, "(%d, 0x%x, 0x%x, 0x%x)", (short)kd->arg1, kd->arg2, kd->arg3, kd->arg4); argsclen = strlen(buf); @@ -1625,29 +2157,29 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) /* Calculate white space out to command */ - if (COLS > MAXCOLS || wideflag) + if (columns > MAXCOLS || wideflag) { - clen = COLS - (tsclen + nmclen + argsclen + 20); + clen = columns - (tsclen + nmclen + argsclen + 20); } else - clen = COLS - (tsclen + nmclen + argsclen + 12); + clen = columns - (tsclen + nmclen + argsclen + 12); if(clen > 0) { - printf(buf); /* print the kdargs */ + printf("%s", buf); /* print the kdargs */ memset(buf, ' ', clen); buf[clen] = '\0'; - printf(buf); + printf("%s", buf); } else if ((argsclen + clen) > 0) { /* no room so wipe out the kdargs */ memset(buf, ' ', (argsclen + clen)); buf[argsclen + clen] = '\0'; - printf(buf); + printf("%s", buf); } - if (COLS > MAXCOLS || wideflag) + if (columns > MAXCOLS || wideflag) printf("%-20.20s\n", map->command); else printf("%-12.12s\n", map->command); @@ -1666,7 +2198,6 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) ti->arg4 = kd->arg4; ti->pathptr = (long *)0; ti->pathname[0] = 0; - break; default: @@ -1675,42 +2206,98 @@ enter_syscall(int thread, int type, kd_buf *kd, char *name, double now) fflush (0); } +/* + * Handle system call extended trace data. + * pread and pwrite: + * Wipe out the kd args that were collected upon syscall_entry + * because it is the extended info that we really want, and it + * is all we really need. +*/ + +void +extend_syscall(int thread, int type, kd_buf *kd, char *name, double now) +{ + struct th_info *ti; + + switch (type) { + case BSC_pread_extended: + if ((ti = find_thread(thread, BSC_pread)) == (struct th_info *)0) + return; + ti->arg1 = kd->arg1; /* the fd */ + ti->arg2 = kd->arg2; /* nbytes */ + ti->arg3 = kd->arg3; /* top half offset */ + ti->arg4 = kd->arg4; /* bottom half offset */ + break; + case BSC_pwrite_extended: + if ((ti = find_thread(thread, BSC_pwrite)) == (struct th_info *)0) + return; + ti->arg1 = kd->arg1; /* the fd */ + ti->arg2 = kd->arg2; /* nbytes */ + ti->arg3 = kd->arg3; /* top half offset */ + ti->arg4 = kd->arg4; /* bottom half offset */ + break; + default: + return; + } +} void exit_syscall(char *sc_name, int thread, int type, int error, int retval, int has_fd, int has_ret, double now) { - struct th_info *ti; + struct th_info *ti; + + if ((ti = find_thread(thread, type)) == (struct th_info *)0) + return; + + if (check_filter_mode(ti, type, error, retval, sc_name)) + format_print(ti, sc_name, thread, type, error, retval, has_fd, has_ret, now, ti->stime, ti->waited, ti->pathname, NULL); + + if (ti == &th_state[cur_max - 1]) + cur_max--; + ti->thread = 0; +} + + + +void +format_print(struct th_info *ti, char *sc_name, int thread, int type, int error, int retval, + int has_fd, int has_ret, double now, double stime, int waited, char *pathname, struct diskio *dio) +{ int secs; int usecs; int nopadding; long long l_usecs; long curr_time; + char *command_name; kd_threadmap *map; kd_threadmap *find_thread_map(); int len = 0; int clen = 0; + char *framework_name; char buf[MAXCOLS]; - if ((ti = find_thread(thread, type)) == (struct th_info *)0) - return; - map = find_thread_map(thread); + command_name = ""; + + if (dio) + command_name = dio->issuing_command; + else { + if ((map = find_thread_map(thread))) + command_name = map->command; + } + l_usecs = (long long)(now / divisor); secs = l_usecs / 1000000; - - if (bias_secs == 0) { - curr_time = time((long *)0); - bias_secs = curr_time - secs; - } curr_time = bias_secs + secs; sprintf(buf, "%-8.8s", &(ctime(&curr_time)[11])); clen = strlen(buf); - if (COLS > MAXCOLS || wideflag) { + if (columns > MAXCOLS || wideflag) { nopadding = 0; usecs = l_usecs - (long long)((long long)secs * 1000000); sprintf(&buf[clen], ".%03ld", (long)usecs / 1000); clen = strlen(buf); + if ((type >> 24) != FILEMGR_CLASS) { if (find_thread(thread, -1)) { sprintf(&buf[clen], " "); @@ -1721,53 +2308,89 @@ exit_syscall(char *sc_name, int thread, int type, int error, int retval, } else nopadding = 1; - if (((type >> 24) == FILEMGR_CLASS) && (COLS > MAXCOLS || wideflag)) + if (((type >> 24) == FILEMGR_CLASS) && (columns > MAXCOLS || wideflag)) sprintf(&buf[clen], " %-18.18s", sc_name); else sprintf(&buf[clen], " %-15.15s", sc_name); clen = strlen(buf); - - if (COLS > MAXCOLS || wideflag) { + + framework_name = (char *)0; + + if (columns > MAXCOLS || wideflag) { + if (has_ret == 7) { + sprintf(&buf[clen], " D=0x%8.8x", dio->blkno); + + clen = strlen(buf); + + if (dio->io_errno) + sprintf(&buf[clen], " [%3d] ", dio->io_errno); + else + sprintf(&buf[clen], " B=0x%-6x /dev/%s", dio->iosize, find_disk_name(dio->dev)); + } else { + + off_t offset_reassembled = 0LL; + if (has_fd == 2 && error == 0) sprintf(&buf[clen], " F=%-3d", retval); else if (has_fd == 1) sprintf(&buf[clen], " F=%-3d", ti->arg1); - else if (has_ret != 2) + else if (has_ret != 2 && has_ret != 6) sprintf(&buf[clen], " "); clen = strlen(buf); - if (error) + if (has_ret == 2 || has_ret == 6) + framework_name = lookup_name(retval); + + if (error && has_ret != 6) sprintf(&buf[clen], "[%3d] ", error); else if (has_ret == 3) sprintf(&buf[clen], "O=0x%8.8x", ti->arg3); else if (has_ret == 5) sprintf(&buf[clen], "O=0x%8.8x", retval); else if (has_ret == 2) - sprintf(&buf[clen], " A=0x%8.8x ", retval); + sprintf(&buf[clen], " A=0x%8.8x ", retval); + else if (has_ret == 6) + sprintf(&buf[clen], " A=0x%8.8x B=0x%-8x", retval, error); else if (has_ret == 1) sprintf(&buf[clen], " B=0x%-6x", retval); else if (has_ret == 4) - sprintf(&buf[clen], "R=0x%-8x", retval); + sprintf(&buf[clen], "B=0x%-8x", retval); + else if (has_ret == 8) /* BSC_select */ + sprintf(&buf[clen], " S=%-3d ", retval); + else if (has_ret == 9) /* BSC_pread, BSC_pwrite */ + { + sprintf(&buf[clen], "B=0x%-8x", retval); + clen = strlen(buf); + offset_reassembled = (((off_t)(unsigned int)(ti->arg3)) << 32) | (unsigned int)(ti->arg4); + if ((offset_reassembled >> 32) != 0) + sprintf(&buf[clen], "O=0x%16.16qx", (off_t)offset_reassembled); + else + sprintf(&buf[clen], "O=0x%8.8qx", (off_t)offset_reassembled); + } else sprintf(&buf[clen], " "); - clen = strlen(buf); + } + clen = strlen(buf); } - printf(buf); + printf("%s", buf); /* Calculate space available to print pathname */ - if (COLS > MAXCOLS || wideflag) - clen = COLS - (clen + 13 + 20); + if (columns > MAXCOLS || wideflag) + clen = columns - (clen + 13 + 20); else - clen = COLS - (clen + 13 + 12); + clen = columns - (clen + 13 + 12); if ((type >> 24) != FILEMGR_CLASS && !nopadding) clen -= 3; - sprintf(&buf[0], " %s ", ti->pathname); + if (framework_name) + sprintf(&buf[0], " %s ", framework_name); + else + sprintf(&buf[0], " %s ", pathname); len = strlen(buf); if (clen > len) @@ -1778,43 +2401,39 @@ exit_syscall(char *sc_name, int thread, int type, int error, int retval, */ memset(&buf[len], ' ', clen - len); buf[clen] = '\0'; - printf(buf); + printf("%s", buf); } else if (clen == len) { - printf(buf); + printf("%s", buf); } else if ((clen > 0) && (clen < len)) { /* This prints the tail end of the pathname */ buf[len-clen] = ' '; - printf(&buf[len - clen]); + printf("%s", &buf[len - clen]); } - usecs = (unsigned long)(((double)now - ti->stime) / divisor); + usecs = (unsigned long)((now - stime) / divisor); secs = usecs / 1000000; usecs -= secs * 1000000; if ((type >> 24) != FILEMGR_CLASS && !nopadding) printf(" "); - printf(" %2ld.%06ld", (long)secs, (long)usecs); - if (ti->waited) + printf(" %2ld.%06ld", (unsigned long)secs, (unsigned long)usecs); + + if (waited) printf(" W"); else printf(" "); - if (map) { - if (COLS > MAXCOLS || wideflag) - printf(" %-20.20s", map->command); - else - printf(" %-12.12s", map->command); - } - printf("\n"); + if (columns > MAXCOLS || wideflag) + printf(" %-20.20s", command_name); + else + printf(" %-12.12s", command_name); - if (ti == &th_state[cur_max - 1]) - cur_max--; - ti->thread = 0; + printf("\n"); fflush (0); } @@ -1832,9 +2451,9 @@ char *s; if (set_remove_flag) set_remove(); - printf("fs_usage: "); + fprintf(stderr, "fs_usage: "); if (s) - printf("%s", s); + fprintf(stderr, "%s", s); exit(1); } @@ -1842,77 +2461,124 @@ char *s; void getdivisor() { + struct mach_timebase_info mti; - 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; - - extern void MKGetTimeBaseInfo(unsigned int *, unsigned int *, unsigned int *, unsigned int *, unsigned int *); + mach_timebase_info(&mti); - MKGetTimeBaseInfo (&delta, &abs_to_ns_num, &abs_to_ns_denom, - &proc_to_abs_num, &proc_to_abs_denom); - - divisor = ((double)abs_to_ns_denom / (double)abs_to_ns_num) * 1000; + divisor = ((double)mti.denom / (double)mti.numer) * 1000; } void read_command_map() { size_t size; + int i; + int prev_total_threads; int mib[6]; if (mapptr) { free(mapptr); mapptr = 0; } + + prev_total_threads = total_threads; total_threads = bufinfo.nkdthreads; size = bufinfo.nkdthreads * sizeof(kd_threadmap); if (size) { - if (mapptr = (kd_threadmap *) malloc(size)) - bzero (mapptr, size); - else + if ((mapptr = (kd_threadmap *) malloc(size))) { - printf("Thread map is not initialized -- this is not fatal\n"); - return; + bzero (mapptr, size); + + /* 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 */ + free(mapptr); + mapptr = 0; + } } } - - /* 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) + + if (mapptr && (filter_mode & (NETWORK_FILTER | FILESYS_FILTER))) { - /* This is not fatal -- just means I cant map command strings */ + if (fdmapptr) + { + /* We accept the fact that we lose file descriptor state if the + kd_buffer wraps */ + for (i = 0; i < prev_total_threads; i++) + { + if (fdmapptr[i].fd_setptr) + free (fdmapptr[i].fd_setptr); + } + free(fdmapptr); + fdmapptr = 0; + } - printf("Can't read the thread map -- this is not fatal\n"); - free(mapptr); - mapptr = 0; - return; + size = total_threads * sizeof(fd_threadmap); + if ((fdmapptr = (fd_threadmap *) malloc(size))) + { + bzero (fdmapptr, size); + /* reinitialize file descriptor state map */ + for (i = 0; i < total_threads; i++) + { + fdmapptr[i].fd_thread = mapptr[i].thread; + fdmapptr[i].fd_valid = mapptr[i].valid; + fdmapptr[i].fd_setsize = 0; + fdmapptr[i].fd_setptr = 0; + } + } + } + + /* Resolve any LaunchCFMApp command names */ + if (mapptr && arguments) + { + for (i=0; i < total_threads; i++) + { + int pid; + + pid = mapptr[i].valid; + + if (pid == 0 || pid == 1) + continue; + else if (!strncmp(mapptr[i].command,"LaunchCFMA", 10)) + { + (void)get_real_command_name(pid, mapptr[i].command, sizeof(mapptr[i].command)); + } + } } - return; } -void create_map_entry(int thread, char *command) +void create_map_entry(int thread, int pid, char *command) { int i, n; kd_threadmap *map; + fd_threadmap *fdmap = 0; if (!mapptr) return; for (i = 0, map = 0; !map && i < total_threads; i++) { - if (mapptr[i].thread == thread ) - map = &mapptr[i]; /* Reuse this entry, the thread has been reassigned */ + if ((int)mapptr[i].thread == thread ) + { + map = &mapptr[i]; /* Reuse this entry, the thread has been + * reassigned */ + if ((filter_mode & (NETWORK_FILTER | FILESYS_FILTER)) && fdmapptr) + { + fdmap = &fdmapptr[i]; + if (fdmap->fd_thread != thread) /* This shouldn't happen */ + fdmap = (fd_threadmap *)0; + } + } } if (!map) /* look for invalid entries that I can reuse*/ @@ -1921,6 +2587,10 @@ void create_map_entry(int thread, char *command) { if (mapptr[i].valid == 0 ) map = &mapptr[i]; /* Reuse this invalid entry */ + if ((filter_mode & (NETWORK_FILTER | FILESYS_FILTER)) && fdmapptr) + { + fdmap = &fdmapptr[i]; + } } } @@ -1935,6 +2605,14 @@ void create_map_entry(int thread, char *command) mapptr = (kd_threadmap *) realloc(mapptr, n * sizeof(kd_threadmap)); bzero(&mapptr[total_threads], total_threads*sizeof(kd_threadmap)); map = &mapptr[total_threads]; + + if ((filter_mode & (NETWORK_FILTER | FILESYS_FILTER)) && fdmapptr) + { + fdmapptr = (fd_threadmap *)realloc(fdmapptr, n * sizeof(fd_threadmap)); + bzero(&fdmapptr[total_threads], total_threads*sizeof(fd_threadmap)); + fdmap = &fdmapptr[total_threads]; + } + total_threads = n; } @@ -1947,6 +2625,23 @@ void create_map_entry(int thread, char *command) */ (void)strncpy (map->command, command, MAXCOMLEN); map->command[MAXCOMLEN] = '\0'; + + if (fdmap) + { + fdmap->fd_valid = 1; + fdmap->fd_thread = thread; + if (fdmap->fd_setptr) + { + free(fdmap->fd_setptr); + fdmap->fd_setptr = (unsigned long *)0; + } + fdmap->fd_setsize = 0; + } + + if (pid == 0 || pid == 1) + return; + else if (!strncmp(map->command, "LaunchCFMA", 10)) + (void)get_real_command_name(pid, map->command, sizeof(map->command)); } @@ -1961,7 +2656,7 @@ kd_threadmap *find_thread_map(int thread) for (i = 0; i < total_threads; i++) { map = &mapptr[i]; - if (map->valid && (map->thread == thread)) + if (map->valid && ((int)map->thread == thread)) { return(map); } @@ -1969,17 +2664,52 @@ kd_threadmap *find_thread_map(int thread) return ((kd_threadmap *)0); } +fd_threadmap *find_fd_thread_map(int thread) +{ + int i; + fd_threadmap *fdmap = 0; + + if (!fdmapptr) + return((fd_threadmap *)0); + + for (i = 0; i < total_threads; i++) + { + fdmap = &fdmapptr[i]; + if (fdmap->fd_valid && ((int)fdmap->fd_thread == thread)) + { + return(fdmap); + } + } + return ((fd_threadmap *)0); +} + void kill_thread_map(int thread) { kd_threadmap *map; + fd_threadmap *fdmap; - if (map = find_thread_map(thread)) { + if ((map = find_thread_map(thread))) { map->valid = 0; map->thread = 0; map->command[0] = '\0'; } + + if ((filter_mode & (NETWORK_FILTER | FILESYS_FILTER))) + { + if ((fdmap = find_fd_thread_map(thread))) + { + fdmap->fd_valid = 0; + fdmap->fd_thread = 0; + if (fdmap->fd_setptr) + { + free (fdmap->fd_setptr); + fdmap->fd_setptr = (unsigned long *)0; + } + fdmap->fd_setsize = 0; + } + } } void @@ -2012,3 +2742,705 @@ argtopid(str) } + +char *lookup_name(unsigned long addr) +{ + register int i; + register int start, last; + + + if (numFrameworks == 0 || addr < frameworkInfo[0].address || addr > frameworkInfo[numFrameworks].address) + return (0); + + start = 0; + last = numFrameworks; + + for (i = numFrameworks / 2; i >= 0 && i < numFrameworks; ) { + + if (addr >= frameworkInfo[i].address && addr < frameworkInfo[i+1].address) + return(frameworkInfo[i].name); + + if (addr >= frameworkInfo[i].address) { + start = i; + i = start + ((last - i) / 2); + } else { + last = i; + i = start + ((i - start) / 2); + } + } + return (0); +} + + +/* + * Comparison routines for sorting + */ +static int compareFrameworkAddress(const void *aa, const void *bb) +{ + LibraryInfo *a = (LibraryInfo *)aa; + LibraryInfo *b = (LibraryInfo *)bb; + + if (a->address < b->address) return -1; + if (a->address == b->address) return 0; + return 1; +} + + +int scanline(char *inputstring,char **argv) +{ + int n = 0; + char **ap = argv, *p, *val; + + for (p = inputstring; p != NULL; ) + { + while ((val = strsep(&p, " \t")) != NULL && *val == '\0'); + *ap++ = val; + n++; + } + *ap = 0; + return n; +} + + +int ReadSegAddrTable() +{ + char buf[1024]; + + FILE *fd; + unsigned long frameworkAddress, frameworkDataAddress, previousFrameworkAddress; + char frameworkName[256]; + char *tokens[64]; + int ntokens; + char *substring,*ptr; + int founddylib = 0; + + + bzero(buf, sizeof(buf)); + bzero(tokens, sizeof(tokens)); + + numFrameworks = 0; + + if ((fd = fopen(seg_addr_table, "r")) == 0) + { + return 0; + } + fgets(buf, 1023, fd); + + if (*buf == '#') + { + founddylib = 0; + frameworkName[0] = 0; + previousFrameworkAddress = 0; + + while (fgets(buf, 1023, fd) && numFrameworks < (MAXINDEX - 2)) + { + /* + * Get rid of EOL + */ + buf[strlen(buf)-1] = 0; + + if (strncmp(buf, "# dyld:", 7) == 0) { + /* + * the next line in the file will contain info about dyld + */ + founddylib = 1; + continue; + } + /* + * This is a split library line: parse it into 3 tokens + */ + ntokens = scanline(buf, tokens); + + if (ntokens < 3) + continue; + + frameworkAddress = strtoul(tokens[0], 0, 16); + frameworkDataAddress = strtoul(tokens[1], 0, 16); + + if (founddylib) { + /* + * dyld entry is of a different form from the std split library + * it consists of a base address and a size instead of a code + * and data base address + */ + frameworkInfo[numFrameworks].address = frameworkAddress; + frameworkInfo[numFrameworks+1].address = frameworkAddress + frameworkDataAddress; + + frameworkInfo[numFrameworks].name = (char *)"dylib"; + frameworkInfo[numFrameworks+1].name = (char *)0; + + numFrameworks += 2; + founddylib = 0; + + continue; + } + + /* + * Make sure that we have 2 addresses and a path + */ + if (!frameworkAddress) + continue; + if (!frameworkDataAddress) + continue; + if (*tokens[2] != '/') + continue; + if (frameworkAddress == previousFrameworkAddress) + continue; + previousFrameworkAddress = frameworkAddress; + + /* + * Extract lib name from path name + */ + if ((substring = strrchr(tokens[2], '.'))) + { + /* + * There is a ".": name is whatever is between the "/" around the "." + */ + while ( *substring != '/') { /* find "/" before "." */ + substring--; + } + substring++; + strcpy(frameworkName, substring); /* copy path from "/" */ + substring = frameworkName; + + while ( *substring != '/' && *substring) /* find "/" after "." and stop string there */ + substring++; + *substring = 0; + } + else + { + /* + * No ".": take segment after last "/" + */ + ptr = tokens[2]; + substring = ptr; + + while (*ptr) + { + if (*ptr == '/') + substring = ptr + 1; + ptr++; + } + strcpy(frameworkName, substring); + } + frameworkInfo[numFrameworks].address = frameworkAddress; + frameworkInfo[numFrameworks+1].address = frameworkDataAddress; + + frameworkInfo[numFrameworks].name = (char *)malloc(strlen(frameworkName) + 1); + strcpy(frameworkInfo[numFrameworks].name, frameworkName); + frameworkInfo[numFrameworks+1].name = frameworkInfo[numFrameworks].name; + + numFrameworks += 2; + } + } + frameworkInfo[numFrameworks].address = frameworkInfo[numFrameworks - 1].address + 0x800000; + frameworkInfo[numFrameworks].name = (char *)0; + + fclose(fd); + + qsort(frameworkInfo, numFrameworks, sizeof(LibraryInfo), compareFrameworkAddress); + + return 1; +} + + +struct diskio *insert_diskio(int type, int bp, int dev, int blkno, int io_size, int thread, double curtime) +{ + register struct diskio *dio; + register kd_threadmap *map; + + if ((dio = free_diskios)) + free_diskios = dio->next; + else { + if ((dio = (struct diskio *)malloc(sizeof(struct diskio))) == NULL) + return (NULL); + } + dio->prev = NULL; + + dio->type = type; + dio->bp = bp; + dio->dev = dev; + dio->blkno = blkno; + dio->iosize = io_size; + dio->issued_time = curtime; + dio->issuing_thread = thread; + + if ((map = find_thread_map(thread))) + { + strncpy(dio->issuing_command, map->command, MAXCOMLEN); + dio->issuing_command[MAXCOMLEN-1] = '\0'; + } + else + strcpy(dio->issuing_command, ""); + + dio->next = busy_diskios; + if (dio->next) + dio->next->prev = dio; + busy_diskios = dio; + + return (dio); +} + + +struct diskio *complete_diskio(int bp, int io_errno, int resid, int thread, double curtime) +{ + register struct diskio *dio; + + for (dio = busy_diskios; dio; dio = dio->next) { + if (dio->bp == bp) { + + if (dio == busy_diskios) { + if ((busy_diskios = dio->next)) + dio->next->prev = NULL; + } else { + if (dio->next) + dio->next->prev = dio->prev; + dio->prev->next = dio->next; + } + dio->iosize -= resid; + dio->io_errno = io_errno; + dio->completed_time = curtime; + dio->completion_thread = thread; + + return (dio); + } + } + return ((struct diskio *)0); +} + + +void free_diskio(struct diskio *dio) +{ + dio->next = free_diskios; + free_diskios = dio; +} + + +void print_diskio(struct diskio *dio) +{ + register char *p; + + switch (dio->type) { + + case P_RdMeta: + p = " RdMeta"; + break; + case P_WrMeta: + p = " WrMeta"; + break; + case P_RdData: + p = " RdData"; + break; + case P_WrData: + p = " WrData"; + break; + case P_PgIn: + p = " PgIn"; + break; + case P_PgOut: + p = " PgOut"; + break; + case P_RdMetaAsync: + p = " RdMeta[async]"; + break; + case P_WrMetaAsync: + p = " WrMeta[async]"; + break; + case P_RdDataAsync: + p = " RdData[async]"; + break; + case P_WrDataAsync: + p = " WrData[async]"; + break; + case P_PgInAsync: + p = " PgIn[async]"; + break; + case P_PgOutAsync: + p = " PgOut[async]"; + break; + default: + p = " "; + break; + } + if (check_filter_mode(NULL, dio->type,0, 0, p)) + format_print(NULL, p, dio->issuing_thread, dio->type, 0, 0, 0, 7, dio->completed_time, dio->issued_time, 1, "", dio); +} + + +void cache_disk_names() +{ + struct stat st; + DIR *dirp = NULL; + struct dirent *dir; + struct diskrec *dnp; + + + if ((dirp = opendir("/dev")) == NULL) + return; + + while ((dir = readdir(dirp)) != NULL) { + char nbuf[MAXPATHLEN]; + + if (dir->d_namlen < 5 || strncmp("disk", dir->d_name, 4)) + continue; + sprintf(nbuf, "%s/%s", "/dev", dir->d_name); + + if (stat(nbuf, &st) < 0) + continue; + + if ((dnp = (struct diskrec *)malloc(sizeof(struct diskrec))) == NULL) + continue; + + if ((dnp->diskname = (char *)malloc(dir->d_namlen + 1)) == NULL) { + free(dnp); + continue; + } + strncpy(dnp->diskname, dir->d_name, dir->d_namlen); + dnp->diskname[dir->d_namlen] = 0; + dnp->dev = st.st_rdev; + + dnp->next = disk_list; + disk_list = dnp; + } + (void) closedir(dirp); +} + + +char *find_disk_name(int dev) +{ + struct diskrec *dnp; + + if (dev == NFS_DEV) + return ("NFS"); + + for (dnp = disk_list; dnp; dnp = dnp->next) { + if (dnp->dev == dev) + return (dnp->diskname); + } + return ("NOTFOUND"); +} + +void +fs_usage_fd_set(thread, fd) + unsigned int thread; + unsigned int fd; +{ + int n; + fd_threadmap *fdmap; + + if(!(fdmap = find_fd_thread_map(thread))) + return; + + /* If the map is not allocated, then now is the time */ + if (fdmap->fd_setptr == (unsigned long *)0) + { + fdmap->fd_setptr = (unsigned long *)malloc(FS_USAGE_NFDBYTES(FS_USAGE_FD_SETSIZE)); + if (fdmap->fd_setptr) + { + fdmap->fd_setsize = FS_USAGE_FD_SETSIZE; + bzero(fdmap->fd_setptr,(FS_USAGE_NFDBYTES(FS_USAGE_FD_SETSIZE))); + } + else + return; + } + + /* If the map is not big enough, then reallocate it */ + while (fdmap->fd_setsize < fd) + { + fprintf(stderr, "reallocating bitmap for threadid %d, fd = %d, setsize = %d\n", + thread, fd, fdmap->fd_setsize); + n = fdmap->fd_setsize * 2; + fdmap->fd_setptr = (unsigned long *)realloc(fdmap->fd_setptr, (FS_USAGE_NFDBYTES(n))); + bzero(&fdmap->fd_setptr[(fdmap->fd_setsize/FS_USAGE_NFDBITS)], (FS_USAGE_NFDBYTES(fdmap->fd_setsize))); + fdmap->fd_setsize = n; + } + + /* set the bit */ + fdmap->fd_setptr[fd/FS_USAGE_NFDBITS] |= (1 << ((fd) % FS_USAGE_NFDBITS)); + + return; +} + +/* + Return values: + 0 : File Descriptor bit is not set + 1 : File Descriptor bit is set +*/ + +int +fs_usage_fd_isset(thread, fd) + unsigned int thread; + unsigned int fd; +{ + int ret = 0; + fd_threadmap *fdmap; + + if(!(fdmap = find_fd_thread_map(thread))) + return(ret); + + if (fdmap->fd_setptr == (unsigned long *)0) + return (ret); + + if (fd < fdmap->fd_setsize) + ret = fdmap->fd_setptr[fd/FS_USAGE_NFDBITS] & (1 << (fd % FS_USAGE_NFDBITS)); + + return (ret); +} + +void +fs_usage_fd_clear(thread, fd) + unsigned int thread; + unsigned int fd; +{ + fd_threadmap *map; + + if (!(map = find_fd_thread_map(thread))) + return; + + if (map->fd_setptr == (unsigned long *)0) + return; + + /* clear the bit */ + if (fd < map->fd_setsize) + map->fd_setptr[fd/FS_USAGE_NFDBITS] &= ~(1 << (fd % FS_USAGE_NFDBITS)); + + return; +} + + +/* + * ret = 1 means print the entry + * ret = 0 means don't print the entry + */ +int +check_filter_mode(struct th_info * ti, int type, int error, int retval, char *sc_name) +{ + int ret = 0; + int network_fd_isset = 0; + unsigned int fd; + + if (filter_mode == DEFAULT_DO_NOT_FILTER) + return(1); + + if (!strcmp (sc_name, "CACHE_HIT")) { + if (filter_mode & CACHEHIT_FILTER) + /* Do not print if cachehit filter is set */ + return(0); + return (1); + } + + if (filter_mode & EXEC_FILTER) + { + if (!strcmp (sc_name, "execve")) + return(1); + return(0); + } + if ( !(filter_mode & (FILESYS_FILTER | NETWORK_FILTER))) + return(1); + + + if (ti == (struct th_info *)0) + { + if(filter_mode & FILESYS_FILTER) + ret = 1; + else + ret = 0; + return(ret); + } + + switch (type) { + case BSC_close: + fd = ti->arg1; + network_fd_isset = fs_usage_fd_isset(ti->thread, fd); + if (error == 0) + { + fs_usage_fd_clear(ti->thread,fd); + } + + if (network_fd_isset) + { + if (filter_mode & NETWORK_FILTER) + ret = 1; + } + else if (filter_mode & FILESYS_FILTER) + ret = 1; + break; + case BSC_read: + case BSC_write: + /* we don't care about error in this case */ + fd = ti->arg1; + network_fd_isset = fs_usage_fd_isset(ti->thread, fd); + if (network_fd_isset) + { + if (filter_mode & NETWORK_FILTER) + ret = 1; + } + else if (filter_mode & FILESYS_FILTER) + ret = 1; + break; + case BSC_accept: + case BSC_socket: + fd = retval; + if (error == 0) + fs_usage_fd_set(ti->thread, fd); + if (filter_mode & NETWORK_FILTER) + ret = 1; + break; + case BSC_recvfrom: + case BSC_sendto: + case BSC_recvmsg: + case BSC_sendmsg: + case BSC_connect: + case BSC_bind: + case BSC_listen: + fd = ti->arg1; + if (error == 0) + fs_usage_fd_set(ti->thread, fd); + if (filter_mode & NETWORK_FILTER) + ret = 1; + break; + case BSC_select: + case BSC_socketpair: + /* Cannot determine info about file descriptors */ + if (filter_mode & NETWORK_FILTER) + ret = 1; + break; + case BSC_dup: + case BSC_dup2: + ret=0; /* We track these cases for fd state only */ + fd = ti->arg1; /* oldd */ + network_fd_isset = fs_usage_fd_isset(ti->thread, fd); + if (error == 0 && network_fd_isset) + { + /* then we are duping a socket descriptor */ + fd = retval; /* the new fd */ + fs_usage_fd_set(ti->thread, fd); + } + break; + + default: + if (filter_mode & FILESYS_FILTER) + ret = 1; + break; + } + + return(ret); +} + +/* + * Allocate a buffer that is large enough to hold the maximum arguments + * to execve(). This is used when getting the arguments to programs + * when we see LaunchCFMApps. If this fails, it is not fatal, we will + * simply not resolve the command name. + */ + +void +init_arguments_buffer() +{ + + int mib[2]; + size_t size; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) + return; + +#if 1 + /* Hack to avoid kernel bug. */ + if (argmax > 8192) { + argmax = 8192; + } +#endif + + arguments = (char *)malloc(argmax); + + return; +} + + +int +get_real_command_name(int pid, char *cbuf, int csize) +{ + /* + * Get command and arguments. + */ + char *cp; + int mib[4]; + char *command_beg, *command, *command_end; + + if (cbuf == NULL) { + return(0); + } + + if (arguments) + bzero(arguments, argmax); + else + return(0); + + /* + * A sysctl() is made to find out the full path that the command + * was called with. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS; + mib[2] = pid; + mib[3] = 0; + + if (sysctl(mib, 3, arguments, (size_t *)&argmax, NULL, 0) < 0) { + return(0); + } + + /* Skip the saved exec_path. */ + for (cp = arguments; cp < &arguments[argmax]; cp++) { + if (*cp == '\0') { + /* End of exec_path reached. */ + break; + } + } + if (cp == &arguments[argmax]) { + return(0); + } + + /* Skip trailing '\0' characters. */ + for (; cp < &arguments[argmax]; cp++) { + if (*cp != '\0') { + /* Beginning of first argument reached. */ + break; + } + } + if (cp == &arguments[argmax]) { + return(0); + } + command_beg = cp; + + /* + * Make sure that the command is '\0'-terminated. This protects + * against malicious programs; under normal operation this never + * ends up being a problem.. + */ + for (; cp < &arguments[argmax]; cp++) { + if (*cp == '\0') { + /* End of first argument reached. */ + break; + } + } + if (cp == &arguments[argmax]) { + return(0); + } + command_end = command = cp; + + /* Get the basename of command. */ + for (command--; command >= command_beg; command--) { + if (*command == '/') { + command++; + break; + } + } + + (void) strncpy(cbuf, (char *)command, csize); + cbuf[csize-1] = '\0'; + + return(1); +}