" startup exit abexit valgrindabexit all none\n"
" --track-fds=no|yes|all track open file descriptors? [no]\n"
" all includes reporting inherited file descriptors\n"
+" --modify-fds=no|high modify newly open file descriptors? [no]\n"
" --time-stamp=no|yes add timestamps to log messages? [no]\n"
" --log-fd=<number> log messages to file descriptor [2=stderr]\n"
" --log-file=<file> log messages to <file>\n"
VG_(fmsg_bad_option)(arg,
"Bad argument, should be 'yes', 'all' or 'no'\n");
}
+ else if VG_STR_CLO(arg, "--modify-fds", tmp_str) {
+ if (VG_(strcmp)(tmp_str, "high") == 0)
+ VG_(clo_modify_fds) = 1;
+ else if (VG_(strcmp)(tmp_str, "no") == 0)
+ VG_(clo_modify_fds) = 0;
+ else
+ VG_(fmsg_bad_option)(arg,
+ "Bad argument, should be 'high' or 'no'\n");
+ }
else if VG_BOOL_CLOM(cloPD, arg, "--trace-children", VG_(clo_trace_children)) {}
else if VG_BOOL_CLOM(cloPD, arg, "--child-silent-after-fork",
VG_(clo_child_silent_after_fork)) {}
Bool VG_(clo_run_libc_freeres) = True;
Bool VG_(clo_run_cxx_freeres) = True;
UInt VG_(clo_track_fds) = 0;
+UInt VG_(clo_modify_fds) = 0;
Bool VG_(clo_show_below_main)= False;
Bool VG_(clo_keep_debuginfo) = False;
Bool VG_(clo_show_emwarns) = False;
// Returned string must not be modified nor free'd.
extern const HChar *ML_(find_fd_recorded_by_fd)(Int fd);
+extern int ML_(get_next_new_fd)(Int fd);
+
// Used when killing threads -- we must not kill a thread if it's the thread
// that would do Valgrind's final cleanup and output.
extern
#undef UW
#undef SR
+/* Helper macro for POST handlers that return a new file in RES.
+ If possible sets RES (through SET_STATUS_Success) to a new
+ (not yet seem before) file descriptor. */
+#define POST_newFd_RES \
+ do { \
+ if (VG_(clo_modify_fds) == 1) { \
+ int newFd = ML_(get_next_new_fd)(RES); \
+ if (newFd != RES) \
+ SET_STATUS_Success(newFd); \
+ } \
+ } while (0)
/////////////////////////////////////////////////////////////////
/* Count of open file descriptors. */
static Int fd_count = 0;
+/* Last used file descriptor for "new" fds.
+ Used (and updated) in get_next_new_fd to find a new (not yet used)
+ fd number to return in syscall wrappers. */
+static Int last_new_fd = 0;
+
+/* Replace (dup2) the fd with the highest file descriptor available.
+ Close the fd and return the newly created file descriptor on success.
+ Keep track of the last_new_fd and return the initial fd on failure. */
+int ML_(get_next_new_fd)(int fd)
+{
+ int next_new_fd;
+
+ /* Check if last_new needs to wrap around. */
+ if (last_new_fd == 0)
+ last_new_fd = VG_(fd_hard_limit);
+
+ next_new_fd = last_new_fd - 1;
+
+ /* Find the next fd number not in use. If we have to wrap around,
+ just use fd itself. */
+ while (next_new_fd >= 0 && ML_(fd_recorded)(next_new_fd))
+ next_new_fd--;
+ if (next_new_fd < 0)
+ next_new_fd = fd;
+
+ /* Duplicate and close the existing fd if needed. */
+ if (next_new_fd != fd) {
+ SysRes res = VG_(dup2)(fd, next_new_fd);
+ if (!sr_isError(res))
+ VG_(close)(fd);
+ else
+ next_new_fd = fd;
+
+ /* Record what the last new fd was we returned. */
+ last_new_fd = next_new_fd;
+ } else {
+ /* There was no lower "new" fd found. Lets wrap around for
+ the next round. */
+ last_new_fd = VG_(fd_hard_limit);
+ }
+
+ return next_new_fd;
+}
Int ML_(get_fd_count)(void)
POST(sys_dup)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "dup", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
POST(sys_open)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "open", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
POST(sys_creat)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "creat", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
POST(sys_epoll_create)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "epoll_create", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
POST(sys_epoll_create1)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "epoll_create1", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
}
POST(sys_eventfd)
{
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "eventfd", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
}
POST(sys_eventfd2)
{
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "eventfd2", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
POST(sys_fanotify_init)
{
+ POST_newFd_RES;
vg_assert(SUCCESS);
if (!ML_(fd_allowed)(RES, "fanotify_init", tid, True)) {
VG_(close)(RES);
POST(sys_inotify_init)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "inotify_init", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
POST(sys_openat)
{
vg_assert(SUCCESS);
+ POST_newFd_RES;
if (!ML_(fd_allowed)(RES, "openat", tid, True)) {
VG_(close)(RES);
SET_STATUS_Failure( VKI_EMFILE );
</listitem>
</varlistentry>
+ <varlistentry id="opt.modify-fds" xreflabel="--modify-fds">
+ <term>
+ <option><![CDATA[--modify-fds=<no|high> [default: no] ]]></option>
+ </term>
+ <listitem>
+ <para>When enabled, when the program opens a new file descriptor,
+ the highest available file descriptor is returned instead of the
+ lowest one.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="opt.time-stamp" xreflabel="--time-stamp">
<term>
<option><![CDATA[--time-stamp=<yes|no> [default: no] ]]></option>
/* Track open file descriptors? 0 = No, 1 = Yes, 2 = All (including std) */
extern UInt VG_(clo_track_fds);
+extern UInt VG_(clo_modify_fds);
+
/* Used to expand file names. "option_name" is the option name, eg.
"--log-file". 'format' is what follows, eg. "cachegrind.out.%p". In
log-track-fds.stderr.exp log-track-fds.vgtest \
xml-track-fds.stderr.exp xml-track-fds.vgtest \
fdbaduse.stderr.exp fdbaduse.vgtest \
- use_after_close.stderr.exp use_after_close.vgtest
+ use_after_close.stderr.exp use_after_close.vgtest \
+ track_new.stderr.exp track_new.stdout.exp \
+ track_new.stderr.exp.debian32 track_new.vgtest
check_PROGRAMS = \
socket_close \
file_dclose \
fdbaduse \
- use_after_close
+ use_after_close \
+ track_new
if HAVE_STATIC_LIBC
if ! VGCONF_OS_IS_LINUX
startup exit abexit valgrindabexit all none
--track-fds=no|yes|all track open file descriptors? [no]
all includes reporting inherited file descriptors
+ --modify-fds=no|high modify newly open file descriptors? [no]
--time-stamp=no|yes add timestamps to log messages? [no]
--log-fd=<number> log messages to file descriptor [2=stderr]
--log-file=<file> log messages to <file>
startup exit abexit valgrindabexit all none
--track-fds=no|yes|all track open file descriptors? [no]
all includes reporting inherited file descriptors
+ --modify-fds=no|high modify newly open file descriptors? [no]
--time-stamp=no|yes add timestamps to log messages? [no]
--log-fd=<number> log messages to file descriptor [2=stderr]
--log-file=<file> log messages to <file>
perl -p -e 's/^Open file descriptor [0-9]*: .*/Open file descriptor ...: .../' |
perl -p -e 's/^Open file descriptor [0-9]*:$/Open file descriptor ...:/' |
perl -p -e 's/File descriptor [0-9]*: .* is already closed/File descriptor ...: ... is already closed/' |
+perl -p -e 's/File descriptor [0-9]* was closed already/File descriptor was closed already/' |
perl -p -e 's/127.0.0.1:[0-9]*/127.0.0.1:.../g' |
perl -p -e 's/socket\.c:[1-9][0-9]*/in \/...libc.../' |
perl -p -e "s/: open \(/: creat (/" |
# arm64 write resolved to file:line with debuginfo
perl -p -e "s/write\.c:[1-9][0-9]*/in \/...libc.../" |
+#ppc64le
+sed 's/__dprintf.*/dprintf/' |
+
+# Remove "internal" _functions
+sed '/by 0x........: _/d' |
+
+# Remove symbol versioning
+sed -E 's/ ([a-zA-Z0-9_]+)@@?[A-Z0-9._]+/ \1/' |
+
+perl -p -e "s/\(dprintf.c:[0-9]*\)/(in \/...libc...)/" |
+perl -p -e "s/\(open.c:[0-9]*\)/(in \/...libc...)/" |
+perl -p -e "s/\(lseek(?:64)?.c:[0-9]*\)/(in \/...libc...)/" |
# FreeBSD specific fdleak filters
perl -p -e 's/ _close / close /;s/ _openat / creat /;s/ _write/ write/;s/internet/AF_INET socket 4: 127.0.0.1:... <-> 127.0.0.1:.../' |
sed "s/by 0x........: (below main)/by 0x........: main/" |
sed "s/by 0x........: main (.*)/by 0x........: main/" |
+sed "s/by 0x........: open (.*)/by 0x........: open/" |
+sed "s/by 0x........: write (.*)/by 0x........: write/" |
+sed "s/by 0x........: close (.*)/by 0x........: close/" |
# With glibc debuginfo installed we might see syscall-template.S,
# dup2.c close.c or creat64.c
--- /dev/null
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int
+main ()
+{
+ int oldfd = open ("foobar.txt", O_RDWR|O_CREAT, S_IRUSR | S_IWUSR);
+ /*... do something with oldfd ...*/
+ close (oldfd);
+
+ /* Lets open another file... */
+ int newfd = open ("foobad.txt", O_RDWR|O_CREAT, S_IRUSR | S_IWUSR);
+ /* ... oops we are using the wrong fd (but same number...) */
+ dprintf (oldfd, "some new text\n");
+
+ close (newfd);
+ return 0;
+}
--- /dev/null
+File descriptor was closed already
+ at 0x........: write (in /...libc...)
+ by 0x........: dprintf (in /...libc...)
+ by 0x........: main
+ Previously closed
+ at 0x........: close (in /...libc...)
+ by 0x........: main
+ Originally opened
+ at 0x........: creat (in /...libc...)
+ by 0x........: main
--- /dev/null
+File descriptor was closed already
+ at 0x........: llseek (in /...libc...)
+ by 0x........: dprintf (in /...libc...)
+ by 0x........: main
+ Previously closed
+ at 0x........: close (in /...libc...)
+ by 0x........: main
+ Originally opened
+ at 0x........: creat (in /...libc...)
+ by 0x........: main
--- /dev/null
+prog: track_new
+prereq: test -x track_new
+vgopts: -q --track-fds=yes --modify-fds=high
+stderr_filter: filter_fdleak
bad
-File descriptor 3 was closed already
+File descriptor was closed already
at 0x........: write (in /...libc...)
by 0x........: main
Previously closed