X-Git-Url: http://git.lttng.org/?a=blobdiff_plain;f=libust%2Ftracectl.c;h=80d09ae093045fa88b6c1fcac8923ced5ef2402b;hb=b5b073e22d34bec71259d39b8946354f170f01a9;hp=9dfc8af6f6ad6ccb3b611cbb1136c552238046f0;hpb=1e2944cb1e15d658b5991026e5604c56f551e2ab;p=ust.git diff --git a/libust/tracectl.c b/libust/tracectl.c index 9dfc8af..80d09ae 100644 --- a/libust/tracectl.c +++ b/libust/tracectl.c @@ -25,14 +25,15 @@ #include #include #include +#include -#include +#include -#include "marker.h" +#include #include "tracer.h" -#include "localerr.h" +#include "usterr.h" #include "ustcomm.h" -#include "relay.h" /* FIXME: remove */ +#include "buffers.h" /* FIXME: remove */ #include "marker-control.h" //#define USE_CLONE @@ -45,6 +46,11 @@ char consumer_stack[10000]; +/* This should only be accessed by the constructor, before the creation + * of the listener, and then only by the listener. + */ +s64 pidunique = -1LL; + struct list_head blocked_consumers = LIST_HEAD_INIT(blocked_consumers); static struct ustcomm_app ustcomm_app; @@ -54,9 +60,8 @@ struct tracecmd { /* no padding */ uint16_t command; }; -//struct listener_arg { -// int pipe_fd; -//}; +/* volatile because shared between the listener and the main thread */ +volatile sig_atomic_t buffers_to_export = 0; struct trctl_msg { /* size: the size of all the fields except size itself */ @@ -82,13 +87,26 @@ struct blocked_consumer { struct ustcomm_server server; struct ustcomm_source src; - /* args to ltt_do_get_subbuf */ - struct rchan_buf *rbuf; - struct ltt_channel_buf_struct *lttbuf; + /* args to ust_buffers_do_get_subbuf */ + struct ust_buffer *buf; struct list_head list; }; +static long long make_pidunique(void) +{ + s64 retval; + struct timeval tv; + + gettimeofday(&tv, NULL); + + retval = tv.tv_sec; + retval <<= 32; + retval |= tv.tv_usec; + + return retval; +} + static void print_markers(FILE *fp) { struct marker_iter iter; @@ -98,15 +116,13 @@ static void print_markers(FILE *fp) marker_iter_start(&iter); while(iter.marker) { - fprintf(fp, "marker: %s_%s %d \"%s\"\n", iter.marker->channel, iter.marker->name, (int)imv_read(iter.marker->state), iter.marker->format); + 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(); } -void do_command(struct tracecmd *cmd) -{ -} +static int init_socket(void); /* This needs to be called whenever a new thread is created. It notifies * liburcu of the new thread. @@ -139,10 +155,39 @@ void notif_cb(void) } } -static void inform_consumer_daemon(void) +/* Ask the daemon to collect a trace called trace_name and being + * produced by this pid. + * + * The trace must be at least allocated. (It can also be started.) + * This is because _ltt_trace_find is used. + */ + +static void inform_consumer_daemon(const char *trace_name) { - ustcomm_request_consumer(getpid(), "metadata"); - ustcomm_request_consumer(getpid(), "ust"); + int i; + struct ltt_trace_struct *trace; + pid_t pid = getpid(); + int result; + + ltt_lock_traces(); + + trace = _ltt_trace_find(trace_name); + if(trace == NULL) { + WARN("inform_consumer_daemon: could not find trace \"%s\"; it is probably already destroyed", trace_name); + goto finish; + } + + for(i=0; i < trace->nr_channels; i++) { + result = ustcomm_request_consumer(pid, trace->channels[i].channel_name); + if(result == -1) { + WARN("Failed to request collection for channel %s. Is the daemon available?", trace->channels[i].channel_name); + /* continue even if fail */ + } + buffers_to_export++; + } + + finish: + ltt_unlock_traces(); } void process_blocked_consumers(void) @@ -204,13 +249,13 @@ void process_blocked_consumers(void) continue; } - result = ltt_do_get_subbuf(bc->rbuf, bc->lttbuf, &consumed_old); + result = ust_buffers_do_get_subbuf(bc->buf, &consumed_old); if(result == -EAGAIN) { WARN("missed buffer?"); continue; } else if(result < 0) { - DBG("ltt_do_get_subbuf: error: %s", strerror(-result)); + DBG("ust_buffers_do_get_subbuf: error: %s", strerror(-result)); } asprintf(&reply, "%s %ld", "OK", consumed_old); result = ustcomm_send_reply(&bc->server, reply, &bc->src); @@ -254,7 +299,7 @@ void *listener_main(void *p) continue; } - DBG("received a message! it's: %s\n", recvbuf); + DBG("received a message! it's: %s", recvbuf); len = strlen(recvbuf); if(!strcmp(recvbuf, "print_markers")) { @@ -293,7 +338,7 @@ void *listener_main(void *p) return (void *)1; } - inform_consumer_daemon(); + inform_consumer_daemon(trace_name); result = ltt_trace_start(trace_name); if(result < 0) { @@ -377,16 +422,15 @@ void *listener_main(void *p) } for(i=0; inr_channels; i++) { - struct rchan *rchan = trace->channels[i].trans_channel_data; - struct rchan_buf *rbuf = rchan->buf; - struct ltt_channel_struct *ltt_channel = (struct ltt_channel_struct *)rchan->private_data; + struct ust_channel *channel = &trace->channels[i]; + struct ust_buffer *buf = channel->buf; if(!strcmp(trace->channels[i].channel_name, channel_name)) { char *reply; - DBG("the shmid for the requested channel is %d", rbuf->shmid); - DBG("the shmid for its buffer structure is %d", ltt_channel->buf_shmid); - asprintf(&reply, "%d %d", rbuf->shmid, ltt_channel->buf_shmid); + DBG("the shmid for the requested channel is %d", buf->shmid); + DBG("the shmid for its buffer structure is %d", channel->buf_shmid); + asprintf(&reply, "%d %d", buf->shmid, channel->buf_shmid); result = ustcomm_send_reply(&ustcomm_app.server, reply, &src); if(result) { @@ -399,6 +443,8 @@ void *listener_main(void *p) break; } } + + buffers_to_export--; } else if(nth_token_is(recvbuf, "get_n_subbufs", 0) == 1) { struct ltt_trace_struct *trace; @@ -424,13 +470,13 @@ void *listener_main(void *p) } for(i=0; inr_channels; i++) { - struct rchan *rchan = trace->channels[i].trans_channel_data; + struct ust_channel *channel = &trace->channels[i]; if(!strcmp(trace->channels[i].channel_name, channel_name)) { char *reply; - DBG("the n_subbufs for the requested channel is %zd", rchan->n_subbufs); - asprintf(&reply, "%zd", rchan->n_subbufs); + DBG("the n_subbufs for the requested channel is %d", channel->subbuf_cnt); + asprintf(&reply, "%d", channel->subbuf_cnt); result = ustcomm_send_reply(&ustcomm_app.server, reply, &src); if(result) { @@ -468,13 +514,13 @@ void *listener_main(void *p) } for(i=0; inr_channels; i++) { - struct rchan *rchan = trace->channels[i].trans_channel_data; + struct ust_channel *channel = &trace->channels[i]; if(!strcmp(trace->channels[i].channel_name, channel_name)) { char *reply; - DBG("the subbuf_size for the requested channel is %zd", rchan->subbuf_size); - asprintf(&reply, "%zd", rchan->subbuf_size); + DBG("the subbuf_size for the requested channel is %zd", channel->subbuf_size); + asprintf(&reply, "%zd", channel->subbuf_size); result = ustcomm_send_reply(&ustcomm_app.server, reply, &src); if(result) { @@ -519,11 +565,10 @@ void *listener_main(void *p) } for(i=0; inr_channels; i++) { - struct rchan *rchan = trace->channels[i].trans_channel_data; + struct ust_channel *channel = &trace->channels[i]; if(!strcmp(trace->channels[i].channel_name, channel_name)) { - struct rchan_buf *rbuf = rchan->buf; - struct ltt_channel_buf_struct *lttbuf = trace->channels[i].buf; + struct ust_buffer *buf = channel->buf; struct blocked_consumer *bc; bc = (struct blocked_consumer *) malloc(sizeof(struct blocked_consumer)); @@ -532,9 +577,8 @@ void *listener_main(void *p) goto next_cmd; } bc->fd_consumer = src.fd; - bc->fd_producer = lttbuf->data_ready_fd_read; - bc->rbuf = rbuf; - bc->lttbuf = lttbuf; + bc->fd_producer = buf->data_ready_fd_read; + bc->buf = buf; bc->src = src; bc->server = ustcomm_app.server; @@ -582,21 +626,20 @@ void *listener_main(void *p) } for(i=0; inr_channels; i++) { - struct rchan *rchan = trace->channels[i].trans_channel_data; + struct ust_channel *channel = &trace->channels[i]; if(!strcmp(trace->channels[i].channel_name, channel_name)) { - struct rchan_buf *rbuf = rchan->buf; - struct ltt_channel_buf_struct *lttbuf = trace->channels[i].buf; + struct ust_buffer *buf = channel->buf; char *reply; long consumed_old=0; - result = ltt_do_put_subbuf(rbuf, lttbuf, consumed_old); + result = ust_buffers_do_put_subbuf(buf, consumed_old); if(result < 0) { - WARN("ltt_do_put_subbuf: error (subbuf=%s)", channel_name); + WARN("ust_buffers_do_put_subbuf: error (subbuf=%s)", channel_name); asprintf(&reply, "%s", "ERROR"); } else { - DBG("ltt_do_put_subbuf: success (subbuf=%s)", channel_name); + DBG("ust_buffers_do_put_subbuf: success (subbuf=%s)", channel_name); asprintf(&reply, "%s", "OK"); } @@ -649,6 +692,19 @@ void *listener_main(void *p) WARN("could not disable marker; channel=%s, name=%s", channel_name, marker_name); } } + else if(nth_token_is(recvbuf, "get_pidunique", 0) == 1) { + char *reply; + + asprintf(&reply, "%lld", pidunique); + + result = ustcomm_send_reply(&ustcomm_app.server, reply, &src); + if(result) { + ERR("listener: get_pidunique: ustcomm_send_reply failed"); + goto next_cmd; + } + + free(reply); + } // else if(nth_token_is(recvbuf, "get_notifications", 0) == 1) { // struct ltt_trace_struct *trace; // char trace_name[] = "auto"; @@ -707,43 +763,34 @@ void *listener_main(void *p) } } +volatile sig_atomic_t have_listener = 0; + void create_listener(void) { #ifdef USE_CLONE static char listener_stack[16384]; + int result; +#else + pthread_t thread; #endif + if(have_listener) { + WARN("not creating listener because we already had one"); + return; + } + #ifdef USE_CLONE - result = clone(listener_main, listener_stack+sizeof(listener_stack)-1, CLONE_FS | CLONE_FILES | CLONE_VM | CLONE_SIGHAND | CLONE_THREAD, NULL); + result = clone((int (*)(void *)) listener_main, listener_stack+sizeof(listener_stack)-1, CLONE_FS | CLONE_FILES | CLONE_VM | CLONE_SIGHAND | CLONE_THREAD, NULL); if(result == -1) { perror("clone"); + return; } #else - pthread_t thread; pthread_create(&thread, NULL, listener_main, NULL); #endif -} - -/* The signal handler itself. Signals must be setup so there cannot be - nested signals. */ -void sighandler(int sig) -{ - static char have_listener = 0; - DBG("sighandler"); - - if(!have_listener) { - create_listener(); - have_listener = 1; - } -} - -/* Called by the app signal handler to chain it to us. */ - -void chain_signal(void) -{ - sighandler(USTSIGNAL); + have_listener = 1; } static int init_socket(void) @@ -751,84 +798,61 @@ static int init_socket(void) return ustcomm_init_app(getpid(), &ustcomm_app); } -/* FIXME: reenable this to delete socket file. */ +#define AUTOPROBE_DISABLED 0 +#define AUTOPROBE_ENABLE_ALL 1 +#define AUTOPROBE_ENABLE_REGEX 2 +static int autoprobe_method = AUTOPROBE_DISABLED; +static regex_t autoprobe_regex; -#if 0 -static void destroy_socket(void) +static void auto_probe_connect(struct marker *m) { int result; - if(mysocketfile[0] == '\0') - return; + char* concat_name = NULL; + const char *probe_name = "default"; - result = unlink(mysocketfile); - if(result == -1) { - PERROR("unlink"); + if(autoprobe_method == AUTOPROBE_DISABLED) { + return; } -} -#endif - -static int init_signal_handler(void) -{ - /* Attempt to handler SIGIO. If the main program wants to - * handle it, fine, it'll override us. They it'll have to - * use the chaining function. - */ - - int result; - struct sigaction act; - - result = sigemptyset(&act.sa_mask); - if(result == -1) { - PERROR("sigemptyset"); - return -1; + 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)", + m->channel, m->name); + return; + } + if (regexec(&autoprobe_regex, concat_name, 0, NULL, 0)) { + free(concat_name); + return; + } + free(concat_name); } - act.sa_handler = sighandler; - act.sa_flags = SA_RESTART; + result = ltt_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); - /* Only defer ourselves. Also, try to restart interrupted - * syscalls to disturb the traced program as little as possible. - */ - result = sigaction(SIGIO, &act, NULL); - if(result == -1) { - PERROR("sigaction"); - return -1; - } + DBG("auto connected marker %s (addr: %p) %s to probe default", m->channel, m, m->name); - return 0; } -static void auto_probe_connect(struct marker *m) +static void __attribute__((constructor)) init() { int result; + char* autoprobe_val = NULL; - result = ltt_marker_connect(m->channel, m->name, "default"); - if(result && result != -EEXIST) - ERR("ltt_marker_connect (marker = %s/%s, errno = %d)", m->channel, m->name, -result); - - DBG("just auto connected marker %s %s to probe default", m->channel, m->name); -} + /* Assign the pidunique, to be able to differentiate the processes with same + * pid, (before and after an exec). + */ + pidunique = make_pidunique(); -static void __attribute__((constructor(101))) init0() -{ /* Initialize RCU in case the constructor order is not good. */ urcu_init(); /* It is important to do this before events start to be generated. */ ust_register_thread(); - DBG("UST_AUTOPROBE constructor"); - if(getenv("UST_AUTOPROBE")) { - marker_set_new_marker_cb(auto_probe_connect); - } -} - -static void __attribute__((constructor(1000))) init() -{ - int result; - - DBG("UST_TRACE constructor"); + DBG("Tracectl constructor"); /* Must create socket before signal handler to prevent races. */ @@ -837,10 +861,54 @@ static void __attribute__((constructor(1000))) init() ERR("init_socket error"); return; } - result = init_signal_handler(); - if(result == -1) { - ERR("init_signal_handler error"); - return; + + create_listener(); + + autoprobe_val = getenv("UST_AUTOPROBE"); + if(autoprobe_val) { + struct marker_iter iter; + + DBG("Autoprobe enabled."); + + /* Ensure markers are initialized */ + //init_markers(); + + /* Ensure marker control is initialized, for the probe */ + init_marker_control(); + + /* first, set the callback that will connect the + * probe on new markers + */ + if(autoprobe_val[0] == '/') { + result = regcomp(&autoprobe_regex, autoprobe_val+1, 0); + if (result) { + char regexerr[150]; + + regerror(result, &autoprobe_regex, regexerr, sizeof(regexerr)); + ERR("cannot parse regex %s (%s), will ignore UST_AUTOPROBE", autoprobe_val, regexerr); + /* don't crash the application just for this */ + } + else { + autoprobe_method = AUTOPROBE_ENABLE_REGEX; + } + } + else { + /* just enable all instrumentation */ + autoprobe_method = AUTOPROBE_ENABLE_ALL; + } + + marker_set_new_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); + } } if(getenv("UST_TRACE")) { @@ -884,7 +952,10 @@ static void __attribute__((constructor(1000))) init() ERR("ltt_trace_start failed"); return; } - inform_consumer_daemon(); + + /* Do this after the trace is started in order to avoid creating confusion + * if the trace fails to start. */ + inform_consumer_daemon(trace_name); } @@ -895,11 +966,17 @@ static void __attribute__((constructor(1000))) init() } /* This is only called if we terminate normally, not with an unhandled signal, - * so we cannot rely on it. */ + * so we cannot rely on it. However, for now, LTTV requires that the header of + * the last sub-buffer contain a valid end time for the trace. This is done + * automatically only when the trace is properly stopped. + * + * If the traced program crashed, it is always possible to manually add the + * right value in the header, or to open the trace in text mode. + * + * FIXME: Fix LTTV so it doesn't need this. + */ -/* This destructor probably isn't needed, because ustd can do crash recovery. */ -#if 0 -static void __attribute__((destructor)) fini() +static void destroy_traces(void) { int result; @@ -916,11 +993,91 @@ static void __attribute__((destructor)) fini() if(result == -1) { ERR("ltt_trace_destroy error"); } +} + +static int trace_recording(void) +{ + int retval = 0; + struct ltt_trace_struct *trace; + + ltt_lock_traces(); + + list_for_each_entry(trace, <t_traces.head, list) { + if(trace->active) { + retval = 1; + break; + } + } + + ltt_unlock_traces(); - destroy_socket(); + return retval; +} + +#if 0 +static int have_consumer(void) +{ + return !list_empty(&blocked_consumers); } #endif +int restarting_usleep(useconds_t usecs) +{ + struct timespec tv; + int result; + + tv.tv_sec = 0; + tv.tv_nsec = usecs * 1000; + + do { + result = nanosleep(&tv, &tv); + } while(result == -1 && errno == EINTR); + + return result; +} + +/* 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 + * 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. + * + * FIXME: For now, this only works for the first trace created in a + * process. + */ + +static void __attribute__((destructor)) keepalive() +{ + if(trace_recording() && buffers_to_export) { + int total = 0; + DBG("Keeping process alive for consumer daemon..."); + while(buffers_to_export) { + const int interv = 200000; + restarting_usleep(interv); + total += interv; + + if(total >= 3000000) { + WARN("non-consumed buffers remaining after wait limit; not waiting anymore"); + break; + } + } + DBG("Finally dying..."); + } + + destroy_traces(); + + ustcomm_fini_app(&ustcomm_app); +} + +void ust_potential_exec(void) +{ + trace_mark(ust, potential_exec, MARK_NOARGS); + + DBG("test"); + + keepalive(); +} + /* Notify ust that there was a fork. This needs to be called inside * the new process, anytime a process whose memory is not shared with * the parent is created. If this function is not called, the events @@ -929,15 +1086,37 @@ static void __attribute__((destructor)) fini() void ust_fork(void) { + struct blocked_consumer *bc; + struct blocked_consumer *deletable_bc = NULL; + int result; + DBG("ust: forking"); ltt_trace_stop("auto"); ltt_trace_destroy("auto"); - ltt_trace_alloc("auto"); - ltt_trace_start("auto"); - init_socket(); + /* Delete all active connections */ + ustcomm_close_all_connections(&ustcomm_app.server); + + /* Delete all blocked consumers */ + list_for_each_entry(bc, &blocked_consumers, list) { + close(bc->fd_producer); + close(bc->fd_consumer); + free(deletable_bc); + deletable_bc = bc; + list_del(&bc->list); + } + have_listener = 0; create_listener(); - ustcomm_request_consumer(getpid(), "metadata"); - ustcomm_request_consumer(getpid(), "ust"); + init_socket(); + ltt_trace_setup("auto"); + result = ltt_trace_set_type("auto", "ustrelay"); + if(result < 0) { + ERR("ltt_trace_set_type failed"); + return; + } + + ltt_trace_alloc("auto"); + ltt_trace_start("auto"); + inform_consumer_daemon("auto"); }