Fix: Lock FD tracker across fork
[lttng-ust.git] / liblttng-ust / lttng-ust-comm.c
index 97ecdf1e99001bac3ddda7b7476d2e5a08b6c566..47ba36e5c9f1d9f1871c267469049708a7dd1e1e 100644 (file)
  */
 
 #define _LGPL_SOURCE
+#define _GNU_SOURCE
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
+#include <dlfcn.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
@@ -34,6 +36,7 @@
 #include <time.h>
 #include <assert.h>
 #include <signal.h>
+#include <limits.h>
 #include <urcu/uatomic.h>
 #include <urcu/futex.h>
 #include <urcu/compiler.h>
 #include <lttng/ust-ctl.h>
 #include <urcu/tls-compat.h>
 #include <ust-comm.h>
+#include <ust-fd.h>
 #include <usterr-signal-safe.h>
 #include <helper.h>
 #include "tracepoint-internal.h"
 #include "lttng-tracer-core.h"
 #include "compat.h"
-#include "../libringbuffer/tlsfixup.h"
-#include "lttng-ust-baddr.h"
+#include "../libringbuffer/rb-init.h"
+#include "lttng-ust-statedump.h"
 #include "clock.h"
+#include "../libringbuffer/getcpu.h"
+#include "getenv.h"
+
+/* Concatenate lttng ust shared library name with its major version number. */
+#define LTTNG_UST_LIB_SO_NAME "liblttng-ust.so." __ust_stringify(CONFIG_LTTNG_UST_LIBRARY_VERSION_MAJOR)
 
 /*
  * Has lttng ust comm constructor been called ?
@@ -77,6 +86,8 @@ static int initialized;
  *
  * ust_lock nests within the dynamic loader lock (within glibc) because
  * it is taken within the library constructor.
+ *
+ * The ust fd tracker lock nests within the ust_mutex.
  */
 static pthread_mutex_t ust_mutex = PTHREAD_MUTEX_INITIALIZER;
 
@@ -103,6 +114,14 @@ static pthread_mutex_t ust_fork_mutex = PTHREAD_MUTEX_INITIALIZER;
 /* Should the ust comm thread quit ? */
 static int lttng_ust_comm_should_quit;
 
+/*
+ * This variable can be tested by applications to check whether
+ * lttng-ust is loaded. They simply have to define their own
+ * "lttng_ust_loaded" weak symbol, and test it. It is set to 1 by the
+ * library constructor.
+ */
+int lttng_ust_loaded __attribute__((weak));
+
 /*
  * Return 0 on success, -1 if should quit.
  * The lock is taken in both cases.
@@ -111,8 +130,15 @@ static int lttng_ust_comm_should_quit;
 int ust_lock(void)
 {
        sigset_t sig_all_blocked, orig_mask;
-       int ret;
+       int ret, oldstate;
 
+       ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
+       if (ret) {
+               ERR("pthread_setcancelstate: %s", strerror(ret));
+       }
+       if (oldstate != PTHREAD_CANCEL_ENABLE) {
+               ERR("pthread_setcancelstate: unexpected oldstate");
+       }
        sigfillset(&sig_all_blocked);
        ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask);
        if (ret) {
@@ -140,8 +166,15 @@ int ust_lock(void)
 void ust_lock_nocheck(void)
 {
        sigset_t sig_all_blocked, orig_mask;
-       int ret;
+       int ret, oldstate;
 
+       ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate);
+       if (ret) {
+               ERR("pthread_setcancelstate: %s", strerror(ret));
+       }
+       if (oldstate != PTHREAD_CANCEL_ENABLE) {
+               ERR("pthread_setcancelstate: unexpected oldstate");
+       }
        sigfillset(&sig_all_blocked);
        ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask);
        if (ret) {
@@ -161,7 +194,7 @@ void ust_lock_nocheck(void)
 void ust_unlock(void)
 {
        sigset_t sig_all_blocked, orig_mask;
-       int ret;
+       int ret, oldstate;
 
        sigfillset(&sig_all_blocked);
        ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask);
@@ -174,6 +207,13 @@ void ust_unlock(void)
        if (ret) {
                ERR("pthread_sigmask: %s", strerror(ret));
        }
+       ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
+       if (ret) {
+               ERR("pthread_setcancelstate: %s", strerror(ret));
+       }
+       if (oldstate != PTHREAD_CANCEL_DISABLE) {
+               ERR("pthread_setcancelstate: unexpected oldstate");
+       }
 }
 
 /*
@@ -190,7 +230,11 @@ static sem_t constructor_wait;
 /*
  * Doing this for both the global and local sessiond.
  */
-static int sem_count = { 2 };
+enum {
+       sem_count_initial_value = 4,
+};
+
+static int sem_count = sem_count_initial_value;
 
 /*
  * Counting nesting within lttng-ust. Used to ensure that calling fork()
@@ -205,7 +249,7 @@ struct sock_info {
        const char *name;
        pthread_t ust_listener; /* listener thread */
        int root_handle;
-       int constructor_sem_posted;
+       int registration_done;
        int allowed;
        int global;
        int thread_active;
@@ -218,6 +262,7 @@ struct sock_info {
        char *wait_shm_mmap;
        /* Keep track of lazy state dump not performed yet. */
        int statedump_pending;
+       int initial_statedump_done;
 };
 
 /* Socket from app (connect) to session daemon (listen) for communication */
