*/
#define _GNU_SOURCE
+#define _LGPL_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ust/marker.h>
#include <ust/tracepoint.h>
+#include <ust/tracepoint-internal.h>
#include <ust/tracectl.h>
#include <ust/clock.h>
#include "tracer.h"
-#include "usterr.h"
+#include "usterr_signal_safe.h"
#include "ustcomm.h"
#include "buffers.h"
#include "marker-control.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[];
-static struct cds_list_head open_buffers_list = CDS_LIST_HEAD_INIT(open_buffers_list);
-
static struct cds_list_head ust_socks = CDS_LIST_HEAD_INIT(ust_socks);
/* volatile because shared between the listener and the main thread */
return retval;
}
-static void print_markers(FILE *fp)
+static void print_ust_marker(FILE *fp)
{
- struct marker_iter iter;
-
- lock_markers();
- marker_iter_reset(&iter);
- marker_iter_start(&iter);
-
- 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);
- marker_iter_next(&iter);
- }
- unlock_markers();
+ struct ust_marker_iter iter;
+
+ lock_ust_marker();
+ ust_marker_iter_reset(&iter);
+ ust_marker_iter_start(&iter);
+
+ while (iter.ust_marker) {
+ fprintf(fp, "ust_marker: %s/%s %d \"%s\" %p\n",
+ (*iter.ust_marker)->channel,
+ (*iter.ust_marker)->name,
+ (int)(*iter.ust_marker)->state,
+ (*iter.ust_marker)->format,
+ NULL); /*
+ * location is null for now, will be added
+ * to a different table.
+ */
+ ust_marker_iter_next(&iter);
+ }
+ unlock_ust_marker();
}
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();
}
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();
CMM_STORE_SHARED(buffers_to_export, CMM_LOAD_SHARED(buffers_to_export)-1);
}
- /* The buffer has been exported, ergo, we can add it to the
- * list of open buffers
- */
- cds_list_add(&buf->open_buffers_list, &open_buffers_list);
-
unlock_traces:
ltt_unlock_traces();
return retval;
}
-static void listener_cleanup(void *ptr)
+static void release_listener_mutex(void *ptr)
{
- ustcomm_del_named_sock(listen_sock, 0);
+ pthread_mutex_unlock(&listener_thread_data_mutex);
}
-static void force_subbuf_switch()
+static void listener_cleanup(void *ptr)
{
- struct ust_buffer *buf;
-
- cds_list_for_each_entry(buf, &open_buffers_list,
- open_buffers_list) {
- ltt_force_switch(buf, FORCE_FLUSH);
+ 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);
}
-/* Simple commands are those which need only respond with a return value. */
-static int process_simple_client_cmd(int command, char *recv_buf)
+static int force_subbuf_switch(const char *trace_name)
{
- int result;
+ struct ust_trace *trace;
+ int i, j, retval = 0;
- switch(command) {
- case SET_SOCK_PATH:
- {
- 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;
+ ltt_lock_traces();
+ trace = _ltt_trace_find(trace_name);
+ if (!trace) {
+ retval = -ENODATA;
+ DBG("Cannot find trace. It was likely destroyed by the user.");
+ goto unlock_traces;
+ }
+
+ for (i = 0; i < trace->nr_channels; i++) {
+ for (j = 0; j < trace->channels[i].n_cpus; j++) {
+ ltt_force_switch(trace->channels[i].buf[j],
+ FORCE_FLUSH);
}
- return setenv("UST_DAEMON_SOCKET", sock_msg->field, 1);
}
- case FORCE_SUBBUF_SWITCH:
- /* FIXME: return codes? */
- force_subbuf_switch();
-
- break;
-
- default:
- return -EINVAL;
- }
+unlock_traces:
+ ltt_unlock_traces();
- return 0;
+ return retval;
}
-
static int process_trace_cmd(int command, char *trace_name)
{
int result;
return result;
}
return 0;
+ case FORCE_SUBBUF_SWITCH:
+ DBG("force switch");
+
+ result = force_subbuf_switch(trace_name);
+ if (result < 0) {
+ ERR("force_subbuf_switch failed");
+ return result;
+ }
+ return 0;
}
return 0;
}
-static void process_marker_cmd(int sock, int command,
- struct ustcomm_marker_info *marker_inf)
+static void process_ust_marker_cmd(int sock, int command,
+ struct ustcomm_ust_marker_info *ust_marker_inf)
{
struct ustcomm_header _reply_header;
struct ustcomm_header *reply_header = &_reply_header;
- int result;
+ int result = 0;
memset(reply_header, 0, sizeof(*reply_header));
switch(command) {
case ENABLE_MARKER:
- result = ltt_marker_connect(marker_inf->channel,
- marker_inf->marker,
+ result = ltt_ust_marker_connect(ust_marker_inf->channel,
+ ust_marker_inf->ust_marker,
"default");
if (result < 0) {
- WARN("could not enable marker; channel=%s,"
+ WARN("could not enable ust_marker; channel=%s,"
" name=%s",
- marker_inf->channel,
- marker_inf->marker);
+ ust_marker_inf->channel,
+ ust_marker_inf->ust_marker);
}
break;
case DISABLE_MARKER:
- result = ltt_marker_disconnect(marker_inf->channel,
- marker_inf->marker,
+ result = ltt_ust_marker_disconnect(ust_marker_inf->channel,
+ ust_marker_inf->ust_marker,
"default");
if (result < 0) {
- WARN("could not disable marker; channel=%s,"
+ WARN("could not disable ust_marker; channel=%s,"
" name=%s",
- marker_inf->channel,
- marker_inf->marker);
+ ust_marker_inf->channel,
+ ust_marker_inf->ust_marker);
}
break;
}
case ENABLE_MARKER:
case DISABLE_MARKER:
{
- struct ustcomm_marker_info *marker_inf;
- marker_inf = (struct ustcomm_marker_info *)recv_buf;
- result = ustcomm_unpack_marker_info(marker_inf);
+ struct ustcomm_ust_marker_info *ust_marker_inf;
+ ust_marker_inf = (struct ustcomm_ust_marker_info *)recv_buf;
+ result = ustcomm_unpack_ust_marker_info(ust_marker_inf);
if (result < 0) {
- ERR("couldn't unpack marker info");
+ ERR("couldn't unpack ust_marker info");
reply_header->result = -EINVAL;
goto send_response;
}
- process_marker_cmd(sock, recv_header->command, marker_inf);
+ process_ust_marker_cmd(sock, recv_header->command, ust_marker_inf);
return;
}
case LIST_MARKERS:
ERR("opening memstream failed");
return;
}
- print_markers(fp);
+ print_ust_marker(fp);
fclose(fp);
- reply_header->size = size;
+ reply_header->size = size + 1; /* Include final \0 */
result = ustcomm_send(sock, reply_header, ptr);
free(ptr);
if (result < 0) {
- PERROR("failed to send markers list");
+ PERROR("failed to send ust_marker list");
}
break;
print_trace_events(fp);
fclose(fp);
- reply_header->size = size;
+ reply_header->size = size + 1; /* Include final \0 */
result = ustcomm_send(sock, reply_header, ptr);
goto send_response;
}
+ case SET_SOCK_PATH:
+ {
+ struct ustcomm_single_field *sock_msg;
+ sock_msg = (struct ustcomm_single_field *)recv_buf;
+ result = ustcomm_unpack_single_field(sock_msg);
+ if (result < 0) {
+ reply_header->result = -EINVAL;
+ goto send_response;
+ }
+
+ reply_header->result = setenv("UST_DAEMON_SOCKET",
+ sock_msg->field, 1);
+
+ goto send_response;
+ }
case START:
case SETUP_TRACE:
case ALLOC_TRACE:
case START_TRACE:
case STOP_TRACE:
case DESTROY_TRACE:
+ case FORCE_SUBBUF_SWITCH:
{
struct ustcomm_single_field *trace_inf =
(struct ustcomm_single_field *)recv_buf;
}
default:
- reply_header->result =
- process_simple_client_cmd(recv_header->command,
- recv_buf);
- goto send_response;
+ reply_header->result = -EINVAL;
+ goto send_response;
}
return;
}
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 int autoprobe_method = AUTOPROBE_DISABLED;
static regex_t autoprobe_regex;
-static void auto_probe_connect(struct marker *m)
+static void auto_probe_connect(struct ust_marker *m)
{
int result;
} else if (autoprobe_method == AUTOPROBE_ENABLE_REGEX) {
result = asprintf(&concat_name, "%s/%s", m->channel, m->name);
if (result == -1) {
- ERR("auto_probe_connect: asprintf failed (marker %s/%s)",
+ ERR("auto_probe_connect: asprintf failed (ust_marker %s/%s)",
m->channel, m->name);
return;
}
free(concat_name);
}
- result = ltt_marker_connect(m->channel, m->name, probe_name);
+ result = ltt_ust_marker_connect(m->channel, m->name, probe_name);
if (result && result != -EEXIST)
- ERR("ltt_marker_connect (marker = %s/%s, errno = %d)", m->channel, m->name, -result);
+ ERR("ltt_ust_marker_connect (ust_marker = %s/%s, errno = %d)", m->channel, m->name, -result);
- DBG("auto connected marker %s (addr: %p) %s to probe default", m->channel, m, m->name);
+ DBG("auto connected ust_marker %s (addr: %p) %s to probe default", m->channel, m, m->name);
}
static struct ustcomm_sock * init_app_socket(int epoll_fd)
{
- char *name;
+ char *dir_name, *sock_name;
int result;
- struct ustcomm_sock *sock;
+ struct ustcomm_sock *sock = NULL;
+ time_t mtime;
+
+ dir_name = ustcomm_user_sock_dir();
+ if (!dir_name)
+ return NULL;
+
+ mtime = ustcomm_pid_st_mtime(getpid());
+ if (!mtime) {
+ goto free_dir_name;
+ }
- result = asprintf(&name, "%s/%d", SOCK_DIR, (int)getpid());
+ result = asprintf(&sock_name, "%s/%d.%ld", dir_name,
+ (int) getpid(), (long) mtime);
if (result < 0) {
ERR("string overflow allocating socket name, "
"UST thread bailing");
- return NULL;
+ goto free_dir_name;
}
- result = ensure_dir_exists(SOCK_DIR);
+ result = ensure_dir_exists(dir_name, S_IRWXU);
if (result == -1) {
ERR("Unable to create socket directory %s, UST thread bailing",
- SOCK_DIR);
- goto free_name;
+ dir_name);
+ goto free_sock_name;
}
- sock = ustcomm_init_named_socket(name, epoll_fd);
+ sock = ustcomm_init_named_socket(sock_name, epoll_fd);
if (!sock) {
ERR("Error initializing named socket (%s). Check that directory"
- "exists and that it is writable. UST thread bailing", name);
- goto free_name;
+ "exists and that it is writable. UST thread bailing", sock_name);
+ goto free_sock_name;
}
- free(name);
- return sock;
+free_sock_name:
+ free(sock_name);
+free_dir_name:
+ free(dir_name);
-free_name:
- free(name);
- return NULL;
+ return sock;
}
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 */
- struct timespec ts;
+
/* Default clock source */
ust_clock_source = CLOCK_TRACE;
if (clock_gettime(ust_clock_source, &ts) != 0) {
autoprobe_val = getenv("UST_AUTOPROBE");
if (autoprobe_val) {
- struct marker_iter iter;
+ struct ust_marker_iter iter;
DBG("Autoprobe enabled.");
- /* Ensure markers are initialized */
- //init_markers();
+ /* Ensure ust_marker are initialized */
+ //init_ust_marker();
- /* Ensure marker control is initialized, for the probe */
- init_marker_control();
+ /* Ensure ust_marker control is initialized, for the probe */
+ init_ust_marker_control();
/* first, set the callback that will connect the
- * probe on new markers
+ * probe on new ust_marker
*/
if (autoprobe_val[0] == '/') {
result = regcomp(&autoprobe_regex, autoprobe_val+1, 0);
autoprobe_method = AUTOPROBE_ENABLE_ALL;
}
- marker_set_new_marker_cb(auto_probe_connect);
+ ust_marker_set_new_ust_marker_cb(auto_probe_connect);
/* Now, connect the probes that were already registered. */
- marker_iter_reset(&iter);
- marker_iter_start(&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);
- marker_iter_next(&iter);
+ ust_marker_iter_reset(&iter);
+ ust_marker_iter_start(&iter);
+
+ DBG("now iterating on ust_marker already registered");
+ while (iter.ust_marker) {
+ DBG("now iterating on ust_marker %s", (*iter.ust_marker)->name);
+ auto_probe_connect(*iter.ust_marker);
+ ust_marker_iter_next(&iter);
}
}
DBG("starting early tracing");
- /* Ensure marker control is initialized */
- init_marker_control();
+ /* Ensure ust_marker control is initialized */
+ init_ust_marker_control();
- /* Ensure markers are initialized */
- init_markers();
+ /* Ensure ust_marker are initialized */
+ init_ust_marker();
/* Ensure buffers are initialized, for the transport to be available.
* We are about to set a trace type and it will fail without this.
init_ustrelay_transport();
/* FIXME: When starting early tracing (here), depending on the
- * order of constructors, it is very well possible some marker
+ * order of constructors, it is very well possible some ust_marker
* sections are not yet registered. Because of this, some
* channels may not be registered. Yet, we are about to ask the
* daemon to collect the channels. Channels which are not yet
void ust_potential_exec(void)
{
- trace_mark(ust, potential_exec, MARK_NOARGS);
+ ust_marker(potential_exec, UST_MARKER_NOARGS);
DBG("test");
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 */
/* 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);
+ /*
+ * 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 scoket file */
- ustcomm_del_named_sock(listen_sock, 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);
+ rcu_bp_before_fork();
}
/* 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 */
+ rcu_bp_after_fork_parent();
+ /* Release mutexes and reenable signals */
ust_after_fork_common(fork_info);
}
void ust_after_fork_child(ust_fork_info_t *fork_info)
{
- /* First sanitize the child */
+ /* Release urcu mutexes */
+ rcu_bp_after_fork_child();
+
+ /* Sanitize the child */
ust_fork();
- /* Then reenable interrupts */
+ /* Then release mutexes and reenable signals */
ust_after_fork_common(fork_info);
}