*/
/*
-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 */
#include <nlist.h>
#include <fcntl.h>
#include <string.h>
+#include <dirent.h>
#include <sys/types.h>
#include <sys/param.h>
#include <libc.h>
#include <termios.h>
-#include <bsd/curses.h>
#include <sys/ioctl.h>
#ifndef KERNEL_PRIVATE
#include <sys/sysctl.h>
#include <errno.h>
#import <mach/clock_types.h>
+#import <mach/mach_time.h>
#include <err.h>
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.
struct th_info {
int in_filemgr;
int thread;
+ int pid;
int type;
int arg1;
int arg2;
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
#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
#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
#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;
}
+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);
exit(1);
}
-
+int
main(argc, argv)
int argc;
char *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) {
myname++;
}
}
+
- while ((ch = getopt(argc, argv, "ew")) != EOF) {
+ while ((ch = getopt(argc, argv, "ewf:")) != EOF) {
switch(ch) {
case 'e':
exclude_pids = 1;
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);
}
{
argtopid("Terminal");
argtopid("telnetd");
+ argtopid("telnet");
argtopid("sshd");
argtopid("rlogind");
argtopid("tcsh");
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();
set_enable(1);
getdivisor();
+ init_arguments_buffer();
/* main loop */
while (1) {
- usleep(1000 * 50);
+ usleep(1000 * 20);
sample_sc();
}
void
find_proc_names()
{
- size_t bufSize = 0;
+ size_t bufSize = 0;
struct kinfo_proc *kp;
int quit();
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)
{
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++;
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);
}
}
void
set_remove()
{
- extern int errno;
-
errno = 0;
mib[0] = CTL_KERN;
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;
}
}
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;
ti->thread = thread;
ti->child_thread = kd[i].arg1;
+ ti->pid = kd[i].arg2;
continue;
case TRACE_STRING_NEWTHREAD:
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:
case MACH_sched:
case MACH_stkhandoff:
- if (ti = find_thread(thread, 0))
- ti->waited = 1;
+ mark_thread_waited(thread);
continue;
case VFS_LOOKUP:
*sargptr++ = kd[i].arg3;
*sargptr++ = kd[i].arg4;
ti->pathptr = sargptr;
-
} else {
sargptr = ti->pathptr;
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;
}
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;
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;
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;
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;
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;
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);
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;
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);
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);
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:
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:
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;
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);
/*
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);
ti->arg4 = kd->arg4;
ti->pathptr = (long *)0;
ti->pathname[0] = 0;
-
break;
default:
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], " ");
} 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)
*/
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);
}
if (set_remove_flag)
set_remove();
- printf("fs_usage: ");
+ fprintf(stderr, "fs_usage: ");
if (s)
- printf("%s", s);
+ fprintf(stderr, "%s", s);
exit(1);
}
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*/
{
if (mapptr[i].valid == 0 )
map = &mapptr[i]; /* Reuse this invalid entry */
+ if ((filter_mode & (NETWORK_FILTER | FILESYS_FILTER)) && fdmapptr)
+ {
+ fdmap = &fdmapptr[i];
+ }
}
}
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;
}
*/
(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));
}
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);
}
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
}
+
+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);
+}