[ltt-dev] [PATCH] Add formatted dump module v3
Mathieu Desnoyers
compudj at krystal.dyndns.org
Tue Jan 18 13:49:23 EST 2011
* Vincent Attard (vincent.attard at polymtl.ca) wrote:
> This new plugin prints a formated output of each events in a trace. The
> output format is defined as a parameter with printf like syntax.It provides
> a default format easy to read and the original textDump format for backward
> compatibility.
> Updated: Use glib functions for solving buffer overflow problem and correct
> indentation.
>
> Signed-off-by: Vincent Attard <vincent.attard at polymtl.ca>
> Reviewed-by: Yannick Brosseau <yannick.brosseau at polymtl.ca>
> ---
> lttv/modules/text/Makefile.am | 3 +-
> lttv/modules/text/formattedDump.c | 367 +++++++++++++++++++++++++++++++++++++
> 2 files changed, 369 insertions(+), 1 deletions(-)
> create mode 100644 lttv/modules/text/formattedDump.c
>
> diff --git a/lttv/modules/text/Makefile.am b/lttv/modules/text/Makefile.am
> index 29e8990..4f11cb6 100644
> --- a/lttv/modules/text/Makefile.am
> +++ b/lttv/modules/text/Makefile.am
> @@ -3,7 +3,7 @@ AM_LDFLAGS = $(MODULE_LDFLAGS)
>
> libdir = ${lttvplugindir}
>
> -lib_LTLIBRARIES = libtextDump.la libbatchAnalysis.la libtextFilter.la libprecomputeState.la libdepanalysis.la libsync_chain_batch.la
> +lib_LTLIBRARIES = libtextDump.la libbatchAnalysis.la libtextFilter.la libprecomputeState.la libdepanalysis.la libsync_chain_batch.la libformattedDump.la
>
> libtextDump_la_SOURCES = textDump.c
> libbatchAnalysis_la_SOURCES = batchAnalysis.c
> @@ -11,6 +11,7 @@ libtextFilter_la_SOURCES = textFilter.c
> libprecomputeState_la_SOURCES = precomputeState.c
> libdepanalysis_la_SOURCES = depanalysis.c sstack.c
> libsync_chain_batch_la_SOURCES = sync_chain_batch.c
> +libformattedDump_la_SOURCES = formattedDump.c
>
> noinst_HEADERS = \
> batchanalysis.h \
> diff --git a/lttv/modules/text/formattedDump.c b/lttv/modules/text/formattedDump.c
> new file mode 100644
> index 0000000..d87d801
> --- /dev/null
> +++ b/lttv/modules/text/formattedDump.c
> @@ -0,0 +1,367 @@
> +/* This file is part of the Linux Trace Toolkit viewer
> + * Copyright (C) 2003-2004 Michel Dagenais
> + * 2005 Mathieu Desnoyers
> + * 2011 Vincent Attard <vinc.attard at gmail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License Version 2 as
> + * published by the Free Software Foundation;
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
> + * MA 02111-1307, USA.
> + */
> +
> +/*
> + * Formatted dump plugin prints a formatted output of each events in a trace. The
> + * output format is defined as a parameter. It provides a default format easy to
> + * read and the original textDump format for backward compatibility.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <lttv/lttv.h>
> +#include <lttv/option.h>
> +#include <lttv/module.h>
> +#include <lttv/hook.h>
> +#include <lttv/attribute.h>
> +#include <lttv/iattribute.h>
> +#include <lttv/stats.h>
> +#include <lttv/filter.h>
> +#include <lttv/print.h>
> +#include <ltt/ltt.h>
> +#include <ltt/event.h>
> +#include <ltt/trace.h>
> +#include <stdio.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <stdlib.h>
> +
> +static gboolean a_no_field_names;
> +static gboolean a_state;
> +static gboolean a_text;
> +
> +static char *a_file_name = NULL;
> +static char *a_format = NULL;
> +
> +static LttvHooks *before_traceset;
> +static LttvHooks *event_hook;
> +
> +static const char default_format[] =
> + "channel:%c event:%e timestamp:%t elapsed:%l cpu:%u pid:%d ppid:%i "\
The \ at the end of line is not needed.
> + "tgpid:%g process:%p brand:%b state:%a payload:{ %m }";
> +static const char textDump_format[] =
> + "%c.%e: %s.%n (%r/%c_%u), %d, %g, %p, %b, %i, %y, %a { %m }";
> +
> +static FILE *a_file;
> +
> +static GString *a_string;
> +
> +static gboolean open_output_file(void *hook_data, void *call_data)
> +{
> + g_info("Open the output file");
> +
> + if (a_file_name == NULL) {
> + a_file = stdout;
> + } else {
> + a_file = fopen(a_file_name, "w");
> + }
for single-line if/else, you could do without the { }. Especially given
that these are not nested.
if (a_file_name == NULL)
a_file = stdout;
else
a_file = fopen(a_file_name, "w");
> +
> + if (a_file == NULL) {
> + g_error("cannot open file %s", a_file_name);
> + }
same here. Indentation is wrong here too.
> + return FALSE;
extra space before return. Please consider using checkpatch.pl from the
Linux kernel (it's in /scripts).
> +}
> +
> +static int write_event_content(void *hook_data, void *call_data)
> +{
> + gboolean result;
> +
> + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes());
> +
> + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
> +
> + LttvTracefileState *tfs = (LttvTracefileState *)call_data;
> +
> + LttEvent *e;
> +
> + LttvAttributeValue value_filter;
> +
> + LttvFilter *filter;
> +
> + guint cpu = tfs->cpu;
> + LttvTraceState *ts = (LttvTraceState*)tfc->t_context;
> + LttvProcessState *process = ts->running_process[cpu];
> +
> + e = ltt_tracefile_get_event(tfc->tf);
> +
> + result = lttv_iattribute_find_by_path(attributes, "filter/lttv_filter",
> + LTTV_POINTER, &value_filter);
> + g_assert(result);
> + filter = (LttvFilter*)*(value_filter.v_pointer);
> +
> + /*
> + * call to the filter if available
> + */
> + if (filter->head != NULL)
> + if (!lttv_filter_tree_parse(filter->head,e,tfc->tf,
> + tfc->t_context->t,tfc,NULL,NULL))
> + return FALSE;
> +
> + lttv_event_to_string(e, a_string, TRUE, !a_no_field_names, tfs);
> +
> + if (a_state) {
> + g_string_append_printf(a_string, "%s ",
> + g_quark_to_string(process->state->s));
> + }
could remove { }.
> +
> + g_string_append_printf(a_string,"\n");
> +
> + fputs(a_string->str, a_file);
> + return FALSE;
> +}
> +
> +void lttv_event_to_string(LttEvent *e, GString *string_buffer, gboolean mandatory_fields,
> + gboolean field_names, LttvTracefileState *tfs)
> +{
> + struct marker_field *field;
> + struct marker_info *info;
> + LttTime time;
> +
> + static LttTime time_prev = {0,0};
> + /*
> + * TODO:
> + * Added this static value into state.c and reset each time you do a seek
> + * for using it in the GUI.
> + */
> + LttTime elapse;
> + const char *fmt;
> + int i;
> +
> + guint cpu = tfs->cpu;
> + LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context;
> + LttvProcessState *process = ts->running_process[cpu];
> +
> + info = marker_get_info_from_id(tfs->parent.tf->mdata, e->event_id);
> + if (mandatory_fields) {
> + time = ltt_event_time(e);
> + /* Calculate elapsed time between current and previous event */
> + if (time_prev.tv_sec == 0 && time_prev.tv_nsec == 0) {
> + time_prev = ltt_event_time(e);
> + elapse.tv_sec = 0;
> + elapse.tv_nsec = 0;
> + /*
> + * TODO:
> + * Keep in mind that you should add the ability to restore the previous
> + * event time to state.c if you want to reuse this code into the GUI.
> + */
indentation is wrong for this comment and other comments below.
> + } else {
> + elapse = ltt_time_sub(time, time_prev);
> + time_prev = time;
> + }
> + }
> +
> + if (a_text) {
> + /* textDump format (used with -T command option) */
> + fmt = textDump_format;
> + }
> + else if (!a_format) {
} else if (!a_format) {
> + /* Default format (used if no option) */
> + fmt = default_format;
> + } else {
> + /*
> + * formattedDump format
> + * (used with -F command option following by the desired format)
> + */
> + fmt = a_format;
> + }
> +
> + g_string_set_size(string_buffer, 0);
> + /*
> + * Switch case:
> + * all '%-' are replaced by the desired value in 'string_buffer'
> + */
> + int len;
> + len = strlen(fmt);
> + for (i = 0; i < len; i++) {
> + if (fmt[i] == '%') {
> + switch (fmt[++i]) {
> + case 't':
> + g_string_append_printf(string_buffer, "%ld:%02ld:%02ld.%09ld",
> + time.tv_sec/3600, (time.tv_sec%3600)/60, time.tv_sec%60,
> + time.tv_nsec);
> + break;
> + case 'c':
> + g_string_append(string_buffer,
> + g_quark_to_string(ltt_tracefile_name(tfs->parent.tf)));
> + break;
> + case 'e':
> + g_string_append(string_buffer,
> + g_quark_to_string(info->name));
> + break;
> + case 'd':
> + g_string_append_printf(string_buffer, "%u", process->pid);
> + break;
> + case 's':
> + g_string_append_printf(string_buffer, "%ld", time.tv_sec);
> + break;
> + case 'n':
> + g_string_append_printf(string_buffer, "%ld", time.tv_nsec);
> + break;
> + case 'i':
> + g_string_append_printf(string_buffer, "%u", process->ppid);
> + break;
> + case 'g':
> + g_string_append_printf(string_buffer, "%u", process->tgid);
> + break;
> + case 'p':
> + g_string_append(string_buffer,
> + g_quark_to_string(process->name));
> + break;
> + case 'b':
> + g_string_append(string_buffer,
> + g_quark_to_string(process->brand));
> + break;
> + case 'u':
> + g_string_append_printf(string_buffer, "%u", cpu);
> + break;
> + case 'l':
> + g_string_append_printf(string_buffer, "%ld.%09ld",
> + elapse.tv_sec, elapse.tv_nsec);
> + break;
> + case 'a':
> + g_string_append(string_buffer,
> + g_quark_to_string(process->state->t));
> + break;
> + case 'm':
> + {
> + /* Get and print markers and tracepoints fields into 'marker' */
> + if (marker_get_num_fields(info) == 0) {
> + break;
> + }
could remove {}
> + for (field = marker_get_field(info, 0);
> + field != marker_get_field(info, marker_get_num_fields(info));
> + field++) {
> + if (field != marker_get_field(info, 0)) {
> + g_string_append(string_buffer, ", ");
> + }
could remove {}
> + lttv_print_field(e, field, string_buffer, field_names, tfs);
> + }
> + }
> + break;
> + case 'r':
> + g_string_append(string_buffer, g_quark_to_string(
> + ltt_trace_name(ltt_tracefile_get_trace(tfs->parent.tf))));
> + break;
> + case '%':
> + g_string_append_c(string_buffer, '%');
> + break;
> + case 'y':
> + g_string_append_printf(string_buffer, "0x%" PRIx64,
> + process->current_function);
> + break;
> +
> + }
> + } else {
> + /* Copy every character if not equals to '%' */
> + g_string_append_c(string_buffer, fmt[i]);
> + }
> + }
> +}
> +
> +static void init()
> +{
> + gboolean result;
> +
> + LttvAttributeValue value;
> +
> + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes());
> +
> + g_info("Init formattedDump.c");
> +
> + a_string = g_string_new("");
> +
> + a_file_name = NULL;
> + lttv_option_add("output", 'o',
> + "output file where the text is written",
> + "file name",
> + LTTV_OPT_STRING, &a_file_name, NULL, NULL);
> +
> + a_format= NULL;
> + lttv_option_add("format", 'F',
> + "output the desired format\n\
I prefer the multi-line strings like this:
"blah blah "
"again more "
"final."
to the format below (which uses the preprocessor to create one huge
line).
> + FORMAT controls the output. Interpreted sequences are:\n\
> + \n\
> + %c channel name\n\
> + %p process name\n\
> + %e event name\n\
> + %r path to trace\n\
> + %t timestamp (e.g., 2:08:54.025684145)\n\
> + %s seconds\n\
> + %n nanoseconds\n\
> + %l elapsed time with the previous event\n\
> + %d pid\n\
> + %i ppid\n\
> + %g tgid\n\
> + %u cpu\n\
> + %b brand\n\
> + %a state\n\
> + %y memory address\n\
> + %m markers and tracepoints fields\n",
> + "format string",
> + LTTV_OPT_STRING, &a_format, NULL, NULL);
> +
> + a_text = FALSE;
> + lttv_option_add("text", 'T',
> + "output the textDump format",
> + "",
> + LTTV_OPT_NONE, &a_text, NULL, NULL);
> +
> + result = lttv_iattribute_find_by_path(attributes, "hooks/event",
> + LTTV_POINTER, &value);
> + g_assert(result);
> + event_hook = *(value.v_pointer);
> + g_assert(event_hook);
> + lttv_hooks_add(event_hook, write_event_content, NULL, LTTV_PRIO_DEFAULT);
> +
> + result = lttv_iattribute_find_by_path(attributes, "hooks/traceset/before",
> + LTTV_POINTER, &value);
> + g_assert(result);
> + before_traceset = *(value.v_pointer);
> + g_assert(before_traceset);
> + lttv_hooks_add(before_traceset, open_output_file, NULL,
> + LTTV_PRIO_DEFAULT);
> +
> +}
> +
> +static void destroy()
> +{
> + g_info("Destroy formattedDump");
> +
> + lttv_option_remove("format");
> +
> + lttv_option_remove("output");
> +
> + lttv_option_remove("text");
> +
> + g_string_free(a_string, TRUE);
> +
> + lttv_hooks_remove_data(event_hook, write_event_content, NULL);
> +
> + lttv_hooks_remove_data(before_traceset, open_output_file, NULL);
> +
> +}
> +
> +
> +LTTV_MODULE("formattedDump", "Print events with desired format in a file", \
Do we really need the \ at end of lines here ?
Thanks,
Mathieu
> + "Produce a detailed formatted text printout of a trace", \
> + init, destroy, "batchAnalysis", "option")
> +
> --
> 1.7.0.4
>
>
> _______________________________________________
> ltt-dev mailing list
> ltt-dev at lists.casi.polymtl.ca
> http://lists.casi.polymtl.ca/cgi-bin/mailman/listinfo/ltt-dev
>
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
More information about the lttng-dev
mailing list