@@ -226,7 +271,8 @@ struct sock_info global_apps = {
        .global = 1,
 
        .root_handle = -1,
-       .allowed = 1,
+       .registration_done = 0,
+       .allowed = 0,
        .thread_active = 0,
 
        .sock_path = LTTNG_DEFAULT_RUNDIR "/" LTTNG_UST_SOCK_FILENAME,
@@ -236,6 +282,7 @@ struct sock_info global_apps = {
        .wait_shm_path = "/" LTTNG_UST_WAIT_FILENAME,
 
        .statedump_pending = 0,
+       .initial_statedump_done = 0,
 };
 
 /* TODO: allow global_apps_sock_path override */
@@ -244,6 +291,7 @@ struct sock_info local_apps = {
        .name = "local",
        .global = 0,
        .root_handle = -1,
+       .registration_done = 0,
        .allowed = 0,   /* Check setuid bit first */
        .thread_active = 0,
 
@@ -251,6 +299,7 @@ struct sock_info local_apps = {
        .notify_socket = -1,
 
        .statedump_pending = 0,
+       .initial_statedump_done = 0,
 };
 
 static int wait_poll_fallback;
@@ -305,6 +354,26 @@ extern void lttng_ring_buffer_client_discard_exit(void);
 extern void lttng_ring_buffer_client_discard_rt_exit(void);
 extern void lttng_ring_buffer_metadata_client_exit(void);
 
+static char *get_map_shm(struct sock_info *sock_info);
+
+ssize_t lttng_ust_read(int fd, void *buf, size_t len)
+{
+       ssize_t ret;
+       size_t copied = 0, to_copy = len;
+
+       do {
+               ret = read(fd, buf + copied, to_copy);
+               if (ret > 0) {
+                       copied += ret;
+                       to_copy -= ret;
+               }
+       } while ((ret > 0 && to_copy > 0)
+               || (ret < 0 && errno == EINTR));
+       if (ret > 0) {
+               ret = copied;
+       }
+       return ret;
+}
 /*
  * Returns the HOME directory path. Caller MUST NOT free(3) the returned
  * pointer.
@@ -314,11 +383,11 @@ const char *get_lttng_home_dir(void)
 {
        const char *val;
 
-       val = (const char *) getenv("LTTNG_HOME");
+       val = (const char *) lttng_getenv("LTTNG_HOME");
        if (val != NULL) {
                return val;
        }
-       return (const char *) getenv("HOME");
+       return (const char *) lttng_getenv("HOME");
 }
 
 /*
@@ -346,6 +415,17 @@ void lttng_fixup_urcu_bp_tls(void)
        rcu_read_unlock();
 }
 
+void lttng_ust_fixup_tls(void)
+{
+       lttng_fixup_urcu_bp_tls();
+       lttng_fixup_ringbuffer_tls();
+       lttng_fixup_vtid_tls();
+       lttng_fixup_nest_count_tls();
+       lttng_fixup_procname_tls();
+       lttng_fixup_ust_mutex_nest_tls();
+       lttng_ust_fixup_fd_tracker_tls();
+}
+
 int lttng_get_notify_socket(void *owner)
 {
        struct sock_info *info = owner;
@@ -367,25 +447,48 @@ void print_cmd(int cmd, int handle)
                lttng_ust_obj_get_name(handle), handle);
 }
 
+static
+int setup_global_apps(void)
+{
+       int ret = 0;
+       assert(!global_apps.wait_shm_mmap);
+
+       global_apps.wait_shm_mmap = get_map_shm(&global_apps);
+       if (!global_apps.wait_shm_mmap) {
+               WARN("Unable to get map shm for global apps. Disabling LTTng-UST global tracing.");
+               global_apps.allowed = 0;
+               ret = -EIO;
+               goto error;
+       }
+
+       global_apps.allowed = 1;
+error:
+       return ret;
+}
 static
 int setup_local_apps(void)
 {
+       int ret = 0;
        const char *home_dir;
        uid_t uid;
 
+       assert(!local_apps.wait_shm_mmap);
+
        uid = getuid();
        /*
         * Disallow per-user tracing for setuid binaries.
         */
        if (uid != geteuid()) {
                assert(local_apps.allowed == 0);
-               return 0;
+               ret = 0;
+               goto end;
        }
        home_dir = get_lttng_home_dir();
        if (!home_dir) {
                WARN("HOME environment variable not set. Disabling LTTng-UST per-user tracing.");
                assert(local_apps.allowed == 0);
-               return -ENOENT;
+               ret = -ENOENT;
+               goto end;
        }
        local_apps.allowed = 1;
        snprintf(local_apps.sock_path, PATH_MAX, "%s/%s/%s",
@@ -395,12 +498,21 @@ int setup_local_apps(void)
        snprintf(local_apps.wait_shm_path, PATH_MAX, "/%s-%u",
                LTTNG_UST_WAIT_FILENAME,
                uid);
-       return 0;
+
+       local_apps.wait_shm_mmap = get_map_shm(&local_apps);
+       if (!local_apps.wait_shm_mmap) {
+               WARN("Unable to get map shm for local apps. Disabling LTTng-UST per-user tracing.");
+               local_apps.allowed = 0;
+               ret = -EIO;
+               goto end;
+       }
+end:
+       return ret;
 }
 
 /*
- * Get notify_sock timeout, in ms.
- * -1: don't wait. 0: wait forever. >0: timeout, in ms.
+ * Get socket timeout, in ms.
+ * -1: wait forever. 0: don't wait. >0: timeout, in ms.
  */
 static
 long get_timeout(void)
@@ -408,22 +520,33 @@ long get_timeout(void)
        long constructor_delay_ms = LTTNG_UST_DEFAULT_CONSTRUCTOR_TIMEOUT_MS;
 
        if (!got_timeout_env) {
-               str_timeout = getenv("LTTNG_UST_REGISTER_TIMEOUT");
+               str_timeout = lttng_getenv("LTTNG_UST_REGISTER_TIMEOUT");
                got_timeout_env = 1;
        }
        if (str_timeout)
                constructor_delay_ms = strtol(str_timeout, NULL, 10);
+       /* All negative values are considered as "-1". */
+       if (constructor_delay_ms < -1)
+               constructor_delay_ms = -1;
        return constructor_delay_ms;
 }
 
+/* Timeout for notify socket send and recv. */
 static
 long get_notify_sock_timeout(void)
 {
        return get_timeout();
 }
 
+/* Timeout for connecting to cmd and notify sockets. */
+static
+long get_connect_sock_timeout(void)
+{
+       return get_timeout();
+}
+
 /*
- * Return values: -1: don't wait. 0: wait forever. 1: timeout wait.
+ * Return values: -1: wait forever. 0: don't wait. 1: timeout wait.
  */
 static
 int get_constructor_timeout(struct timespec *constructor_timeout)
@@ -446,7 +569,8 @@ int get_constructor_timeout(struct timespec *constructor_timeout)
         */
        ret = clock_gettime(CLOCK_REALTIME, constructor_timeout);
        if (ret) {
-               return -1;
+               /* Don't wait. */
+               return 0;
        }
        constructor_timeout->tv_sec += constructor_delay_ms / 1000UL;
        constructor_timeout->tv_nsec +=
@@ -455,9 +579,23 @@ int get_constructor_timeout(struct timespec *constructor_timeout)
                constructor_timeout->tv_sec++;
                constructor_timeout->tv_nsec -= 1000000000UL;
        }
