Refactor libinterfork and add support for clone() interception
authorPierre-Marc Fournier <pierre-marc.fournier@polymtl.ca>
Wed, 20 Jan 2010 06:03:57 +0000 (01:03 -0500)
committerPierre-Marc Fournier <pierre-marc.fournier@polymtl.ca>
Wed, 20 Jan 2010 19:41:11 +0000 (14:41 -0500)
include/Makefile.am
include/ust/tracectl.h [new file with mode: 0644]
libinterfork/interfork.c
libust/tracectl.c

index ea66be86e5b53b1d33e6c3201b0f2cd10aa1bd36..bb8d2e78d5c0ad2f022a35042eeb947e9c49cdb4 100644 (file)
@@ -1,4 +1,4 @@
 nobase_include_HEADERS = ust/immediate.h ust/kernelcompat.h ust/marker.h \
-       ust/tracepoint.h ust/processor.h ust/probe.h ust/ust.h
+       ust/tracepoint.h ust/processor.h ust/probe.h ust/ust.h ust/tracectl.h
 
 noinst_HEADERS = share.h usterr.h
diff --git a/include/ust/tracectl.h b/include/ust/tracectl.h
new file mode 100644 (file)
index 0000000..7e3ec19
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef UST_TRACECTL_H
+#define UST_TRACECTL_H
+
+typedef struct ust_fork_info {
+       sigset_t orig_sigs;
+} ust_fork_info_t;
+
+extern void ust_potential_exec(void);
+
+extern void ust_before_fork(ust_fork_info_t *fork_info);
+extern void ust_after_fork_parent(ust_fork_info_t *fork_info);
+extern void ust_after_fork_child(ust_fork_info_t *fork_info);
+
+#endif /* UST_TRACECTL_H */
index e9dce9bfb188d7f60032d04a5f2b9d9dfd5f9f2b..a92a2bd76f50251cf139303f9603c737e6ffba62 100644 (file)
 #include <unistd.h>
 #include <stdio.h>
 #include <signal.h>
+#include <ust/ust.h>
+#include <sched.h>
+#include <stdarg.h>
+#include <ust/tracectl.h>
 #include "usterr.h"
 
-extern void ust_fork(void);
-extern void ust_potential_exec(void);
-
 pid_t fork(void)
 {
        static pid_t (*plibc_func)(void) = NULL;
+       ust_fork_info_t fork_info;
 
        pid_t retval;
 
-       int result;
-       sigset_t all_sigs;
-       sigset_t orig_sigs;
-
        if(plibc_func == NULL) {
                plibc_func = dlsym(RTLD_NEXT, "fork");
                if(plibc_func == NULL) {
@@ -43,37 +41,17 @@ pid_t fork(void)
                }
        }
 
-       /* Disable interrupts. This is to avoid that the child
-        * intervenes before it is properly setup for tracing. It is
-        * safer to disable all signals, because then we know we are not
-        * breaking anything by restoring the original mask.
-        */
-
-       /* FIXME:
-               - only do this if tracing is active
-       */
-
-       /* Disable signals */
-       sigfillset(&all_sigs);
-       result = sigprocmask(SIG_BLOCK, &all_sigs, &orig_sigs);
-       if(result == -1) {
-               PERROR("sigprocmask");
-               return -1;
-       }
+       ust_before_fork(&fork_info);
 
        /* Do the real fork */
        retval = plibc_func();
 
        if(retval == 0) {
                /* child */
-               ust_fork();
+               ust_after_fork_child(&fork_info);
        }
-
-       /* Restore signals */
-       result = sigprocmask(SIG_BLOCK, &orig_sigs, NULL);
-       if(result == -1) {
-               PERROR("sigprocmask");
-               return -1;
+       else {
+               ust_after_fork_parent(&fork_info);
        }
 
        return retval;
@@ -88,7 +66,7 @@ int execve(const char *filename, char *const argv[], char *const envp[])
        if(plibc_func == NULL) {
                plibc_func = dlsym(RTLD_NEXT, "execve");
                if(plibc_func == NULL) {
-                       fprintf(stderr, "libcwrap: unable to find execve\n");
+                       fprintf(stderr, "libinterfork: unable to find execve\n");
                        return -1;
                }
        }
@@ -99,3 +77,65 @@ int execve(const char *filename, char *const argv[], char *const envp[])
 
        return retval;
 }
+
+struct interfork_clone_info {
+       int (*fn)(void *);
+       void *arg;
+       ust_fork_info_t *fork_info;
+};
+
+static int clone_fn(void *arg)
+{
+       struct interfork_clone_info *info = (struct interfork_clone_info *)arg;
+
+       /* clone is now done and we are in child */
+       ust_after_fork_child(&info->fork_info);
+
+       return info->fn(info->arg);
+}
+
+int clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...)
+{
+       static int (*plibc_func)(int (*fn)(void *), void *child_stack, int flags, void *arg, pid_t *ptid, struct user_desc *tls, pid_t *ctid) = NULL;
+
+       /* varargs */
+       pid_t *ptid;
+       struct user_desc *tls;
+       pid_t *ctid;
+
+       int retval;
+
+       va_list ap;
+
+       va_start(ap, arg);
+       ptid = va_arg(ap, pid_t *);
+       tls = va_arg(ap, struct user_desc *);
+       ctid = va_arg(ap, pid_t *);
+       va_end(ap);
+
+       if(plibc_func == NULL) {
+               plibc_func = dlsym(RTLD_NEXT, "clone");
+               if(plibc_func == NULL) {
+                       fprintf(stderr, "libinterfork: unable to find clone\n");
+                       return -1;
+               }
+       }
+
+       if(flags & CLONE_VM) {
+               /* creating a thread, no need to intervene, just pass on the arguments */
+               retval = plibc_func(fn, child_stack, flags, arg, ptid, tls, ctid);
+       }
+       else {
+               /* creating a real process, we need to intervene */
+               struct interfork_clone_info info = { fn: fn, arg: arg };
+
+               ust_before_fork(&info.fork_info);
+
+               retval = plibc_func(clone_fn, child_stack, flags, &info, ptid, tls, ctid);
+
+               /* The child doesn't get here */
+               ust_after_fork_parent(&info.fork_info);
+       }
+
+       return retval;
+}
index f9312b4340238e79e3e2dd6abf51d82bd962cca1..5e59ca7bc0e8f5cf3ffee07cd6b25e06b3a98a3b 100644 (file)
@@ -30,6 +30,7 @@
 #include <urcu-bp.h>
 
 #include <ust/marker.h>
