#include <semaphore.h>
#include <time.h>
#include <assert.h>
+#include <signal.h>
#include <urcu/uatomic.h>
#include <lttng-ust-comm.h>
#include <ust/usterr-signal-safe.h>
#include <ust/lttng-ust-abi.h>
#include <ust/tracepoint.h>
+#include <ust/tracepoint-internal.h>
+#include <ust/ust.h>
/*
* Has lttng ust comm constructor been called ?
static int initialized;
/*
- * communication thread mutex. Held when handling a command, also held
- * by fork() to deal with removal of threads, and by exit path.
+ * The ust_lock/ust_unlock lock is used as a communication thread mutex.
+ * Held when handling a command, also held by fork() to deal with
+ * removal of threads, and by exit path.
*/
-static pthread_mutex_t lttng_ust_comm_mutex = PTHREAD_MUTEX_INITIALIZER;
/* Should the ust comm thread quit ? */
static int lttng_ust_comm_should_quit;
*/
if (getuid() != geteuid()) {
local_apps.allowed = 0;
- return;
+ return 0;
} else {
local_apps.allowed = 1;
}
const struct objd_ops *ops;
struct lttcomm_ust_reply lur;
- pthread_mutex_lock(<tng_ust_comm_mutex);
+ ust_lock();
memset(&lur, 0, sizeof(lur));
}
ret = send_reply(sock, &lur);
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
return ret;
}
/* Restart trying to connect to the session daemon */
restart:
- pthread_mutex_lock(<tng_ust_comm_mutex);
+ ust_lock();
if (lttng_ust_comm_should_quit) {
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
goto quit;
}
*/
ret = handle_register_done(sock_info);
assert(!ret);
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
sleep(5);
goto restart;
}
ret = lttng_abi_create_root_handle();
if (ret) {
ERR("Error creating root handle");
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
goto quit;
}
sock_info->root_handle = ret;
*/
ret = handle_register_done(sock_info);
assert(!ret);
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
sleep(5);
goto restart;
}
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
for (;;) {
ssize_t len;
}
}
+static
+void lttng_ust_cleanup(int exiting)
+{
+ cleanup_sock_info(&global_apps);
+ if (local_apps.allowed) {
+ cleanup_sock_info(&local_apps);
+ }
+ lttng_ust_abi_exit();
+ ltt_events_exit();
+ ltt_ring_buffer_client_discard_exit();
+ ltt_ring_buffer_client_overwrite_exit();
+ ltt_ring_buffer_metadata_client_exit();
+ exit_tracepoint();
+ if (!exiting) {
+ /* Reinitialize values for fork */
+ sem_count = 2;
+ lttng_ust_comm_should_quit = 0;
+ initialized = 0;
+ }
+}
+
void __attribute__((destructor)) lttng_ust_exit(void)
{
int ret;
* mutexes to ensure it is not in a mutex critical section when
* pthread_cancel is later called.
*/
- pthread_mutex_lock(<tng_ust_comm_mutex);
+ ust_lock();
lttng_ust_comm_should_quit = 1;
- pthread_mutex_unlock(<tng_ust_comm_mutex);
+ ust_unlock();
ret = pthread_cancel(global_apps.ust_listener);
if (ret) {
ERR("Error cancelling global ust listener thread");
}
-
- cleanup_sock_info(&global_apps);
-
if (local_apps.allowed) {
ret = pthread_cancel(local_apps.ust_listener);
if (ret) {
ERR("Error cancelling local ust listener thread");
}
+ }
+ lttng_ust_cleanup(1);
+}
- cleanup_sock_info(&local_apps);
+/*
+ * We exclude the worker threads across fork and clone (except
+ * CLONE_VM), because these system calls only keep the forking thread
+ * running in the child. Therefore, we don't want to call fork or clone
+ * in the middle of an tracepoint or ust tracing state modification.
+ * Holding this mutex protects these structures across fork and clone.
+ */
+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 ret;
+
+ /* Disable signals */
+ sigfillset(&all_sigs);
+ ret = sigprocmask(SIG_BLOCK, &all_sigs, &fork_info->orig_sigs);
+ if (ret == -1) {
+ PERROR("sigprocmask");
}
+ ust_lock();
+ rcu_bp_before_fork();
+}
- lttng_ust_abi_exit();
- ltt_events_exit();
- ltt_ring_buffer_client_discard_exit();
- ltt_ring_buffer_client_overwrite_exit();
- ltt_ring_buffer_metadata_client_exit();
- exit_tracepoint();
+static void ust_after_fork_common(ust_fork_info_t *fork_info)
+{
+ int ret;
+
+ DBG("process %d", getpid());
+ ust_unlock();
+ /* Restore signals */
+ ret = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL);
+ if (ret == -1) {
+ PERROR("sigprocmask");
+ }
+}
+
+void ust_after_fork_parent(ust_fork_info_t *fork_info)
+{
+ DBG("process %d", getpid());
+ rcu_bp_after_fork_parent();
+ /* Release mutexes and reenable signals */
+ ust_after_fork_common(fork_info);
+}
+
+/*
+ * After fork, in the child, we need to cleanup all the leftover state,
+ * except the worker thread which already magically disappeared thanks
+ * to the weird Linux fork semantics. After tyding up, we call
+ * lttng_ust_init() again to start over as a new PID.
+ *
+ * This is meant for forks() that have tracing in the child between the
+ * fork and following exec call (if there is any).
+ */
+void ust_after_fork_child(ust_fork_info_t *fork_info)
+{
+ DBG("process %d", getpid());
+ /* Release urcu mutexes */
+ rcu_bp_after_fork_child();
+ lttng_ust_cleanup(0);
+ lttng_ust_init();
+ /* Release mutexes and reenable signals */
+ ust_after_fork_common(fork_info);
}