+       /* Timeout wait (constructor_delay_ms). */
        return 1;
 }
 
+static
+void get_allow_blocking(void)
+{
+       const char *str_allow_blocking =
+               lttng_getenv("LTTNG_UST_ALLOW_BLOCKING");
+
+       if (str_allow_blocking) {
+               DBG("%s environment variable is set",
+                       "LTTNG_UST_ALLOW_BLOCKING");
+               lttng_ust_ringbuffer_set_allow_blocking();
+       }
+}
+
 static
 int register_to_sessiond(int socket, enum ustctl_socket_type type)
 {
@@ -494,45 +632,85 @@ int send_reply(int sock, struct ustcomm_ust_reply *lur)
 }
 
 static
-int handle_register_done(struct sock_info *sock_info)
+void decrement_sem_count(unsigned int count)
 {
        int ret;
 
-       if (sock_info->constructor_sem_posted)
-               return 0;
-       sock_info->constructor_sem_posted = 1;
+       assert(uatomic_read(&sem_count) >= count);
+
        if (uatomic_read(&sem_count) <= 0) {
-               return 0;
+               return;
        }
-       ret = uatomic_add_return(&sem_count, -1);
+
+       ret = uatomic_add_return(&sem_count, -count);
        if (ret == 0) {
                ret = sem_post(&constructor_wait);
                assert(!ret);
        }
+}
+
+static
+int handle_register_done(struct sock_info *sock_info)
+{
+       if (sock_info->registration_done)
+               return 0;
+       sock_info->registration_done = 1;
+
+       decrement_sem_count(1);
+       if (!sock_info->statedump_pending) {
+               sock_info->initial_statedump_done = 1;
+               decrement_sem_count(1);
+       }
+
+       return 0;
+}
+
+static
+int handle_register_failed(struct sock_info *sock_info)
+{
+       if (sock_info->registration_done)
+               return 0;
+       sock_info->registration_done = 1;
+       sock_info->initial_statedump_done = 1;
+
+       decrement_sem_count(2);
+
        return 0;
 }
 
 /*
  * Only execute pending statedump after the constructor semaphore has
- * been posted by each listener thread. This means statedump will only
- * be performed after the "registration done" command is received from
- * each session daemon the application is connected to.
+ * been posted by the current listener thread. This means statedump will
+ * only be performed after the "registration done" command is received
+ * from this thread's session daemon.
  *
  * This ensures we don't run into deadlock issues with the dynamic
  * loader mutex, which is held while the constructor is called and
  * waiting on the constructor semaphore. All operations requiring this
  * dynamic loader lock need to be postponed using this mechanism.
+ *
+ * In a scenario with two session daemons connected to the application,
+ * it is possible that the first listener thread which receives the
+ * registration done command issues its statedump while the dynamic
+ * loader lock is still held by the application constructor waiting on
+ * the semaphore. It will however be allowed to proceed when the
+ * second session daemon sends the registration done command to the
+ * second listener thread. This situation therefore does not produce
+ * a deadlock.
  */
 static
 void handle_pending_statedump(struct sock_info *sock_info)
 {
-       int ctor_passed = sock_info->constructor_sem_posted;
-
-       if (ctor_passed && sock_info->statedump_pending) {
+       if (sock_info->registration_done && sock_info->statedump_pending) {
                sock_info->statedump_pending = 0;
                pthread_mutex_lock(&ust_fork_mutex);
                lttng_handle_pending_statedump(sock_info);
                pthread_mutex_unlock(&ust_fork_mutex);
+
+               if (!sock_info->initial_statedump_done) {
+                       sock_info->initial_statedump_done = 1;
+                       decrement_sem_count(1);
+               }
        }
 }
 
@@ -544,19 +722,20 @@ int handle_message(struct sock_info *sock_info,
        const struct lttng_ust_objd_ops *ops;
        struct ustcomm_ust_reply lur;
        union ust_args args;
+       char ctxstr[LTTNG_UST_SYM_NAME_LEN];    /* App context string. */
        ssize_t len;
 
        memset(&lur, 0, sizeof(lur));
 
        if (ust_lock()) {
                ret = -LTTNG_UST_ERR_EXITING;
-               goto end;
+               goto error;
        }
 
        ops = objd_ops(lum->handle);
        if (!ops) {
                ret = -ENOENT;
-               goto end;
+               goto error;
        }
 
        switch (lum->cmd) {
@@ -617,12 +796,12 @@ int handle_message(struct sock_info *sock_info,
                                }
                                ret = len;
                                free(bytecode);
-                               goto end;
+                               goto error;
                        } else {
                                DBG("incorrect filter data message size: %zd", len);
                                ret = -EINVAL;
                                free(bytecode);
-                               goto end;
+                               goto error;
                        }
                }
                bytecode->bc.len = lum->u.filter.data_size;
@@ -682,12 +861,12 @@ int handle_message(struct sock_info *sock_info,
                                }
                                ret = len;
                                free(node);
-                               goto end;
+                               goto error;
                        } else {
                                DBG("Incorrect exclusion data message size: %zd", len);
                                ret = -EINVAL;
                                free(node);
-                               goto end;
+                               goto error;
                        }
                }
                if (ops->cmd) {
@@ -728,11 +907,11 @@ int handle_message(struct sock_info *sock_info,
                                        goto error;
                                }
                                ret = len;
-                               goto end;
+                               goto error;
                        } else {
                                DBG("incorrect channel data message size: %zd", len);
                                ret = -EINVAL;
-                               goto end;
+                               goto error;
                        }
                }
                args.channel.chan_data = chan_data;
