summaryrefslogtreecommitdiffstats
path: root/lldb/examples/interposing
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/examples/interposing')
-rw-r--r--lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp972
-rw-r--r--lldb/examples/interposing/darwin/fd_interposing/Makefile7
2 files changed, 979 insertions, 0 deletions
diff --git a/lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp b/lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp
new file mode 100644
index 00000000000..88130f5a4ab
--- /dev/null
+++ b/lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp
@@ -0,0 +1,972 @@
+//===-- FDInterposing.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file helps with catching double close calls on unix integer file
+// descriptors by interposing functions for all file descriptor create and
+// close operations. A stack backtrace for every create and close function is
+// maintained, and every create and close operation is logged. When a double
+// file descriptor close is encountered, it will be logged.
+//
+// To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES
+// environment variable as follows:
+// For sh:
+// DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable
+// For tcsh:
+// (setenv DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib ; /path/to/executable)
+//
+// Other environment variables that can alter the default actions of this
+// interposing shared library include:
+//
+// "FileDescriptorStackLoggingNoCompact"
+//
+// With this environment variable set, all file descriptor create and
+// delete operations will be permanantly maintained in the event map.
+// The default action is to compact the create/delete events by removing
+// any previous file descriptor create events that are matched with a
+// corresponding file descriptor delete event when the next valid file
+// descriptor create event is detected.
+//
+// "FileDescriptorMinimalLogging"
+//
+// By default every file descriptor create and delete operation is logged
+// (to STDOUT by default, see the "FileDescriptorLogFile"). This can be
+// suppressed to only show errors and warnings by setting this environment
+// variable (the value in not important).
+//
+// "FileDescriptorLogFile=<path>"
+//
+// By default logging goes to STDOUT_FILENO, but this can be changed by
+// setting FileDescriptorLogFile. The value is a path to a file that
+// will be opened and used for logging.
+//===----------------------------------------------------------------------===//
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <execinfo.h>
+#include <libgen.h>
+#include <mach-o/dyld.h>
+#include <mach-o/dyld-interposing.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <tr1/memory> // for std::tr1::shared_ptr
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include <map>
+
+//----------------------------------------------------------------------
+/// @def DISALLOW_COPY_AND_ASSIGN(TypeName)
+/// Macro definition for easily disallowing copy constructor and
+/// assignment operators in C++ classes.
+//----------------------------------------------------------------------
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+TypeName(const TypeName&); \
+const TypeName& operator=(const TypeName&)
+
+namespace fd_interposing {
+
+//----------------------------------------------------------------------
+// String class so we can get formatted strings without having to worry
+// about the memory storage since it will allocate the memory it needs.
+//----------------------------------------------------------------------
+class String
+{
+public:
+ String () :
+ m_str (NULL)
+ {}
+
+ String (const char *format, ...) :
+ m_str (NULL)
+ {
+ va_list args;
+ va_start (args, format);
+ vprintf (format, args);
+ va_end (args);
+ }
+
+ ~String()
+ {
+ reset();
+ }
+
+ void
+ reset (char *s = NULL)
+ {
+ if (m_str)
+ ::free (m_str);
+ m_str = s;
+ }
+
+ const char *
+ c_str () const
+ {
+ return m_str;
+ }
+
+ void
+ printf (const char *format, ...)
+ {
+ va_list args;
+ va_start (args, format);
+ vprintf (format, args);
+ va_end (args);
+ }
+ void
+ vprintf (const char *format, va_list args)
+ {
+ reset();
+ ::vasprintf (&m_str, format, args);
+ }
+
+ void
+ log (int log_fd)
+ {
+ if (m_str && log_fd >= 0)
+ {
+ const int len = strlen(m_str);
+ if (len > 0)
+ {
+ write (log_fd, m_str, len);
+ const char last_char = m_str[len-1];
+ if (!(last_char == '\n' || last_char == '\r'))
+ write (log_fd, "\n", 1);
+ }
+ }
+ }
+protected:
+ char *m_str;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN (String);
+};
+
+//----------------------------------------------------------------------
+// Type definitions
+//----------------------------------------------------------------------
+typedef std::vector<void *> Frames;
+class FDEvent;
+typedef std::vector<void *> Frames;
+typedef std::tr1::shared_ptr<FDEvent> FDEventSP;
+typedef std::tr1::shared_ptr<String> StringSP;
+
+
+//----------------------------------------------------------------------
+// FDEvent
+//
+// A class that describes a file desciptor event.
+//
+// File descriptor events fall into one of two categories: create events
+// and delete events.
+//----------------------------------------------------------------------
+class FDEvent
+{
+public:
+ FDEvent (int fd, int err, const StringSP &string_sp, bool is_create, const Frames& frames) :
+ m_string_sp (string_sp),
+ m_frames (frames.begin(), frames.end()),
+ m_fd (fd),
+ m_err (err),
+ m_is_create (is_create)
+ {}
+
+ ~FDEvent () {}
+
+ bool
+ IsCreateEvent() const
+ {
+ return m_is_create;
+ }
+
+ bool
+ IsDeleteEvent() const
+ {
+ return !m_is_create;
+ }
+
+ Frames &
+ GetFrames ()
+ {
+ return m_frames;
+ }
+
+ const Frames &
+ GetFrames () const
+ {
+ return m_frames;
+ }
+
+ int
+ GetFD () const
+ {
+ return m_fd;
+ }
+
+ int
+ GetError () const
+ {
+ return m_err;
+ }
+
+ void
+ Dump (int log_fd) const;
+
+ void
+ SetCreateEvent (FDEventSP &create_event_sp)
+ {
+ m_create_event_sp = create_event_sp;
+ }
+
+private:
+ // A shared pointer to a String that describes this event in
+ // detail (all args and return and error values)
+ StringSP m_string_sp;
+ // The frames for the stack backtrace for this event
+ Frames m_frames;
+ // If this is a file descriptor delete event, this might contain
+ // the correspoding file descriptor create event
+ FDEventSP m_create_event_sp;
+ // The file descriptor for this event
+ int m_fd;
+ // The error code (if any) for this event
+ int m_err;
+ // True if this event is a file descriptor create event, false
+ // if it is a file descriptor delete event
+ bool m_is_create;
+};
+
+//----------------------------------------------------------------------
+// Templatized class that will save errno only if the "value" it is
+// constructed with is equal to INVALID. When the class goes out of
+// scope, it will restore errno if it was saved.
+//----------------------------------------------------------------------
+template <int INVALID>
+class Errno
+{
+public:
+ // Save errno only if we are supposed to
+ Errno (int value) :
+ m_saved_errno ((value == INVALID) ? errno : 0),
+ m_restore (value == INVALID)
+ {
+ }
+
+ // Restore errno only if we are supposed to
+ ~Errno()
+ {
+ if (m_restore)
+ errno = m_saved_errno;
+ }
+
+ // Accessor for the saved value of errno
+ int
+ get_errno() const
+ {
+ return m_saved_errno;
+ }
+
+protected:
+ const int m_saved_errno;
+ const bool m_restore;
+};
+
+typedef Errno<-1> InvalidFDErrno;
+typedef Errno<-1> NegativeErrorErrno;
+typedef std::vector<FDEventSP> FDEventArray;
+typedef std::map<int, FDEventArray> FDEventMap;
+
+//----------------------------------------------------------------------
+// Globals
+//----------------------------------------------------------------------
+// Global event map that contains all file descriptor events. As file
+// descriptor create and close events come in, they will get filled
+// into this map (protected by g_mutex). When a file descriptor close
+// event is detected, the open event will be removed and placed into
+// the close event so if something tries to double close a file
+// descriptor we can show the previous close event and the file
+// desctiptor event that created it. When a new file descriptor create
+// event comes in, we will remove the previous one for that file
+// desctiptor unless the environment variable "FileDescriptorStackLoggingNoCompact"
+// is set. The file desctiptor history can be accessed using the
+// get_fd_history() function.
+static FDEventMap g_fd_event_map;
+// A mutex to protect access to our data structures in g_fd_event_map
+// and also our logging messages
+static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
+// Log all file descriptor create and close events by default. Only log
+// warnings and erros if the "FileDescriptorMinimalLogging" environment
+// variable is set.
+static int g_log_all_calls = 1;
+// We compact the file descriptor events by default. Set the environment
+// varible "FileDescriptorStackLoggingNoCompact" to keep a full history.
+static int g_compact = 1;
+
+//----------------------------------------------------------------------
+// Mutex class that will lock a mutex when it is constructed, and unlock
+// it when is goes out of scope
+//----------------------------------------------------------------------
+class Locker
+{
+public:
+ Locker (pthread_mutex_t *mutex_ptr) :
+ m_mutex_ptr(mutex_ptr)
+ {
+ ::pthread_mutex_lock (m_mutex_ptr);
+ }
+
+ // This allows clients to test try and acquire the mutex...
+ Locker (pthread_mutex_t *mutex_ptr, bool &lock_acquired) :
+ m_mutex_ptr(NULL)
+ {
+ lock_acquired = ::pthread_mutex_trylock(mutex_ptr) == 0;
+ if (lock_acquired)
+ m_mutex_ptr = mutex_ptr;
+ }
+
+ ~Locker ()
+ {
+ if (m_mutex_ptr)
+ ::pthread_mutex_unlock (m_mutex_ptr);
+ }
+protected:
+ pthread_mutex_t *m_mutex_ptr;
+};
+
+static void
+log (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+
+static void
+log (int log_fd, const FDEvent *event, const char *format, ...) __attribute__ ((format (printf, 3, 4)));
+
+static void
+backtrace_log (const char *format, ...) __attribute__ ((format (printf, 1, 2)));
+
+static void
+log_to_fd (int log_fd, const char *format, ...) __attribute__ ((format (printf, 2, 3)));
+
+static inline size_t
+get_backtrace (Frames &frame_buffer, size_t frames_to_remove)
+{
+ void *frames[2048];
+ int count = ::backtrace (&frames[0], sizeof(frames)/sizeof(void*));
+ if (count > frames_to_remove)
+ frame_buffer.assign (&frames[frames_to_remove], &frames[count]);
+ else
+ frame_buffer.assign (&frames[0], &frames[count]);
+ while (frame_buffer.back() < (void *)1024)
+ frame_buffer.pop_back();
+ return frame_buffer.size();
+}
+
+static int
+get_logging_fd ()
+{
+ static int g_log_fd = STDOUT_FILENO;
+ static char program_fullpath[PATH_MAX];
+ static int initialized = 0;
+
+ const int pid = getpid();
+
+ if (!initialized)
+ {
+ initialized = 1;
+
+ // Keep all stack info around for all fd create and delete calls.
+ // Otherwise we will remove the fd create call when a corresponding
+ // fd delete call is received
+ if (getenv("FileDescriptorStackLoggingNoCompact"))
+ g_compact = 0;
+
+ if (getenv("FileDescriptorMinimalLogging"))
+ g_log_all_calls = 0;
+
+ char program_basename[PATH_MAX];
+ // If DST is NULL, then return the number of bytes needed.
+ uint32_t len = sizeof(program_fullpath);
+ if (_NSGetExecutablePath (program_fullpath, &len) == 0)
+ {
+ strncpy (program_basename, program_fullpath, sizeof(program_basename));
+ const char *program_basename_cstr = basename(program_basename);
+ if (program_basename_cstr)
+ {
+ // Only let this interposing happen on the first time this matches
+ // and stop this from happening so any child processes don't also
+ // log their file descriptors
+ ::unsetenv ("DYLD_INSERT_LIBRARIES");
+ const char *log_path = getenv ("FileDescriptorLogFile");
+ if (log_path)
+ g_log_fd = ::creat (log_path, 0660);
+ if (g_log_fd >= 0)
+ log ("Logging file descriptor functions process '%s' (pid = %i)\n", program_fullpath, pid);
+ }
+ }
+ }
+ return g_log_fd;
+}
+
+void
+log_to_fd (int log_fd, const char *format, va_list args)
+{
+ if (format && format[0] && log_fd >= 0)
+ {
+ char buffer[PATH_MAX];
+ const int count = ::vsnprintf (buffer, sizeof(buffer), format, args);
+ if (count > 0)
+ write (log_fd, buffer, count);
+ }
+}
+
+void
+log_to_fd (int log_fd, const char *format, ...)
+{
+ if (format && format[0])
+ {
+ va_list args;
+ va_start (args, format);
+ log_to_fd (log_fd, format, args);
+ va_end (args);
+ }
+}
+
+void
+log (const char *format, va_list args)
+{
+ log_to_fd (get_logging_fd (), format, args);
+}
+
+void
+log (const char *format, ...)
+{
+ if (format && format[0])
+ {
+ va_list args;
+ va_start (args, format);
+ log (format, args);
+ va_end (args);
+ }
+}
+
+void
+log (int log_fd, const FDEvent *event, const char *format, ...)
+{
+ if (format && format[0])
+ {
+ va_list args;
+ va_start (args, format);
+ log_to_fd (log_fd, format, args);
+ va_end (args);
+ }
+ if (event)
+ event->Dump(log_fd);
+}
+
+void
+FDEvent::Dump (int log_fd) const
+{
+ if (log_fd >= 0)
+ {
+ log_to_fd (log_fd, "%s\n", m_string_sp->c_str());
+ if (!m_frames.empty())
+ ::backtrace_symbols_fd (m_frames.data(), m_frames.size(), log_fd);
+
+ if (m_create_event_sp)
+ {
+ log_to_fd (log_fd, "\nfd=%i was created with this event:\n", m_fd);
+ m_create_event_sp->Dump (log_fd);
+ log_to_fd (log_fd, "\n");
+ }
+ }
+}
+
+
+void
+backtrace_log (const char *format, ...)
+{
+ const int log_fd = get_logging_fd ();
+ if (log_fd >= 0)
+ {
+ if (format && format[0])
+ {
+ va_list args;
+ va_start (args, format);
+ log (format, args);
+ va_end (args);
+ }
+
+ Frames frames;
+ if (get_backtrace(frames, 3))
+ ::backtrace_symbols_fd (frames.data(), frames.size(), log_fd);
+ }
+
+}
+
+void
+save_backtrace (int fd, int err, const StringSP &string_sp, bool is_create);
+
+void
+save_backtrace (int fd, int err, const StringSP &string_sp, bool is_create)
+{
+ Frames frames;
+ get_backtrace(frames, 2);
+
+ FDEventSP fd_event_sp (new FDEvent (fd, err, string_sp, is_create, frames));
+
+ FDEventMap::iterator pos = g_fd_event_map.find (fd);
+
+ if (pos != g_fd_event_map.end())
+ {
+ // We have history for this fd...
+
+ FDEventArray &event_array = g_fd_event_map[fd];
+ if (fd_event_sp->IsCreateEvent())
+ {
+ // The current fd event is a function that creates
+ // a descriptor, check in case last event was
+ // a create event.
+ if (event_array.back()->IsCreateEvent())
+ {
+ const int log_fd = get_logging_fd();
+ // Two fd create functions in a row, we missed
+ // a function that closes a fd...
+ log (log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor create event fd=%i (we missed a file descriptor close event):\n", fd);
+ }
+ else if (g_compact)
+ {
+ // We are compacting so we remove previous create event
+ // when we get the correspinding delete event
+ event_array.pop_back();
+ }
+ }
+ else
+ {
+ // The current fd event is a function that deletes
+ // a descriptor, check in case last event for this
+ // fd was a delete event (double close!)
+ if (event_array.back()->IsDeleteEvent())
+ {
+ const int log_fd = get_logging_fd();
+ // Two fd delete functions in a row, we must
+ // have missed some function that opened a descriptor
+ log (log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor close event for fd=%d (we missed the file descriptor create event):\n", fd);
+ }
+ else if (g_compact)
+ {
+ // Since this is a close event, we want to remember the open event
+ // that this close if for...
+ fd_event_sp->SetCreateEvent(event_array.back());
+ // We are compacting so we remove previous create event
+ // when we get the correspinding delete event
+ event_array.pop_back();
+ }
+ }
+
+ event_array.push_back(fd_event_sp);
+ }
+ else
+ {
+ g_fd_event_map[fd].push_back(fd_event_sp);
+ }
+}
+
+//----------------------------------------------------------------------
+// socket() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+socket$__interposed__ (int domain, int type, int protocol)
+{
+ Locker locker (&g_mutex);
+ const int fd = ::socket (domain, type, protocol);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String);
+ if (fd == -1)
+ description_sp->printf("socket (domain = %i, type = %i, protocol = %i) => fd=%i errno = %i", domain, type, protocol, fd, fd_errno.get_errno());
+ else
+ description_sp->printf("socket (domain = %i, type = %i, protocol = %i) => fd=%i", domain, type, protocol, fd);
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// socketpair() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+socketpair$__interposed__ (int domain, int type, int protocol, int fds[2])
+{
+ Locker locker (&g_mutex);
+ fds[0] = -1;
+ fds[1] = -1;
+ const int err = socketpair (domain, type, protocol, fds);
+ NegativeErrorErrno err_errno(err);
+ StringSP description_sp(new String ("socketpair (domain=%i, type=%i, protocol=%i, {fd=%i, fd=%i}) -> err=%i", domain, type, protocol, fds[0], fds[1], err));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fds[0] >= 0)
+ save_backtrace (fds[0], err_errno.get_errno(), description_sp, true);
+ if (fds[1] >= 0)
+ save_backtrace (fds[1], err_errno.get_errno(), description_sp, true);
+ return err;
+}
+
+//----------------------------------------------------------------------
+// open() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+open$__interposed__ (const char *path, int oflag, int mode)
+{
+ Locker locker (&g_mutex);
+ int fd = -2;
+ StringSP description_sp(new String);
+ if (oflag & O_CREAT)
+ {
+ fd = open (path, oflag, mode);
+ description_sp->printf("open (path = '%s', oflag = %i, mode = %i) -> fd=%i", path, oflag, mode, fd);
+ }
+ else
+ {
+ fd = open (path, oflag);
+ description_sp->printf("open (path = '%s', oflag = %i) -> fd=%i", path, oflag, fd);
+ }
+
+ InvalidFDErrno fd_errno(fd);
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// open$NOCANCEL() interpose function
+//----------------------------------------------------------------------
+extern "C" int open$NOCANCEL(const char *, int, ...);
+extern "C" int __open_nocancel(const char *, int, ...);
+extern "C" int
+open$NOCANCEL$__interposed__ (const char *path, int oflag, int mode)
+{
+ Locker locker (&g_mutex);
+ const int fd = open$NOCANCEL (path, oflag, mode);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("open$NOCANCEL (path = '%s', oflag = %i, mode = %i) -> fd=%i", path, oflag, mode, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+extern "C" int __open_extended(const char *, int, uid_t, gid_t, int, struct kauth_filesec *);
+
+//----------------------------------------------------------------------
+// __open_extended() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+__open_extended$__interposed__ (const char *path, int oflag, uid_t uid, gid_t gid, int mode, struct kauth_filesec *fsacl)
+{
+ Locker locker (&g_mutex);
+ const int fd = __open_extended (path, oflag, uid, gid, mode, fsacl);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("__open_extended (path='%s', oflag=%i, uid=%i, gid=%i, mode=%i, fsacl=%p) -> fd=%i", path, oflag, uid, gid, mode, fsacl, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// kqueue() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+kqueue$__interposed__ (void)
+{
+ Locker locker (&g_mutex);
+ const int fd = ::kqueue ();
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("kqueue () -> fd=%i", fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+
+}
+
+//----------------------------------------------------------------------
+// shm_open() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+shm_open$__interposed__ (const char *path, int oflag, int mode)
+{
+ Locker locker (&g_mutex);
+ const int fd = shm_open (path, oflag, mode);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("shm_open (path = '%s', oflag = %i, mode = %i) -> fd=%i", path, oflag, mode, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// accept() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+accept$__interposed__ (int socket, struct sockaddr *address, socklen_t *address_len)
+{
+ Locker locker (&g_mutex);
+ const int fd = accept (socket, address, address_len);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("accept (socket=%i, ...) -> fd=%i", socket, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+
+//----------------------------------------------------------------------
+// accept$NOCANCEL() interpose function
+//----------------------------------------------------------------------
+extern "C" int accept$NOCANCEL (int, struct sockaddr * __restrict, socklen_t * __restrict);
+extern "C" int
+accept$NOCANCEL$__interposed__ (int socket, struct sockaddr *address, socklen_t *address_len)
+{
+ Locker locker (&g_mutex);
+ const int fd = accept$NOCANCEL (socket, address, address_len);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("accept$NOCANCEL (socket=%i, ...) -> fd=%i", socket, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// dup() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+dup$__interposed__ (int fd2)
+{
+ Locker locker (&g_mutex);
+ const int fd = dup (fd2);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("dup (fd2=%i) -> fd=%i", fd2, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// dup2() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+dup2$__interposed__ (int fd1, int fd2)
+{
+ Locker locker (&g_mutex);
+
+ const int fd = dup2(fd1, fd2);
+ InvalidFDErrno fd_errno(fd);
+ StringSP description_sp(new String ("dup2 fd1=%i, fd2=%i) -> fd=%i", fd1, fd2, fd));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ // If "fd2" is already opened, it will be closed during the
+ // dup2 call below, so we need to see if we have fd2 in our
+ // open map and treat it as a close(fd2)
+ FDEventMap::iterator pos = g_fd_event_map.find (fd2);
+ if (pos != g_fd_event_map.end() && pos->second.back()->IsCreateEvent())
+ save_backtrace (fd2, 0, description_sp, false);
+
+ if (fd >= 0)
+ save_backtrace (fd, fd_errno.get_errno(), description_sp, true);
+ return fd;
+}
+
+//----------------------------------------------------------------------
+// close() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+close$__interposed__ (int fd)
+{
+ Locker locker (&g_mutex);
+ const int err = close(fd);
+ NegativeErrorErrno err_errno(err);
+ StringSP description_sp (new String);
+ if (err == -1)
+ description_sp->printf("close (fd=%i) => %i errno = %i (%s))", fd, err, err_errno.get_errno(), strerror(err_errno.get_errno()));
+ else
+ description_sp->printf("close (fd=%i) => %i", fd, err);
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+
+ if (err == 0)
+ {
+ if (fd >= 0)
+ save_backtrace (fd, err, description_sp, false);
+ }
+ else if (err == -1)
+ {
+ if (err_errno.get_errno() == EBADF && fd != -1)
+ {
+ backtrace_log ("\nerror: close on fd=%d resulted in EBADF in process %i\n", fd, getpid());
+
+ FDEventMap::iterator pos = g_fd_event_map.find (fd);
+ if (pos != g_fd_event_map.end())
+ {
+ log (get_logging_fd(), pos->second.back().get(), "\nfd=%d was previously closed with this event:\n", fd);
+ }
+ }
+ }
+ return err;
+}
+
+//----------------------------------------------------------------------
+// close$NOCANCEL() interpose function
+//----------------------------------------------------------------------
+extern "C" int close$NOCANCEL(int);
+extern "C" int
+close$NOCANCEL$__interposed__ (int fd)
+{
+ Locker locker (&g_mutex);
+ const int err = close$NOCANCEL(fd);
+ NegativeErrorErrno err_errno(err);
+ StringSP description_sp (new String);
+ if (err == -1)
+ description_sp->printf("close$NOCANCEL (fd=%i) => %i errno = %i (%s))", fd, err, err_errno.get_errno(), strerror(err_errno.get_errno()));
+ else
+ description_sp->printf("close$NOCANCEL (fd=%i) => %i", fd, err);
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+
+ if (err == 0)
+ {
+ if (fd >= 0)
+ save_backtrace (fd, err, description_sp, false);
+ }
+ else if (err == -1)
+ {
+ if (err_errno.get_errno() == EBADF && fd != -1)
+ {
+ backtrace_log ("\nInvoking close$NOCANCEL (fd=%d) in process %i resulted in %i %s\n", fd, getpid(), err_errno.get_errno(), err_errno.get_errno() ? strerror (err_errno.get_errno()) : "");
+
+ FDEventMap::iterator pos = g_fd_event_map.find (fd);
+ if (pos != g_fd_event_map.end())
+ {
+ log (get_logging_fd(), pos->second.back().get(), "\nPrevious close(fd=%d) was done here:\n", fd);
+ }
+ }
+ }
+ return err;
+
+}
+
+//----------------------------------------------------------------------
+// pipe() interpose function
+//----------------------------------------------------------------------
+extern "C" int
+pipe$__interposed__ (int fds[2])
+{
+ Locker locker (&g_mutex);
+ fds[0] = -1;
+ fds[1] = -1;
+ const int err = pipe (fds);
+ const int saved_errno = errno;
+ StringSP description_sp(new String ("pipe ({fd=%i, fd=%i}) -> err=%i", fds[0], fds[1], err));
+ if (g_log_all_calls)
+ description_sp->log (get_logging_fd());
+ if (fds[0] >= 0)
+ save_backtrace (fds[0], saved_errno, description_sp, true);
+ if (fds[1] >= 0)
+ save_backtrace (fds[1], saved_errno, description_sp, true);
+ errno = saved_errno;
+ return err;
+}
+
+//----------------------------------------------------------------------
+// get_fd_history()
+//
+// This function allows runtime access to the file descriptor history.
+//
+// @param[in] log_fd
+// The file descriptor to log to
+//
+// @param[in] fd
+// The file descriptor whose history should be dumped
+//----------------------------------------------------------------------
+extern "C" void
+get_fd_history (int log_fd, int fd)
+{
+ // "create" below needs to be outside of the mutex locker scope
+ if (log_fd >= 0)
+ {
+ bool got_lock = false;
+ Locker locker (&g_mutex, got_lock);
+ if (got_lock)
+ {
+ FDEventMap::iterator pos = g_fd_event_map.find (fd);
+ log_to_fd (log_fd, "Dumping file descriptor history for fd=%i:\n", fd);
+ if (pos != g_fd_event_map.end())
+ {
+ FDEventArray &event_array = g_fd_event_map[fd];
+ const size_t num_events = event_array.size();
+ for (size_t i=0; i<num_events; ++i)
+ event_array[i]->Dump (log_fd);
+ }
+ else
+ {
+ log_to_fd (log_fd, "error: no file descriptor events found for fd=%i\n", fd);
+ }
+ }
+ else
+ {
+ log_to_fd (log_fd, "error: fd event mutex is locked...\n");
+ }
+ }
+}
+
+//----------------------------------------------------------------------
+// Interposing
+//----------------------------------------------------------------------
+// FD creation routines
+DYLD_INTERPOSE(accept$__interposed__, accept);
+DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL);
+DYLD_INTERPOSE(dup$__interposed__, dup);
+DYLD_INTERPOSE(dup2$__interposed__, dup2);
+DYLD_INTERPOSE(kqueue$__interposed__, kqueue);
+DYLD_INTERPOSE(open$__interposed__, open);
+DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL);
+DYLD_INTERPOSE(open$NOCANCEL$__interposed__, __open_nocancel);
+DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended);
+DYLD_INTERPOSE(pipe$__interposed__, pipe);
+DYLD_INTERPOSE(shm_open$__interposed__, shm_open);
+DYLD_INTERPOSE(socket$__interposed__, socket);
+DYLD_INTERPOSE(socketpair$__interposed__, socketpair);
+
+// FD deleting routines
+DYLD_INTERPOSE(close$__interposed__, close);
+DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);
+
+} // namespace fd_interposing
+
+
diff --git a/lldb/examples/interposing/darwin/fd_interposing/Makefile b/lldb/examples/interposing/darwin/fd_interposing/Makefile
new file mode 100644
index 00000000000..72c9263872c
--- /dev/null
+++ b/lldb/examples/interposing/darwin/fd_interposing/Makefile
@@ -0,0 +1,7 @@
+LEVEL = ../../../../test/make
+
+DYLIB_NO_EXECUTABLE := YES
+DYLIB_NAME := FDInterposing
+DYLIB_CXX_SOURCES := FDInterposing.cpp
+
+include $(LEVEL)/Makefile.rules
OpenPOWER on IntegriCloud