X-Git-Url: https://git.lttng.org/?a=blobdiff_plain;f=liblttng-ust%2Flttng-ust-comm.c;h=4104a3f563fd400709e7e7d3e8a73a1437321d88;hb=59df18ab09d8254d817852ac08cbd74f5cb5f7d0;hp=ad02dd9d366faaf043f9b13bc638eef371b99a4a;hpb=4d3c95233beec21079b6f895a7a55ae1421ee5cc;p=lttng-ust.git diff --git a/liblttng-ust/lttng-ust-comm.c b/liblttng-ust/lttng-ust-comm.c index ad02dd9d..4104a3f5 100644 --- a/liblttng-ust/lttng-ust-comm.c +++ b/liblttng-ust/lttng-ust-comm.c @@ -22,7 +22,6 @@ #define _LGPL_SOURCE #include #include -#include #include #include #include @@ -37,15 +36,17 @@ #include #include #include +#include -#include #include -#include #include -#include -#include #include +#include +#include +#include "tracepoint-internal.h" #include "ltt-tracer-core.h" +#include "compat.h" +#include "../libringbuffer/tlsfixup.h" /* * Has lttng ust comm constructor been called ? @@ -77,6 +78,12 @@ static sem_t constructor_wait; */ static int sem_count = { 2 }; +/* + * Counting nesting within lttng-ust. Used to ensure that calling fork() + * from liblttng-ust does not execute the pre/post fork handlers. + */ +static int __thread lttng_ust_nest_count; + /* * Info about socket and associated listener thread. */ @@ -129,6 +136,15 @@ extern void ltt_ring_buffer_client_overwrite_exit(void); extern void ltt_ring_buffer_client_discard_exit(void); extern void ltt_ring_buffer_metadata_client_exit(void); +/* + * Force a read (imply TLS fixup for dlopen) of TLS variables. + */ +static +void lttng_fixup_nest_count_tls(void) +{ + asm volatile ("" : : "m" (lttng_ust_nest_count)); +} + static int setup_local_apps(void) { @@ -159,7 +175,6 @@ static int register_app_to_sessiond(int socket) { ssize_t ret; - int prctl_ret; struct { uint32_t major; uint32_t minor; @@ -167,6 +182,7 @@ int register_app_to_sessiond(int socket) pid_t ppid; uid_t uid; gid_t gid; + uint32_t bits_per_long; char name[16]; /* process name */ } reg_msg; @@ -176,11 +192,8 @@ int register_app_to_sessiond(int socket) reg_msg.ppid = getppid(); reg_msg.uid = getuid(); reg_msg.gid = getgid(); - prctl_ret = prctl(PR_GET_NAME, (unsigned long) reg_msg.name, 0, 0, 0); - if (prctl_ret) { - ERR("Error executing prctl"); - return -errno; - } + reg_msg.bits_per_long = CAA_BITS_PER_LONG; + lttng_ust_getprocname(reg_msg.name); ret = ustcomm_send_unix_sock(socket, ®_msg, sizeof(reg_msg)); if (ret >= 0 && ret != sizeof(reg_msg)) @@ -237,6 +250,8 @@ int handle_message(struct sock_info *sock_info, const struct lttng_ust_objd_ops *ops; struct ustcomm_ust_reply lur; int shm_fd, wait_fd; + union ust_args args; + ssize_t len; ust_lock(); @@ -269,7 +284,8 @@ int handle_message(struct sock_info *sock_info, default: if (ops->cmd) ret = ops->cmd(lum->handle, lum->cmd, - (unsigned long) &lum->u); + (unsigned long) &lum->u, + &args); else ret = -ENOSYS; break; @@ -285,25 +301,30 @@ end: //lur.ret_code = USTCOMM_SESSION_FAIL; lur.ret_code = ret; } - switch (lum->cmd) { - case LTTNG_UST_STREAM: - /* - * Special-case reply to send stream info. - * Use lum.u output. - */ - lur.u.stream.memory_map_size = lum->u.stream.memory_map_size; - shm_fd = lum->u.stream.shm_fd; - wait_fd = lum->u.stream.wait_fd; - break; - case LTTNG_UST_METADATA: - case LTTNG_UST_CHANNEL: - lur.u.channel.memory_map_size = lum->u.channel.memory_map_size; - shm_fd = lum->u.channel.shm_fd; - wait_fd = lum->u.channel.wait_fd; - break; - case LTTNG_UST_VERSION: - lur.u.version = lum->u.version; - break; + if (ret >= 0) { + switch (lum->cmd) { + case LTTNG_UST_STREAM: + /* + * Special-case reply to send stream info. + * Use lum.u output. + */ + lur.u.stream.memory_map_size = *args.stream.memory_map_size; + shm_fd = *args.stream.shm_fd; + wait_fd = *args.stream.wait_fd; + break; + case LTTNG_UST_METADATA: + case LTTNG_UST_CHANNEL: + lur.u.channel.memory_map_size = *args.channel.memory_map_size; + shm_fd = *args.channel.shm_fd; + wait_fd = *args.channel.wait_fd; + break; + case LTTNG_UST_TRACER_VERSION: + lur.u.version = lum->u.version; + break; + case LTTNG_UST_TRACEPOINT_LIST_GET: + memcpy(&lur.u.tracepoint, &lum->u.tracepoint, sizeof(lur.u.tracepoint)); + break; + } } ret = send_reply(sock, &lur); if (ret < 0) { @@ -315,14 +336,20 @@ end: || lum->cmd == LTTNG_UST_CHANNEL || lum->cmd == LTTNG_UST_METADATA) && lur.ret_code == USTCOMM_OK) { + int sendret = 0; + /* we also need to send the file descriptors. */ ret = ustcomm_send_fds_unix_sock(sock, &shm_fd, &shm_fd, 1, sizeof(int)); if (ret < 0) { perror("send shm_fd"); - goto error; + sendret = ret; } + /* + * The sessiond expects 2 file descriptors, even upon + * error. + */ ret = ustcomm_send_fds_unix_sock(sock, &wait_fd, &wait_fd, 1, sizeof(int)); @@ -330,19 +357,83 @@ end: perror("send wait_fd"); goto error; } + if (sendret) { + ret = sendret; + goto error; + } } + /* + * LTTNG_UST_TRACEPOINT_FIELD_LIST_GET needs to send the field + * after the reply. + */ + if (lur.ret_code == USTCOMM_OK) { + switch (lum->cmd) { + case LTTNG_UST_TRACEPOINT_FIELD_LIST_GET: + len = ustcomm_send_unix_sock(sock, + &args.field_list.entry, + sizeof(args.field_list.entry)); + if (len != sizeof(args.field_list.entry)) { + ret = -1; + goto error; + } + } + } + /* + * We still have the memory map reference, and the fds have been + * sent to the sessiond. We can therefore close those fds. Note + * that we keep the write side of the wait_fd open, but close + * the read side. + */ + if (lur.ret_code == USTCOMM_OK) { + switch (lum->cmd) { + case LTTNG_UST_STREAM: + if (shm_fd >= 0) { + ret = close(shm_fd); + if (ret) { + PERROR("Error closing stream shm_fd"); + } + *args.stream.shm_fd = -1; + } + if (wait_fd >= 0) { + ret = close(wait_fd); + if (ret) { + PERROR("Error closing stream wait_fd"); + } + *args.stream.wait_fd = -1; + } + break; + case LTTNG_UST_METADATA: + case LTTNG_UST_CHANNEL: + if (shm_fd >= 0) { + ret = close(shm_fd); + if (ret) { + PERROR("Error closing channel shm_fd"); + } + *args.channel.shm_fd = -1; + } + if (wait_fd >= 0) { + ret = close(wait_fd); + if (ret) { + PERROR("Error closing channel wait_fd"); + } + *args.channel.wait_fd = -1; + } + break; + } + } + error: ust_unlock(); return ret; } static -void cleanup_sock_info(struct sock_info *sock_info) +void cleanup_sock_info(struct sock_info *sock_info, int exiting) { int ret; if (sock_info->socket != -1) { - ret = close(sock_info->socket); + ret = ustcomm_close_unix_sock(sock_info->socket); if (ret) { ERR("Error closing apps socket"); } @@ -356,7 +447,13 @@ void cleanup_sock_info(struct sock_info *sock_info) sock_info->root_handle = -1; } sock_info->constructor_sem_posted = 0; - if (sock_info->wait_shm_mmap) { + /* + * wait_shm_mmap is used by listener threads outside of the + * ust lock, so we cannot tear it down ourselves, because we + * cannot join on these threads. Leave this task to the OS + * process exit. + */ + if (!exiting && sock_info->wait_shm_mmap) { ret = munmap(sock_info->wait_shm_mmap, sysconf(_SC_PAGE_SIZE)); if (ret) { ERR("Error unmapping wait shm"); @@ -399,7 +496,9 @@ int get_wait_shm(struct sock_info *sock_info, size_t mmap_size) * If the open failed because the file did not exist, try * creating it ourself. */ + lttng_ust_nest_count++; pid = fork(); + lttng_ust_nest_count--; if (pid > 0) { int status; @@ -408,7 +507,7 @@ int get_wait_shm(struct sock_info *sock_info, size_t mmap_size) * shared memory map will have been created. */ pid = wait(&status); - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { wait_shm_fd = -1; goto end; } @@ -556,14 +655,15 @@ void wait_for_sessiond(struct sock_info *sock_info) if (ret < 0) { if (errno == EFAULT) { wait_poll_fallback = 1; - WARN( + 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"); } - PERROR("futex"); } } return; @@ -615,7 +715,7 @@ restart: } if (sock_info->socket != -1) { - ret = close(sock_info->socket); + ret = ustcomm_close_unix_sock(sock_info->socket); if (ret) { ERR("Error closing %s apps socket", sock_info->name); } @@ -676,6 +776,20 @@ restart: switch (len) { case 0: /* orderly shutdown */ DBG("%s ltt-sessiond has performed an orderly shutdown\n", sock_info->name); + ust_lock(); + /* + * Either sessiond has shutdown or refused us by closing the socket. + * In either case, we don't want to delay construction execution, + * and we need to wait before retry. + */ + prev_connect_failed = 1; + /* + * If we cannot register to the sessiond daemon, don't + * delay constructor execution. + */ + ret = handle_register_done(sock_info); + assert(!ret); + ust_unlock(); goto end; case sizeof(lum): DBG("message received\n"); @@ -685,6 +799,7 @@ restart: } continue; case -1: + DBG("Receive failed from lttng-sessiond with errno %d", errno); if (errno == ECONNRESET) { ERR("%s remote end closed connection\n", sock_info->name); goto end; @@ -751,12 +866,23 @@ int get_timeout(struct timespec *constructor_timeout) void __attribute__((constructor)) lttng_ust_init(void) { struct timespec constructor_timeout; + sigset_t sig_all_blocked, orig_parent_mask; int timeout_mode; int ret; if (uatomic_xchg(&initialized, 1) == 1) return; + /* + * Fixup interdependency between TLS fixup mutex (which happens + * to be the dynamic linker mutex) and ust_lock, taken within + * the ust lock. + */ + lttng_fixup_event_tls(); + lttng_fixup_ringbuffer_tls(); + lttng_fixup_vtid_tls(); + lttng_fixup_nest_count_tls(); + /* * We want precise control over the order in which we construct * our sub-libraries vs starting to receive commands from @@ -778,16 +904,39 @@ void __attribute__((constructor)) lttng_ust_init(void) if (ret) { ERR("Error setting up to local apps"); } - ret = pthread_create(&local_apps.ust_listener, NULL, - ust_listener_thread, &local_apps); + /* A new thread created by pthread_create inherits the signal mask + * from the parent. To avoid any signal being received by the + * listener thread, we block all signals temporarily in the parent, + * while we create the listener thread. + */ + sigfillset(&sig_all_blocked); + ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_parent_mask); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + + ret = pthread_create(&global_apps.ust_listener, NULL, + ust_listener_thread, &global_apps); + if (ret) { + ERR("pthread_create global: %s", strerror(ret)); + } if (local_apps.allowed) { - ret = pthread_create(&global_apps.ust_listener, NULL, - ust_listener_thread, &global_apps); + ret = pthread_create(&local_apps.ust_listener, NULL, + ust_listener_thread, &local_apps); + if (ret) { + ERR("pthread_create local: %s", strerror(ret)); + } } else { handle_register_done(&local_apps); } + /* Restore original signal mask in parent */ + ret = pthread_sigmask(SIG_SETMASK, &orig_parent_mask, NULL); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + switch (timeout_mode) { case 1: /* timeout wait */ do { @@ -814,10 +963,17 @@ void __attribute__((constructor)) lttng_ust_init(void) static void lttng_ust_cleanup(int exiting) { - cleanup_sock_info(&global_apps); + cleanup_sock_info(&global_apps, exiting); if (local_apps.allowed) { - cleanup_sock_info(&local_apps); + cleanup_sock_info(&local_apps, exiting); } + /* + * The teardown in this function all affect data structures + * accessed under the UST lock by the listener thread. This + * lock, along with the lttng_ust_comm_should_quit flag, ensure + * that none of these threads are accessing this data at this + * point. + */ lttng_ust_abi_exit(); lttng_ust_events_exit(); ltt_ring_buffer_client_discard_exit(); @@ -851,16 +1007,27 @@ void __attribute__((destructor)) lttng_ust_exit(void) lttng_ust_comm_should_quit = 1; ust_unlock(); + /* cancel threads */ ret = pthread_cancel(global_apps.ust_listener); if (ret) { - ERR("Error cancelling global ust listener thread"); + ERR("Error cancelling global ust listener thread: %s", + strerror(ret)); } if (local_apps.allowed) { ret = pthread_cancel(local_apps.ust_listener); if (ret) { - ERR("Error cancelling local ust listener thread"); + ERR("Error cancelling local ust listener thread: %s", + strerror(ret)); } } + /* + * Do NOT join threads: use of sys_futex makes it impossible to + * join the threads without using async-cancel, but async-cancel + * is delivered by a signal, which could hit the target thread + * anywhere in its code path, including while the ust_lock() is + * held, causing a deadlock for the other thread. Let the OS + * cleanup the threads if there are stalled in a syscall. + */ lttng_ust_cleanup(1); } @@ -871,7 +1038,7 @@ void __attribute__((destructor)) lttng_ust_exit(void) * 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) +void ust_before_fork(sigset_t *save_sigset) { /* * Disable signals. This is to avoid that the child intervenes @@ -882,9 +1049,11 @@ void ust_before_fork(ust_fork_info_t *fork_info) sigset_t all_sigs; int ret; + if (lttng_ust_nest_count) + return; /* Disable signals */ sigfillset(&all_sigs); - ret = sigprocmask(SIG_BLOCK, &all_sigs, &fork_info->orig_sigs); + ret = sigprocmask(SIG_BLOCK, &all_sigs, save_sigset); if (ret == -1) { PERROR("sigprocmask"); } @@ -892,25 +1061,27 @@ void ust_before_fork(ust_fork_info_t *fork_info) rcu_bp_before_fork(); } -static void ust_after_fork_common(ust_fork_info_t *fork_info) +static void ust_after_fork_common(sigset_t *restore_sigset) { int ret; DBG("process %d", getpid()); ust_unlock(); /* Restore signals */ - ret = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL); + ret = sigprocmask(SIG_SETMASK, restore_sigset, NULL); if (ret == -1) { PERROR("sigprocmask"); } } -void ust_after_fork_parent(ust_fork_info_t *fork_info) +void ust_after_fork_parent(sigset_t *restore_sigset) { + if (lttng_ust_nest_count) + return; DBG("process %d", getpid()); rcu_bp_after_fork_parent(); /* Release mutexes and reenable signals */ - ust_after_fork_common(fork_info); + ust_after_fork_common(restore_sigset); } /* @@ -922,14 +1093,16 @@ void ust_after_fork_parent(ust_fork_info_t *fork_info) * 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) +void ust_after_fork_child(sigset_t *restore_sigset) { + if (lttng_ust_nest_count) + return; DBG("process %d", getpid()); /* Release urcu mutexes */ rcu_bp_after_fork_child(); lttng_ust_cleanup(0); lttng_context_vtid_reset(); /* Release mutexes and reenable signals */ - ust_after_fork_common(fork_info); + ust_after_fork_common(restore_sigset); lttng_ust_init(); }