@@ -749,12 +928,13 @@ int handle_message(struct sock_info *sock_info,
        {
                /* Receive shm_fd, wakeup_fd */
                ret = ustcomm_recv_stream_from_sessiond(sock,
-                       &lum->u.stream.len,
+                       NULL,
                        &args.stream.shm_fd,
                        &args.stream.wakeup_fd);
                if (ret) {
-                       goto end;
+                       goto error;
                }
+
                if (ops->cmd)
                        ret = ops->cmd(lum->handle, lum->cmd,
                                        (unsigned long) &lum->u,
@@ -763,6 +943,64 @@ int handle_message(struct sock_info *sock_info,
                        ret = -ENOSYS;
                break;
        }
+       case LTTNG_UST_CONTEXT:
+               switch (lum->u.context.ctx) {
+               case LTTNG_UST_CONTEXT_APP_CONTEXT:
+               {
+                       char *p;
+                       size_t ctxlen, recvlen;
+
+                       ctxlen = strlen("$app.") + lum->u.context.u.app_ctx.provider_name_len - 1
+                                       + strlen(":") + lum->u.context.u.app_ctx.ctx_name_len;
+                       if (ctxlen >= LTTNG_UST_SYM_NAME_LEN) {
+                               ERR("Application context string length size is too large: %zu bytes",
+                                       ctxlen);
+                               ret = -EINVAL;
+                               goto error;
+                       }
+                       strcpy(ctxstr, "$app.");
+                       p = &ctxstr[strlen("$app.")];
+                       recvlen = ctxlen - strlen("$app.");
+                       len = ustcomm_recv_unix_sock(sock, p, recvlen);
+                       switch (len) {
+                       case 0: /* orderly shutdown */
+                               ret = 0;
+                               goto error;
+                       default:
+                               if (len == recvlen) {
+                                       DBG("app context data received");
+                                       break;
+                               } else if (len < 0) {
+                                       DBG("Receive failed from lttng-sessiond with errno %d", (int) -len);
+                                       if (len == -ECONNRESET) {
+                                               ERR("%s remote end closed connection", sock_info->name);
+                                               ret = len;
+                                               goto error;
+                                       }
+                                       ret = len;
+                                       goto error;
+                               } else {
+                                       DBG("incorrect app context data message size: %zd", len);
+                                       ret = -EINVAL;
+                                       goto error;
+                               }
+                       }
+                       /* Put : between provider and ctxname. */
+                       p[lum->u.context.u.app_ctx.provider_name_len - 1] = ':';
+                       args.app_context.ctxname = ctxstr;
+                       break;
+               }
+               default:
+                       break;
+               }
+               if (ops->cmd) {
+                       ret = ops->cmd(lum->handle, lum->cmd,
+                                       (unsigned long) &lum->u,
+                                       &args, sock_info);
+               } else {
+                       ret = -ENOSYS;
+               }
+               break;
        default:
                if (ops->cmd)
                        ret = ops->cmd(lum->handle, lum->cmd,
@@ -773,7 +1011,6 @@ int handle_message(struct sock_info *sock_info,
                break;
        }
 
-end:
        lur.handle = lum->handle;
        lur.cmd = lum->cmd;
        lur.ret_val = ret;
@@ -823,6 +1060,21 @@ end:
                }
        }
        DBG("Return value: %d", lur.ret_val);
+
+       ust_unlock();
+
+       /*
+        * Performed delayed statedump operations outside of the UST
+        * lock. We need to take the dynamic loader lock before we take
+        * the UST lock internally within handle_pending_statedump().
+         */
+       handle_pending_statedump(sock_info);
+
+       if (ust_lock()) {
+               ret = -LTTNG_UST_ERR_EXITING;
+               goto error;
+       }
+
        ret = send_reply(sock, &lur);
        if (ret < 0) {
                DBG("error sending reply");
@@ -853,13 +1105,6 @@ end:
 error:
        ust_unlock();
 
-       /*
-        * Performed delayed statedump operations outside of the UST
-        * lock. We need to take the dynamic loader lock before we take
-        * the UST lock internally within handle_pending_statedump().
-         */
-       handle_pending_statedump(sock_info);
-
        return ret;
 }
 
@@ -875,7 +1120,8 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting)
                }
                sock_info->root_handle = -1;
        }