+#include <ust/tracectl.h>
 #include "tracer.h"
 #include "usterr.h"
 #include "ustcomm.h"
@@ -1122,9 +1123,13 @@ void ust_potential_exec(void)
  * the new process, anytime a process whose memory is not shared with
  * the parent is created. If this function is not called, the events
  * of the new process will not be collected.
+ *
+ * Signals should be disabled before the fork and reenabled only after
+ * this call in order to guarantee tracing is not started before ust_fork()
+ * sanitizes the new process.
  */
 
-void ust_fork(void)
+static void ust_fork(void)
 {
        struct blocked_consumer *bc;
        struct blocked_consumer *deletable_bc = NULL;
@@ -1160,3 +1165,55 @@ void ust_fork(void)
        inform_consumer_daemon("auto");
 }
 
+void ust_before_fork(ust_fork_info_t *fork_info)
+{
+        /* Disable signals. This is to avoid that the child
+         * intervenes before it is properly setup for tracing. It is
+         * safer to disable all signals, because then we know we are not
+         * breaking anything by restoring the original mask.
+         */
+       sigset_t all_sigs;
+       int result;
+
+        /* FIXME:
+                - only do this if tracing is active
+        */
+
+        /* Disable signals */
+        sigfillset(&all_sigs);
+        result = sigprocmask(SIG_BLOCK, &all_sigs, &fork_info->orig_sigs);
+        if(result == -1) {
+                PERROR("sigprocmask");
+                return;
+        }
+}
+
+/* Don't call this function directly in a traced program */
+static void ust_after_fork_common(ust_fork_info_t *fork_info)
+{
+       int result;
+       sigset_t orig_sigs;
+
+        /* Restore signals */
+        result = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL);
+        if(result == -1) {
+                PERROR("sigprocmask");
+                return;
+        }
+}
+
+void ust_after_fork_parent(ust_fork_info_t *fork_info)
+{
+       /* Reenable signals */
+       ust_after_fork_common(fork_info);
+}
+
+void ust_after_fork_child(ust_fork_info_t *fork_info)
+{
+       /* First sanitize the child */
+       ust_fork();
+
+       /* Then reenable interrupts */
+       ust_after_fork_common(fork_info);
+}
+
This page took 0.028897 seconds and 4 git commands to generate.