xref: /openbsd-src/gnu/llvm/lldb/examples/interposing/darwin/fd_interposing/FDInterposing.cpp (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 //===-- FDInterposing.cpp ---------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file helps with catching double close calls on unix integer file
10 // descriptors by interposing functions for all file descriptor create and
11 // close operations. A stack backtrace for every create and close function is
12 // maintained, and every create and close operation is logged. When a double
13 // file descriptor close is encountered, it will be logged.
14 //
15 // To enable the interposing in a darwin program, set the DYLD_INSERT_LIBRARIES
16 // environment variable as follows:
17 // For sh:
18 //  DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib /path/to/executable
19 // For tcsh:
20 //  (setenv DYLD_INSERT_LIBRARIES=/path/to/FDInterposing.dylib ;
21 //  /path/to/executable)
22 //
23 // Other environment variables that can alter the default actions of this
24 // interposing shared library include:
25 //
26 // "FileDescriptorStackLoggingNoCompact"
27 //
28 //      With this environment variable set, all file descriptor create and
29 //      delete operations will be permanantly maintained in the event map.
30 //      The default action is to compact the create/delete events by removing
31 //      any previous file descriptor create events that are matched with a
32 //      corresponding file descriptor delete event when the next valid file
33 //      descriptor create event is detected.
34 //
35 // "FileDescriptorMinimalLogging"
36 //
37 //      By default every file descriptor create and delete operation is logged
38 //      (to STDOUT by default, see the "FileDescriptorLogFile"). This can be
39 //      suppressed to only show errors and warnings by setting this environment
40 //      variable (the value in not important).
41 //
42 // "FileDescriptorLogFile=<path>"
43 //
44 //      By default logging goes to STDOUT_FILENO, but this can be changed by
45 //      setting FileDescriptorLogFile. The value is a path to a file that
46 //      will be opened and used for logging.
47 //===----------------------------------------------------------------------===//
48 
49 #include <assert.h>
50 #include <dirent.h>
51 #include <errno.h>
52 #include <execinfo.h>
53 #include <fcntl.h>
54 #include <libgen.h>
55 #include <mach-o/dyld-interposing.h>
56 #include <mach-o/dyld.h>
57 #include <map>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <string>
62 #include <sys/event.h>
63 #include <sys/mman.h>
64 #include <sys/socket.h>
65 #include <sys/time.h>
66 #include <sys/types.h>
67 #include <tr1/memory>
68 #include <unistd.h>
69 #include <vector>
70 
71 /// \def DISALLOW_COPY_AND_ASSIGN(TypeName)
72 ///     Macro definition for easily disallowing copy constructor and
73 ///     assignment operators in C++ classes.
74 #define DISALLOW_COPY_AND_ASSIGN(TypeName)                                     \
75   TypeName(const TypeName &);                                                  \
76   const TypeName &operator=(const TypeName &)
77 
78 extern "C" {
79 int accept$NOCANCEL(int, struct sockaddr *__restrict, socklen_t *__restrict);
80 int close$NOCANCEL(int);
81 int open$NOCANCEL(const char *, int, ...);
82 int __open_extended(const char *, int, uid_t, gid_t, int,
83                     struct kauth_filesec *);
84 }
85 
86 namespace fd_interposing {
87 
88 // String class so we can get formatted strings without having to worry
89 // about the memory storage since it will allocate the memory it needs.
90 class String {
91 public:
92   String() : m_str(NULL) {}
93 
94   String(const char *format, ...) : m_str(NULL) {
95     va_list args;
96     va_start(args, format);
97     vprintf(format, args);
98     va_end(args);
99   }
100 
101   ~String() { reset(); }
102 
103   void reset(char *s = NULL) {
104     if (m_str)
105       ::free(m_str);
106     m_str = s;
107   }
108 
109   const char *c_str() const { return m_str; }
110 
111   void printf(const char *format, ...) {
112     va_list args;
113     va_start(args, format);
114     vprintf(format, args);
115     va_end(args);
116   }
117   void vprintf(const char *format, va_list args) {
118     reset();
119     ::vasprintf(&m_str, format, args);
120   }
121 
122   void log(int log_fd) {
123     if (m_str && log_fd >= 0) {
124       const int len = strlen(m_str);
125       if (len > 0) {
126         write(log_fd, m_str, len);
127         const char last_char = m_str[len - 1];
128         if (!(last_char == '\n' || last_char == '\r'))
129           write(log_fd, "\n", 1);
130       }
131     }
132   }
133 
134 protected:
135   char *m_str;
136 
137 private:
138   DISALLOW_COPY_AND_ASSIGN(String);
139 };
140 
141 // Type definitions
142 typedef std::vector<void *> Frames;
143 class FDEvent;
144 typedef std::vector<void *> Frames;
145 typedef std::tr1::shared_ptr<FDEvent> FDEventSP;
146 typedef std::tr1::shared_ptr<String> StringSP;
147 
148 // FDEvent
149 //
150 // A class that describes a file desciptor event.
151 //
152 // File descriptor events fall into one of two categories: create events
153 // and delete events.
154 class FDEvent {
155 public:
156   FDEvent(int fd, int err, const StringSP &string_sp, bool is_create,
157           const Frames &frames)
158       : m_string_sp(string_sp), m_frames(frames.begin(), frames.end()),
159         m_fd(fd), m_err(err), m_is_create(is_create) {}
160 
161   ~FDEvent() {}
162 
163   bool IsCreateEvent() const { return m_is_create; }
164 
165   bool IsDeleteEvent() const { return !m_is_create; }
166 
167   Frames &GetFrames() { return m_frames; }
168 
169   const Frames &GetFrames() const { return m_frames; }
170 
171   int GetFD() const { return m_fd; }
172 
173   int GetError() const { return m_err; }
174 
175   void Dump(int log_fd) const;
176 
177   void SetCreateEvent(FDEventSP &create_event_sp) {
178     m_create_event_sp = create_event_sp;
179   }
180 
181 private:
182   // A shared pointer to a String that describes this event in
183   // detail (all args and return and error values)
184   StringSP m_string_sp;
185   // The frames for the stack backtrace for this event
186   Frames m_frames;
187   // If this is a file descriptor delete event, this might contain
188   // the correspoding file descriptor create event
189   FDEventSP m_create_event_sp;
190   // The file descriptor for this event
191   int m_fd;
192   // The error code (if any) for this event
193   int m_err;
194   // True if this event is a file descriptor create event, false
195   // if it is a file descriptor delete event
196   bool m_is_create;
197 };
198 
199 // Templatized class that will save errno only if the "value" it is
200 // constructed with is equal to INVALID. When the class goes out of
201 // scope, it will restore errno if it was saved.
202 template <int INVALID> class Errno {
203 public:
204   // Save errno only if we are supposed to
205   Errno(int value)
206       : m_saved_errno((value == INVALID) ? errno : 0),
207         m_restore(value == INVALID) {}
208 
209   // Restore errno only if we are supposed to
210   ~Errno() {
211     if (m_restore)
212       errno = m_saved_errno;
213   }
214 
215   // Accessor for the saved value of errno
216   int get_errno() const { return m_saved_errno; }
217 
218 protected:
219   const int m_saved_errno;
220   const bool m_restore;
221 };
222 
223 typedef Errno<-1> InvalidFDErrno;
224 typedef Errno<-1> NegativeErrorErrno;
225 typedef std::vector<FDEventSP> FDEventArray;
226 typedef std::map<int, FDEventArray> FDEventMap;
227 
228 // Globals
229 // Global event map that contains all file descriptor events. As file
230 // descriptor create and close events come in, they will get filled
231 // into this map (protected by g_mutex). When a file descriptor close
232 // event is detected, the open event will be removed and placed into
233 // the close event so if something tries to double close a file
234 // descriptor we can show the previous close event and the file
235 // desctiptor event that created it. When a new file descriptor create
236 // event comes in, we will remove the previous one for that file
237 // desctiptor unless the environment variable
238 // "FileDescriptorStackLoggingNoCompact"
239 // is set. The file desctiptor history can be accessed using the
240 // get_fd_history() function.
241 static FDEventMap g_fd_event_map;
242 // A mutex to protect access to our data structures in g_fd_event_map
243 // and also our logging messages
244 static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
245 // Log all file descriptor create and close events by default. Only log
246 // warnings and erros if the "FileDescriptorMinimalLogging" environment
247 // variable is set.
248 static int g_log_all_calls = 1;
249 // We compact the file descriptor events by default. Set the environment
250 // varible "FileDescriptorStackLoggingNoCompact" to keep a full history.
251 static int g_compact = 1;
252 // The current process ID
253 static int g_pid = -1;
254 static bool g_enabled = true;
255 // Mutex class that will lock a mutex when it is constructed, and unlock
256 // it when is goes out of scope
257 class Locker {
258 public:
259   Locker(pthread_mutex_t *mutex_ptr) : m_mutex_ptr(mutex_ptr) {
260     ::pthread_mutex_lock(m_mutex_ptr);
261   }
262 
263   // This allows clients to test try and acquire the mutex...
264   Locker(pthread_mutex_t *mutex_ptr, bool &lock_acquired) : m_mutex_ptr(NULL) {
265     lock_acquired = ::pthread_mutex_trylock(mutex_ptr) == 0;
266     if (lock_acquired)
267       m_mutex_ptr = mutex_ptr;
268   }
269 
270   ~Locker() {
271     if (m_mutex_ptr)
272       ::pthread_mutex_unlock(m_mutex_ptr);
273   }
274 
275 protected:
276   pthread_mutex_t *m_mutex_ptr;
277 };
278 
279 static void log(const char *format, ...) __attribute__((format(printf, 1, 2)));
280 
281 static void log(int log_fd, const FDEvent *event, const char *format, ...)
282     __attribute__((format(printf, 3, 4)));
283 
284 static void backtrace_log(const char *format, ...)
285     __attribute__((format(printf, 1, 2)));
286 
287 static void backtrace_error(const char *format, ...)
288     __attribute__((format(printf, 1, 2)));
289 
290 static void log_to_fd(int log_fd, const char *format, ...)
291     __attribute__((format(printf, 2, 3)));
292 
293 static inline size_t get_backtrace(Frames &frame_buffer,
294                                    size_t frames_to_remove) {
295   void *frames[2048];
296   int count = ::backtrace(&frames[0], sizeof(frames) / sizeof(void *));
297   if (count > frames_to_remove)
298     frame_buffer.assign(&frames[frames_to_remove], &frames[count]);
299   else
300     frame_buffer.assign(&frames[0], &frames[count]);
301   while (frame_buffer.back() < (void *)1024)
302     frame_buffer.pop_back();
303   return frame_buffer.size();
304 }
305 
306 static int g_log_fd = STDOUT_FILENO;
307 static int g_initialized = 0;
308 
309 const char *get_process_fullpath(bool force = false) {
310   static char g_process_fullpath[PATH_MAX] = {0};
311   if (force || g_process_fullpath[0] == '\0') {
312     // If DST is NULL, then return the number of bytes needed.
313     uint32_t len = sizeof(g_process_fullpath);
314     if (_NSGetExecutablePath(g_process_fullpath, &len) != 0)
315       strncpy(g_process_fullpath, "<error>", sizeof(g_process_fullpath));
316   }
317   return g_process_fullpath;
318 }
319 
320 // Returns the current process ID, or -1 if inserposing not enabled for
321 // this process
322 static int get_interposed_pid() {
323   if (!g_enabled)
324     return -1;
325 
326   const pid_t pid = getpid();
327   if (g_pid != pid) {
328     if (g_pid == -1) {
329       g_pid = pid;
330       log("Interposing file descriptor create and delete functions for %s "
331           "(pid=%i)\n",
332           get_process_fullpath(true), pid);
333     } else {
334       log("pid=%i: disabling interposing file descriptor create and delete "
335           "functions for child process %s (pid=%i)\n",
336           g_pid, get_process_fullpath(true), pid);
337       g_enabled = false;
338       return -1;
339     }
340     // Log when our process changes
341   }
342   return g_pid;
343 }
344 
345 static int get_logging_fd() {
346   if (!g_enabled)
347     return -1;
348 
349   if (!g_initialized) {
350     g_initialized = 1;
351 
352     const pid_t pid = get_interposed_pid();
353 
354     if (g_enabled) {
355       // Keep all stack info around for all fd create and delete calls.
356       // Otherwise we will remove the fd create call when a corresponding
357       // fd delete call is received
358       if (getenv("FileDescriptorStackLoggingNoCompact"))
359         g_compact = 0;
360 
361       if (getenv("FileDescriptorMinimalLogging"))
362         g_log_all_calls = 0;
363 
364       const char *log_path = getenv("FileDescriptorLogFile");
365       if (log_path)
366         g_log_fd = ::creat(log_path, 0660);
367       else
368         g_log_fd = STDOUT_FILENO;
369 
370       // Only let this interposing happen on the first time this matches
371       // and stop this from happening so any child processes don't also
372       // log their file descriptors
373       ::unsetenv("DYLD_INSERT_LIBRARIES");
374     } else {
375       log("pid=%i: logging disabled\n", getpid());
376     }
377   }
378   return g_log_fd;
379 }
380 
381 void log_to_fd(int log_fd, const char *format, va_list args) {
382   if (format && format[0] && log_fd >= 0) {
383     char buffer[PATH_MAX];
384     const int count = ::vsnprintf(buffer, sizeof(buffer), format, args);
385     if (count > 0)
386       write(log_fd, buffer, count);
387   }
388 }
389 
390 void log_to_fd(int log_fd, const char *format, ...) {
391   if (format && format[0]) {
392     va_list args;
393     va_start(args, format);
394     log_to_fd(log_fd, format, args);
395     va_end(args);
396   }
397 }
398 
399 void log(const char *format, va_list args) {
400   log_to_fd(get_logging_fd(), format, args);
401 }
402 
403 void log(const char *format, ...) {
404   if (format && format[0]) {
405     va_list args;
406     va_start(args, format);
407     log(format, args);
408     va_end(args);
409   }
410 }
411 
412 void log(int log_fd, const FDEvent *event, const char *format, ...) {
413   if (format && format[0]) {
414     va_list args;
415     va_start(args, format);
416     log_to_fd(log_fd, format, args);
417     va_end(args);
418   }
419   if (event)
420     event->Dump(log_fd);
421 }
422 
423 void FDEvent::Dump(int log_fd) const {
424   if (log_fd >= 0) {
425     log_to_fd(log_fd, "%s\n", m_string_sp->c_str());
426     if (!m_frames.empty())
427       ::backtrace_symbols_fd(m_frames.data(), m_frames.size(), log_fd);
428 
429     if (m_create_event_sp) {
430       log_to_fd(log_fd, "\nfd=%i was created with this event:\n", m_fd);
431       m_create_event_sp->Dump(log_fd);
432       log_to_fd(log_fd, "\n");
433     }
434   }
435 }
436 
437 void backtrace_log(const char *format, ...) {
438   const int log_fd = get_logging_fd();
439   if (log_fd >= 0) {
440     if (format && format[0]) {
441       va_list args;
442       va_start(args, format);
443       log(format, args);
444       va_end(args);
445     }
446 
447     Frames frames;
448     if (get_backtrace(frames, 2))
449       ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);
450   }
451 }
452 
453 void backtrace_error(const char *format, ...) {
454   const int pid = get_interposed_pid();
455   if (pid >= 0) {
456     const int log_fd = get_logging_fd();
457     if (log_fd >= 0) {
458       log("\nerror: %s (pid=%i): ", get_process_fullpath(), pid);
459 
460       if (format && format[0]) {
461         va_list args;
462         va_start(args, format);
463         log(format, args);
464         va_end(args);
465       }
466 
467       Frames frames;
468       if (get_backtrace(frames, 2))
469         ::backtrace_symbols_fd(frames.data(), frames.size(), log_fd);
470     }
471   }
472 }
473 
474 void save_backtrace(int fd, int err, const StringSP &string_sp,
475                     bool is_create) {
476   Frames frames;
477   get_backtrace(frames, 2);
478 
479   FDEventSP fd_event_sp(new FDEvent(fd, err, string_sp, is_create, frames));
480 
481   FDEventMap::iterator pos = g_fd_event_map.find(fd);
482 
483   if (pos != g_fd_event_map.end()) {
484     // We have history for this fd...
485 
486     FDEventArray &event_array = g_fd_event_map[fd];
487     if (fd_event_sp->IsCreateEvent()) {
488       // The current fd event is a function that creates
489       // a descriptor, check in case last event was
490       // a create event.
491       if (event_array.back()->IsCreateEvent()) {
492         const int log_fd = get_logging_fd();
493         // Two fd create functions in a row, we missed
494         // a function that closes a fd...
495         log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "
496                                        "create event fd=%i (we missed a file "
497                                        "descriptor close event):\n",
498             fd);
499       } else if (g_compact) {
500         // We are compacting so we remove previous create event
501         // when we get the correspinding delete event
502         event_array.pop_back();
503       }
504     } else {
505       // The current fd event is a function that deletes
506       // a descriptor, check in case last event for this
507       // fd was a delete event (double close!)
508       if (event_array.back()->IsDeleteEvent()) {
509         const int log_fd = get_logging_fd();
510         // Two fd delete functions in a row, we must
511         // have missed some function that opened a descriptor
512         log(log_fd, fd_event_sp.get(), "\nwarning: unmatched file descriptor "
513                                        "close event for fd=%d (we missed the "
514                                        "file descriptor create event):\n",
515             fd);
516       } else if (g_compact) {
517         // Since this is a close event, we want to remember the open event
518         // that this close if for...
519         fd_event_sp->SetCreateEvent(event_array.back());
520         // We are compacting so we remove previous create event
521         // when we get the correspinding delete event
522         event_array.pop_back();
523       }
524     }
525 
526     event_array.push_back(fd_event_sp);
527   } else {
528     g_fd_event_map[fd].push_back(fd_event_sp);
529   }
530 }
531 
532 // socket() interpose function
533 extern "C" int socket$__interposed__(int domain, int type, int protocol) {
534   const int pid = get_interposed_pid();
535   if (pid >= 0) {
536     Locker locker(&g_mutex);
537     const int fd = ::socket(domain, type, protocol);
538     InvalidFDErrno fd_errno(fd);
539     StringSP description_sp(new String);
540     if (fd == -1)
541       description_sp->printf("pid=%i: socket (domain = %i, type = %i, protocol "
542                              "= %i) => fd=%i  errno = %i",
543                              pid, domain, type, protocol, fd,
544                              fd_errno.get_errno());
545     else
546       description_sp->printf(
547           "pid=%i: socket (domain = %i, type = %i, protocol = %i) => fd=%i",
548           pid, domain, type, protocol, fd);
549     if (g_log_all_calls)
550       description_sp->log(get_logging_fd());
551     if (fd >= 0)
552       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
553     return fd;
554   } else {
555     return ::socket(domain, type, protocol);
556   }
557 }
558 
559 // socketpair() interpose function
560 extern "C" int socketpair$__interposed__(int domain, int type, int protocol,
561                                          int fds[2]) {
562   const int pid = get_interposed_pid();
563   if (pid >= 0) {
564     Locker locker(&g_mutex);
565     fds[0] = -1;
566     fds[1] = -1;
567     const int err = socketpair(domain, type, protocol, fds);
568     NegativeErrorErrno err_errno(err);
569     StringSP description_sp(
570         new String("pid=%i: socketpair (domain=%i, type=%i, protocol=%i, "
571                    "{fd=%i, fd=%i}) -> err=%i",
572                    pid, domain, type, protocol, fds[0], fds[1], err));
573     if (g_log_all_calls)
574       description_sp->log(get_logging_fd());
575     if (fds[0] >= 0)
576       save_backtrace(fds[0], err_errno.get_errno(), description_sp, true);
577     if (fds[1] >= 0)
578       save_backtrace(fds[1], err_errno.get_errno(), description_sp, true);
579     return err;
580   } else {
581     return socketpair(domain, type, protocol, fds);
582   }
583 }
584 
585 // open() interpose function
586 extern "C" int open$__interposed__(const char *path, int oflag, int mode) {
587   const int pid = get_interposed_pid();
588   if (pid >= 0) {
589     Locker locker(&g_mutex);
590     int fd = -2;
591     StringSP description_sp(new String);
592     if (oflag & O_CREAT) {
593       fd = ::open(path, oflag, mode);
594       description_sp->printf(
595           "pid=%i: open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
596           path, oflag, mode, fd);
597     } else {
598       fd = ::open(path, oflag);
599       description_sp->printf("pid=%i: open (path = '%s', oflag = %i) -> fd=%i",
600                              pid, path, oflag, fd);
601     }
602 
603     InvalidFDErrno fd_errno(fd);
604     if (g_log_all_calls)
605       description_sp->log(get_logging_fd());
606     if (fd >= 0)
607       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
608     return fd;
609   } else {
610     return ::open(path, oflag, mode);
611   }
612 }
613 
614 // open$NOCANCEL() interpose function
615 extern "C" int open$NOCANCEL$__interposed__(const char *path, int oflag,
616                                             int mode) {
617   const int pid = get_interposed_pid();
618   if (pid >= 0) {
619     Locker locker(&g_mutex);
620     const int fd = ::open$NOCANCEL(path, oflag, mode);
621     InvalidFDErrno fd_errno(fd);
622     StringSP description_sp(new String(
623         "pid=%i: open$NOCANCEL (path = '%s', oflag = %i, mode = %i) -> fd=%i",
624         pid, path, oflag, mode, fd));
625     if (g_log_all_calls)
626       description_sp->log(get_logging_fd());
627     if (fd >= 0)
628       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
629     return fd;
630   } else {
631     return ::open$NOCANCEL(path, oflag, mode);
632   }
633 }
634 
635 // __open_extended() interpose function
636 extern "C" int __open_extended$__interposed__(const char *path, int oflag,
637                                               uid_t uid, gid_t gid, int mode,
638                                               struct kauth_filesec *fsacl) {
639   const int pid = get_interposed_pid();
640   if (pid >= 0) {
641     Locker locker(&g_mutex);
642     const int fd = ::__open_extended(path, oflag, uid, gid, mode, fsacl);
643     InvalidFDErrno fd_errno(fd);
644     StringSP description_sp(
645         new String("pid=%i: __open_extended (path='%s', oflag=%i, uid=%i, "
646                    "gid=%i, mode=%i, fsacl=%p) -> fd=%i",
647                    pid, path, oflag, uid, gid, mode, fsacl, fd));
648     if (g_log_all_calls)
649       description_sp->log(get_logging_fd());
650     if (fd >= 0)
651       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
652     return fd;
653   } else {
654     return ::__open_extended(path, oflag, uid, gid, mode, fsacl);
655   }
656 }
657 
658 // kqueue() interpose function
659 extern "C" int kqueue$__interposed__(void) {
660   const int pid = get_interposed_pid();
661   if (pid >= 0) {
662     Locker locker(&g_mutex);
663     const int fd = ::kqueue();
664     InvalidFDErrno fd_errno(fd);
665     StringSP description_sp(new String("pid=%i: kqueue () -> fd=%i", pid, fd));
666     if (g_log_all_calls)
667       description_sp->log(get_logging_fd());
668     if (fd >= 0)
669       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
670     return fd;
671   } else {
672     return ::kqueue();
673   }
674 }
675 
676 // shm_open() interpose function
677 extern "C" int shm_open$__interposed__(const char *path, int oflag, int mode) {
678   const int pid = get_interposed_pid();
679   if (pid >= 0) {
680     Locker locker(&g_mutex);
681     const int fd = ::shm_open(path, oflag, mode);
682     InvalidFDErrno fd_errno(fd);
683     StringSP description_sp(new String(
684         "pid=%i: shm_open (path = '%s', oflag = %i, mode = %i) -> fd=%i", pid,
685         path, oflag, mode, fd));
686     if (g_log_all_calls)
687       description_sp->log(get_logging_fd());
688     if (fd >= 0)
689       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
690     return fd;
691   } else {
692     return ::shm_open(path, oflag, mode);
693   }
694 }
695 
696 // accept() interpose function
697 extern "C" int accept$__interposed__(int socket, struct sockaddr *address,
698                                      socklen_t *address_len) {
699   const int pid = get_interposed_pid();
700   if (pid >= 0) {
701     Locker locker(&g_mutex);
702     const int fd = ::accept(socket, address, address_len);
703     InvalidFDErrno fd_errno(fd);
704     StringSP description_sp(new String(
705         "pid=%i: accept (socket=%i, ...) -> fd=%i", pid, socket, fd));
706     if (g_log_all_calls)
707       description_sp->log(get_logging_fd());
708     if (fd >= 0)
709       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
710     return fd;
711   } else {
712     return ::accept(socket, address, address_len);
713   }
714 }
715 
716 // accept$NOCANCEL() interpose function
717 extern "C" int accept$NOCANCEL$__interposed__(int socket,
718                                               struct sockaddr *address,
719                                               socklen_t *address_len) {
720   const int pid = get_interposed_pid();
721   if (pid >= 0) {
722     Locker locker(&g_mutex);
723     const int fd = ::accept$NOCANCEL(socket, address, address_len);
724     InvalidFDErrno fd_errno(fd);
725     StringSP description_sp(new String(
726         "pid=%i: accept$NOCANCEL (socket=%i, ...) -> fd=%i", pid, socket, fd));
727     if (g_log_all_calls)
728       description_sp->log(get_logging_fd());
729     if (fd >= 0)
730       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
731     return fd;
732   } else {
733     return ::accept$NOCANCEL(socket, address, address_len);
734   }
735 }
736 
737 // dup() interpose function
738 extern "C" int dup$__interposed__(int fd2) {
739   const int pid = get_interposed_pid();
740   if (pid >= 0) {
741     Locker locker(&g_mutex);
742     const int fd = ::dup(fd2);
743     InvalidFDErrno fd_errno(fd);
744     StringSP description_sp(
745         new String("pid=%i: dup (fd2=%i) -> fd=%i", pid, fd2, fd));
746     if (g_log_all_calls)
747       description_sp->log(get_logging_fd());
748     if (fd >= 0)
749       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
750     return fd;
751   } else {
752     return ::dup(fd2);
753   }
754 }
755 
756 // dup2() interpose function
757 extern "C" int dup2$__interposed__(int fd1, int fd2) {
758   const int pid = get_interposed_pid();
759   if (pid >= 0) {
760     Locker locker(&g_mutex);
761     // If "fd2" is already opened, it will be closed during the
762     // dup2 call below, so we need to see if we have fd2 in our
763     // open map and treat it as a close(fd2)
764     FDEventMap::iterator pos = g_fd_event_map.find(fd2);
765     StringSP dup2_close_description_sp(
766         new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> will close (fd=%i)", pid,
767                    fd1, fd2, fd2));
768     if (pos != g_fd_event_map.end() && pos->second.back()->IsCreateEvent())
769       save_backtrace(fd2, 0, dup2_close_description_sp, false);
770 
771     const int fd = ::dup2(fd1, fd2);
772     InvalidFDErrno fd_errno(fd);
773     StringSP description_sp(new String("pid=%i: dup2 (fd1=%i, fd2=%i) -> fd=%i",
774                                        pid, fd1, fd2, fd));
775     if (g_log_all_calls)
776       description_sp->log(get_logging_fd());
777 
778     if (fd >= 0)
779       save_backtrace(fd, fd_errno.get_errno(), description_sp, true);
780     return fd;
781   } else {
782     return ::dup2(fd1, fd2);
783   }
784 }
785 
786 // close() interpose function
787 extern "C" int close$__interposed__(int fd) {
788   const int pid = get_interposed_pid();
789   if (pid >= 0) {
790     Locker locker(&g_mutex);
791     const int err = close(fd);
792     NegativeErrorErrno err_errno(err);
793     StringSP description_sp(new String);
794     if (err == -1)
795       description_sp->printf("pid=%i: close (fd=%i) => %i errno = %i (%s))",
796                              pid, fd, err, err_errno.get_errno(),
797                              strerror(err_errno.get_errno()));
798     else
799       description_sp->printf("pid=%i: close (fd=%i) => %i", pid, fd, err);
800     if (g_log_all_calls)
801       description_sp->log(get_logging_fd());
802 
803     if (err == 0) {
804       if (fd >= 0)
805         save_backtrace(fd, err, description_sp, false);
806     } else if (err == -1) {
807       if (err_errno.get_errno() == EBADF && fd != -1) {
808         backtrace_error("close (fd=%d) resulted in EBADF:\n", fd);
809 
810         FDEventMap::iterator pos = g_fd_event_map.find(fd);
811         if (pos != g_fd_event_map.end()) {
812           log(get_logging_fd(), pos->second.back().get(),
813               "\nfd=%d was previously %s with this event:\n", fd,
814               pos->second.back()->IsCreateEvent() ? "opened" : "closed");
815         }
816       }
817     }
818     return err;
819   } else {
820     return close(fd);
821   }
822 }
823 
824 // close$NOCANCEL() interpose function
825 extern "C" int close$NOCANCEL$__interposed__(int fd) {
826   const int pid = get_interposed_pid();
827   if (pid >= 0) {
828     Locker locker(&g_mutex);
829     const int err = close$NOCANCEL(fd);
830     NegativeErrorErrno err_errno(err);
831     StringSP description_sp(new String);
832     if (err == -1)
833       description_sp->printf(
834           "pid=%i: close$NOCANCEL (fd=%i) => %i errno = %i (%s))", pid, fd, err,
835           err_errno.get_errno(), strerror(err_errno.get_errno()));
836     else
837       description_sp->printf("pid=%i: close$NOCANCEL (fd=%i) => %i", pid, fd,
838                              err);
839     if (g_log_all_calls)
840       description_sp->log(get_logging_fd());
841 
842     if (err == 0) {
843       if (fd >= 0)
844         save_backtrace(fd, err, description_sp, false);
845     } else if (err == -1) {
846       if (err_errno.get_errno() == EBADF && fd != -1) {
847         backtrace_error("close$NOCANCEL (fd=%d) resulted in EBADF\n:", fd);
848 
849         FDEventMap::iterator pos = g_fd_event_map.find(fd);
850         if (pos != g_fd_event_map.end()) {
851           log(get_logging_fd(), pos->second.back().get(),
852               "\nfd=%d was previously %s with this event:\n", fd,
853               pos->second.back()->IsCreateEvent() ? "opened" : "closed");
854         }
855       }
856     }
857     return err;
858   } else {
859     return close$NOCANCEL(fd);
860   }
861 }
862 
863 // pipe() interpose function
864 extern "C" int pipe$__interposed__(int fds[2]) {
865   const int pid = get_interposed_pid();
866   if (pid >= 0) {
867     Locker locker(&g_mutex);
868     fds[0] = -1;
869     fds[1] = -1;
870     const int err = pipe(fds);
871     const int saved_errno = errno;
872     StringSP description_sp(new String(
873         "pid=%i: pipe ({fd=%i, fd=%i}) -> err=%i", pid, fds[0], fds[1], err));
874     if (g_log_all_calls)
875       description_sp->log(get_logging_fd());
876     if (fds[0] >= 0)
877       save_backtrace(fds[0], saved_errno, description_sp, true);
878     if (fds[1] >= 0)
879       save_backtrace(fds[1], saved_errno, description_sp, true);
880     errno = saved_errno;
881     return err;
882   } else {
883     return pipe(fds);
884   }
885 }
886 
887 // get_fd_history()
888 //
889 // This function allows runtime access to the file descriptor history.
890 //
891 // @param[in] log_fd
892 //      The file descriptor to log to
893 //
894 // @param[in] fd
895 //      The file descriptor whose history should be dumped
896 extern "C" void get_fd_history(int log_fd, int fd) {
897   // "create" below needs to be outside of the mutex locker scope
898   if (log_fd >= 0) {
899     bool got_lock = false;
900     Locker locker(&g_mutex, got_lock);
901     if (got_lock) {
902       FDEventMap::iterator pos = g_fd_event_map.find(fd);
903       log_to_fd(log_fd, "Dumping file descriptor history for fd=%i:\n", fd);
904       if (pos != g_fd_event_map.end()) {
905         FDEventArray &event_array = g_fd_event_map[fd];
906         const size_t num_events = event_array.size();
907         for (size_t i = 0; i < num_events; ++i)
908           event_array[i]->Dump(log_fd);
909       } else {
910         log_to_fd(log_fd, "error: no file descriptor events found for fd=%i\n",
911                   fd);
912       }
913     } else {
914       log_to_fd(log_fd, "error: fd event mutex is locked...\n");
915     }
916   }
917 }
918 
919 // Interposing
920 // FD creation routines
921 DYLD_INTERPOSE(accept$__interposed__, accept);
922 DYLD_INTERPOSE(accept$NOCANCEL$__interposed__, accept$NOCANCEL);
923 DYLD_INTERPOSE(dup$__interposed__, dup);
924 DYLD_INTERPOSE(dup2$__interposed__, dup2);
925 DYLD_INTERPOSE(kqueue$__interposed__, kqueue);
926 DYLD_INTERPOSE(open$__interposed__, open);
927 DYLD_INTERPOSE(open$NOCANCEL$__interposed__, open$NOCANCEL);
928 DYLD_INTERPOSE(__open_extended$__interposed__, __open_extended);
929 DYLD_INTERPOSE(pipe$__interposed__, pipe);
930 DYLD_INTERPOSE(shm_open$__interposed__, shm_open);
931 DYLD_INTERPOSE(socket$__interposed__, socket);
932 DYLD_INTERPOSE(socketpair$__interposed__, socketpair);
933 
934 // FD deleting routines
935 DYLD_INTERPOSE(close$__interposed__, close);
936 DYLD_INTERPOSE(close$NOCANCEL$__interposed__, close$NOCANCEL);
937 
938 } // namespace fd_interposing
939