-       sock_info->constructor_sem_posted = 0;
+       sock_info->registration_done = 0;
+       sock_info->initial_statedump_done = 0;
 
        /*
         * wait_shm_mmap, socket and notify socket are used by listener
@@ -905,7 +1151,12 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting)
                long page_size;
 
                page_size = sysconf(_SC_PAGE_SIZE);
-               if (page_size > 0) {
+               if (page_size <= 0) {
+                       if (!page_size) {
+                               errno = EINVAL;
+                       }
+                       PERROR("Error in sysconf(_SC_PAGE_SIZE)");
+               } else {
                        ret = munmap(sock_info->wait_shm_mmap, page_size);
                        if (ret) {
                                ERR("Error unmapping wait shm");
@@ -1087,21 +1338,47 @@ char *get_map_shm(struct sock_info *sock_info)
        char *wait_shm_mmap;
 
        page_size = sysconf(_SC_PAGE_SIZE);
-       if (page_size < 0) {
+       if (page_size <= 0) {
+               if (!page_size) {
+                       errno = EINVAL;
+               }
+               PERROR("Error in sysconf(_SC_PAGE_SIZE)");
                goto error;
        }
 
+       lttng_ust_lock_fd_tracker();
        wait_shm_fd = get_wait_shm(sock_info, page_size);
        if (wait_shm_fd < 0) {
+               lttng_ust_unlock_fd_tracker();
                goto error;
        }
+
+       ret = lttng_ust_add_fd_to_tracker(wait_shm_fd);
+       if (ret < 0) {
+               ret = close(wait_shm_fd);
+               if (!ret) {
+                       PERROR("Error closing fd");
+               }
+               lttng_ust_unlock_fd_tracker();
+               goto error;
+       }
+
+       wait_shm_fd = ret;
+       lttng_ust_unlock_fd_tracker();
+
        wait_shm_mmap = mmap(NULL, page_size, PROT_READ,
                  MAP_SHARED, wait_shm_fd, 0);
+
        /* close shm fd immediately after taking the mmap reference */
+       lttng_ust_lock_fd_tracker();
        ret = close(wait_shm_fd);
