X-Git-Url: http://git.lttng.org/?p=ust.git;a=blobdiff_plain;f=libust%2Fserialize.c;h=bd947ab1f90d9cd1358bae1538d0f80de2429e62;hp=ee8b8284d1eb8039b4891a1872b08e652d0890e6;hb=e9b58dc0868620ba4c0e63b605602f2044a02251;hpb=6eb05e4feb4700acdc9d0e1e2778859562ad1bb7 diff --git a/libust/serialize.c b/libust/serialize.c index ee8b828..bd947ab 100644 --- a/libust/serialize.c +++ b/libust/serialize.c @@ -24,24 +24,30 @@ * va_list * to ltt_vtrace. */ +#define _GNU_SOURCE +#include +#include #include -//ust// #include -//ust// #include -//ust// #include -//ust// #include #include #include #include -#include #define _LGPL_SOURCE #include #include -#include "relay.h" -#include -//#include "list.h" +#include +#include +#include "buffers.h" +#include "tracer.h" #include "usterr.h" +#include "ust_snprintf.h" + +/* + * Because UST core defines a non-const PAGE_SIZE, define PAGE_SIZE_STATIC here. + * It is just an approximation for the tracer stack. + */ +#define PAGE_SIZE_STATIC 4096 enum ltt_type { LTT_TYPE_SIGNED_INT, @@ -50,6 +56,16 @@ enum ltt_type { LTT_TYPE_NONE, }; +/* + * Special stack for the tracer. Keeps serialization offsets for each field. + * Per-thread. Deals with reentrancy from signals by simply ensuring that + * interrupting signals put the stack back to its original position. + */ +#define TRACER_STACK_LEN (PAGE_SIZE_STATIC / sizeof(unsigned long)) +static unsigned long __thread tracer_stack[TRACER_STACK_LEN]; + +static unsigned int __thread tracer_stack_pos; + #define LTT_ATTRIBUTE_NETWORK_BYTE_ORDER (1<<1) /* @@ -252,8 +268,9 @@ parse_end: * Field width and precision are *not* supported. * %n not supported. */ -static inline const char *parse_c_type(const char *fmt, - char *c_size, enum ltt_type *c_type) +static inline +const char *parse_c_type(const char *fmt, char *c_size, enum ltt_type *c_type, + char *outfmt) { int qualifier; /* 'h', 'l', or 'L' for integer fields */ /* 'z' support added 23/7/1999 S.H. */ @@ -285,6 +302,13 @@ repeat: } } + if (outfmt) { + if (qualifier != -1) + *outfmt++ = (char)qualifier; + *outfmt++ = *fmt; + *outfmt = 0; + } + switch (*fmt) { case 'c': *c_type = LTT_TYPE_UNSIGNED_INT; @@ -337,11 +361,13 @@ parse_end: return fmt; } -static inline size_t serialize_trace_data(struct rchan_buf *buf, +static inline size_t serialize_trace_data(struct ust_buffer *buf, size_t buf_offset, char trace_size, enum ltt_type trace_type, char c_size, enum ltt_type c_type, - int *largest_align, va_list *args) + unsigned int *stack_pos_ctx, + int *largest_align, + va_list *args) { union { unsigned long v_ulong; @@ -378,16 +404,13 @@ static inline size_t serialize_trace_data(struct rchan_buf *buf, case LTT_TYPE_UNSIGNED_INT: switch (c_size) { case 1: - tmp.v_ulong = (unsigned long)(uint8_t) - va_arg(*args, unsigned int); + tmp.v_ulong = (unsigned long)(uint8_t)va_arg(*args, unsigned int); break; case 2: - tmp.v_ulong = (unsigned long)(uint16_t) - va_arg(*args, unsigned int); + tmp.v_ulong = (unsigned long)(uint16_t)va_arg(*args, unsigned int); break; case 4: - tmp.v_ulong = (unsigned long)(uint32_t) - va_arg(*args, unsigned int); + tmp.v_ulong = (unsigned long)(uint32_t)va_arg(*args, unsigned int); break; case 8: tmp.v_uint64 = va_arg(*args, uint64_t); @@ -400,10 +423,20 @@ static inline size_t serialize_trace_data(struct rchan_buf *buf, tmp.v_string.s = va_arg(*args, const char *); if ((unsigned long)tmp.v_string.s < PAGE_SIZE) tmp.v_string.s = ""; - tmp.v_string.len = strlen(tmp.v_string.s)+1; + if (!buf) { + /* + * Reserve tracer stack entry. + */ + tracer_stack_pos++; + assert(tracer_stack_pos <= TRACER_STACK_LEN); + barrier(); + tracer_stack[*stack_pos_ctx] = + strlen(tmp.v_string.s) + 1; + } + tmp.v_string.len = tracer_stack[(*stack_pos_ctx)++]; if (buf) - ltt_relay_write(buf, buf_offset, tmp.v_string.s, - tmp.v_string.len); + ust_buffers_strncpy(buf, buf_offset, tmp.v_string.s, + tmp.v_string.len); buf_offset += tmp.v_string.len; goto copydone; default: @@ -430,31 +463,31 @@ static inline size_t serialize_trace_data(struct rchan_buf *buf, switch (trace_size) { case 1: if (c_size == 8) - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint8_t[]){ (uint8_t)tmp.v_uint64 }, sizeof(uint8_t)); else - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint8_t[]){ (uint8_t)tmp.v_ulong }, sizeof(uint8_t)); break; case 2: if (c_size == 8) - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint16_t[]){ (uint16_t)tmp.v_uint64 }, sizeof(uint16_t)); else - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint16_t[]){ (uint16_t)tmp.v_ulong }, sizeof(uint16_t)); break; case 4: if (c_size == 8) - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint32_t[]){ (uint32_t)tmp.v_uint64 }, sizeof(uint32_t)); else - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint32_t[]){ (uint32_t)tmp.v_ulong }, sizeof(uint32_t)); break; @@ -463,7 +496,7 @@ static inline size_t serialize_trace_data(struct rchan_buf *buf, * c_size cannot be other than 8 here because * trace_size > 4. */ - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint64_t[]){ (uint64_t)tmp.v_uint64 }, sizeof(uint64_t)); break; @@ -480,12 +513,12 @@ static inline size_t serialize_trace_data(struct rchan_buf *buf, if (buf) { switch (trace_type) { case LTT_TYPE_SIGNED_INT: - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (int64_t[]){ (int64_t)tmp.v_ulong }, sizeof(int64_t)); break; case LTT_TYPE_UNSIGNED_INT: - ltt_relay_write(buf, buf_offset, + ust_buffers_write(buf, buf_offset, (uint64_t[]){ (uint64_t)tmp.v_ulong }, sizeof(uint64_t)); break; @@ -501,95 +534,11 @@ copydone: return buf_offset; } -static notrace void skip_space(const char **ps) -{ - while(**ps == ' ') - (*ps)++; -} - -static notrace void copy_token(char **out, const char **in) -{ - while(**in != ' ' && **in != '\0') { - **out = **in; - (*out)++; - (*in)++; - } -} - -/* serialize_to_text - * - * Given a format string and a va_list of arguments, convert them to a - * human-readable string. - * - * @outbuf: the buffer to output the string to - * @bufsize: the max size that can be used in outbuf - * @fmt: the marker format string - * @ap: a va_list that contains the arguments corresponding to fmt - * - * Return value: the number of chars that have been put in outbuf, excluding - * the final \0, or, if the buffer was too small, the number of chars that - * would have been written in outbuf if it had been large enough. - * - * outbuf may be NULL. The return value may then be used be allocate an - * appropriate outbuf. - * - */ - -notrace -int serialize_to_text(char *outbuf, int bufsize, const char *fmt, va_list ap) -{ - int fmt_len = strlen(fmt); - char *new_fmt = alloca(fmt_len + 1); - const char *orig_fmt_p = fmt; - char *new_fmt_p = new_fmt; - char false_buf; - int result; - enum { none, cfmt, tracefmt, argname } prev_token = none; - - while(*orig_fmt_p != '\0') { - if(*orig_fmt_p == '%') { - prev_token = cfmt; - copy_token(&new_fmt_p, &orig_fmt_p); - } - else if(*orig_fmt_p == '#') { - prev_token = tracefmt; - do { - orig_fmt_p++; - } while(*orig_fmt_p != ' ' && *orig_fmt_p != '\0'); - } - else if(*orig_fmt_p == ' ') { - if(prev_token == argname) { - *new_fmt_p = '='; - new_fmt_p++; - } - else if(prev_token == cfmt) { - *new_fmt_p = ' '; - new_fmt_p++; - } - - skip_space(&orig_fmt_p); - } - else { - prev_token = argname; - copy_token(&new_fmt_p, &orig_fmt_p); - } - } - - *new_fmt_p = '\0'; - - if(outbuf == NULL) { - /* use this false_buffer for compatibility with pre-C99 */ - outbuf = &false_buf; - bufsize = 1; - } - result = vsnprintf(outbuf, bufsize, new_fmt, ap); - - return result; -} - -notrace size_t ltt_serialize_data(struct rchan_buf *buf, size_t buf_offset, +notrace size_t ltt_serialize_data(struct ust_buffer *buf, size_t buf_offset, struct ltt_serialize_closure *closure, - void *serialize_private, int *largest_align, + void *serialize_private, + unsigned int stack_pos_ctx, + int *largest_align, const char *fmt, va_list *args) { char trace_size = 0, c_size = 0; /* @@ -614,7 +563,7 @@ notrace size_t ltt_serialize_data(struct rchan_buf *buf, size_t buf_offset, ++fmt; /* skip first '%' */ if (*fmt == '%') /* Escaped %% */ break; - fmt = parse_c_type(fmt, &c_size, &c_type); + fmt = parse_c_type(fmt, &c_size, &c_type, NULL); /* * Output c types if no trace types has been * specified. @@ -629,7 +578,9 @@ notrace size_t ltt_serialize_data(struct rchan_buf *buf, size_t buf_offset, buf_offset = serialize_trace_data(buf, buf_offset, trace_size, trace_type, c_size, c_type, - largest_align, args); + &stack_pos_ctx, + largest_align, + args); trace_size = 0; c_size = 0; trace_type = LTT_TYPE_NONE; @@ -647,25 +598,29 @@ notrace size_t ltt_serialize_data(struct rchan_buf *buf, size_t buf_offset, * Assume that the padding for alignment starts at a sizeof(void *) address. */ static notrace size_t ltt_get_data_size(struct ltt_serialize_closure *closure, - void *serialize_private, int *largest_align, + void *serialize_private, + unsigned int stack_pos_ctx, int *largest_align, const char *fmt, va_list *args) { ltt_serialize_cb cb = closure->callbacks[0]; closure->cb_idx = 0; return (size_t)cb(NULL, 0, closure, serialize_private, - largest_align, fmt, args); + stack_pos_ctx, largest_align, fmt, args); } static notrace -void ltt_write_event_data(struct rchan_buf *buf, size_t buf_offset, +void ltt_write_event_data(struct ust_buffer *buf, size_t buf_offset, struct ltt_serialize_closure *closure, - void *serialize_private, int largest_align, + void *serialize_private, + unsigned int stack_pos_ctx, + int largest_align, const char *fmt, va_list *args) { ltt_serialize_cb cb = closure->callbacks[0]; closure->cb_idx = 0; buf_offset += ltt_align(buf_offset, largest_align); - cb(buf, buf_offset, closure, serialize_private, NULL, fmt, args); + cb(buf, buf_offset, closure, serialize_private, stack_pos_ctx, NULL, + fmt, args); } @@ -678,18 +633,18 @@ notrace void ltt_vtrace(const struct marker *mdata, void *probe_data, uint16_t eID; size_t data_size, slot_size; unsigned int chan_index; - struct ltt_channel_struct *channel; - struct ltt_trace_struct *trace, *dest_trace = NULL; - struct rchan_buf *buf; - void *transport_data; + struct ust_channel *channel; + struct ust_trace *trace, *dest_trace = NULL; + struct ust_buffer *buf; u64 tsc; long buf_offset; va_list args_copy; struct ltt_serialize_closure closure; struct ltt_probe_private_data *private_data = call_data; void *serialize_private = NULL; -//ust// int cpu; + int cpu; unsigned int rflags; + unsigned int stack_pos_ctx; /* * This test is useful for quickly exiting static tracing when no trace @@ -699,9 +654,12 @@ notrace void ltt_vtrace(const struct marker *mdata, void *probe_data, return; rcu_read_lock(); //ust// rcu_read_lock_sched_notrace(); -//ust// cpu = smp_processor_id(); -//ust// __get_cpu_var(ltt_nesting)++; - ltt_nesting++; + cpu = ust_get_cpu(); + + /* Force volatile access. */ + STORE_SHARED(ltt_nesting, LOAD_SHARED(ltt_nesting) + 1); + stack_pos_ctx = tracer_stack_pos; + barrier(); pdata = (struct ltt_active_marker *)probe_data; eID = mdata->event_id; @@ -721,7 +679,8 @@ notrace void ltt_vtrace(const struct marker *mdata, void *probe_data, */ largest_align = 1; /* must be non-zero for ltt_align */ data_size = ltt_get_data_size(&closure, serialize_private, - &largest_align, fmt, &args_copy); + stack_pos_ctx, &largest_align, + fmt, &args_copy); largest_align = min_t(int, largest_align, sizeof(void *)); va_end(args_copy); @@ -754,33 +713,42 @@ notrace void ltt_vtrace(const struct marker *mdata, void *probe_data, if (!channel->active) continue; + /* If a new cpu was plugged since the trace was started, we did + * not add it to the trace, and therefore we write the event to + * cpu 0. + */ + if(cpu >= channel->n_cpus) { + cpu = 0; + } + /* reserve space : header and data */ - ret = ltt_reserve_slot(trace, channel, &transport_data, - data_size, &slot_size, &buf_offset, - &tsc, &rflags, - largest_align); + ret = ltt_reserve_slot(channel, trace, data_size, largest_align, + cpu, &buf, &slot_size, &buf_offset, + &tsc, &rflags); if (unlikely(ret < 0)) continue; /* buffer full */ va_copy(args_copy, *args); /* FIXME : could probably encapsulate transport better. */ //ust// buf = ((struct rchan *)channel->trans_channel_data)->buf[cpu]; - buf = ((struct rchan *)channel->trans_channel_data)->buf; + buf = channel->buf[cpu]; /* Out-of-order write : header and data */ - buf_offset = ltt_write_event_header(trace, - channel, buf, buf_offset, + buf_offset = ltt_write_event_header(channel, buf, buf_offset, eID, data_size, tsc, rflags); ltt_write_event_data(buf, buf_offset, &closure, - serialize_private, - largest_align, fmt, &args_copy); + serialize_private, + stack_pos_ctx, largest_align, + fmt, &args_copy); va_end(args_copy); /* Out-of-order commit */ - ltt_commit_slot(channel, &transport_data, buf_offset, - data_size, slot_size); - DBG("just commited event at offset %ld and size %zd", buf_offset, slot_size); + ltt_commit_slot(channel, buf, buf_offset, data_size, slot_size); + DBG("just commited event (%s/%s) at offset %ld and size %zd", mdata->channel, mdata->name, buf_offset, slot_size); } -//ust// __get_cpu_var(ltt_nesting)--; - ltt_nesting--; + + barrier(); + tracer_stack_pos = stack_pos_ctx; + STORE_SHARED(ltt_nesting, LOAD_SHARED(ltt_nesting) - 1); + rcu_read_unlock(); //ust// rcu_read_unlock_sched_notrace(); } @@ -795,6 +763,88 @@ notrace void ltt_trace(const struct marker *mdata, void *probe_data, va_end(args); } -//ust// MODULE_LICENSE("GPL"); -//ust// MODULE_AUTHOR("Mathieu Desnoyers"); -//ust// MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Serializer"); +static notrace void skip_space(const char **ps) +{ + while(**ps == ' ') + (*ps)++; +} + +static notrace void copy_token(char **out, const char **in) +{ + while(**in != ' ' && **in != '\0') { + **out = **in; + (*out)++; + (*in)++; + } +} + +/* serialize_to_text + * + * Given a format string and a va_list of arguments, convert them to a + * human-readable string. + * + * @outbuf: the buffer to output the string to + * @bufsize: the max size that can be used in outbuf + * @fmt: the marker format string + * @ap: a va_list that contains the arguments corresponding to fmt + * + * Return value: the number of chars that have been put in outbuf, excluding + * the final \0, or, if the buffer was too small, the number of chars that + * would have been written in outbuf if it had been large enough. + * + * outbuf may be NULL. The return value may then be used be allocate an + * appropriate outbuf. + * + */ + +notrace +int serialize_to_text(char *outbuf, int bufsize, const char *fmt, va_list ap) +{ + int fmt_len = strlen(fmt); + char *new_fmt = alloca(fmt_len + 1); + const char *orig_fmt_p = fmt; + char *new_fmt_p = new_fmt; + char false_buf; + int result; + enum { none, cfmt, tracefmt, argname } prev_token = none; + + while(*orig_fmt_p != '\0') { + if(*orig_fmt_p == '%') { + prev_token = cfmt; + copy_token(&new_fmt_p, &orig_fmt_p); + } + else if(*orig_fmt_p == '#') { + prev_token = tracefmt; + do { + orig_fmt_p++; + } while(*orig_fmt_p != ' ' && *orig_fmt_p != '\0'); + } + else if(*orig_fmt_p == ' ') { + if(prev_token == argname) { + *new_fmt_p = '='; + new_fmt_p++; + } + else if(prev_token == cfmt) { + *new_fmt_p = ' '; + new_fmt_p++; + } + + skip_space(&orig_fmt_p); + } + else { + prev_token = argname; + copy_token(&new_fmt_p, &orig_fmt_p); + } + } + + *new_fmt_p = '\0'; + + if(outbuf == NULL) { + /* use this false_buffer for compatibility with pre-C99 */ + outbuf = &false_buf; + bufsize = 1; + } + result = ust_safe_vsnprintf(outbuf, bufsize, new_fmt, ap); + + return result; +}