Advantages over existing simpletrace backend: - More than 6 elements (vitually unlimited) arguments can be traced. - This allows to trace strings (variable size element) as well.
Signed-off-by: Harsh Prateek Bora <ha...@linux.vnet.ibm.com> --- scripts/tracetool.py | 82 ++++++++++++++----- trace/simple.c | 226 ++++++++++++++++++++++++++++---------------------- trace/simple.h | 38 +++++++-- 3 files changed, 218 insertions(+), 128 deletions(-) diff --git a/scripts/tracetool.py b/scripts/tracetool.py index 6cbbdd7..1073818 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -178,43 +178,83 @@ def nop_c(events): def simple_h(events): print '#include "trace/simple.h"' print - - for num, event in enumerate(events): - if len(event.args): - argstr = event.args.names() - arg_prefix = ', (uint64_t)(uintptr_t)' - cast_args = arg_prefix + arg_prefix.join(argstr) - simple_args = (str(num) + cast_args) - else: - simple_args = str(num) - - print '''static inline void trace_%(name)s(%(args)s) -{ - trace%(argc)d(%(trace_args)s); -}''' % { + for event in events: + print 'void trace_%(name)s(%(args)s);' % { 'name': event.name, - 'args': event.args, - 'argc': len(event.args), - 'trace_args': simple_args + 'args': event.args } - print + print print '#define NR_TRACE_EVENTS %d' % len(events) print 'extern TraceEvent trace_list[NR_TRACE_EVENTS];' - @for_backend("simple", "c") def simple_c(events): print '#include "trace.h"' + print '#include "trace/simple.h"' print print 'TraceEvent trace_list[] = {' print - for event in events: print '{.tp_name = "%(name)s", .state=0},' % { - 'name': event.name, + 'name': event.name } print print '};' + print + + for num, event in enumerate(events): + sizes = [] + for type_, name in event.args: + if type_is_string(type_): + sizes.append("4 + strlen(%s)" % name) + else: + sizes.append("8") + sizestr = " + ".join(sizes) + if len(event.args) == 0: + sizestr = '0' + + print '''void trace_%(name)s(%(args)s) +{ + TraceBufferRecord rec; + + if (!trace_list[%(event_id)s].state) { + return; + } + + if (trace_record_start(&rec, %(event_id)s, %(sizestr)s)) { + return; /* Trace Buffer Full, Event Dropped ! */ + } +''' % { + 'name': event.name, + 'args': event.args, + 'event_id': num, + 'sizestr': sizestr, +} + if len(event.args) > 0: + for type_, name in event.args: + # string + if type_is_string(type_): + print ''' + trace_record_write_str(&rec, %(name)s);''' % { + 'name': name +} + # pointer var (not string) + elif type_.endswith('*'): + print ''' + trace_record_write_u64(&rec, (uint64_t)(uint64_t *)%(name)s);''' % { + 'name': name +} + # primitive data type + else: + print ''' + trace_record_write_u64(&rec, (uint64_t)%(name)s);''' % { + 'name': name +} + + print ''' + trace_record_finish(&rec); +} +''' ################################################## # backend: stderr diff --git a/trace/simple.c b/trace/simple.c index b639dda..de5c3d3 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -27,7 +27,7 @@ #define HEADER_MAGIC 0xf2b177cb0aa429b4ULL /** Trace file version number, bump if format changes */ -#define HEADER_VERSION 0 +#define HEADER_VERSION 2 /** Records were dropped event ID */ #define DROPPED_EVENT_ID (~(uint64_t)0 - 1) @@ -35,23 +35,6 @@ /** Trace record is valid */ #define TRACE_RECORD_VALID ((uint64_t)1 << 63) -/** Trace buffer entry */ -typedef struct { - uint64_t event; - uint64_t timestamp_ns; - uint64_t x1; - uint64_t x2; - uint64_t x3; - uint64_t x4; - uint64_t x5; - uint64_t x6; -} TraceRecord; - -enum { - TRACE_BUF_LEN = 4096, - TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4, -}; - /* * Trace records are written out by a dedicated thread. The thread waits for * records to become available, writes them out, and then waits again. @@ -62,11 +45,35 @@ static GCond *trace_empty_cond; static bool trace_available; static bool trace_writeout_enabled; -static TraceRecord trace_buf[TRACE_BUF_LEN]; +enum { + TRACE_BUF_LEN = 4096 * 64, + TRACE_BUF_FLUSH_THRESHOLD = TRACE_BUF_LEN / 4, +}; + +uint8_t trace_buf[TRACE_BUF_LEN]; static unsigned int trace_idx; +static unsigned int writeout_idx; +static uint64_t dropped_events; static FILE *trace_fp; static char *trace_file_name = NULL; +/* * Trace buffer entry */ +typedef struct { + uint64_t event; /* TraceEventID */ + uint64_t timestamp_ns; + uint32_t length; /* in bytes */ + uint32_t reserved; /* unused */ + uint8_t arguments[]; /* arguments position affects ST_REC_HDR_LEN */ +} TraceRecord; + +/* * Trace record header length */ +#define ST_REC_HDR_LEN 24 + +int trace_alloc_record(TraceBufferRecord *rec, TraceEventID event, uint32_t datasize); +static void read_from_buffer(unsigned int idx, uint8_t *dataptr, uint32_t size); +static void write_to_buffer(unsigned int idx, uint8_t *dataptr, uint32_t size); +void trace_mark_record_complete(TraceBufferRecord *rec); + /** * Read a trace record from the trace buffer * @@ -75,16 +82,22 @@ static char *trace_file_name = NULL; * * Returns false if the record is not valid. */ -static bool get_trace_record(unsigned int idx, TraceRecord *record) +static bool get_trace_record(unsigned int idx, TraceRecord **recordptr) { - if (!(trace_buf[idx].event & TRACE_RECORD_VALID)) { + uint8_t temp_rec[ST_REC_HDR_LEN]; + TraceRecord *record = (TraceRecord *) temp_rec; + read_from_buffer(idx, temp_rec, ST_REC_HDR_LEN); + + if (!(record->event & TRACE_RECORD_VALID)) { return false; } __sync_synchronize(); /* read memory barrier before accessing record */ - *record = trace_buf[idx]; - record->event &= ~TRACE_RECORD_VALID; + *recordptr = g_malloc(record->length); + /* make a copy of record to avoid being overwritten */ + read_from_buffer(idx, (uint8_t *)*recordptr, record->length); + (*recordptr)->event &= ~TRACE_RECORD_VALID; return true; } @@ -120,29 +133,31 @@ static void wait_for_trace_records_available(void) static gpointer writeout_thread(gpointer opaque) { - TraceRecord record; - unsigned int writeout_idx = 0; - unsigned int num_available, idx; + TraceRecord *recordptr; + unsigned int idx = 0; size_t unused __attribute__ ((unused)); for (;;) { wait_for_trace_records_available(); - num_available = trace_idx - writeout_idx; - if (num_available > TRACE_BUF_LEN) { - record = (TraceRecord){ - .event = DROPPED_EVENT_ID, - .x1 = num_available, - }; - unused = fwrite(&record, sizeof(record), 1, trace_fp); - writeout_idx += num_available; + if (dropped_events) { + recordptr = g_malloc(ST_REC_HDR_LEN + sizeof(dropped_events)); + recordptr->event = DROPPED_EVENT_ID, + recordptr->timestamp_ns = get_clock(); + recordptr->length = ST_REC_HDR_LEN + sizeof(dropped_events), + recordptr->reserved = 0; + *(uint64_t *) &(recordptr->arguments[0]) = dropped_events; + dropped_events = 0; + unused = fwrite(recordptr, recordptr->length, 1, trace_fp); } - idx = writeout_idx % TRACE_BUF_LEN; - while (get_trace_record(idx, &record)) { - trace_buf[idx].event = 0; /* clear valid bit */ - unused = fwrite(&record, sizeof(record), 1, trace_fp); - idx = ++writeout_idx % TRACE_BUF_LEN; + while (get_trace_record(idx, &recordptr)) { + unused = fwrite(recordptr, recordptr->length, 1, trace_fp); + writeout_idx += recordptr->length; + g_free(recordptr); + recordptr = (TraceRecord *) &trace_buf[idx]; + recordptr->event = 0; + idx = writeout_idx % TRACE_BUF_LEN; } fflush(trace_fp); @@ -150,72 +165,101 @@ static gpointer writeout_thread(gpointer opaque) return NULL; } -static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, - uint64_t x4, uint64_t x5, uint64_t x6) +int trace_record_start(TraceBufferRecord *rec, TraceEventID id, size_t arglen) { - unsigned int idx; - uint64_t timestamp; - - if (!trace_list[event].state) { - return; - } - - timestamp = get_clock(); - - idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, 1) % TRACE_BUF_LEN; - trace_buf[idx] = (TraceRecord){ - .event = event, - .timestamp_ns = timestamp, - .x1 = x1, - .x2 = x2, - .x3 = x3, - .x4 = x4, - .x5 = x5, - .x6 = x6, - }; - __sync_synchronize(); /* write barrier before marking as valid */ - trace_buf[idx].event |= TRACE_RECORD_VALID; - - if ((idx + 1) % TRACE_BUF_FLUSH_THRESHOLD == 0) { - flush_trace_file(false); - } + return trace_alloc_record(rec, id, arglen); /* return 0 on success */ } -void trace0(TraceEventID event) +void trace_record_write_u64(TraceBufferRecord *rec, uint64_t val) { - trace(event, 0, 0, 0, 0, 0, 0); + write_to_buffer(rec->rec_off, (uint8_t *)&val, sizeof(uint64_t)); + rec->rec_off += sizeof(uint64_t); } -void trace1(TraceEventID event, uint64_t x1) +void trace_record_write_str(TraceBufferRecord *rec, const char *s) { - trace(event, x1, 0, 0, 0, 0, 0); + /* Write string length first */ + uint32_t slen = strlen(s); + write_to_buffer(rec->rec_off, (uint8_t *)&slen, sizeof(slen)); + rec->rec_off += sizeof(slen); + /* Write actual string now */ + write_to_buffer(rec->rec_off, (uint8_t *)s, slen); + rec->rec_off += slen; } -void trace2(TraceEventID event, uint64_t x1, uint64_t x2) +void trace_record_finish(TraceBufferRecord *rec) { - trace(event, x1, x2, 0, 0, 0, 0); + trace_mark_record_complete(rec); } -void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3) +int trace_alloc_record(TraceBufferRecord *rec, TraceEventID event, uint32_t datasize) { - trace(event, x1, x2, x3, 0, 0, 0); + unsigned int idx, rec_off; + uint32_t rec_len = ST_REC_HDR_LEN + datasize; + uint64_t timestamp_ns = get_clock(); + + if ((rec_len + trace_idx - writeout_idx) > TRACE_BUF_LEN) { + /* Trace Buffer Full, Event dropped ! */ + dropped_events++; + return 1; + } + idx = g_atomic_int_exchange_and_add((gint *)&trace_idx, rec_len) % TRACE_BUF_LEN; + + /* To check later if threshold crossed */ + rec->next_tbuf_idx = trace_idx % TRACE_BUF_LEN; + + rec_off = idx; + write_to_buffer(rec_off, (uint8_t*)&event, sizeof(event)); + rec_off += sizeof(event); + write_to_buffer(rec_off, (uint8_t*)×tamp_ns, sizeof(timestamp_ns)); + rec_off += sizeof(timestamp_ns); + write_to_buffer(rec_off, (uint8_t*)&rec_len, sizeof(rec_len)); + rec_off += sizeof(rec_len); + + rec->tbuf_idx = idx; + rec->rec_off = (idx + ST_REC_HDR_LEN) % TRACE_BUF_LEN; + return 0; } -void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4) +static void read_from_buffer(unsigned int idx, uint8_t *dataptr, uint32_t size) { - trace(event, x1, x2, x3, x4, 0, 0); + uint32_t x = 0; + while (x < size) { + if (idx >= TRACE_BUF_LEN) { + idx = idx % TRACE_BUF_LEN; + } + dataptr[x++] = trace_buf[idx++]; + } } -void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5) +static void write_to_buffer(unsigned int idx, uint8_t *dataptr, uint32_t size) { - trace(event, x1, x2, x3, x4, x5, 0); + uint32_t x = 0; + while (x < size) { + if (idx >= TRACE_BUF_LEN) { + idx = idx % TRACE_BUF_LEN; + } + trace_buf[idx++] = dataptr[x++]; + } } -void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) +void trace_mark_record_complete(TraceBufferRecord *rec) { - trace(event, x1, x2, x3, x4, x5, x6); + uint8_t temp_rec[ST_REC_HDR_LEN]; + TraceRecord *record = (TraceRecord *) temp_rec; + read_from_buffer(rec->tbuf_idx, temp_rec, ST_REC_HDR_LEN); + __sync_synchronize(); /* write barrier before marking as valid */ + record->event |= TRACE_RECORD_VALID; + write_to_buffer(rec->tbuf_idx, temp_rec, ST_REC_HDR_LEN); + + if (rec->next_tbuf_idx > TRACE_BUF_FLUSH_THRESHOLD && + rec->tbuf_idx <= TRACE_BUF_FLUSH_THRESHOLD) { + flush_trace_file(false); + } } + + void st_set_trace_file_enabled(bool enable) { if (enable == !!trace_fp) { @@ -231,7 +275,8 @@ void st_set_trace_file_enabled(bool enable) static const TraceRecord header = { .event = HEADER_EVENT_ID, .timestamp_ns = HEADER_MAGIC, - .x1 = HEADER_VERSION, + /* Older log readers will check for version at next location */ + .length = HEADER_VERSION, }; trace_fp = fopen(trace_file_name, "wb"); @@ -288,23 +333,6 @@ void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, trace_file_name, trace_fp ? "on" : "off"); } -void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...)) -{ - unsigned int i; - - for (i = 0; i < TRACE_BUF_LEN; i++) { - TraceRecord record; - - if (!get_trace_record(i, &record)) { - continue; - } - stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64 - " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n", - record.event, record.x1, record.x2, - record.x3, record.x4, record.x5, - record.x6); - } -} void st_flush_trace_buffer(void) { diff --git a/trace/simple.h b/trace/simple.h index 466e75b..6755e99 100644 --- a/trace/simple.h +++ b/trace/simple.h @@ -22,17 +22,39 @@ typedef struct { bool state; } TraceEvent; -void trace0(TraceEventID event); -void trace1(TraceEventID event, uint64_t x1); -void trace2(TraceEventID event, uint64_t x1, uint64_t x2); -void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3); -void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4); -void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5); -void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6); -void st_print_trace(FILE *stream, fprintf_function stream_printf); void st_print_trace_file_status(FILE *stream, fprintf_function stream_printf); void st_set_trace_file_enabled(bool enable); bool st_set_trace_file(const char *file); void st_flush_trace_buffer(void); +typedef struct { + unsigned int tbuf_idx; + unsigned int next_tbuf_idx; + unsigned int rec_off; +} TraceBufferRecord; + +/** + * Initialize a trace record and claim space for it in the buffer + * + * @arglen number of bytes required for arguments + */ +int trace_record_start(TraceBufferRecord *rec, TraceEventID id, size_t arglen); + +/** + * Append a 64-bit argument to a trace record + */ +void trace_record_write_u64(TraceBufferRecord *rec, uint64_t val); + +/** + * Append a string argument to a trace record + */ +void trace_record_write_str(TraceBufferRecord *rec, const char *s); + +/** + * Mark a trace record completed + * + * Don't append any more arguments to the trace record after calling this. + */ +void trace_record_finish(TraceBufferRecord *rec); + #endif /* TRACE_SIMPLE_H */ -- 1.7.1.1