-       if (ret) {
+       if (!ret) {
+               lttng_ust_delete_fd_from_tracker(wait_shm_fd);
+       } else {
                PERROR("Error closing fd");
        }
+       lttng_ust_unlock_fd_tracker();
+
        if (wait_shm_mmap == MAP_FAILED) {
                DBG("mmap error (can be caused by race with sessiond). Fallback to poll mode.");
                goto error;
@@ -1115,40 +1392,45 @@ error:
 static
 void wait_for_sessiond(struct sock_info *sock_info)
 {
-       int ret;
-
+       /* Use ust_lock to check if we should quit. */
        if (ust_lock()) {
                goto quit;
        }
        if (wait_poll_fallback) {
                goto error;
        }
-       if (!sock_info->wait_shm_mmap) {
-               sock_info->wait_shm_mmap = get_map_shm(sock_info);
-               if (!sock_info->wait_shm_mmap)
-                       goto error;
-       }
        ust_unlock();
 
+       assert(sock_info->wait_shm_mmap);
+
        DBG("Waiting for %s apps sessiond", sock_info->name);
        /* Wait for futex wakeup */
-       if (uatomic_read((int32_t *) sock_info->wait_shm_mmap) == 0) {
-               ret = futex_async((int32_t *) sock_info->wait_shm_mmap,
-                       FUTEX_WAIT, 0, NULL, NULL, 0);
-               if (ret < 0) {
-                       if (errno == EFAULT) {
-                               wait_poll_fallback = 1;
-                               DBG(
+       if (uatomic_read((int32_t *) sock_info->wait_shm_mmap))
+               goto end_wait;
+
+       while (futex_async((int32_t *) sock_info->wait_shm_mmap,
+                       FUTEX_WAIT, 0, NULL, NULL, 0)) {
+               switch (errno) {
+               case EWOULDBLOCK:
+                       /* Value already changed. */
+                       goto end_wait;
+               case EINTR:
+                       /* Retry if interrupted by signal. */
+                       break;  /* Get out of switch. */
+               case EFAULT:
+                       wait_poll_fallback = 1;
+                       DBG(
 "Linux kernels 2.6.33 to 3.0 (with the exception of stable versions) "
 "do not support FUTEX_WAKE on read-only memory mappings correctly. "
 "Please upgrade your kernel "
 "(fix is commit 9ea71503a8ed9184d2d0b8ccc4d269d05f7940ae in Linux kernel "
 "mainline). LTTng-UST will use polling mode fallback.");
-                               if (ust_debug())
-                                       PERROR("futex");
-                       }
+                       if (ust_debug())
+                               PERROR("futex");
+                       goto end_wait;
                }
        }
+end_wait:
        return;
 
 quit:
@@ -1171,9 +1453,19 @@ static
 void *ust_listener_thread(void *arg)
 {
        struct sock_info *sock_info = arg;
-       int sock, ret, prev_connect_failed = 0, has_waited = 0;
+       int sock, ret, prev_connect_failed = 0, has_waited = 0, fd;
        long timeout;
 
+       lttng_ust_fixup_tls();
+       /*
+        * If available, add '-ust' to the end of this thread's
+        * process name
+        */
+       ret = lttng_ust_setustprocname();
+       if (ret) {
+               ERR("Unable to set UST process name");
+       }
+
        /* Restart trying to connect to the session daemon */
 restart:
        if (prev_connect_failed) {
@@ -1187,12 +1479,18 @@ restart:
                         * deals with a killed or broken session daemon.
                         */
                        sleep(5);
+               } else {
+                       has_waited = 1;
                }
-               has_waited = 1;
                prev_connect_failed = 0;
        }
 
+       if (ust_lock()) {
+               goto quit;
+       }
+
        if (sock_info->socket != -1) {
+               /* FD tracker is updated by ustcomm_close_unix_sock() */
                ret = ustcomm_close_unix_sock(sock_info->socket);
                if (ret) {
                        ERR("Error closing %s ust cmd socket",
@@ -1201,6 +1499,7 @@ restart:
                sock_info->socket = -1;
        }
        if (sock_info->notify_socket != -1) {
+               /* FD tracker is updated by ustcomm_close_unix_sock() */
                ret = ustcomm_close_unix_sock(sock_info->notify_socket);
                if (ret) {
                        ERR("Error closing %s ust notify socket",
@@ -1209,6 +1508,7 @@ restart:
                sock_info->notify_socket = -1;
        }
 
+
        /*
         * Register. We need to perform both connect and sending
         * registration message before doing the next connect otherwise
@@ -1217,26 +1517,45 @@ restart:
         * first connect registration message.
         */
        /* Connect cmd socket */
-       ret = ustcomm_connect_unix_sock(sock_info->sock_path);
+       lttng_ust_lock_fd_tracker();
+       ret = ustcomm_connect_unix_sock(sock_info->sock_path,
+               get_connect_sock_timeout());
        if (ret < 0) {
+               lttng_ust_unlock_fd_tracker();
                DBG("Info: sessiond not accepting connections to %s apps socket", sock_info->name);
                prev_connect_failed = 1;
 
-               if (ust_lock()) {
-                       goto quit;
-               }
-
                /*
                 * If we cannot find the sessiond daemon, don't delay
                 * constructor execution.
                 */
-               ret = handle_register_done(sock_info);
+               ret = handle_register_failed(sock_info);
                assert(!ret);
                ust_unlock();
                goto restart;
        }
+       fd = ret;
+       ret = lttng_ust_add_fd_to_tracker(fd);
+       if (ret < 0) {
+               ret = close(fd);
+               if (ret) {
+                       PERROR("close on sock_info->socket");
+               }
+               ret = -1;
+               lttng_ust_unlock_fd_tracker();
+               ust_unlock();
+               goto quit;
+       }
+
        sock_info->socket = ret;
+       lttng_ust_unlock_fd_tracker();
 
+       ust_unlock();
+       /*
+        * Unlock/relock ust lock because connect is blocking (with
+        * timeout). Don't delay constructors on the ust lock for too
+        * long.
+        */
        if (ust_lock()) {
                goto quit;
        }
@@ -1264,34 +1583,66 @@ restart:
                 * If we cannot register to the sessiond daemon, don't
                 * delay constructor execution.
                 */
-               ret = handle_register_done(sock_info);
+               ret = handle_register_failed(sock_info);
                assert(!ret);
                ust_unlock();
                goto restart;
        }
 
        ust_unlock();
+       /*
+        * Unlock/relock ust lock because connect is blocking (with
+        * timeout). Don't delay constructors on the ust lock for too
+        * long.
+        */
+       if (ust_lock()) {
+               goto quit;
+       }
 
        /* Connect notify socket */
-       ret = ustcomm_connect_unix_sock(sock_info->sock_path);
+       lttng_ust_lock_fd_tracker();
+       ret = ustcomm_connect_unix_sock(sock_info->sock_path,
+               get_connect_sock_timeout());
        if (ret < 0) {
+               lttng_ust_unlock_fd_tracker();
                DBG("Info: sessiond not accepting connections to %s apps socket", sock_info->name);
                prev_connect_failed = 1;
 
-               if (ust_lock()) {
-                       goto quit;
-               }
-
                /*
                 * If we cannot find the sessiond daemon, don't delay
                 * constructor execution.
                 */
-               ret = handle_register_done(sock_info);
+               ret = handle_register_failed(sock_info);
                assert(!ret);
                ust_unlock();
                goto restart;
        }
+
+       fd = ret;
+       ret = lttng_ust_add_fd_to_tracker(fd);
+       if (ret < 0) {
+               ret = close(fd);
+               if (ret) {
+                       PERROR("close on sock_info->notify_socket");
+               }
+               ret = -1;
+               lttng_ust_unlock_fd_tracker();
+               ust_unlock();
+               goto quit;
+       }
+
        sock_info->notify_socket = ret;
+       lttng_ust_unlock_fd_tracker();
+
+       ust_unlock();
+       /*
+        * Unlock/relock ust lock because connect is blocking (with
+        * timeout). Don't delay constructors on the ust lock for too
+        * long.
+        */
+       if (ust_lock()) {
+               goto quit;
+       }
 
        timeout = get_notify_sock_timeout();
        if (timeout >= 0) {
@@ -1315,10 +1666,6 @@ restart:
                WARN("Unsupported timeout value %ld", timeout);
        }
 
-       if (ust_lock()) {
-               goto quit;
-       }
-
        ret = register_to_sessiond(sock_info->notify_socket,
                        USTCTL_SOCKET_NOTIFY);
        if (ret < 0) {
@@ -1329,7 +1676,7 @@ restart:
                 * If we cannot register to the sessiond daemon, don't
                 * delay constructor execution.
                 */
-               ret = handle_register_done(sock_info);
+               ret = handle_register_failed(sock_info);
                assert(!ret);
                ust_unlock();
                goto restart;
@@ -1359,7 +1706,7 @@ restart:
                         * If we cannot register to the sessiond daemon, don't
                         * delay constructor execution.
                         */
-                       ret = handle_register_done(sock_info);
+                       ret = handle_register_failed(sock_info);
                        assert(!ret);
                        ust_unlock();
                        goto end;
@@ -1367,7 +1714,13 @@ restart:
                        print_cmd(lum.cmd, lum.handle);
                        ret = handle_message(sock_info, sock, &lum);
                        if (ret) {
-                               ERR("Error handling message for %s socket", sock_info->name);
+                               ERR("Error handling message for %s socket",
+                                       sock_info->name);
+                               /*
+                                * Close socket if protocol error is
+                                * detected.
+                                */
+                               goto end;
                        }
                        continue;
                default:
@@ -1421,6 +1774,7 @@ void __attribute__((constructor)) lttng_ust_init(void)
        pthread_attr_t thread_attr;
        int timeout_mode;
        int ret;
+       void *handle;
 
        if (uatomic_xchg(&initialized, 1) == 1)
                return;
@@ -1430,12 +1784,29 @@ void __attribute__((constructor)) lttng_ust_init(void)
         * to be the dynamic linker mutex) and ust_lock, taken within
         * the ust lock.
         */
-       lttng_fixup_urcu_bp_tls();
-       lttng_fixup_ringbuffer_tls();
-       lttng_fixup_vtid_tls();
-       lttng_fixup_nest_count_tls();
-       lttng_fixup_procname_tls();
-       lttng_fixup_ust_mutex_nest_tls();
+       lttng_ust_fixup_tls();
+
+       lttng_ust_loaded = 1;
+
+       /*
+        * We need to ensure that the liblttng-ust library is not unloaded to avoid
+        * the unloading of code used by the ust_listener_threads as we can not
+        * reliably know when they exited. To do that, manually load
+        * liblttng-ust.so to increment the dynamic loader's internal refcount for
+        * this library so it never becomes zero, thus never gets unloaded from the
+        * address space of the process. Since we are already running in the
+        * constructor of the LTTNG_UST_LIB_SO_NAME library, calling dlopen will
+        * simply increment the refcount and no additionnal work is needed by the
+        * dynamic loader as the shared library is already loaded in the address
+        * space. As a safe guard, we use the RTLD_NODELETE flag to prevent
+        * unloading of the UST library if its refcount becomes zero (which should
+        * never happen). Do the return value check but discard the handle at the
+        * end of the function as it's not needed.
+        */
+       handle = dlopen(LTTNG_UST_LIB_SO_NAME, RTLD_LAZY | RTLD_NODELETE);
+       if (!handle) {
+               ERR("dlopen of liblttng-ust shared library (%s).", LTTNG_UST_LIB_SO_NAME);
+       }
 
        /*
         * We want precise control over the order in which we construct
@@ -1444,16 +1815,18 @@ void __attribute__((constructor)) lttng_ust_init(void)
         * sessiond before the init functions are completed).
         */
        init_usterr();
+       lttng_ust_getenv_init();        /* Needs init_usterr() to be completed. */
        init_tracepoint();
+       lttng_ust_init_fd_tracker();
        lttng_ust_clock_init();
-       lttng_ust_baddr_statedump_init();
+       lttng_ust_getcpu_init();
+       lttng_ust_statedump_init();
        lttng_ring_buffer_metadata_client_init();
        lttng_ring_buffer_client_overwrite_init();
        lttng_ring_buffer_client_overwrite_rt_init();
        lttng_ring_buffer_client_discard_init();
        lttng_ring_buffer_client_discard_rt_init();
        lttng_perf_counter_init();
-       lttng_context_init();
        /*
         * Invoke ust malloc wrapper init before starting other threads.
         */
@@ -1461,11 +1834,22 @@ void __attribute__((constructor)) lttng_ust_init(void)
 
        timeout_mode = get_constructor_timeout(&constructor_timeout);
 
+       get_allow_blocking();
+
        ret = sem_init(&constructor_wait, 0, 0);
-       assert(!ret);
+       if (ret) {
+               PERROR("sem_init");
+       }
+
+       ret = setup_global_apps();
+       if (ret) {
+               assert(global_apps.allowed == 0);
+               DBG("global apps setup returned %d", ret);
+       }
 
        ret = setup_local_apps();
        if (ret) {
+               assert(local_apps.allowed == 0);
                DBG("local apps setup returned %d", ret);
        }
 
@@ -1489,14 +1873,18 @@ void __attribute__((constructor)) lttng_ust_init(void)
                ERR("pthread_attr_setdetachstate: %s", strerror(ret));
        }
 
-       pthread_mutex_lock(&ust_exit_mutex);
-       ret = pthread_create(&global_apps.ust_listener, &thread_attr,
-                       ust_listener_thread, &global_apps);
-       if (ret) {
-               ERR("pthread_create global: %s", strerror(ret));
+       if (global_apps.allowed) {
+               pthread_mutex_lock(&ust_exit_mutex);
+               ret = pthread_create(&global_apps.ust_listener, &thread_attr,
+                               ust_listener_thread, &global_apps);
+               if (ret) {
+                       ERR("pthread_create global: %s", strerror(ret));
+               }
+               global_apps.thread_active = 1;
+               pthread_mutex_unlock(&ust_exit_mutex);
+       } else {
+               handle_register_done(&global_apps);
        }
-       global_apps.thread_active = 1;
-       pthread_mutex_unlock(&ust_exit_mutex);
 
        if (local_apps.allowed) {
                pthread_mutex_lock(&ust_exit_mutex);
@@ -1527,17 +1915,34 @@ void __attribute__((constructor)) lttng_ust_init(void)
                        ret = sem_timedwait(&constructor_wait,
                                        &constructor_timeout);
                } while (ret < 0 && errno == EINTR);
-               if (ret < 0 && errno == ETIMEDOUT) {
-                       ERR("Timed out waiting for lttng-sessiond");
-               } else {
-                       assert(!ret);
+               if (ret < 0) {
+                       switch (errno) {
+                       case ETIMEDOUT:
+                               ERR("Timed out waiting for lttng-sessiond");
+                               break;
+                       case EINVAL:
+                               PERROR("sem_timedwait");
+                               break;
+                       default:
+                               ERR("Unexpected error \"%s\" returned by sem_timedwait",
+                                       strerror(errno));
+                       }
                }
                break;
        case -1:/* wait forever */
                do {
                        ret = sem_wait(&constructor_wait);
                } while (ret < 0 && errno == EINTR);
-               assert(!ret);
+               if (ret < 0) {
+                       switch (errno) {
+                       case EINVAL:
+                               PERROR("sem_wait");
+                               break;
+                       default:
+                               ERR("Unexpected error \"%s\" returned by sem_wait",
+                                       strerror(errno));
+                       }
+               }
                break;
        case 0: /* no timeout */
                break;
@@ -1548,9 +1953,9 @@ static
 void lttng_ust_cleanup(int exiting)
 {
        cleanup_sock_info(&global_apps, exiting);
-       if (local_apps.allowed) {
-               cleanup_sock_info(&local_apps, exiting);
-       }
+       cleanup_sock_info(&local_apps, exiting);
+       local_apps.allowed = 0;
+       global_apps.allowed = 0;
        /*
         * The teardown in this function all affect data structures
         * accessed under the UST lock by the listener thread. This
@@ -1560,18 +1965,17 @@ void lttng_ust_cleanup(int exiting)
         */
        lttng_ust_abi_exit();
        lttng_ust_events_exit();
-       lttng_context_exit();
        lttng_perf_counter_exit();
        lttng_ring_buffer_client_discard_rt_exit();
        lttng_ring_buffer_client_discard_exit();
        lttng_ring_buffer_client_overwrite_rt_exit();
        lttng_ring_buffer_client_overwrite_exit();
        lttng_ring_buffer_metadata_client_exit();
-       lttng_ust_baddr_statedump_destroy();
+       lttng_ust_statedump_destroy();
        exit_tracepoint();
        if (!exiting) {
                /* Reinitialize values for fork */
-               sem_count = 2;
+               sem_count = sem_count_initial_value;
                lttng_ust_comm_should_quit = 0;
                initialized = 0;
        }
@@ -1647,6 +2051,9 @@ void ust_before_fork(sigset_t *save_sigset)
        sigset_t all_sigs;
        int ret;
 
+       /* Fixup lttng-ust TLS. */
+       lttng_ust_fixup_tls();
+
        if (URCU_TLS(lttng_ust_nest_count))
                return;
        /* Disable signals */
@@ -1659,7 +2066,8 @@ void ust_before_fork(sigset_t *save_sigset)
        pthread_mutex_lock(&ust_fork_mutex);
 
        ust_lock_nocheck();
-       rcu_bp_before_fork();
+       urcu_bp_before_fork();
+       lttng_ust_lock_fd_tracker();
 }
 
 static void ust_after_fork_common(sigset_t *restore_sigset)
@@ -1667,6 +2075,7 @@ static void ust_after_fork_common(sigset_t *restore_sigset)
        int ret;
 
        DBG("process %d", getpid());
+       lttng_ust_unlock_fd_tracker();
        ust_unlock();
 
        pthread_mutex_unlock(&ust_fork_mutex);
@@ -1683,7 +2092,7 @@ void ust_after_fork_parent(sigset_t *restore_sigset)
        if (URCU_TLS(lttng_ust_nest_count))
                return;
        DBG("process %d", getpid());
-       rcu_bp_after_fork_parent();
+       urcu_bp_after_fork_parent();
        /* Release mutexes and reenable signals */
        ust_after_fork_common(restore_sigset);
 }
@@ -1701,11 +2110,13 @@ void ust_after_fork_child(sigset_t *restore_sigset)
 {
        if (URCU_TLS(lttng_ust_nest_count))
                return;
+       lttng_context_vpid_reset();
+       lttng_context_vtid_reset();
+       lttng_context_procname_reset();
        DBG("process %d", getpid());
        /* Release urcu mutexes */
-       rcu_bp_after_fork_child();
+       urcu_bp_after_fork_child();
        lttng_ust_cleanup(0);
-       lttng_context_vtid_reset();
        /* Release mutexes and reenable signals */
        ust_after_fork_common(restore_sigset);
        lttng_ust_init();
This page took 0.038535 seconds and 4 git commands to generate.