X-Git-Url: https://git.liburcu.org/?a=blobdiff_plain;f=libust%2Ftracectl.c;h=b058cb3ce19d0d317790ddbf03103164508d1eb4;hb=e625e9191bcb16522072aa34301cc517392f85ba;hp=7c054670e41de5a186f02f4c4bceb2b09bee8f16;hpb=0222e1213f196b66cbc08cd29093aca4a28e9ffb;p=ust.git diff --git a/libust/tracectl.c b/libust/tracectl.c index 7c05467..b058cb3 100644 --- a/libust/tracectl.c +++ b/libust/tracectl.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "tracer.h" #include "usterr.h" #include "ustcomm.h" @@ -62,6 +63,16 @@ static char receive_buffer[USTCOMM_BUFFER_SIZE]; static char send_buffer[USTCOMM_BUFFER_SIZE]; static int epoll_fd; + +/* + * Listener thread data vs fork() protection mechanism. Ensures that no listener + * thread mutexes and data structures are being concurrently modified or held by + * other threads when fork() is executed. + */ +static pthread_mutex_t listener_thread_data_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Mutex protecting listen_sock. Nests inside listener_thread_data_mutex. */ +static pthread_mutex_t listen_sock_mutex = PTHREAD_MUTEX_INITIALIZER; static struct ustcomm_sock *listen_sock; extern struct chan_info_struct chan_infos[]; @@ -73,6 +84,8 @@ static struct cds_list_head ust_socks = CDS_LIST_HEAD_INIT(ust_socks); /* volatile because shared between the listener and the main thread */ int buffers_to_export = 0; +int ust_clock_source; + static long long make_pidunique(void) { s64 retval; @@ -97,11 +110,11 @@ static void print_markers(FILE *fp) while (iter.marker) { fprintf(fp, "marker: %s/%s %d \"%s\" %p\n", - iter.marker->channel, - iter.marker->name, - (int)imv_read(iter.marker->state), - iter.marker->format, - iter.marker->location); + (*iter.marker)->channel, + (*iter.marker)->name, + (int)imv_read((*iter.marker)->state), + (*iter.marker)->format, + (*iter.marker)->location); marker_iter_next(&iter); } unlock_markers(); @@ -116,16 +129,16 @@ static void print_trace_events(FILE *fp) trace_event_iter_start(&iter); while (iter.trace_event) { - fprintf(fp, "trace_event: %s\n", iter.trace_event->name); + fprintf(fp, "trace_event: %s\n", (*iter.trace_event)->name); trace_event_iter_next(&iter); } unlock_trace_events(); } -static int connect_ustd(void) +static int connect_ustconsumer(void) { int result, fd; - char default_daemon_path[] = SOCK_DIR "/ustd"; + char default_daemon_path[] = SOCK_DIR "/ustconsumer"; char *explicit_daemon_path, *daemon_path; explicit_daemon_path = getenv("UST_DAEMON_SOCKET"); @@ -139,7 +152,7 @@ static int connect_ustd(void) result = ustcomm_connect_path(daemon_path, &fd); if (result < 0) { - WARN("connect_ustd failed, daemon_path: %s", + WARN("connect_ustconsumer failed, daemon_path: %s", daemon_path); return result; } @@ -194,12 +207,12 @@ static void inform_consumer_daemon(const char *trace_name) struct ust_trace *trace; const char *ch_name; - sock = connect_ustd(); + sock = connect_ustconsumer(); if (sock < 0) { return; } - DBG("Connected to ustd"); + DBG("Connected to ustconsumer"); ltt_lock_traces(); @@ -352,7 +365,7 @@ static int set_subbuf_size(const char *trace_name, const char *ch_name, } channel->subbuf_size = power; - DBG("the set_subbuf_size for the requested channel is %u", channel->subbuf_size); + DBG("the set_subbuf_size for the requested channel is %zu", channel->subbuf_size); unlock_traces: ltt_unlock_traces(); @@ -390,7 +403,7 @@ static int set_subbuf_num(const char *trace_name, const char *ch_name, } channel->subbuf_cnt = num; - DBG("the set_subbuf_cnt for the requested channel is %zd", channel->subbuf_cnt); + DBG("the set_subbuf_cnt for the requested channel is %u", channel->subbuf_cnt); unlock_traces: ltt_unlock_traces(); @@ -530,9 +543,19 @@ unlock_traces: return retval; } +static void release_listener_mutex(void *ptr) +{ + pthread_mutex_unlock(&listener_thread_data_mutex); +} + static void listener_cleanup(void *ptr) { - ustcomm_del_named_sock(listen_sock, 0); + pthread_mutex_lock(&listen_sock_mutex); + if (listen_sock) { + ustcomm_del_named_sock(listen_sock, 0); + listen_sock = NULL; + } + pthread_mutex_unlock(&listen_sock_mutex); } static void force_subbuf_switch() @@ -548,20 +571,18 @@ static void force_subbuf_switch() /* Simple commands are those which need only respond with a return value. */ static int process_simple_client_cmd(int command, char *recv_buf) { + int result; + switch(command) { case SET_SOCK_PATH: { - struct ustcomm_sock_path *sock_msg; - sock_msg = (struct ustcomm_sock_path *)recv_buf; - sock_msg->sock_path = - ustcomm_restore_ptr(sock_msg->sock_path, - sock_msg->data, - sizeof(sock_msg->data)); - if (!sock_msg->sock_path) { - - return -EINVAL; + struct ustcomm_single_field *sock_msg; + sock_msg = (struct ustcomm_single_field *)recv_buf; + result = ustcomm_unpack_single_field(sock_msg); + if (result < 0) { + return result; } - return setenv("UST_DAEMON_SOCKET", sock_msg->sock_path, 1); + return setenv("UST_DAEMON_SOCKET", sock_msg->field, 1); } case FORCE_SUBBUF_SWITCH: @@ -827,7 +848,7 @@ static void process_marker_cmd(int sock, int command, { struct ustcomm_header _reply_header; struct ustcomm_header *reply_header = &_reply_header; - int result; + int result = 0; memset(reply_header, 0, sizeof(*reply_header)); @@ -999,22 +1020,22 @@ static void process_client_cmd(struct ustcomm_header *recv_header, } case GET_SOCK_PATH: { - struct ustcomm_sock_path *sock_msg; + struct ustcomm_single_field *sock_msg; char *sock_path_env; - sock_msg = (struct ustcomm_sock_path *)send_buf; + sock_msg = (struct ustcomm_single_field *)send_buf; sock_path_env = getenv("UST_DAEMON_SOCKET"); if (!sock_path_env) { - result = ustcomm_pack_sock_path(reply_header, - sock_msg, - SOCK_DIR "/ustd"); + result = ustcomm_pack_single_field(reply_header, + sock_msg, + SOCK_DIR "/ustconsumer"); } else { - result = ustcomm_pack_sock_path(reply_header, - sock_msg, - sock_path_env); + result = ustcomm_pack_single_field(reply_header, + sock_msg, + sock_path_env); } reply_header->result = result; @@ -1028,10 +1049,10 @@ static void process_client_cmd(struct ustcomm_header *recv_header, case STOP_TRACE: case DESTROY_TRACE: { - struct ustcomm_trace_info *trace_inf = - (struct ustcomm_trace_info *)recv_buf; + struct ustcomm_single_field *trace_inf = + (struct ustcomm_single_field *)recv_buf; - result = ustcomm_unpack_trace_info(trace_inf); + result = ustcomm_unpack_single_field(trace_inf); if (result < 0) { ERR("couldn't unpack trace info"); reply_header->result = -EINVAL; @@ -1040,7 +1061,7 @@ static void process_client_cmd(struct ustcomm_header *recv_header, reply_header->result = process_trace_cmd(recv_header->command, - trace_inf->trace); + trace_inf->field); goto send_response; } @@ -1079,6 +1100,8 @@ void *listener_main(void *p) } for (i = 0; i < nfds; i++) { + pthread_mutex_lock(&listener_thread_data_mutex); + pthread_cleanup_push(release_listener_mutex, NULL); epoll_sock = (struct ustcomm_sock *)events[i].data.ptr; if (epoll_sock == listen_sock) { addr_size = sizeof(struct sockaddr); @@ -1107,6 +1130,7 @@ void *listener_main(void *p) epoll_sock->fd); } } + pthread_cleanup_pop(1); /* release listener mutex */ } } @@ -1231,6 +1255,7 @@ free_name: static void __attribute__((constructor)) init() { + struct timespec ts; int result; char* autoprobe_val = NULL; char* subbuffer_size_val = NULL; @@ -1264,6 +1289,15 @@ static void __attribute__((constructor)) init() create_listener(); + /* Get clock the clock source type */ + + /* Default clock source */ + ust_clock_source = CLOCK_TRACE; + if (clock_gettime(ust_clock_source, &ts) != 0) { + ust_clock_source = CLOCK_MONOTONIC; + DBG("UST traces will not be synchronized with LTTng traces"); + } + autoprobe_val = getenv("UST_AUTOPROBE"); if (autoprobe_val) { struct marker_iter iter; @@ -1303,8 +1337,8 @@ static void __attribute__((constructor)) init() DBG("now iterating on markers already registered"); while (iter.marker) { - DBG("now iterating on marker %s", iter.marker->name); - auto_probe_connect(iter.marker); + DBG("now iterating on marker %s", (*iter.marker)->name); + auto_probe_connect(*iter.marker); marker_iter_next(&iter); } } @@ -1498,7 +1532,7 @@ static void stop_listener(void) } /* This destructor keeps the process alive for a few seconds in order - * to leave time to ustd to connect to its buffers. This is necessary + * to leave time for ustconsumer to connect to its buffers. This is necessary * for programs whose execution is very short. It is also useful in all * programs when tracing is started close to the end of the program * execution. @@ -1558,6 +1592,7 @@ static void ust_fork(void) { struct ust_buffer *buf, *buf_tmp; struct ustcomm_sock *sock, *sock_tmp; + struct ust_trace *trace, *trace_tmp; int result; /* FIXME: technically, the locks could have been taken before the fork */ @@ -1566,11 +1601,14 @@ static void ust_fork(void) /* Get the pid of the new process */ processpid = getpid(); - /* break lock if necessary */ - ltt_unlock_traces(); + /* + * FIXME: This could be prettier, we loop over the list twice and + * following good locking practice should lock around the loop + */ + cds_list_for_each_entry_safe(trace, trace_tmp, <t_traces.head, list) { + ltt_trace_stop(trace->trace_name); + } - ltt_trace_stop("auto"); - ltt_trace_destroy("auto", 1); /* Delete all active connections, but leave them in the epoll set */ cds_list_for_each_entry_safe(sock, sock_tmp, &ust_socks, list) { ustcomm_del_sock(sock, 1); @@ -1579,19 +1617,22 @@ static void ust_fork(void) /* Delete all blocked consumers */ cds_list_for_each_entry_safe(buf, buf_tmp, &open_buffers_list, open_buffers_list) { - result = close(buf->data_ready_fd_read); - if (result == -1) { - PERROR("close"); - } - result = close(buf->data_ready_fd_write); - if (result == -1) { - PERROR("close"); - } cds_list_del(&buf->open_buffers_list); } - /* Clean up the listener socket and epoll, keeping the scoket file */ - ustcomm_del_named_sock(listen_sock, 1); + /* + * FIXME: This could be prettier, we loop over the list twice and + * following good locking practice should lock around the loop + */ + cds_list_for_each_entry_safe(trace, trace_tmp, <t_traces.head, list) { + ltt_trace_destroy(trace->trace_name, 1); + } + + /* Clean up the listener socket and epoll, keeping the socket file */ + if (listen_sock) { + ustcomm_del_named_sock(listen_sock, 1); + listen_sock = NULL; + } close(epoll_fd); /* Re-start the launch sequence */ @@ -1646,6 +1687,16 @@ void ust_before_fork(ust_fork_info_t *fork_info) PERROR("sigprocmask"); return; } + + /* + * Take the fork lock to make sure we are not in the middle of + * something in the listener thread. + */ + pthread_mutex_lock(&listener_thread_data_mutex); + /* + * Hold listen_sock_mutex to protect from listen_sock teardown. + */ + pthread_mutex_lock(&listen_sock_mutex); } /* Don't call this function directly in a traced program */ @@ -1653,6 +1704,9 @@ static void ust_after_fork_common(ust_fork_info_t *fork_info) { int result; + pthread_mutex_unlock(&listen_sock_mutex); + pthread_mutex_unlock(&listener_thread_data_mutex); + /* Restore signals */ result = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL); if (result == -1) { @@ -1663,7 +1717,7 @@ static void ust_after_fork_common(ust_fork_info_t *fork_info) void ust_after_fork_parent(ust_fork_info_t *fork_info) { - /* Reenable signals */ + /* Release mutexes and reenable signals */ ust_after_fork_common(fork_info); } @@ -1672,7 +1726,15 @@ void ust_after_fork_child(ust_fork_info_t *fork_info) /* First sanitize the child */ ust_fork(); - /* Then reenable interrupts */ + /* Then release mutexes and reenable signals */ ust_after_fork_common(fork_info); + + /* + * Make sure we clean up the urcu-bp thread list in the child by running + * the garbage collection before any pthread_create can be called. + * Failure to do so could lead to a deadlock caused by reuse of a thread + * ID before urcu-bp garbage collection is performed. + */ + synchronize_rcu(); }