--- valgrind/coregrind/vg_include.h.~1~ Sun Apr 20 14:21:58 2003 +++ valgrind/coregrind/vg_include.h Sun Apr 20 14:21:58 2003 @@ -232,6 +232,8 @@ extern Int VG_(clo_backtrace_size); /* Engage miscellaneous wierd hacks needed for some progs. */ extern Char* VG_(clo_weird_hacks); +/* List open file descriptors at exit? */ +extern Bool VG_(clo_fd_list); /* Should we run __libc_freeres at exit? Sometimes causes crashes. Default: YES. Note this is subservient to VG_(needs).libc_freeres; if the latter says False, then the setting of VG_(clo_weird_hacks) @@ -1362,6 +1364,9 @@ extern Bool VG_(is_kerror) ( Int res ); +extern void VG_(init_preopened_fds) ( void ); +extern void VG_(fd_stats) ( void ); + #define KERNEL_DO_SYSCALL(thread_id, result_lvalue) \ VG_(load_thread_state)(thread_id); \ VG_(copy_baseBlock_to_m_state_static)(); \ --- valgrind/coregrind/vg_kerneliface.h.~1~ Sun Apr 20 14:21:58 2003 +++ valgrind/coregrind/vg_kerneliface.h Sun Apr 20 14:21:58 2003 @@ -157,6 +157,10 @@ #define VKI_O_DIRECTORY 0200000 /* must be a directory */ #define VKI_O_NOFOLLOW 0400000 /* don't follow links */ +#define VKI_SEEK_SET 0 +#define VKI_SEEK_CUR 1 +#define VKI_SEEK_END 2 + /* Copied from linux-2.4.19/include/linux/stat.h */ #define VKI_S_IRWXU 00700 @@ -411,6 +415,18 @@ #define VKI_MODIFY_LDT_CONTENTS_CODE 2 +/* This is the structure passed to the getdents syscall. */ +/* + * linux/dirent.h + */ +typedef struct vki_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +} vki_dirent; + + #endif /* ndef __VG_KERNELIFACE_H */ --- valgrind/coregrind/vg_main.c.~1~ Sun Apr 20 14:21:58 2003 +++ valgrind/coregrind/vg_main.c Sun Apr 20 14:21:58 2003 @@ -514,6 +514,7 @@ Int VG_(clo_backtrace_size) = 4; Char* VG_(clo_weird_hacks) = NULL; Bool VG_(clo_run_libc_freeres) = True; +Bool VG_(clo_fd_list) = False; Bool VG_(clo_chain_bb) = True; @@ -592,6 +593,7 @@ " --error-limit=no|yes stop showing new errors if too many? [yes]\n" " --trace-children=no|yes Valgrind-ise child processes? [no]\n" " --run-libc-freeres=no|yes Free up glibc memory at exit? [yes]\n" +" --fd-list=no|yes Show open file descriptors at exit? [yes]\n" " --logfile-fd= file descriptor for messages [2=stderr]\n" " --logfile= log messages to .pid\n" " --logsocket=ipaddr:port log messages to socket ipaddr:port\n" @@ -905,6 +907,11 @@ else if (VG_CLO_STREQ(argv[i], "--run-libc-freeres=no")) VG_(clo_run_libc_freeres) = False; + else if (VG_CLO_STREQ(argv[i], "--fd-list=yes")) + VG_(clo_fd_list) = True; + else if (VG_CLO_STREQ(argv[i], "--fd-list=no")) + VG_(clo_fd_list) = False; + else if (VG_CLO_STREQN(15, argv[i], "--sanity-level=")) VG_(sanity_level) = (Int)VG_(atoll)(&argv[i][15]); @@ -1416,6 +1423,9 @@ /* Set up baseBlock offsets and copy the saved machine's state into it. */ vg_init_baseBlock(); + /* Search for file descriptors that are inherited from our parent. */ + VG_(init_preopened_fds)(); + /* Initialise the scheduler, and copy the client's state from baseBlock into VG_(threads)[1]. Must be before: - VG_(sigstartup_actions)() @@ -1477,6 +1487,9 @@ "Warning: pthread scheduler exited due to deadlock"); } + /* Print out file descriptor summary and stats. */ + VG_(fd_stats)(); + if (VG_(needs).core_errors || VG_(needs).skin_errors) VG_(show_all_errors)(); --- valgrind/coregrind/vg_mylibc.c.~1~ Sun Apr 20 14:21:58 2003 +++ valgrind/coregrind/vg_mylibc.c Sun Apr 20 14:21:58 2003 @@ -1172,6 +1172,15 @@ return res; } +Int VG_(lseek) ( Int fd, Long offset, Int whence) +{ + Int res; + /* res = lseek( fd, offset, whence ); */ + res = vg_do_syscall3(__NR_lseek, fd, (UInt)offset, whence); + if (VG_(is_kerror)(res)) res = -1; + return res; +} + Int VG_(stat) ( Char* file_name, struct vki_stat* buf ) { Int res; @@ -1211,6 +1220,26 @@ } +/* Support for a getdents. */ +Int VG_(getdents) (UInt fd, struct vki_dirent *dirp, UInt count) +{ + Int res; + /* res = getdents( fd, dirp, count ); */ + res = vg_do_syscall3(__NR_getdents, fd, (UInt)dirp, count); + if (VG_(is_kerror)(res)) res = -1; + return res; +} + +/* Support for a readlink. */ +Int VG_(readlink) (Char* path, Char* buf, UInt bufsiz) +{ + Int res; + /* res = readlink( path, buf, bufsiz ); */ + res = vg_do_syscall3(__NR_readlink, (UInt)path, (UInt)buf, bufsiz); + if (VG_(is_kerror)(res)) res = -1; + return res; +} + /* You'd be amazed how many places need to know the current pid. */ Int VG_(getpid) ( void ) { --- valgrind/coregrind/vg_syscalls.c.~1~ Sun Apr 20 14:21:58 2003 +++ valgrind/coregrind/vg_syscalls.c Sun Apr 20 14:21:58 2003 @@ -56,6 +56,7 @@ if (VG_(clo_trace_syscalls)) \ VG_(printf)(format, ## args) + /* --------------------------------------------------------------------- Doing mmap, munmap, mremap, mprotect ------------------------------------------------------------------ */ @@ -195,7 +196,179 @@ return False; } +/* One of these is allocated for each open file descriptor. */ + +typedef struct OpenFds +{ + Int fd; /* The file descriptor */ + Char *pathname; /* NULL if not a regular file */ + ExeContext *where_opened; /* NULL if inherited from parent */ + struct OpenFds *next, *prev; +} OpenFds; + +/* List of allocated file descriptors. */ + +static OpenFds *allocated_fds; + +/* Count of open file descriptors. */ + +static int fd_count = 0; + +/* Given a file descriptor, attempt to deduce it's filename. To do this, + we use /proc/self/fd/. If this doesn't point to a file, or if it + doesn't exist, we just return NULL. Otherwise, we return a pointer + to the file name, which the caller is responsible for freeing. */ + +static +Char *resolve_fname(Int fd) +{ + char tmp[28], buf[PATH_MAX]; + + VG_(sprintf)(tmp, "/proc/self/fd/%d", fd); + VG_(memset)(buf, 0, PATH_MAX); + + if(VG_(readlink)(tmp, buf, PATH_MAX) == -1) + return NULL; + + return ((buf[0] == '/') ? VG_(strdup)(buf) : NULL); +} + + +/* Note the fact that a file descriptor was just closed. */ + +static +void record_fd_close(Int tid, Int fd) +{ + OpenFds *i = allocated_fds; + + while(i) { + if(i->fd == fd) { + if(i->prev) + i->prev->next = i->next; + else + allocated_fds = i->next; + if(i->next) + i->next->prev = i->prev; + if(i->pathname) + VG_(free) (i->pathname); + VG_(free) (i); + fd_count--; + break; + } + i = i->next; + } + + if(i == NULL) { + VG_(record_pthread_error)(tid, "Invalid close()"); + VG_(message)(Vg_UserMsg, + " Attempting to close unopened file descriptor %d", fd); + } +} + +/* Note the fact that a file descriptor was just opened. If the + tid is -1, this indicates an inherited fd. If the pathname is NULL, + this indicates a non-standard file (i.e. a pipe or socket or some + such thing.) */ + static +void record_fd_open(Int tid, Int fd, char *pathname) +{ + OpenFds *f = VG_(malloc)(sizeof(OpenFds)); + + f->fd = fd; + f->pathname = pathname; + f->where_opened = (tid == -1) ? + NULL : VG_(get_ExeContext)(&VG_(threads)[tid]); + f->prev = NULL; + f->next = allocated_fds; + if(allocated_fds) + allocated_fds->prev = f; + allocated_fds = f; + fd_count++; +} + +/* Dump out a summary, and optionally a more detailed list, of open file + descriptors. */ + +void VG_(fd_stats) () +{ + OpenFds *i = allocated_fds; + + VG_(message)(Vg_UserMsg, + "FILE DESCRIPTORS: %d open at exit.", fd_count); + + if(!VG_(clo_fd_list)) { + VG_(message)(Vg_UserMsg, "For a list of open file descriptors, " + "rerun with: --fd-list=yes"); + VG_(message)(Vg_UserMsg, ""); + VG_(message)(Vg_UserMsg, ""); + return; + } + + while(i) { + if(i->pathname) { + VG_(message)(Vg_UserMsg, "Open file descriptor %d: %s", i->fd, + i->pathname); + } else { + VG_(message)(Vg_UserMsg, "Open file descriptor %d:", i->fd); + } + if(i->where_opened) { + VG_(pp_ExeContext)(i->where_opened); + VG_(message)(Vg_UserMsg, ""); + } else { + VG_(message)(Vg_UserMsg, " "); + VG_(message)(Vg_UserMsg, ""); + } + i = i->next; + } + + VG_(message)(Vg_UserMsg, ""); +} + +/* If /proc/self/fd doesn't exist for some weird reason (like you've + got a kernel that doesn't have /proc support compiled in), then we + need to find out what file descriptors we inherited from our parent + process some other way. Right now, this is empty, but if I can think + of a clever way of doing this later, I'll fill it in with something. */ + +static +void do_hacky_preopened() +{ +} + +/* Initialize the list of open file descriptors with the file descriptors + we inherited from out parent process. */ + +void VG_(init_preopened_fds)() +{ + int f, ret; + struct vki_dirent d; + + f = VG_(open)("/proc/self/fd", VKI_O_RDONLY, 0); + if(f == -1) { + do_hacky_preopened(); + return; + } + + while((ret = VG_(getdents)(f, &d, sizeof(d))) != 0) { + if(ret == -1) + goto out; + + if(VG_(strcmp)(d.d_name, ".") && VG_(strcmp)(d.d_name, "..")) { + int fno = VG_(atoll)(d.d_name); + + if(fno != f) + record_fd_open(-1, fno, resolve_fname(fno)); + } + + VG_(lseek)(f, d.d_off, VKI_SEEK_SET); + } + +out: + VG_(close)(f); +} + +static UInt get_shm_size ( Int shmid ) { struct shmid_ds buf; @@ -288,6 +461,28 @@ } static +void check_cmsg_for_fds(Int tid, struct msghdr *msg) +{ + struct cmsghdr *cm = CMSG_FIRSTHDR(msg); + + while (cm) { + if (cm->cmsg_level == SOL_SOCKET && + cm->cmsg_type == SCM_RIGHTS ) { + int *fds = (int *) CMSG_DATA(cm); + int fdc = (cm->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) + / sizeof(int); + int i; + + for (i = 0; i < fdc; i++) + record_fd_open (tid, fds[i], resolve_fname(fds[i])); + } + + cm = CMSG_NXTHDR(msg, cm); + } +} + + +static void pre_mem_read_sockaddr ( ThreadState* tst, Char *description, struct sockaddr *sa, UInt salen ) @@ -1364,6 +1559,7 @@ SET_EAX(tid, res); } else { KERNEL_DO_SYSCALL(tid,res); + record_fd_close(tid, arg1); } break; @@ -1372,6 +1568,8 @@ MAYBE_PRINTF("dup ( %d ) --> ", arg1); KERNEL_DO_SYSCALL(tid,res); MAYBE_PRINTF("%d\n", res); + if (!VG_(is_kerror)(res)) + record_fd_open(tid, res, resolve_fname(res)); break; case __NR_dup2: /* syscall 63 */ @@ -1381,6 +1579,8 @@ MAYBE_PRINTF("SYSCALL[%d] dup2 ( %d, %d ) = %d\n", VG_(getpid)(), arg1, arg2, res); + if (!VG_(is_kerror)(res)) + record_fd_open(tid, arg2, resolve_fname(res)); break; case __NR_fcntl: /* syscall 55 */ @@ -2599,6 +2799,8 @@ SYSCALL_TRACK( pre_mem_read_asciiz, tst, "open(pathname)", arg1 ); KERNEL_DO_SYSCALL(tid,res); MAYBE_PRINTF("%d\n",res); + if (!VG_(is_kerror)(res)) + record_fd_open(tid, res, (Char*)arg1); break; case __NR_pipe: /* syscall 42 */ @@ -2613,6 +2815,10 @@ VG_(printf)("SYSCALL[%d] pipe --> (rd %d, wr %d)\n", VG_(getpid)(), ((UInt*)arg1)[0], ((UInt*)arg1)[1] ); + if (!VG_(is_kerror)(res)) { + record_fd_open(tid, ((UInt*)arg1)[0], NULL); + record_fd_open(tid, ((UInt*)arg1)[1], NULL); + } break; case __NR_poll: /* syscall 168 */ @@ -2863,8 +3069,11 @@ SYSCALL_TRACK( pre_mem_write, tst, "socketcall.socketpair(sv)", ((UInt*)arg2)[3], 2*sizeof(int) ); KERNEL_DO_SYSCALL(tid,res); - if (!VG_(is_kerror)(res)) + if (!VG_(is_kerror)(res)) { VG_TRACK( post_mem_write, ((UInt*)arg2)[3], 2*sizeof(int) ); + record_fd_open(tid, ((UInt*)((UInt*)arg2)[3])[0], NULL); + record_fd_open(tid, ((UInt*)((UInt*)arg2)[3])[1], NULL); + } break; case SYS_SOCKET: @@ -2872,6 +3081,8 @@ SYSCALL_TRACK( pre_mem_read, tst, "socketcall.socket(args)", arg2, 3*sizeof(Addr) ); KERNEL_DO_SYSCALL(tid,res); + if(!VG_(is_kerror)(res)) + record_fd_open(tid, res, NULL); break; case SYS_BIND: @@ -2905,6 +3116,8 @@ buf_and_len_post_check ( tst, res, addr_p, addrlen_p, "socketcall.accept(addrlen_out)" ); } + if(!VG_(is_kerror)(res)) + record_fd_open(tid, res, NULL); break; } @@ -3092,8 +3305,10 @@ KERNEL_DO_SYSCALL(tid,res); - if ( !VG_(is_kerror)( res ) ) + if ( !VG_(is_kerror)( res ) ) { msghdr_foreachfield( tst, msg, post_mem_write_recvmsg ); + check_cmsg_for_fds( tid, msg ); + } break; } --- valgrind/include/vg_skin.h.~1~ Sun Apr 20 14:21:58 2003 +++ valgrind/include/vg_skin.h Sun Apr 20 14:21:58 2003 @@ -403,12 +404,15 @@ /* ------------------------------------------------------------------ */ /* unistd.h, fcntl.h, sys/stat.h */ +extern Int VG_(getdents)( UInt fd, struct vki_dirent *dirp, UInt count ); +extern Int VG_(readlink)( Char* path, Char* buf, UInt bufsize ); extern Int VG_(getpid) ( void ); extern Int VG_(getppid) ( void ); extern Int VG_(open) ( const Char* pathname, Int flags, Int mode ); extern Int VG_(read) ( Int fd, void* buf, Int count); extern Int VG_(write) ( Int fd, void* buf, Int count); +extern Int VG_(lseek) ( Int fd, Long offset, Int whence); extern void VG_(close) ( Int fd ); /* Nb: VG_(rename)() declared in stdio.h section above */