#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <dlfcn.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#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 "../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." LTTNG_UST_LIBRARY_VERSION_MAJOR
+
/*
* Has lttng ust comm constructor been called ?
*/
/* 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.
{
const char *val;
- val = (const char *) lttng_secure_getenv("LTTNG_HOME");
+ val = (const char *) lttng_getenv("LTTNG_HOME");
if (val != NULL) {
return val;
}
- return (const char *) lttng_secure_getenv("HOME");
+ return (const char *) lttng_getenv("HOME");
}
/*
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;
}
/*
- * Get notify_sock timeout, in ms.
+ * Get socket timeout, in ms.
* -1: wait forever. 0: don't wait. >0: timeout, in ms.
*/
static
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)
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: wait forever. 0: don't wait. 1: timeout wait.
*/
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)
{
}
}
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");
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;
}
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;
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
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",
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",
sock_info->notify_socket = -1;
}
+
/*
* Register. We need to perform both connect and sending
* registration message before doing the next connect otherwise
* 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.
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;
}
}
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.
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) {
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) {
pthread_attr_t thread_attr;
int timeout_mode;
int ret;
+ void *handle;
if (uatomic_xchg(&initialized, 1) == 1)
return;
* 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
* 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_getcpu_init();
lttng_ust_statedump_init();
timeout_mode = get_constructor_timeout(&constructor_timeout);
+ get_allow_blocking();
+
ret = sem_init(&constructor_wait, 0, 0);
if (ret) {
PERROR("sem_init");
{
cleanup_sock_info(&global_apps, exiting);
cleanup_sock_info(&local_apps, exiting);
+ local_apps.allowed = 0;
/*
* The teardown in this function all affect data structures
* accessed under the UST lock by the listener thread. This
sigset_t all_sigs;
int ret;
+ /* Fixup lttng-ust TLS. */
+ lttng_ust_fixup_tls();
+
if (URCU_TLS(lttng_ust_nest_count))
return;
/* Disable signals */
pthread_mutex_lock(&ust_fork_mutex);
ust_lock_nocheck();
- rcu_bp_before_fork();
+ urcu_bp_before_fork();
}
static void ust_after_fork_common(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);
}
{
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();