#include <ust/marker.h>
#include <ust/tracepoint.h>
#include <ust/tracectl.h>
+#include <ust/clock.h>
#include "tracer.h"
#include "usterr.h"
#include "ustcomm.h"
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[];
/* 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;
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();
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();
}
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();
}
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();
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()
{
struct ustcomm_header _reply_header;
struct ustcomm_header *reply_header = &_reply_header;
- int result;
+ int result = 0;
memset(reply_header, 0, sizeof(*reply_header));
}
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);
epoll_sock->fd);
}
}
+ pthread_cleanup_pop(1); /* release listener mutex */
}
}
static void __attribute__((constructor)) init()
{
+ struct timespec ts;
int result;
char* autoprobe_val = NULL;
char* subbuffer_size_val = NULL;
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;
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);
}
}
{
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 */
/* 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);
/* 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 */
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 */
{
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) {
void ust_after_fork_parent(ust_fork_info_t *fork_info)
{
- /* Reenable signals */
+ /* Release mutexes and reenable signals */
ust_after_fork_common(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();
}