From mboxrd@z Thu Jan 1 00:00:00 1970 From: compudj@krystal.dyndns.org (Mathieu Desnoyers) Date: Sat, 11 Sep 2010 12:22:28 -0400 Subject: [ltt-dev] [PATCH 1/2] Add the support for streaming analysis in the libltttraceread library In-Reply-To: <4C7856F0.6010508@polymtl.ca> References: <4C77F13E.4040708@polymtl.ca> <20100827175336.GB30338@Krystal> <4C7856F0.6010508@polymtl.ca> Message-ID: <20100911162228.GA28088@Krystal> * Oussama El Mfadli (oussama.el-mfadli at polymtl.ca) wrote: > Hi, > thanks for you quick answer. I made the modification. The only > difference in the API is the new parameter in ltt_trace_open(..) that > permit to set a flag in streaming mode. I also added the fallback if > inotify is not available. > > Concerning the function to construct the block index. I modified the > name. I hope they are more clear : > ltt_trace_create_block_index : use when no block index is build > ltt_trace_continue_block_index : use to continue the construct of the > block index > ltt_trace_update_block_index : an internal function used by both create > and continue, because a big part of there code is the same.(This > function is mainly added to prevent duplication of the source code). > > The current plan is that the application poll the fd(available from the > trace structure) to check if new informations was received. Then call > for update. Since the textDump is the only module that used this option, > it should not affect the analysis performance. For the up-coming > implentation in the GUI modules, the callback scheme would probably be > more adapted. > > The patch is following: > > ----------- > > Add the support for streaming analysis in the libltttraceread library > > - Modification of the opening of the tracefile > and the way some informations are retreived > - Add some implementations to append the new arriving blocks into > the index > --- > ltt/ltt-private.h | 3 + > ltt/trace.h | 38 ++++++- > ltt/tracefile.c | 340 > +++++++++++++++++++++++++++++++++++++++++++++++------ > 3 files changed, 345 insertions(+), 36 deletions(-) > > diff --git a/ltt/ltt-private.h b/ltt/ltt-private.h > index 65d73d1..665081a 100644 > --- a/ltt/ltt-private.h > +++ b/ltt/ltt-private.h > @@ -171,6 +171,9 @@ struct LttTracefile { > > /* Current block */ > LttBuffer buffer; //current buffer > + > + /* For streaming usage: in case the tracefile is currently empty */ > + gboolean streaming_is_empty; > }; > > /* The characteristics of the system on which the trace was obtained > diff --git a/ltt/trace.h b/ltt/trace.h > index e16c66f..aec3455 100644 > --- a/ltt/trace.h > +++ b/ltt/trace.h > @@ -25,6 +25,32 @@ > #include > #include > > +#include > + > +//Options used to open a trace > +#define NO_FLAG 0 > +#define TRACE_STREAMING (1 << 0) > + > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) > +#include > + > +#define HAS_INOTIFY > +#else > + > +#undef HAS_INOTIFY > +#endif > +struct inotify_watch { > + int wd; > + char path_channel[PATH_MAX]; > + GQuark name; > + guint num; > +}; > + > +struct inotify_watch_array { > + struct inotify_watch *elem; > + int num; > +}; > + > struct LttTrace { > GQuark pathname; //the pathname of the trace > //LttSystemDescription * system_description;//system description > @@ -47,6 +73,13 @@ struct LttTrace { > LttTime start_time_from_tsc; > > GData *tracefiles; //tracefiles groups > + > + // Keep a inotify file descriptor array to detect the changes in the > files > + struct inotify_watch_array inotify_watch_array; > + int inotify_fd; > + int open_tracefile_counter; > + > + gboolean streaming_is_enable; proper english would be: streaming_is_enabled > }; > > static inline guint ltt_trace_get_num_cpu(LttTrace *t) > @@ -65,8 +98,9 @@ static inline guint ltt_trace_get_num_cpu(LttTrace *t) > > */ > > -LttTrace *ltt_trace_open(const gchar *pathname); > - > +LttTrace *ltt_trace_open(const gchar *pathname, unsigned int flags); > +int open_tracefiles(LttTrace *trace, gchar *root_path, > + gchar *relative_path); > /* copy reopens a trace > * > * return value NULL if error while opening the trace > diff --git a/ltt/tracefile.c b/ltt/tracefile.c > index 54da747..3fbb016 100644 > --- a/ltt/tracefile.c > +++ b/ltt/tracefile.c > @@ -97,8 +97,6 @@ static guint64 cycles_2_ns(LttTracefile *tf, guint64 > cycles); > /* go to the next event */ > static int ltt_seek_next_event(LttTracefile *tf); > > -static int open_tracefiles(LttTrace *trace, gchar *root_path, > - gchar *relative_path); > static int ltt_process_metadata_tracefile(LttTracefile *tf); > static void ltt_tracefile_time_span_get(LttTracefile *tf, > LttTime *start, LttTime *end); > @@ -209,18 +207,14 @@ int get_block_offset_size(LttTracefile *tf, guint > block_num, > return 0; > } > > -int ltt_trace_create_block_index(LttTracefile *tf) > -{ > - 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); > > +static int ltt_trace_update_block_index(LttTracefile *tf, uint64_t > offset, unsigned long firstBlock) > +{ > + 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; > @@ -228,7 +222,7 @@ int ltt_trace_create_block_index(LttTracefile *tf) > tf->buf_index = g_array_set_size(tf->buf_index, i + 1); > off = &g_array_index(tf->buf_index, uint64_t, i); > *off = offset; > - > + Did you add a whitespace ? > /* map block header */ > header = mmap(0, header_map_size, PROT_READ, > MAP_PRIVATE, tf->fd, (off_t)offset); > @@ -253,6 +247,46 @@ int ltt_trace_create_block_index(LttTracefile *tf) > 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_process_block_index(tf, offset, i); hrm, did you try compiling this ? It should be updated to "update" rather than "process". > +} > + > +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); Please check memory allocation error. > + ret = ltt_trace_process_block_index(tf, offset, i); Same here (process -> update). > + return ret; > +} > + > /***************************************************************************** > *Function name > * ltt_tracefile_open : open a trace file, construct a LttTracefile > @@ -263,12 +297,13 @@ int ltt_trace_create_block_index(LttTracefile *tf) > *Return value > * : 0 for success, -1 otherwise. > > ****************************************************************************/ > > > - Useless empty line removal. Please separate coding style fixes from actual features. > 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(); > + Unneeded empty line. > + tf->streaming_is_empty = FALSE; > > //open the file > tf->long_name = g_quark_from_string(fileName); > @@ -289,10 +324,31 @@ static gint ltt_tracefile_open(LttTrace *t, gchar > * fileName, LttTracefile *tf) > // 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; > + if(t->streaming_is_enable == FALSE) > + { > + g_print("The input data file %s does not contain a trace\n", > fileName); > + goto close_file; > + } > + else > + { > + //We are in a streaming mode, so the file can be empty at the > start of the analysis > + tf->streaming_is_empty = TRUE; > + tf->file_size = lTDFStat.st_size; > + tf->events_lost = 0; > + tf->subbuf_corrupt = 0; > + > + tf->buffer.head = NULL; > + return -1; > + } > } > > + tf->buffer.head = NULL; > + > + //store the size of the file > + tf->file_size = lTDFStat.st_size; > + tf->events_lost = 0; > + tf->subbuf_corrupt = 0; > + > /* Temporarily map the buffer start header to get trace information */ > /* Multiple of pages aligned head */ > tf->buffer.head = mmap(0, > @@ -310,11 +366,6 @@ static gint ltt_tracefile_open(LttTrace *t, gchar * > fileName, LttTracefile *tf) > 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()))) { > @@ -353,6 +404,109 @@ end: > return -1; > } > > +/***************************************************************************** > + *Function name > + * ltt_tracefile_update : Update the informations concerning a tracefile rather than "information", I would say something more specific, e.g. must be called periodically to update trace file and file size information. > + * Must be called periodically in order to have the > latest informations > + *Input params > + * tf : the tracefile > + *Return value > + * : 0 for success, -1 otherwise. > + > ****************************************************************************/ > > > +int ltt_tracefile_update(LttTracefile *tf, LttTrace *t) > +{ > + struct stat lTDFStat; /* Trace data file status */ > + if(fstat(tf->fd, &lTDFStat) < 0){ Variable declarations must go before code. Please enable your compiler warnings and address these issues. > + perror("error in getting the tracefile informations."); > + } > + int page_size = getpagesize(); > + > +#ifndef HAS_INOTIFY > + if(tf->file_size < lTDFStat.st_size) > + { > +#endif this could be done by creating a #ifndef HAS_INOTIFY static inline int ltt_tracefile_check_size(...) { return tf->file_size < ....) } #else static inline int ltt_tracefile_check_size(...) { return 0; } #endif and call this from the function. > + //Ajust the size of the file > + tf->file_size = lTDFStat.st_size; > + extra empty line. > + > + if(tf->streaming_is_empty) > + { > + ltt_subbuffer_header_t *header; > + > + /* 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, t)) { > + 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 block index */ > + ltt_trace_create_block_index(tf); > + > + //read the first block > + if(map_block(tf,0)) { Please use a consistent bracket style... if () { } else { } rather than a mix of if () { } else { } > + perror("Cannot map block for tracefile"); > + goto close_file; > + } > + tf->streaming_is_empty = FALSE; > + } > + else > + { > + //Retreive the new subbuffers and index them > + ltt_trace_update_block_index(tf); > + } > + > + //Update the metadata table if applicable > + if(tf->name == LTT_TRACEFILE_NAME_METADATA) > + { > + map_block(tf, tf->buffer.index + 1); > + ltt_process_metadata_tracefile(tf); > + } > + ltt_tracefile_read(tf); > + > +#ifndef HAS_INOTIFY > + return 1; > + } > +#endif > + > + 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: > + if (tf->buf_index) > + g_array_free(tf->buf_index, TRUE); Why are you freeing an array that has not been allocated within this function ? This is asymmetric and unexpected. Can you explain ? Same question for closing the file in this function. > + return -1; > +} > + > > /***************************************************************************** > *Function name > @@ -567,7 +721,63 @@ static __attribute__ ((__unused__)) gboolean > ltt_tracefile_group_has_cpu_online( > } > return 0; > } > +/**************************************************************************** > + * get_absolute_pathname > + * > + * Update the number of tracefile of a "name" group > + * This function is normally called when a tracefile is detected but > cannot be parse at cannot be parsed > + * this point. The group size will count this file. will account for ... > + * > + * return the unique pathname in the system > + * > + * MD : Fixed this function so it uses realpath, dealing well with > + * forgotten cases (.. were not used correctly before). > + * > + > ****************************************************************************/ > > +/* > +* Update the number of tracefile in a "name" group > +* This function is normally called when a tracefile is detected but > cannot be parse at cannot be parsed > +* this point. The group size will count this file. account for... > +* > +* return 1 if a new group is created > +* return 0 otherwise > +* > +**/ > +int update_num_cpu(LttTrace *trace, GQuark name, guint num) > +{ > + struct marker_data *mdata; > + GArray *group; > + int i; > + > + group = g_datalist_id_get_data(&trace->tracefiles, name); > + if (group == NULL) > + { > + group = g_array_sized_new (FALSE, TRUE, sizeof(LttTracefile), 10); > + g_datalist_id_set_data_full(&trace->tracefiles, name, > + group, ltt_tracefile_group_destroy); > + mdata = allocate_marker_data(); > + if (!mdata) > + g_error("Error in allocating marker data"); > + > + group = g_array_set_size(group, num+1); > + for (i = 0; i < group->len; i++) > + g_array_index (group, LttTracefile, i).mdata = mdata; > + return 1; > + } > + if ( num+1 > group->len ) ah, a new coding style here ? if ( ... ) rather than if (...). > + { > + mdata = g_array_index (group, LttTracefile, 0).mdata; > + int old_num = group->len; > + group = g_array_set_size(group, num+1); > + for (i = old_num; i < group->len; i++) > + g_array_index (group, LttTracefile, i).mdata = mdata; > + } > + if( name != LTT_TRACEFILE_NAME_METADATA ) { > + update_num_cpu(trace, LTT_TRACEFILE_NAME_METADATA, num); > + } > + return 0; > +} > > /* Open each tracefile under a specific directory. Put them in a > * GData : permits to access them using their tracefile group pathname. > @@ -579,8 +789,7 @@ static __attribute__ ((__unused__)) gboolean > ltt_tracefile_group_has_cpu_online( > * > * A tracefile group is simply an array where all the per cpu > tracefiles sit. > */ > - > -static int open_tracefiles(LttTrace *trace, gchar *root_path, gchar > *relative_path) > +int open_tracefiles(LttTrace *trace, gchar *root_path, gchar > *relative_path) > { > DIR *dir = opendir(root_path); > struct dirent *entry; > @@ -634,6 +843,19 @@ static int open_tracefiles(LttTrace *trace, gchar > *root_path, gchar *relative_pa > if(S_ISDIR(stat_buf.st_mode)) { > > g_debug("Entering subdirectory...\n"); > + //Monitor the directory if we are in a streaming mode > +#ifdef HAS_INOTIFY see other comment about ifdefs within functions. Please create static inlines instead. > + if(trace->streaming_is_enable == TRUE) > + { > + trace->inotify_watch_array.elem = > realloc(trace->inotify_watch_array.elem, > + ++trace->inotify_watch_array.num * sizeof(struct > inotify_watch)); > + > + > trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].wd = > inotify_add_watch(trace->inotify_fd, path, IN_CREATE | IN_OPEN | > IN_CLOSE_WRITE); > + > strcpy(trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].path_channel, > path); > + > trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].name = > g_quark_from_string(""); > + > trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].num = > -1; > + } > +#endif > ret = open_tracefiles(trace, path, rel_path); > if(ret < 0) continue; > } else if(S_ISREG(stat_buf.st_mode)) { > @@ -647,12 +869,29 @@ static int open_tracefiles(LttTrace *trace, gchar > *root_path, gchar *relative_pa > creation = 0; > if(get_tracefile_name_number(rel_path, &name, &num, &tid, &pgid, > &creation)) > continue; /* invalid name */ > - > +#ifdef HAS_INOTIFY same here Thanks, Mathieu > + //Monitor the directory if we are in a streaming mode > + if(trace->streaming_is_enable == TRUE) > + { > + trace->inotify_watch_array.elem = > realloc(trace->inotify_watch_array.elem, > + ++trace->inotify_watch_array.num * sizeof(struct > inotify_watch)); > + > + > trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].wd = > inotify_add_watch(trace->inotify_fd, path, IN_OPEN | IN_MODIFY | > IN_CLOSE); > + > strcpy(trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].path_channel, > path); > + > trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].name = > name; > + > trace->inotify_watch_array.elem[trace->inotify_watch_array.num-1].num = > num; > + } > +#endif > 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 ? */ > + if(trace->streaming_is_enable == TRUE) > + { > + // Take to account the current file in the Cpu count of the group > + update_num_cpu(trace, name, num); > + } > + continue; /* error opening the tracefile : bad magic number ? */ > + > } > > g_debug("Tracefile name is %s and number is %u", > @@ -678,6 +917,9 @@ static int open_tracefiles(LttTrace *trace, gchar > *root_path, gchar *relative_pa > g_error("Error in allocating marker data"); > } > > + if(trace->streaming_is_enable == TRUE && name != > LTT_TRACEFILE_NAME_METADATA) > + update_num_cpu(trace, LTT_TRACEFILE_NAME_METADATA, num); > + > /* Add the per cpu tracefile to the named group */ > unsigned int old_len = group->len; > if(num+1 > old_len) > @@ -802,8 +1044,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, unsigned int flags) > { > gchar abs_path[PATH_MAX]; > LttTrace * t; > @@ -823,6 +1064,8 @@ LttTrace *ltt_trace_open(const gchar *pathname) > get_absolute_pathname(pathname, abs_path); > t->pathname = g_quark_from_string(abs_path); > > + t->streaming_is_enable = FALSE; > + > g_datalist_init(&t->tracefiles); > > /* Test to see if it looks like a trace */ > @@ -842,7 +1085,18 @@ LttTrace *ltt_trace_open(const gchar *pathname) > } > } > closedir(dir); > - > + > + if(flags & TRACE_STREAMING) > + { > + t->streaming_is_enable = TRUE; > +#ifdef HAS_INOTIFY > + t->inotify_fd = inotify_init(); > + fcntl(t->inotify_fd, F_SETFL, O_NONBLOCK); > + t->inotify_watch_array.elem = NULL; > + t->inotify_watch_array.num = 0; > + t->open_tracefile_counter = 0; > +#endif > + } > /* Open all the tracefiles */ > t->start_freq= 0; > if(open_tracefiles(t, abs_path, "")) { > @@ -858,15 +1112,30 @@ LttTrace *ltt_trace_open(const gchar *pathname) > } > > /* > - * Get the trace information for the metadata_0 tracefile. > * 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; > + if(flags & TRACE_STREAMING) > + { > + //Get the trace information from the metadata tracefile. > + for(i = 0; i < group->len; i++) > + { > + tf = &g_array_index (group, LttTracefile, i); > + header = (ltt_subbuffer_header_t *)tf->buffer.head; > + if(header != NULL) > + break; > + } > + } > + else > + { > + //Get the trace information from the metadata_0 tracefile. > + tf = &g_array_index (group, LttTracefile, 0); > + header = (ltt_subbuffer_header_t *)tf->buffer.head; > + } > + > ret = parse_trace_header(header, tf, t); > g_assert(!ret); > > @@ -910,7 +1179,10 @@ alloc_error: > */ > LttTrace *ltt_trace_copy(LttTrace *self) > { > - return ltt_trace_open(g_quark_to_string(self->pathname)); > + unsigned int flag = NO_FLAG; > + if(self->streaming_is_enable) > + flag |= TRACE_STREAMING; > + return ltt_trace_open(g_quark_to_string(self->pathname), flag); > } > > /* > -- > 1.7.0.4 > -- Mathieu Desnoyers Operating System Efficiency R&D Consultant EfficiOS Inc. http://www.efficios.com