[ltt-dev] [LTTV PATCH 1/3] Add live trace reading support to lttvtraceread lib
Yannick Brosseau
yannick.brosseau at gmail.com
Thu Apr 21 16:52:38 EDT 2011
Add support to read trace that were not previously closed. Useful to read live trace or incorrectly closed trace.
Support the opening of incomplete tracefile and its delayed initialisation
Rework the block index management
Add a ltt_tracefile_update interface
The JNI Interface does not support the live trace for now
Signed-off-by: Yannick Brosseau <yannick.brosseau at gmail.com>
---
ltt/jni_interface.c | 3 +-
ltt/ltt-private.h | 2 +-
ltt/trace.h | 9 +-
ltt/tracefile.c | 344 +++++++++++++++++++++++++++++++++++++--------------
4 files changed, 261 insertions(+), 97 deletions(-)
diff --git a/ltt/jni_interface.c b/ltt/jni_interface.c
index d1bd993..b26af9a 100644
--- a/ltt/jni_interface.c
+++ b/ltt/jni_interface.c
@@ -142,7 +142,8 @@ JNIEXPORT jlong JNICALL Java_org_eclipse_linuxtools_lttng_jni_JniTrace_ltt_1open
}
const char *c_pathname = (*env)->GetStringUTFChars(env, pathname, 0);
- LttTrace *newPtr = ltt_trace_open( c_pathname );
+ /* TODO ybrosseau 2010-12-06: Enable live trace read in JNI API */
+ LttTrace *newPtr = ltt_trace_open(c_pathname, FALSE);
(*env)->ReleaseStringUTFChars(env, pathname, c_pathname);
diff --git a/ltt/ltt-private.h b/ltt/ltt-private.h
index 65d73d1..593ce44 100644
--- a/ltt/ltt-private.h
+++ b/ltt/ltt-private.h
@@ -165,7 +165,7 @@ struct LttTracefile {
uint32_t subbuf_corrupt;
GArray *buf_index; /* index mapping buffer index to offset */
-
+ uint64_t end_timestamp; /* Last timestamp of file */
/* Current event */
LttEvent event; //Event currently accessible in the trace
diff --git a/ltt/trace.h b/ltt/trace.h
index e16c66f..5fc1877 100644
--- a/ltt/trace.h
+++ b/ltt/trace.h
@@ -45,8 +45,10 @@ struct LttTrace {
double offset;
LttTime start_time;
LttTime start_time_from_tsc;
+ gboolean is_live; /* Flag indicating that read trace is currently being recorded */
+ LttTime live_safe_timestamp; /* In a live trace, timestamp were all data should be readable */
- GData *tracefiles; //tracefiles groups
+ GData *tracefiles; /*tracefiles groups*/
};
static inline guint ltt_trace_get_num_cpu(LttTrace *t)
@@ -65,7 +67,7 @@ static inline guint ltt_trace_get_num_cpu(LttTrace *t)
*/
-LttTrace *ltt_trace_open(const gchar *pathname);
+LttTrace *ltt_trace_open(const gchar *pathname, gboolean is_live);
/* copy reopens a trace
*
@@ -77,7 +79,8 @@ static inline GQuark ltt_trace_name(const LttTrace *t)
{
return t->pathname;
}
-
+/* Update the informations concerning a tracefile, normally a live one */
+int ltt_tracefile_update(LttTracefile *tf);
void ltt_trace_close(LttTrace *t);
diff --git a/ltt/tracefile.c b/ltt/tracefile.c
index 012ce10..ca93f28 100644
--- a/ltt/tracefile.c
+++ b/ltt/tracefile.c
@@ -211,25 +211,18 @@ int get_block_offset_size(LttTracefile *tf, guint block_num,
return 0;
}
-int ltt_trace_create_block_index(LttTracefile *tf)
+static int ltt_trace_update_block_index(LttTracefile *tf, uint64_t offset,
+ unsigned long firstBlock)
{
- int page_size = getpagesize();
- uint64_t offset = 0;
- unsigned long i = 0;
- unsigned int header_map_size = PAGE_ALIGN(ltt_subbuffer_header_size());
-
- tf->buf_index = g_array_sized_new(FALSE, TRUE, sizeof(uint64_t),
- DEFAULT_N_BLOCKS);
-
- g_assert(tf->buf_index->len == i);
+ int i = firstBlock;
+ int page_size = getpagesize();
+ unsigned int header_map_size = PAGE_ALIGN(ltt_subbuffer_header_size());
+ g_assert(tf->buf_index->len == i);
while (offset < tf->file_size) {
ltt_subbuffer_header_t *header;
uint64_t *off;
-
- tf->buf_index = g_array_set_size(tf->buf_index, i + 1);
- off = &g_array_index(tf->buf_index, uint64_t, i);
- *off = offset;
+ uint64_t size;
/* map block header */
header = mmap(0, header_map_size, PROT_READ,
@@ -240,21 +233,147 @@ int ltt_trace_create_block_index(LttTracefile *tf)
}
/* read len, offset += len */
- offset += ltt_get_uint32(LTT_GET_BO(tf), &header->sb_size);
+ size = ltt_get_uint32(LTT_GET_BO(tf), &header->sb_size);
+
+ /* Only index completly writen blocks */
+ if (offset + size <= tf->file_size) {
+ tf->buf_index = g_array_set_size(tf->buf_index, i + 1);
+ off = &g_array_index(tf->buf_index, uint64_t, i);
+ *off = offset;
+
+ /* Store current buffer end cycle as the last file timestamp */
+ /* TODO ybrosseau 2010-11-04: Might want to convert it to a LttTime */
+ tf->end_timestamp = ltt_get_uint64(LTT_GET_BO(tf),
+ &header->cycle_count_end);
+
+ ++i;
+ }
+ offset += size;
/* unmap block header */
if(munmap(header, header_map_size)) {
g_warning("unmap size : %u\n", header_map_size);
perror("munmap error");
return -1;
}
- ++i;
}
tf->num_blocks = i;
return 0;
}
+/* parse the new information from the file and reajust the number of blocks.
+ *
+ * Return value : 0 success, -1 error
+ */
+int ltt_trace_continue_block_index(LttTracefile *tf)
+{
+ int ret;
+ uint64_t offset;
+ uint32_t last_block_size;
+ unsigned long i = tf->num_blocks;
+ int page_size = getpagesize();
+ unsigned int header_map_size = PAGE_ALIGN(ltt_subbuffer_header_size());
+
+ get_block_offset_size(tf, tf->num_blocks-1, &offset, &last_block_size);
+
+ ltt_subbuffer_header_t *header_tmp = mmap(0, header_map_size, PROT_READ,
+ MAP_PRIVATE, tf->fd, (off_t)offset);
+ if(header_tmp == MAP_FAILED) {
+ perror("Error in allocating memory for buffer of tracefile");
+ return -1;
+ }
+
+ /* read len, offset += len */
+ offset += ltt_get_uint32(LTT_GET_BO(tf), &header_tmp->sb_size);
+
+ ret = ltt_trace_update_block_index(tf, offset, i);
+
+ return ret;
+}
+
+int ltt_trace_create_block_index(LttTracefile *tf)
+{
+ int ret;
+ uint64_t offset = 0;
+ unsigned long i = 0;
+
+ tf->buf_index = g_array_sized_new(FALSE, TRUE, sizeof(uint64_t),
+ DEFAULT_N_BLOCKS);
+ if(!tf->buf_index)
+ return -1;
+ ret = ltt_trace_update_block_index(tf, offset, i);
+ return ret;
+}
+
+/*
+ Read file header and initialise buffer indexes
+
+ Return value : 0 for success, -1 otherwise.
+*/
+static int ltt_tracefile_init(LttTracefile *tf)
+{
+ ltt_subbuffer_header_t *header;
+ int page_size = getpagesize();
+
+ /* Temporarily map the buffer start header to get trace information */
+ /* Multiple of pages aligned head */
+ tf->buffer.head = mmap(0,
+ PAGE_ALIGN(ltt_subbuffer_header_size()), PROT_READ,
+ MAP_PRIVATE, tf->fd, 0);
+ if(tf->buffer.head == MAP_FAILED) {
+ perror("Error in allocating memory for buffer of tracefile");
+ goto unmap_file;
+ }
+ g_assert( ( (gulong)tf->buffer.head&(8-1) ) == 0); // make sure it's aligned.
+
+ header = (ltt_subbuffer_header_t *)tf->buffer.head;
+
+ if(parse_trace_header(header, tf, tf->trace))
+ {
+ g_warning("parse_trace_header error");
+ goto unmap_file;
+ }
+
+ if(munmap(tf->buffer.head,
+ PAGE_ALIGN(ltt_subbuffer_header_size())))
+ {
+ g_warning("unmap size : %zu\n",
+ PAGE_ALIGN(ltt_subbuffer_header_size()));
+ perror("munmap error");
+ g_assert(0);
+ }
+ tf->buffer.head = NULL;
+
+ /* Create fields offset table */
+ tf->event.fields_offsets = g_array_sized_new(FALSE, FALSE,
+ sizeof(struct LttField), 1);
+ if (!tf->event.fields_offsets) {
+ g_warning("Cannot create fields offset table");
+ goto unmap_file;
+ }
+
+ /* Create block index */
+ ltt_trace_create_block_index(tf);
+
+ if(map_block(tf,0))
+ {
+ perror("Cannot map block for tracefile");
+ goto unmap_file;
+ }
+ return 0;
+ /* Error */
+unmap_file:
+ if(munmap(tf->buffer.head,
+ PAGE_ALIGN(ltt_subbuffer_header_size()))) {
+ g_warning("unmap size : %zu\n",
+ PAGE_ALIGN(ltt_subbuffer_header_size()));
+ perror("munmap error");
+ g_assert(0);
+ }
+ return -1;
+}
+
/*****************************************************************************
*Function name
* ltt_tracefile_open : open a trace file, construct a LttTracefile
@@ -265,94 +384,50 @@ int ltt_trace_create_block_index(LttTracefile *tf)
*Return value
* : 0 for success, -1 otherwise.
****************************************************************************/
-
static gint ltt_tracefile_open(LttTrace *t, gchar * fileName, LttTracefile *tf)
{
struct stat lTDFStat; /* Trace data file status */
- ltt_subbuffer_header_t *header;
- int page_size = getpagesize();
//open the file
tf->long_name = g_quark_from_string(fileName);
tf->trace = t;
tf->fd = open(fileName, O_RDONLY);
tf->buf_index = NULL;
+ tf->num_blocks = 0;
if(tf->fd < 0){
g_warning("Unable to open input data file %s\n", fileName);
goto end;
}
-
+
// Get the file's status
if(fstat(tf->fd, &lTDFStat) < 0){
g_warning("Unable to get the status of the input data file %s\n", fileName);
goto close_file;
}
-
- // Is the file large enough to contain a trace
- if(lTDFStat.st_size <
- (off_t)(ltt_subbuffer_header_size())){
- g_print("The input data file %s does not contain a trace\n", fileName);
- goto close_file;
- }
-
- /* Temporarily map the buffer start header to get trace information */
- /* Multiple of pages aligned head */
- tf->buffer.head = mmap(0,
- PAGE_ALIGN(ltt_subbuffer_header_size()), PROT_READ,
- MAP_PRIVATE, tf->fd, 0);
- if(tf->buffer.head == MAP_FAILED) {
- perror("Error in allocating memory for buffer of tracefile");
- goto close_file;
- }
- g_assert( ( (gulong)tf->buffer.head&(8-1) ) == 0); // make sure it's aligned.
-
- header = (ltt_subbuffer_header_t *)tf->buffer.head;
-
- if(parse_trace_header(header, tf, NULL)) {
- g_warning("parse_trace_header error");
- goto unmap_file;
- }
-
//store the size of the file
tf->file_size = lTDFStat.st_size;
tf->events_lost = 0;
tf->subbuf_corrupt = 0;
-
- if(munmap(tf->buffer.head,
- PAGE_ALIGN(ltt_subbuffer_header_size()))) {
- g_warning("unmap size : %zu\n",
- PAGE_ALIGN(ltt_subbuffer_header_size()));
- perror("munmap error");
- g_assert(0);
- }
tf->buffer.head = NULL;
- /* Create block index */
- ltt_trace_create_block_index(tf);
-
- //read the first block
- if(map_block(tf,0)) {
- perror("Cannot map block for tracefile");
- goto close_file;
+ /* Is the file large enough to contain a trace */
+ if(lTDFStat.st_size < (off_t)(ltt_subbuffer_header_size())){
+ if (t->is_live) {
+ /* It a live trace so the file can be empty at the start of the analysis */
+ goto end;
+ } else {
+ g_print("The input data file %s does not contain a trace\n", fileName);
+ goto close_file;
+ }
+ }
+
+ if(ltt_tracefile_init(tf) < 0) {
+ goto close_file;
}
-
- /* Create fields offset table */
- tf->event.fields_offsets = g_array_sized_new(FALSE, FALSE,
- sizeof(struct LttField), 1);
- if (!tf->event.fields_offsets)
- goto close_file;
return 0;
/* Error */
-unmap_file:
- if(munmap(tf->buffer.head,
- PAGE_ALIGN(ltt_subbuffer_header_size()))) {
- g_warning("unmap size : %zu\n",
- PAGE_ALIGN(ltt_subbuffer_header_size()));
- perror("munmap error");
- g_assert(0);
- }
close_file:
close(tf->fd);
end:
@@ -361,7 +436,6 @@ end:
return -1;
}
-
/*****************************************************************************
*Function name
* ltt_tracefile_close: close a trace file,
@@ -659,9 +733,12 @@ static int open_tracefiles(LttTrace *trace, gchar *root_path, gchar *relative_pa
g_debug("Opening file.\n");
if(ltt_tracefile_open(trace, path, &tmp_tf)) {
- g_info("Error opening tracefile %s", path);
-
- continue; /* error opening the tracefile : bad magic number ? */
+ /* Only consider the error for non live trace */
+ if (!trace->is_live) {
+
+ g_info("Error opening tracefile %s", path);
+ continue; /* error opening the tracefile : bad magic number ? */
+ }
}
g_debug("Tracefile name is %s and number is %u",
@@ -812,7 +889,7 @@ seek_error:
* pathname must be the directory of the trace
*/
-LttTrace *ltt_trace_open(const gchar *pathname)
+LttTrace *ltt_trace_open(const gchar *pathname, gboolean is_live)
{
gchar abs_path[PATH_MAX];
LttTrace * t;
@@ -851,7 +928,10 @@ LttTrace *ltt_trace_open(const gchar *pathname)
}
}
closedir(dir);
-
+
+ t->is_live = is_live;
+ t->live_safe_timestamp = ltt_time_zero;
+
/* Open all the tracefiles */
t->start_freq= 0;
if(open_tracefiles(t, abs_path, "")) {
@@ -867,15 +947,27 @@ LttTrace *ltt_trace_open(const gchar *pathname)
}
/*
- * Get the trace information for the metadata_0 tracefile.
+ * Get the trace information for the first valid metadata tracefile.
+ * In live trace mode, the metadata_0 might be empty
* Getting a correct trace start_time and start_tsc is insured by the fact
* that no subbuffers are supposed to be lost in the metadata channel.
* Therefore, the first subbuffer contains the start_tsc timestamp in its
* buffer header.
*/
g_assert(group->len > 0);
- tf = &g_array_index (group, LttTracefile, 0);
- header = (ltt_subbuffer_header_t *)tf->buffer.head;
+ header = NULL;
+ for(i=0; i<group->len; i++) {
+ tf = &g_array_index (group, LttTracefile, i);
+ header = (ltt_subbuffer_header_t *)tf->buffer.head;
+ if (header) {
+ break;
+ }
+ }
+ if (header == NULL) {
+ g_warning("Trace %s has not one valid metadata tracefile", abs_path);
+ goto find_error;
+ }
+
ret = parse_trace_header(header, tf, t);
g_assert(!ret);
@@ -889,7 +981,7 @@ LttTrace *ltt_trace_open(const gchar *pathname)
for(i=0; i<group->len; i++) {
tf = &g_array_index (group, LttTracefile, i);
- if (tf->cpu_online)
+ if (tf->cpu_online && tf->buffer.head )
if(ltt_process_metadata_tracefile(tf))
goto find_error;
// goto metadata_error;
@@ -908,6 +1000,63 @@ alloc_error:
return NULL;
}
+/*****************************************************************************
+ *Function name
+ * ltt_tracefile_update : Update the informations concerning a tracefile
+ * Must be called periodically to update trace file and file size
+ information.
+ *Input params
+ * tf : the tracefile
+ *Return value
+ * : 1 for success and an event is available
+ 0 for success but no event available,
+ * -1 on error.
+ ****************************************************************************/
+int ltt_tracefile_update(LttTracefile *tf)
+{
+ struct stat lTDFStat; /* Trace data file status */
+ if(fstat(tf->fd, &lTDFStat) < 0){
+ perror("error in getting the tracefile informations.");
+ }
+
+ /* Process the file only on size change */
+ if(tf->file_size < lTDFStat.st_size) {
+ /* Update the size of the file */
+ tf->file_size = lTDFStat.st_size;
+
+ if( tf->file_size >= (off_t)(ltt_subbuffer_header_size()) ) {
+
+ if(tf->buf_index == NULL) {
+ if(ltt_tracefile_init(tf) < 0) {
+ return -1;
+ }
+
+ }
+ else
+ {
+ /* Retrieve the new subbuffers and index them */
+ ltt_trace_continue_block_index(tf);
+ }
+
+ /* Immediatly Update the metadata table */
+ if(tf->name == LTT_TRACEFILE_NAME_METADATA)
+ {
+ ltt_process_metadata_tracefile(tf);
+ } else {
+ /* */
+
+ if(ltt_tracefile_read(tf) == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 0;
+
+}
/* Open another, completely independant, instance of a trace.
*
@@ -919,7 +1068,7 @@ alloc_error:
*/
LttTrace *ltt_trace_copy(LttTrace *self)
{
- return ltt_trace_open(g_quark_to_string(self->pathname));
+ return ltt_trace_open(g_quark_to_string(self->pathname), self->is_live);
}
/*
@@ -941,9 +1090,15 @@ void ltt_tracefile_time_span_get(LttTracefile *tf,
LttTime *start, LttTime *end)
{
int err;
+
err = map_block(tf, 0);
- if(unlikely(err)) {
+ /* Only empty live traces will return ERANGE */
+ if(err == ERANGE) {
+ *start = ltt_time_infinite;
+ *end = ltt_time_zero;
+ return;
+ } else if(unlikely(err)) {
g_error("Can not map block");
*start = ltt_time_infinite;
} else
@@ -1039,7 +1194,9 @@ int ltt_tracefile_seek_time(LttTracefile *tf, LttTime time)
/* seek at the beginning of trace */
err = map_block(tf, 0); /* First block */
- if(unlikely(err)) {
+ if(unlikely(err == ERANGE)) {
+ goto range;
+ } else if(unlikely(err)) {
g_error("Can not map block");
goto fail;
}
@@ -1198,8 +1355,6 @@ LttEvent *ltt_tracefile_get_event(LttTracefile *tf)
return &tf->event;
}
-
-
/*****************************************************************************
*Function name
* ltt_tracefile_read : Read the next event in the tracefile
@@ -1426,6 +1581,11 @@ static gint map_block(LttTracefile * tf, guint block_num)
uint32_t size;
int ret;
+ if(tf->num_blocks == 0) {
+ errno = ERANGE;
+ return ERANGE;
+ }
+
g_assert(block_num < tf->num_blocks);
if(tf->buffer.head != NULL) {
@@ -1668,8 +1828,8 @@ static int ltt_seek_next_event(LttTracefile *tf)
pos += (size_t)tf->event.data_size;
tf->event.offset = pos - tf->buffer.head;
-
- if(tf->event.offset == tf->buffer.data_size) {
+
+ if(tf->event.offset >= tf->buffer.data_size) {
ret = ERANGE;
goto found;
}
--
1.7.2.3
More information about the lttng-dev
mailing list