From: pmf Date: Tue, 31 Jul 2007 19:46:12 +0000 (+0000) Subject: fork resourceview dir from controlflow view X-Git-Tag: v0.12.20~934 X-Git-Url: https://git.lttng.org./?a=commitdiff_plain;h=9e01e6d4aa891aa2900227503bde6e7dd9ae65a1;p=lttv.git fork resourceview dir from controlflow view git-svn-id: http://ltt.polymtl.ca/svn@2555 04897980-b3bd-0310-b5e0-8ef037075253 --- diff --git a/ltt/branches/poly/lttv/modules/gui/Makefile.am b/ltt/branches/poly/lttv/modules/gui/Makefile.am index e92b6fcb..0b25cb35 100644 --- a/ltt/branches/poly/lttv/modules/gui/Makefile.am +++ b/ltt/branches/poly/lttv/modules/gui/Makefile.am @@ -6,7 +6,7 @@ # WARNING : subdirs order is important : mainWin depends on API -SUBDIRS = lttvwindow controlflow detailedevents statistics filter tracecontrol interrupts diskperformance histogram tutorial +SUBDIRS = lttvwindow controlflow detailedevents statistics filter tracecontrol interrupts diskperformance histogram tutorial resourceview diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/Makefile.am b/ltt/branches/poly/lttv/modules/gui/resourceview/Makefile.am new file mode 100644 index 00000000..d70b838e --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/Makefile.am @@ -0,0 +1,38 @@ +# This file is part of the Linux Trace Toolkit viewer +# Copyright (C) 2003-2004 Mathieu Desnoyers +# +# 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. + + +# +# Makefile for LTT New generation user interface : plugins. +# +# Created by Mathieu Desnoyers on September 27, 2003 +# + +AM_CFLAGS = $(GLIB_CFLAGS) +AM_CFLAGS += $(GTK_CFLAGS) +LIBS += $(GLIB_LIBS) +LIBS += $(GTK_LIBS) -L${top_builddir}/lttv/modules/gui/lttvwindow/lttvwindow -llttvwindow + +libdir = ${lttvplugindir} + +lib_LTLIBRARIES = libresourceview.la +libguicontrolflow_la_LDFLAGS = -module -avoid-version +libguicontrolflow_la_SOURCES = module.c eventhooks.c cfv.c processlist.c drawing.c drawitem.c lttv_plugin_cfv.c + +noinst_HEADERS = eventhooks.h cfv.h processlist.h drawing.h drawitem.h lttv_plugin_cfv.h + +EXTRA_DIST = hGuiControlFlowInsert.xpm hLegendInsert.xpm diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/cfv.c b/ltt/branches/poly/lttv/modules/gui/resourceview/cfv.c new file mode 100644 index 00000000..f7a25827 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/cfv.c @@ -0,0 +1,359 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "cfv.h" +#include "drawing.h" +#include "processlist.h" +#include "eventhooks.h" +#include "lttv_plugin_cfv.h" + +extern GSList *g_control_flow_data_list; + +static gboolean +header_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + gtk_widget_set_size_request(drawing->ruler, -1, allocation->height); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + +gboolean cfv_scroll_event(GtkWidget *widget, GdkEventScroll *event, + gpointer data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)data; + unsigned int cell_height = + get_cell_height( + GTK_TREE_VIEW(control_flow_data->process_list->process_list_widget)); + gdouble new; + + switch(event->direction) { + case GDK_SCROLL_UP: + { + new = gtk_adjustment_get_value(control_flow_data->v_adjust) + - cell_height; + } + break; + case GDK_SCROLL_DOWN: + { + new = gtk_adjustment_get_value(control_flow_data->v_adjust) + + cell_height; + } + break; + default: + return FALSE; + } + if(new >= control_flow_data->v_adjust->lower && + new <= control_flow_data->v_adjust->upper + - control_flow_data->v_adjust->page_size) + gtk_adjustment_set_value(control_flow_data->v_adjust, new); + return TRUE; +} + + +/* Toolbar callbacks */ +static void property_button (GtkToolButton *toolbutton, + gpointer user_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)user_data; + + g_printf("CFV Property button clicked\n"); + +} + +/* Toolbar callbacks */ +static void filter_button (GtkToolButton *toolbutton, + gpointer user_data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)user_data; + LttvAttribute *attribute; + LttvAttributeValue value; + gboolean ret; + g_printf("Filter button clicked\n"); + + attribute = LTTV_ATTRIBUTE(lttv_iattribute_find_subdir( + LTTV_IATTRIBUTE(lttv_global_attributes()), + LTTV_VIEWER_CONSTRUCTORS)); + g_assert(attribute); + + ret = lttv_iattribute_find_by_path(LTTV_IATTRIBUTE(attribute), + "guifilter", LTTV_POINTER, &value); + g_assert(ret); + lttvwindow_viewer_constructor constructor = + (lttvwindow_viewer_constructor)*(value.v_pointer); + if(constructor) constructor(&plugin_cfv->parent); + else g_warning("Filter module not loaded."); + + //FIXME : viewer returned. +} + + + +/***************************************************************************** + * Control Flow Viewer class implementation * + *****************************************************************************/ +/** + * Control Flow Viewer's constructor + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the drawing widget. + * @param ParentWindow A pointer to the parent window. + * @return The widget created. + */ +ControlFlowData * +guicontrolflow(LttvPluginTab *ptab) +{ + Tab *tab = ptab->tab; + GtkWidget *tmp_toolbar_icon; + GtkWidget *process_list_widget, *drawing_widget, *drawing_area; + //ControlFlowData* control_flow_data = g_new(ControlFlowData,1) ; + LttvPluginCFV *plugin_cfv = g_object_new(LTTV_TYPE_PLUGIN_CFV, NULL); + GtkTooltips *tooltips = gtk_tooltips_new(); + ControlFlowData* control_flow_data = plugin_cfv->cfd; + control_flow_data->ptab = ptab; + control_flow_data->tab = ptab->tab; + + control_flow_data->v_adjust = + GTK_ADJUSTMENT(gtk_adjustment_new( 0.0, /* Value */ + 0.0, /* Lower */ + 0.0, /* Upper */ + 0.0, /* Step inc. */ + 0.0, /* Page inc. */ + 0.0)); /* page size */ + + /* Create the drawing */ + control_flow_data->drawing = drawing_construct(control_flow_data); + + drawing_widget = + drawing_get_widget(control_flow_data->drawing); + + drawing_area = + drawing_get_drawing_area(control_flow_data->drawing); + + control_flow_data->number_of_process = 0; + control_flow_data->background_info_waiting = 0; + + /* Create the Process list */ + control_flow_data->process_list = processlist_construct(); + + process_list_widget = + processlist_get_widget(control_flow_data->process_list); + + gtk_tree_view_set_vadjustment(GTK_TREE_VIEW(process_list_widget), + GTK_ADJUSTMENT( + control_flow_data->v_adjust)); + + g_signal_connect (G_OBJECT(process_list_widget), + "scroll-event", + G_CALLBACK (cfv_scroll_event), + (gpointer)control_flow_data); + g_signal_connect (G_OBJECT(drawing_area), + "scroll-event", + G_CALLBACK (cfv_scroll_event), + (gpointer)control_flow_data); + + g_signal_connect (G_OBJECT(control_flow_data->process_list->button), + "size-allocate", + G_CALLBACK(header_size_allocate), + (gpointer)control_flow_data->drawing); +#if 0 /* not ready */ + g_signal_connect ( + // G_OBJECT(control_flow_data->process_list->process_list_widget), + G_OBJECT(control_flow_data->process_list->list_store), + "row-changed", + G_CALLBACK (tree_row_activated), + (gpointer)control_flow_data); +#endif //0 + + control_flow_data->hbox = gtk_hbox_new(FALSE, 1); + control_flow_data->toolbar = gtk_toolbar_new(); + gtk_toolbar_set_orientation(GTK_TOOLBAR(control_flow_data->toolbar), + GTK_ORIENTATION_VERTICAL); + + tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), + "guifilter16x16.png"); + gtk_widget_show(tmp_toolbar_icon); + control_flow_data->button_filter = gtk_tool_button_new(tmp_toolbar_icon, + "Filter"); + g_signal_connect (G_OBJECT(control_flow_data->button_filter), + "clicked", + G_CALLBACK (filter_button), + (gpointer)plugin_cfv); + gtk_toolbar_insert(GTK_TOOLBAR(control_flow_data->toolbar), + control_flow_data->button_filter, + 0); + gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(control_flow_data->button_filter), + tooltips, "Open the filter window", NULL); + + tmp_toolbar_icon = create_pixmap (main_window_get_widget(tab), + "properties.png"); + gtk_widget_show(tmp_toolbar_icon); + control_flow_data->button_prop = gtk_tool_button_new(tmp_toolbar_icon, + "Properties"); + g_signal_connect (G_OBJECT(control_flow_data->button_prop), + "clicked", + G_CALLBACK (property_button), + (gpointer)control_flow_data); + gtk_toolbar_insert(GTK_TOOLBAR(control_flow_data->toolbar), + control_flow_data->button_prop, + 1); + + gtk_toolbar_set_style(GTK_TOOLBAR(control_flow_data->toolbar), + GTK_TOOLBAR_ICONS); + + gtk_box_pack_start(GTK_BOX(control_flow_data->hbox), + control_flow_data->toolbar, + FALSE, FALSE, 0); + control_flow_data->h_paned = gtk_hpaned_new(); + control_flow_data->box = gtk_event_box_new(); + gtk_box_pack_end(GTK_BOX(control_flow_data->hbox), + control_flow_data->box, + TRUE, TRUE, 0); + control_flow_data->top_widget = control_flow_data->hbox; + plugin_cfv->parent.top_widget = control_flow_data->top_widget; + gtk_container_add(GTK_CONTAINER(control_flow_data->box), + control_flow_data->h_paned); + + gtk_paned_pack1(GTK_PANED(control_flow_data->h_paned), + process_list_widget, FALSE, TRUE); + gtk_paned_pack2(GTK_PANED(control_flow_data->h_paned), + drawing_widget, TRUE, TRUE); + + gtk_container_set_border_width(GTK_CONTAINER(control_flow_data->box), 1); + + /* Set the size of the drawing area */ + //drawing_Resize(drawing, h, w); + + /* Get trace statistics */ + //control_flow_data->Trace_Statistics = get_trace_statistics(Trace); + + gtk_widget_show(drawing_widget); + gtk_widget_show(process_list_widget); + gtk_widget_show(control_flow_data->h_paned); + gtk_widget_show(control_flow_data->box); + gtk_widget_show(control_flow_data->toolbar); + gtk_widget_show(GTK_WIDGET(control_flow_data->button_prop)); + gtk_widget_show(GTK_WIDGET(control_flow_data->button_filter)); + gtk_widget_show(control_flow_data->hbox); + + g_object_set_data_full( + G_OBJECT(control_flow_data->top_widget), + "plugin_data", + plugin_cfv, + (GDestroyNotify)guicontrolflow_destructor); + + g_object_set_data( + G_OBJECT(drawing_area), + "control_flow_data", + control_flow_data); + + g_control_flow_data_list = g_slist_append( + g_control_flow_data_list, + plugin_cfv); + + control_flow_data->filter = NULL; + + //WARNING : The widget must be + //inserted in the main window before the drawing area + //can be configured (and this must happend bedore sending + //data) + + return control_flow_data; + +} + +/* Destroys widget also */ +void +guicontrolflow_destructor_full(gpointer data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)data; + g_info("CFV.c : guicontrolflow_destructor_full, %p", plugin_cfv); + /* May already have been done by GTK window closing */ + if(GTK_IS_WIDGET(guicontrolflow_get_widget(plugin_cfv->cfd))) + gtk_widget_destroy(guicontrolflow_get_widget(plugin_cfv->cfd)); + //control_flow_data->mw = NULL; + //FIXME guicontrolflow_destructor(control_flow_data); +} + +/* When this destructor is called, the widgets are already disconnected */ +void +guicontrolflow_destructor(gpointer data) +{ + LttvPluginCFV *plugin_cfv = (LttvPluginCFV*)data; + Tab *tab = plugin_cfv->cfd->tab; + ControlFlowData *control_flow_data = plugin_cfv->cfd; + + g_info("CFV.c : guicontrolflow_destructor, %p", plugin_cfv); + g_info("%p, %p, %p", update_time_window_hook, plugin_cfv, tab); + if(GTK_IS_WIDGET(guicontrolflow_get_widget(plugin_cfv->cfd))) + g_info("widget still exists"); + + lttv_filter_destroy(plugin_cfv->cfd->filter); + /* Process List is removed with it's widget */ + //ProcessList_destroy(control_flow_data->process_list); + if(tab != NULL) + { + /* Delete reading hooks */ + lttvwindow_unregister_traceset_notify(tab, + traceset_notify, + control_flow_data); + + lttvwindow_unregister_time_window_notify(tab, + update_time_window_hook, + control_flow_data); + + lttvwindow_unregister_current_time_notify(tab, + update_current_time_hook, + control_flow_data); + + lttvwindow_unregister_redraw_notify(tab, redraw_notify, control_flow_data); + lttvwindow_unregister_continue_notify(tab, + continue_notify, + control_flow_data); + + lttvwindow_events_request_remove_all(control_flow_data->tab, + control_flow_data); + + } + lttvwindowtraces_background_notify_remove(control_flow_data); + g_control_flow_data_list = + g_slist_remove(g_control_flow_data_list, control_flow_data); + + g_info("CFV.c : guicontrolflow_destructor end, %p", control_flow_data); + //g_free(control_flow_data); + g_object_unref(plugin_cfv); +} + + diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/cfv.h b/ltt/branches/poly/lttv/modules/gui/resourceview/cfv.h new file mode 100644 index 00000000..5c2276e4 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/cfv.h @@ -0,0 +1,94 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + + +#ifndef _CFV_H +#define _CFV_H + +#include +#include +#include +#include "processlist.h" +#include + +extern GQuark LTT_NAME_CPU; + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_CONTROLFLOWDATA_DEFINED +#define TYPE_CONTROLFLOWDATA_DEFINED +typedef struct _ControlFlowData ControlFlowData; +#endif //TYPE_CONTROLFLOWDATA_DEFINED + +struct _ControlFlowData { + + GtkWidget *top_widget; + Tab *tab; + LttvPluginTab *ptab; + + GtkWidget *hbox; + GtkWidget *toolbar; /* Vbox that contains the viewer's toolbar */ + GtkToolItem *button_prop; /* Properties button. */ + GtkToolItem *button_filter; /* Properties button. */ + GtkWidget *box; /* box that contains the hpaned. necessary for it to work */ + GtkWidget *h_paned; + + ProcessList *process_list; + + Drawing_t *drawing; + GtkAdjustment *v_adjust ; + + /* Shown events information */ +// TimeWindow time_window; +// LttTime current_time; + + //guint currently_Selected_Event ; + guint number_of_process; + guint background_info_waiting; /* Number of background requests waited for + in order to have all the info ready. */ + + LttvFilter *filter; + +} ; + +/* Control Flow Data constructor */ +ControlFlowData *guicontrolflow(LttvPluginTab *ptab); +void +guicontrolflow_destructor_full(gpointer data); +void +guicontrolflow_destructor(gpointer data); + +static inline GtkWidget *guicontrolflow_get_widget( + ControlFlowData *control_flow_data) +{ + return control_flow_data->top_widget ; +} + +static inline ProcessList *guicontrolflow_get_process_list + (ControlFlowData *control_flow_data) +{ + return control_flow_data->process_list ; +} + + + +#endif // _CFV_H diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/drawing.c b/ltt/branches/poly/lttv/modules/gui/resourceview/drawing.c new file mode 100644 index 00000000..e0b92aa3 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/drawing.c @@ -0,0 +1,1480 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "drawing.h" +#include "eventhooks.h" +#include "cfv.h" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) + +//FIXME +// fixed #define TRACE_NUMBER 0 +#define EXTRA_ALLOC 1024 // pixels + + +#if 0 /* colors for two lines representation */ +GdkColor drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0FFF, 0xFFFF, 0xFFFF }, /* COL_WAIT_FORK : pale blue */ + { 0, 0xFFFF, 0xFFFF, 0x0000 }, /* COL_WAIT_CPU : yellow */ + { 0, 0xFFFF, 0xA000, 0xFCFF }, /* COL_EXIT : pale magenta */ + { 0, 0xFFFF, 0x0000, 0xFFFF }, /* COL_ZOMBIE : purple */ + { 0, 0xFFFF, 0x0000, 0x0000 }, /* COL_WAIT : red */ + { 0, 0x0000, 0xFFFF, 0x0000 }, /* COL_RUN : green */ + { 0, 0x8800, 0xFFFF, 0x8A00 }, /* COL_USER_MODE : pale green */ + { 0, 0x09FF, 0x01FF, 0xFFFF }, /* COL_SYSCALL : blue */ + { 0, 0xF900, 0x4200, 0xFF00 }, /* COL_TRAP : pale purple */ + { 0, 0xFFFF, 0x5AFF, 0x01FF }, /* COL_IRQ : orange */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_MODE_UNKNOWN : white */ + +}; +#endif //0 + + +GdkColor drawing_colors[NUM_COLORS] = +{ /* Pixel, R, G, B */ + { 0, 0, 0, 0 }, /* COL_BLACK */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_WHITE */ + { 0, 0x0000, 0xFF00, 0x0000 }, /* COL_RUN_USER_MODE : green */ + { 0, 0x0100, 0x9E00, 0xFFFF }, /* COL_RUN_SYSCALL : pale blue */ + { 0, 0xFF00, 0xFF00, 0x0100 }, /* COL_RUN_TRAP : yellow */ + { 0, 0xFFFF, 0x5E00, 0x0000 }, /* COL_RUN_IRQ : orange */ + { 0, 0xFFFF, 0x9400, 0x9600 }, /* COL_RUN_SOFT_IRQ : pink */ + { 0, 0x6600, 0x0000, 0x0000 }, /* COL_WAIT : dark red */ + { 0, 0x7700, 0x7700, 0x0000 }, /* COL_WAIT_CPU : dark yellow */ + { 0, 0x6400, 0x0000, 0x5D00 }, /* COL_ZOMBIE : dark purple */ + { 0, 0x0700, 0x6400, 0x0000 }, /* COL_WAIT_FORK : dark green */ + { 0, 0x8900, 0x0000, 0x8400 }, /* COL_EXIT : "less dark" magenta */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF }, /* COL_MODE_UNKNOWN : white */ + { 0, 0xFFFF, 0xFFFF, 0xFFFF } /* COL_UNNAMED : white */ + +}; + +/* +RUN+USER MODE green +RUN+SYSCALL +RUN+TRAP +RUN+IRQ +WAIT+foncé +WAIT CPU + WAIT FORK vert foncé ou jaune +IRQ rouge +TRAP: orange +SYSCALL: bleu pâle + +ZOMBIE + WAIT EXIT +*/ + + +/***************************************************************************** + * drawing functions * + *****************************************************************************/ + +static gboolean +expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ); + +static gboolean +motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data); + + +/* Function responsible for updating the exposed area. + * It must do an events request to the lttvwindow API to ask for this update. + * Note : this function cannot clear the background, because it may + * erase drawing already present (SAFETY). + */ +void drawing_data_request(Drawing_t *drawing, + gint x, gint y, + gint width, + gint height) +{ + if(width < 0) return ; + if(height < 0) return ; + + + Tab *tab = drawing->control_flow_data->tab; + TimeWindow time_window = + lttvwindow_get_time_window(tab); + + ControlFlowData *control_flow_data = drawing->control_flow_data; + // (ControlFlowData*)g_object_get_data( + // G_OBJECT(drawing->drawing_area), "control_flow_data"); + + LttTime start, time_end; + LttTime window_end = time_window.end_time; + + g_debug("req : window start_time : %lu, %lu", time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + g_debug("req : window time width : %lu, %lu", time_window.time_width.tv_sec, + time_window.time_width.tv_nsec); + + g_debug("req : window_end : %lu, %lu", window_end.tv_sec, + window_end.tv_nsec); + + g_debug("x is : %i, x+width is : %i", x, x+width); + + convert_pixels_to_time(drawing->width, x, + time_window, + &start); + + convert_pixels_to_time(drawing->width, x+width, + time_window, + &time_end); + time_end = ltt_time_add(time_end, ltt_time_one); // because main window + // doesn't deliver end time. + + lttvwindow_events_request_remove_all(tab, + control_flow_data); + + { + /* find the tracehooks */ + LttvTracesetContext *tsc = lttvwindow_get_traceset_context(tab); + + LttvTraceset *traceset = tsc->ts; + + guint i, k, l, nb_trace; + + LttvTraceState *ts; + + LttvTracefileState *tfs; + + GArray *hooks; + + LttvTraceHook *hook; + + LttvTraceHookByFacility *thf; + + guint ret; + gint before_hn, after_hn; + + nb_trace = lttv_traceset_number(traceset); + // FIXME (fixed) : eventually request for more traces + for(i = 0 ; i < nb_trace ; i++) { + //for(i = 0; itraces[i]; + + /* Find the eventtype id for the following events and register the + associated by id hooks. */ + + hooks = g_array_new(FALSE, FALSE, sizeof(LttvTraceHook)); + hooks = g_array_set_size(hooks, 18); + before_hn = after_hn = 0; + + /* before hooks */ + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_SYSCALL_ENTRY, + LTT_FIELD_SYSCALL_ID, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_SYSCALL_EXIT, + 0, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_TRAP_ENTRY, + LTT_FIELD_TRAP_ID, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_TRAP_EXIT, + 0, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY, + LTT_FIELD_IRQ_ID, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT, + 0, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_ENTRY, + LTT_FIELD_SOFT_IRQ_ID, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_EXIT, + 0, 0, 0, + before_execmode_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_SCHED_SCHEDULE, + LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE, + before_schedchange_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_EXIT, + LTT_FIELD_PID, 0, 0, + before_process_exit_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_FREE, + LTT_FIELD_PID, 0, 0, + before_process_release_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, LTT_EVENT_STATEDUMP_END, + 0, 0, 0, + before_statedump_end, + events_request, + &g_array_index(hooks, LttvTraceHook, before_hn++)); + if(ret) before_hn--; + +#if 0 + lttv_trace_find_hook(ts->parent.t, + "core", "process", "event_sub_id", + "event_data1", "event_data2", before_process_hook, + &g_array_index(hooks, LttvTraceHook, hn++)); +#endif //0 +#if 0 + lttv_trace_find_hook(ts->parent.t, "core", "process_fork", "child_pid", + NULL, NULL, process_fork, &g_array_index(hooks, LttvTraceHook, hn++)); + + lttv_trace_find_hook(ts->parent.t, "core", "process_exit", NULL, NULL, + NULL, process_exit, &g_array_index(hooks, LttvTraceHook, hn++)); +#endif //0 + + /* after hooks */ + +#if 0 + /**** DESACTIVATED ****/ + lttv_trace_find_hook(ts->parent.t, "core","syscall_entry","syscall_id", + NULL, NULL, after_execmode_hook, &g_array_index(hooks, LttvTraceHook, hn++)); + /**** DESACTIVATED ****/ + lttv_trace_find_hook(ts->parent.t, "core", "syscall_exit", NULL, NULL, + NULL, after_execmode_hook, &g_array_index(hooks, LttvTraceHook, hn++)); + + /**** DESACTIVATED ****/ + lttv_trace_find_hook(ts->parent.t, "core", "trap_entry", "trap_id", + NULL, NULL, after_execmode_hook, &g_array_index(hooks, LttvTraceHook, hn++)); + + /**** DESACTIVATED ****/ + lttv_trace_find_hook(ts->parent.t, "core", "trap_exit", NULL, NULL, NULL, + after_execmode_hook, &g_array_index(hooks, LttvTraceHook, hn++)); + + /**** DESACTIVATED ****/ + lttv_trace_find_hook(ts->parent.t, "core", "irq_entry", "irq_id", NULL, + NULL, after_execmode_hook, &g_array_index(hooks, LttvTraceHook, hn++)); + + /**** DESACTIVATED ****/ + lttv_trace_find_hook(ts->parent.t, "core", "irq_exit", NULL, NULL, NULL, + after_execmode_hook, &g_array_index(hooks, LttvTraceHook, hn++)); +#endif //0 +#if 0 + lttv_trace_find_hook(ts->parent.t, "core", "schedchange", "in", "out", + "out_state", after_schedchange_hook, + &g_array_index(hooks, LttvTraceHook, hn++)); + + lttv_trace_find_hook(ts->parent.t, "core", "process", "event_sub_id", + "event_data1", "event_data2", after_process_hook, + &g_array_index(hooks, LttvTraceHook, hn++)); +#endif //0 + after_hn = before_hn; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_SCHED_SCHEDULE, + LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE, + after_schedchange_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, after_hn++)); + if(ret) after_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_FORK, + LTT_FIELD_PARENT_PID, LTT_FIELD_CHILD_PID, 0, + after_process_fork_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, after_hn++)); + if(ret) after_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_EXIT, + LTT_FIELD_PID, 0, 0, + after_process_exit_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, after_hn++)); + if(ret) after_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_FS, LTT_EVENT_EXEC, + 0, 0, 0, + after_fs_exec_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, after_hn++)); + if(ret) after_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_USER_GENERIC, LTT_EVENT_THREAD_BRAND, + LTT_FIELD_NAME, 0, 0, + after_user_generic_thread_brand_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, after_hn++)); + if(ret) after_hn--; + + ret = lttv_trace_find_hook(ts->parent.t, + LTT_FACILITY_LIST, LTT_EVENT_PROCESS_STATE, + LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME, + after_event_enum_process_hook, + events_request, + &g_array_index(hooks, LttvTraceHook, after_hn++)); + if(ret) after_hn--; + + hooks = g_array_set_size(hooks, after_hn); + +#if 0 + lttv_trace_find_hook(ts->parent.t, "core", "process_fork", "child_pid", + NULL, NULL, process_fork, &g_array_index(hooks, LttvTraceHook, hn++)); + + lttv_trace_find_hook(ts->parent.t, "core", "process_exit", NULL, NULL, + NULL, process_exit, &g_array_index(hooks, LttvTraceHook, hn++)); +#endif //0 + + + + /* Add these hooks to each event_by_id hooks list */ + /* add before */ + for(k = 0 ; k < before_hn ; k++) { + hook = &g_array_index(hooks, LttvTraceHook, k); + for(l=0;lfac_list->len;l++) { + thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l); + lttv_hooks_add(lttv_hooks_by_id_find(event_by_id, thf->id), + thf->h, + thf, + LTTV_PRIO_STATE-5); + } + } + + /* add after */ + for(k = before_hn ; k < after_hn ; k++) { + hook = &g_array_index(hooks, LttvTraceHook, k); + for(l=0;lfac_list->len;l++) { + thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l); + lttv_hooks_add(lttv_hooks_by_id_find(event_by_id, thf->id), + thf->h, + thf, + LTTV_PRIO_STATE+5); + } + } + + events_request->hooks = hooks; + + // Fill the events request + events_request->owner = control_flow_data; + events_request->viewer_data = control_flow_data; + events_request->servicing = FALSE; + events_request->start_time = start; + events_request->start_position = NULL; + events_request->stop_flag = FALSE; + events_request->end_time = time_end; + events_request->num_events = G_MAXUINT; + events_request->end_position = NULL; + events_request->trace = i; //fixed /* FIXME */ + events_request->before_chunk_traceset = before_chunk_traceset; + events_request->before_chunk_trace = NULL; + events_request->before_chunk_tracefile = NULL; + events_request->event = NULL; + events_request->event_by_id = event_by_id; + events_request->after_chunk_tracefile = NULL; + events_request->after_chunk_trace = NULL; + events_request->after_chunk_traceset = after_chunk_traceset; + events_request->before_request = before_request_hook; + events_request->after_request = after_request_hook; + + g_debug("req : start : %lu, %lu", start.tv_sec, + start.tv_nsec); + + g_debug("req : end : %lu, %lu", time_end.tv_sec, + time_end.tv_nsec); + + lttvwindow_events_request(tab, events_request); + + } + + } + +#if 0 + lttv_hooks_add(event, + before_schedchange_hook, + events_request, + LTTV_PRIO_STATE-5); + lttv_hooks_add(event, + after_schedchange_hook, + events_request, + LTTV_PRIO_STATE+5); + lttv_hooks_add(event, + before_execmode_hook, + events_request, + LTTV_PRIO_STATE-5); + lttv_hooks_add(event, + after_execmode_hook, + events_request, + LTTV_PRIO_STATE+5); + lttv_hooks_add(event, + before_process_hook, + events_request, + LTTV_PRIO_STATE-5); + lttv_hooks_add(event, + after_process_hook, + events_request, + LTTV_PRIO_STATE+5); +#endif //0 + +} + + +static void set_last_start(gpointer key, gpointer value, gpointer user_data) +{ + ProcessInfo *process_info = (ProcessInfo*)key; + HashedProcessData *hashed_process_data = (HashedProcessData*)value; + guint x = (guint)user_data; + + hashed_process_data->x.over = x; + hashed_process_data->x.over_used = FALSE; + hashed_process_data->x.over_marked = FALSE; + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + hashed_process_data->x.under = x; + hashed_process_data->x.under_used = FALSE; + hashed_process_data->x.under_marked = FALSE; + hashed_process_data->next_good_time = ltt_time_zero; + + return; +} + +void drawing_data_request_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of data request"); + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = LTTV_TRACESET_CONTEXT(tss); + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + guint width = cfd->drawing->width; + guint x=0; + + cfd->drawing->last_start = events_request->start_time; + + convert_time_to_pixels( + time_window, + events_request->start_time, + width, + &x); + + g_hash_table_foreach(cfd->process_list->process_hash, set_last_start, + (gpointer)x); + +} + +void drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss) +{ + g_debug("Begin of chunk"); + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = &tss->parent.parent; + //LttTime current_time = lttv_traceset_context_get_current_tfc(tsc)->timestamp; + guint i; + LttvTraceset *traceset = tsc->ts; + guint nb_trace = lttv_traceset_number(traceset); + + if(!cfd->process_list->current_hash_data) { + cfd->process_list->current_hash_data = g_new(HashedProcessData**,nb_trace); + for(i = 0 ; i < nb_trace ; i++) { + guint num_cpu = ltt_trace_get_num_cpu(tss->parent.traces[i]->t); + cfd->process_list->current_hash_data[i] = g_new(HashedProcessData*,num_cpu); + memset(cfd->process_list->current_hash_data[i], 0, + sizeof(HashedProcessData*)*num_cpu); + } + } + //cfd->drawing->last_start = LTT_TIME_MIN(current_time, + // events_request->end_time); +} + + +void drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time) +{ + gint x, width; + guint x_end; + + ControlFlowData *cfd = events_request->viewer_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + Drawing_t *drawing = cfd->drawing; + + TimeWindow time_window = + lttvwindow_get_time_window(cfd->tab); + + g_debug("request expose"); + + convert_time_to_pixels( + time_window, + end_time, + drawing->width, + &x_end); + x = drawing->damage_begin; + + width = x_end - x; + + drawing->damage_begin = x+width; + + // FIXME ? + gtk_widget_queue_draw_area ( drawing->drawing_area, + x, 0, + width, drawing->drawing_area->allocation.height); + + /* Update directly when scrolling */ + gdk_window_process_updates(drawing->drawing_area->window, + TRUE); +} + + +/* Callbacks */ + + +/* Create a new backing pixmap of the appropriate size */ +/* As the scaling will always change, it's of no use to copy old + * pixmap. + * + * Only change the size if width changes. The height is specified and changed + * when process ID are added or removed from the process list. + */ +static gboolean +configure_event( GtkWidget *widget, GdkEventConfigure *event, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + + /* First, get the new time interval of the main window */ + /* we assume (see documentation) that the main window + * has updated the time interval before this configure gets + * executed. + */ + //lttvwindow_get_time_window(drawing->control_flow_data->mw, + // &drawing->control_flow_data->time_window); + + /* New pixmap, size of the configure event */ + //GdkPixmap *pixmap = gdk_pixmap_new(widget->window, + // widget->allocation.width + SAFETY, + // widget->allocation.height + SAFETY, + // -1); + + if(widget->allocation.width != drawing->width) { + g_debug("drawing configure event"); + g_debug("New alloc draw size : %i by %i",widget->allocation.width, + widget->allocation.height); + + drawing->width = widget->allocation.width; + + if(drawing->alloc_width < widget->allocation.width) { + //if(drawing->pixmap) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = gdk_pixmap_new(widget->window, + // drawing->width + SAFETY + EXTRA_ALLOC, + // drawing->height + EXTRA_ALLOC, + // -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + EXTRA_ALLOC; + update_pixmap_size(drawing->control_flow_data->process_list, + drawing->alloc_width); + update_index_to_pixmap(drawing->control_flow_data->process_list); + } + //drawing->height = widget->allocation.height; + + //ProcessList_get_height + // (GuiControlFlow_get_process_list(drawing->control_flow_data)), + + + // Clear the image + //gdk_draw_rectangle (drawing->pixmap, + // widget->style->black_gc, + // TRUE, + // 0, 0, + // drawing->width+SAFETY, + // drawing->height); + + //g_info("init data request"); + + + /* Initial data request */ + /* no, do initial data request in the expose event */ + // Do not need to ask for data of 1 pixel : not synchronized with + // main window time at this moment. + //drawing_data_request(drawing, &drawing->pixmap, 0, 0, + // widget->allocation.width, + // widget->allocation.height); + + //drawing->width = widget->allocation.width; + //drawing->height = widget->allocation.height; + + drawing->damage_begin = 0; + drawing->damage_end = widget->allocation.width; + + if((widget->allocation.width != 1 && + widget->allocation.height != 1) + && drawing->damage_begin < drawing->damage_end) + { + + rectangle_pixmap (drawing->control_flow_data->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1); + + + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end - drawing->damage_begin, + drawing->height); + } + } + return TRUE; +} + + +/* Redraw the screen from the backing pixmap */ +static gboolean +expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "control_flow_data"); +#if 0 + if(unlikely(drawing->gc == NULL)) { + drawing->gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->gc, drawing->drawing_area->style->black_gc); + } +#endif //0 + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + LttTime current_time = + lttvwindow_get_current_time(control_flow_data->tab); + + guint cursor_x=0; + + LttTime window_end = time_window.end_time; + + /* update the screen from the pixmap buffer */ +#if 0 + gdk_draw_pixmap(widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + drawing->pixmap, + event->area.x, event->area.y, + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + drawing->height = processlist_get_height(control_flow_data->process_list); +#if 0 + copy_pixmap_to_screen(control_flow_data->process_list, + widget->window, + widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + event->area.x, event->area.y, + event->area.width, event->area.height); +#endif //0 + copy_pixmap_to_screen(control_flow_data->process_list, + widget->window, + drawing->gc, + event->area.x, event->area.y, + event->area.width, event->area.height); + + + /* Erase the dotted lines left.. */ + if(widget->allocation.height > drawing->height) + { + gdk_draw_rectangle (widget->window, + drawing->drawing_area->style->black_gc, + TRUE, + event->area.x, drawing->height, + event->area.width, // do not overlap + widget->allocation.height - drawing->height); + } + if(ltt_time_compare(time_window.start_time, current_time) <= 0 && + ltt_time_compare(window_end, current_time) >= 0) + { + /* Draw the dotted lines */ + convert_time_to_pixels( + time_window, + current_time, + drawing->width, + &cursor_x); + +#if 0 + if(drawing->dotted_gc == NULL) { + + drawing->dotted_gc = gdk_gc_new(drawing->drawing_area->window); + gdk_gc_copy(drawing->dotted_gc, widget->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + } +#endif //0 + gint height_tot = MAX(widget->allocation.height, drawing->height); + gdk_draw_line(widget->window, + drawing->dotted_gc, + cursor_x, 0, + cursor_x, height_tot); + } + return FALSE; +} + +static gboolean +after_expose_event( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + //g_assert(0); + g_debug("AFTER EXPOSE"); + + return FALSE; + + +} + +#if 0 +void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data) +{ + ControlFlowData *cfd = (ControlFlowData*)user_data; + Drawing_t *drawing = cfd->drawing; + GtkTreeView *treeview = cfd->process_list->process_list_widget; + gint *path_indices; + gint height; + + path_indices = gtk_tree_path_get_indices (arg1); + + height = get_cell_height(cfd->process_list, + GTK_TREE_VIEW(treeview)); + drawing->horizontal_sel = height * path_indices[0]; + g_critical("new hor sel : %i", drawing->horizontal_sel); +} +#endif //0 + +/* mouse click */ +static gboolean +button_press_event( GtkWidget *widget, GdkEventButton *event, gpointer user_data ) +{ + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "control_flow_data"); + Drawing_t *drawing = control_flow_data->drawing; + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + + g_debug("click"); + if(event->button == 1) + { + LttTime time; + + /* left mouse button click */ + g_debug("x click is : %f", event->x); + + convert_pixels_to_time(drawing->width, (guint)event->x, + time_window, + &time); + + lttvwindow_report_current_time(control_flow_data->tab, time); + + } + + return FALSE; +} + +static gboolean +scrollbar_size_allocate(GtkWidget *widget, + GtkAllocation *allocation, + gpointer user_data) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + + gtk_widget_set_size_request(drawing->padding, allocation->width, -1); + //gtk_widget_queue_resize(drawing->padding); + //gtk_widget_queue_resize(drawing->ruler); + gtk_container_check_resize(GTK_CONTAINER(drawing->ruler_hbox)); + return 0; +} + + + +Drawing_t *drawing_construct(ControlFlowData *control_flow_data) +{ + Drawing_t *drawing = g_new(Drawing_t, 1); + + drawing->control_flow_data = control_flow_data; + + drawing->vbox = gtk_vbox_new(FALSE, 1); + + + drawing->ruler_hbox = gtk_hbox_new(FALSE, 1); + drawing->ruler = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->ruler, -1, 27); + + drawing->padding = gtk_drawing_area_new (); + //gtk_widget_set_size_request(drawing->padding, -1, 27); + gtk_box_pack_start(GTK_BOX(drawing->ruler_hbox), drawing->ruler, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->ruler_hbox), drawing->padding, + FALSE, FALSE, 0); + + + + drawing->drawing_area = gtk_drawing_area_new (); + + drawing->gc = NULL; + + drawing->hbox = gtk_hbox_new(FALSE, 1); + drawing->viewport = gtk_viewport_new(NULL, control_flow_data->v_adjust); + drawing->scrollbar = gtk_vscrollbar_new(control_flow_data->v_adjust); + gtk_box_pack_start(GTK_BOX(drawing->hbox), drawing->viewport, + TRUE, TRUE, 0); + gtk_box_pack_end(GTK_BOX(drawing->hbox), drawing->scrollbar, + FALSE, FALSE, 0); + + //drawing->scrolled_window = + // gtk_scrolled_window_new (NULL, + // control_flow_data->v_adjust); + + //gtk_scrolled_window_set_policy( + // GTK_SCROLLED_WINDOW(drawing->scrolled_window), + // GTK_POLICY_NEVER, + // GTK_POLICY_AUTOMATIC); + + gtk_container_add(GTK_CONTAINER(drawing->viewport), + drawing->drawing_area); + //gtk_scrolled_window_add_with_viewport( + // GTK_SCROLLED_WINDOW(drawing->scrolled_window), + // drawing->drawing_area); + + gtk_box_pack_start(GTK_BOX(drawing->vbox), drawing->ruler_hbox, + FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(drawing->vbox), drawing->hbox, + TRUE, TRUE, 0); + + drawing->pango_layout = + gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + drawing->height = 1; + drawing->width = 1; + drawing->depth = 0; + drawing->alloc_height = 1; + drawing->alloc_width = 1; + + drawing->damage_begin = 0; + drawing->damage_end = 0; + drawing->horizontal_sel = -1; + + //gtk_widget_set_size_request(drawing->drawing_area->window, 50, 50); + g_object_set_data_full( + G_OBJECT(drawing->drawing_area), + "Link_drawing_Data", + drawing, + (GDestroyNotify)drawing_destroy); + + g_object_set_data( + G_OBJECT(drawing->ruler), + "drawing", + drawing); + + + //gtk_widget_modify_bg( drawing->drawing_area, + // GTK_STATE_NORMAL, + // &CF_Colors[BLACK]); + + //gdk_window_get_geometry(drawing->drawing_area->window, + // NULL, NULL, + // &(drawing->width), + // &(drawing->height), + // -1); + + //drawing->pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width, + // drawing->height, + // drawing->depth); + + //drawing->pixmap = NULL; + +// drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, +// drawing->drawing_area->allocation.width, +// drawing->drawing_area->allocation.height, +// -1); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "configure_event", + G_CALLBACK (configure_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->ruler), + "expose_event", + G_CALLBACK(expose_ruler), + (gpointer)drawing); + + gtk_widget_add_events(drawing->ruler, GDK_POINTER_MOTION_MASK); + + g_signal_connect (G_OBJECT(drawing->ruler), + "motion-notify-event", + G_CALLBACK(motion_notify_ruler), + (gpointer)drawing); + + + g_signal_connect (G_OBJECT(drawing->scrollbar), + "size-allocate", + G_CALLBACK(scrollbar_size_allocate), + (gpointer)drawing); + + + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (expose_event), + (gpointer)drawing); + + g_signal_connect_after (G_OBJECT(drawing->drawing_area), + "expose_event", + G_CALLBACK (after_expose_event), + (gpointer)drawing); + + g_signal_connect (G_OBJECT(drawing->drawing_area), + "button-press-event", + G_CALLBACK (button_press_event), + (gpointer)drawing); + + + gtk_widget_show(drawing->ruler); + gtk_widget_show(drawing->padding); + gtk_widget_show(drawing->ruler_hbox); + + gtk_widget_show(drawing->drawing_area); + //gtk_widget_show(drawing->scrolled_window); + gtk_widget_show(drawing->viewport); + gtk_widget_show(drawing->scrollbar); + gtk_widget_show(drawing->hbox); + + /* Allocate the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + gboolean success[NUM_COLORS]; + gdk_colormap_alloc_colors(colormap, drawing_colors, NUM_COLORS, FALSE, + TRUE, success); + + drawing->gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + drawing->dotted_gc = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + + gdk_gc_copy(drawing->gc, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + gdk_gc_copy(drawing->dotted_gc, + main_window_get_widget(control_flow_data->tab)->style->white_gc); + + gint8 dash_list[] = { 1, 2 }; + gdk_gc_set_line_attributes(drawing->dotted_gc, + 1, + GDK_LINE_ON_OFF_DASH, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + gdk_gc_set_dashes(drawing->dotted_gc, + 0, + dash_list, + 2); + + drawing->ruler_gc_butt = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_butt, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + drawing->ruler_gc_round = + gdk_gc_new(GDK_DRAWABLE(main_window_get_widget(control_flow_data->tab)->window)); + gdk_gc_copy(drawing->ruler_gc_round, + main_window_get_widget(control_flow_data->tab)->style->black_gc); + + + gdk_gc_set_line_attributes(drawing->ruler_gc_butt, + 2, + GDK_LINE_SOLID, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + + gdk_gc_set_line_attributes(drawing->ruler_gc_round, + 2, + GDK_LINE_SOLID, + GDK_CAP_ROUND, + GDK_JOIN_ROUND); + + + return drawing; +} + +void drawing_destroy(Drawing_t *drawing) +{ + g_info("drawing_destroy %p", drawing); + + /* Free the colors */ + GdkColormap* colormap = gdk_colormap_get_system(); + + gdk_colormap_free_colors(colormap, drawing_colors, NUM_COLORS); + + + + // Do not unref here, Drawing_t destroyed by it's widget. + //g_object_unref( G_OBJECT(drawing->drawing_area)); + if(drawing->gc != NULL) + gdk_gc_unref(drawing->gc); + + g_object_unref(drawing->pango_layout); + if(drawing->dotted_gc != NULL) gdk_gc_unref(drawing->dotted_gc); + if(drawing->ruler_gc_butt != NULL) gdk_gc_unref(drawing->ruler_gc_butt); + if(drawing->ruler_gc_round != NULL) gdk_gc_unref(drawing->ruler_gc_round); + + g_free(drawing); + g_info("drawing_destroy end"); +} + +GtkWidget *drawing_get_drawing_area(Drawing_t *drawing) +{ + return drawing->drawing_area; +} + +GtkWidget *drawing_get_widget(Drawing_t *drawing) +{ + return drawing->vbox; +} + +void drawing_draw_line( Drawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC) +{ + gdk_draw_line (pixmap, + GC, + x1, y1, x2, y2); +} + +void drawing_clear(Drawing_t *drawing) +{ + //if (drawing->pixmap) + // gdk_pixmap_unref(drawing->pixmap); + ControlFlowData *cfd = drawing->control_flow_data; + + + rectangle_pixmap(cfd->process_list, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, // do not overlap + -1); + + //drawing->height = 1; + /* Allocate a new pixmap with new height */ + //drawing->pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY + EXTRA_ALLOC, + // drawing->height + EXTRA_ALLOC, + // -1); + //drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + //drawing->alloc_height = drawing->height + EXTRA_ALLOC; + + //gtk_widget_set_size_request(drawing->drawing_area, + // -1, + // drawing->height); + //gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw ( drawing->drawing_area); +} + +#if 0 +/* Insert a square corresponding to a new process in the list */ +/* Applies to whole drawing->width */ +void drawing_insert_square(Drawing_t *drawing, + guint y, + guint height) +{ + //GdkRectangle update_rect; + gboolean reallocate = FALSE; + GdkPixmap *new_pixmap; + + /* Allocate a new pixmap with new height */ + if(drawing->alloc_height < drawing->height + height) { + + new_pixmap = gdk_pixmap_new(drawing->drawing_area->window, + drawing->width + SAFETY + EXTRA_ALLOC, + drawing->height + height + EXTRA_ALLOC, + -1); + drawing->alloc_width = drawing->width + SAFETY + EXTRA_ALLOC; + drawing->alloc_height = drawing->height + height + EXTRA_ALLOC; + reallocate = TRUE; + + /* Copy the high region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + } else { + new_pixmap = drawing->pixmap; + } + + //GdkPixmap *pixmap = gdk_pixmap_new(drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height + height, + // -1); + + /* add an empty square */ + gdk_draw_rectangle (new_pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, y, + drawing->width + SAFETY, // do not overlap + height); + + /* copy the bottom of the region */ + gdk_draw_pixmap (new_pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y, + 0, y + height, + drawing->width+SAFETY, drawing->height - y); + + + if(reallocate && likely(drawing->pixmap)) { + gdk_pixmap_unref(drawing->pixmap); + drawing->pixmap = new_pixmap; + } + + if(unlikely(drawing->height==1)) drawing->height = height; + else drawing->height += height; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, drawing->height-y); +} + + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(Drawing_t *drawing, + guint y, + guint height) +{ + GdkPixmap *pixmap; + + if(unlikely((guint)drawing->height == height)) { + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // 1, + // -1); + pixmap = drawing->pixmap; + drawing->height=1; + } else { + /* Allocate a new pixmap with new height */ + //pixmap = gdk_pixmap_new( + // drawing->drawing_area->window, + // drawing->width + SAFETY, + // drawing->height - height, + // -1); + /* Keep the same preallocated pixmap */ + pixmap = drawing->pixmap; + + /* Copy the high region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, 0, + 0, 0, + drawing->width + SAFETY, y); + + /* Copy up the bottom of the region */ + gdk_draw_pixmap (pixmap, + drawing->drawing_area->style->black_gc, + drawing->pixmap, + 0, y + height, + 0, y, + drawing->width, drawing->height - y - height); + + drawing->height-=height; + } + + //if(likely(drawing->pixmap)) + // gdk_pixmap_unref(drawing->pixmap); + + //drawing->pixmap = pixmap; + + gtk_widget_set_size_request(drawing->drawing_area, + -1, + drawing->height); + gtk_widget_queue_resize_no_redraw(drawing->drawing_area); + /* ask for the buffer to be redrawn */ + gtk_widget_queue_draw_area ( drawing->drawing_area, + 0, y, + drawing->width, MAX(drawing->height-y, 1)); +} +#endif //0 + +void drawing_update_ruler(Drawing_t *drawing, TimeWindow *time_window) +{ + GtkRequisition req; + GdkRectangle rect; + + req.width = drawing->ruler->allocation.width; + req.height = drawing->ruler->allocation.height; + + + rect.x = 0; + rect.y = 0; + rect.width = req.width; + rect.height = req.height; + + gtk_widget_queue_draw(drawing->ruler); + //gtk_widget_draw( drawing->ruler, &rect); +} + +/* Redraw the ruler */ +static gboolean +expose_ruler( GtkWidget *widget, GdkEventExpose *event, gpointer user_data ) +{ + Drawing_t *drawing = (Drawing_t*)user_data; + TimeWindow time_window = lttvwindow_get_time_window(drawing->control_flow_data->tab); + gchar text[255]; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc; + PangoRectangle ink_rect; + gint global_width=0; + GdkColor foreground = { 0, 0, 0, 0 }; + GdkColor background = { 0, 0xffff, 0xffff, 0xffff }; + + LttTime window_end = time_window.end_time; + LttTime half_width = + ltt_time_div(time_window.time_width,2.0); + LttTime window_middle = + ltt_time_add(half_width, + time_window.start_time); + g_debug("ruler expose event"); + + gdk_draw_rectangle (drawing->ruler->window, + drawing->ruler->style->white_gc, + TRUE, + event->area.x, event->area.y, + event->area.width, + event->area.height); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + event->area.x, 1, + event->area.x + event->area.width, 1); + + + snprintf(text, 255, "%lus\n%luns", + time_window.start_time.tv_sec, + time_window.start_time.tv_nsec); + + layout = gtk_widget_create_pango_layout(drawing->drawing_area, NULL); + + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + + pango_font_description_set_size(FontDesc, 6*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + 0, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_round, + 1, 1, + 1, 7); + + + snprintf(text, 255, "%lus\n%luns", window_end.tv_sec, + window_end.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width - ink_rect.width, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width-1, 1, + drawing->ruler->allocation.width-1, 7); + } + + + snprintf(text, 255, "%lus\n%luns", window_middle.tv_sec, + window_middle.tv_nsec); + + pango_layout_set_text(layout, text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + global_width += ink_rect.width; + + if(global_width <= drawing->ruler->allocation.width) + { + gdk_draw_layout_with_colors(drawing->ruler->window, + drawing->ruler_gc_butt, + (drawing->ruler->allocation.width - ink_rect.width)/2, + 6, + layout, &foreground, &background); + + gdk_draw_line (drawing->ruler->window, + drawing->ruler_gc_butt, + drawing->ruler->allocation.width/2, 1, + drawing->ruler->allocation.width/2, 7); + + + + + } + + g_object_unref(layout); + + return FALSE; +} + + +/* notify mouse on ruler */ +static gboolean +motion_notify_ruler(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) +{ + //g_debug("motion"); + //eventually follow mouse and show time here + return 0; +} diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/drawing.h b/ltt/branches/poly/lttv/modules/gui/resourceview/drawing.h new file mode 100644 index 00000000..58f562a2 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/drawing.h @@ -0,0 +1,221 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + +#ifndef _DRAWING_H +#define _DRAWING_H + +#include +#include +#include +#include +#include +#include +#include +#include "cfv.h" +#include "drawitem.h" + + +#define SAFETY 50 // safety pixels at right and bottom of pixmap buffer + +typedef enum _draw_color { + COL_BLACK, + COL_WHITE, + COL_RUN_USER_MODE,/* green */ + COL_RUN_SYSCALL, /* pale blue */ + COL_RUN_TRAP, /* yellow */ + COL_RUN_IRQ, /* orange */ + COL_RUN_SOFT_IRQ, /* red */ + COL_WAIT, /* dark red */ + COL_WAIT_CPU, /* dark yellow */ + COL_ZOMBIE, /* dark purple */ + COL_WAIT_FORK, /* dark green */ + COL_EXIT, /* "less dark" magenta */ + COL_MODE_UNKNOWN, /* white */ + COL_UNNAMED, /* white */ + NUM_COLORS } draw_color; + +extern GdkColor drawing_colors[NUM_COLORS]; + +/* This part of the viewer does : + * Draw horizontal lines, getting graphic context as arg. + * Copy region of the screen into another. + * Modify the boundaries to reflect a scale change. (resize) + * Refresh the physical screen with the pixmap + * A helper function is provided here to convert from time to process + * identifier to pixels and the contrary (will be useful for mouse selection). + * Insert an empty square in the drawing, moving the bottom part. + * + * Note: The last point is exactly why it would not be so easy to add the + * vertical line functionnality as in the original version of LTT. In order + * to do so, we should keep all processes in the list for the duration of + * all the trace instead of dynamically adding and removing them when we + * scroll. Another possibility is to redraw all the visible area when a new + * process is added to the list. The second solution seems more appropriate + * to me. + * + * + * The pixmap used has the width of the physical window, but the height + * of the shown processes. + */ + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +#ifndef TYPE_CONTROLFLOWDATA_DEFINED +#define TYPE_CONTROLFLOWDATA_DEFINED +typedef struct _ControlFlowData ControlFlowData; +#endif //TYPE_CONTROLFLOWDATA_DEFINED + +struct _Drawing_t { + GtkWidget *vbox; + GtkWidget *drawing_area; + //GtkWidget *scrolled_window; + GtkWidget *hbox; + GtkWidget *viewport; + GtkWidget *scrollbar; + + GtkWidget *ruler_hbox; + GtkWidget *ruler; + GtkWidget *padding; + //GdkPixmap *pixmap; + ControlFlowData *control_flow_data; + + PangoLayout *pango_layout; + + gint height, width, depth; + /* height and width of allocated buffer pixmap */ + gint alloc_height, alloc_width; + + /* X coordinate of damaged region */ + gint damage_begin, damage_end; /* damaged region to be exposed, + updated per chunk */ + LttTime last_start; + GdkGC *dotted_gc; + GdkGC *gc; + GdkGC *ruler_gc_butt; + GdkGC *ruler_gc_round; + + /* Position of the horizontal selector, -1 for none */ + gint horizontal_sel; +}; + +Drawing_t *drawing_construct(ControlFlowData *control_flow_data); +void drawing_destroy(Drawing_t *drawing); + +GtkWidget *drawing_get_widget(Drawing_t *drawing); +GtkWidget *drawing_get_drawing_area(Drawing_t *drawing); + + +void drawing_data_request(Drawing_t *drawing, + gint x, gint y, + gint width, + gint height); + +void drawing_draw_line( Drawing_t *drawing, + GdkPixmap *pixmap, + guint x1, guint y1, + guint x2, guint y2, + GdkGC *GC); + +//void drawing_copy( Drawing_t *drawing, +// guint xsrc, guint ysrc, +// guint xdest, guint ydest, +// guint width, guint height); + +/* Clear the drawing : make it 1xwidth. */ +void drawing_clear(Drawing_t *drawing); + +/* Insert a square corresponding to a new process in the list */ +void drawing_insert_square(Drawing_t *drawing, + guint y, + guint height); + +/* Remove a square corresponding to a removed process in the list */ +void drawing_remove_square(Drawing_t *drawing, + guint y, + guint height); + +void drawing_update_ruler(Drawing_t *drawing, TimeWindow *time_window); + +void drawing_request_expose(EventsRequest *events_request, + LttvTracesetState *tss, + LttTime end_time); + +void drawing_data_request_begin(EventsRequest *events_request, + LttvTracesetState *tss); +void drawing_chunk_begin(EventsRequest *events_request, LttvTracesetState *tss); + + + +void +tree_row_activated(GtkTreeModel *treemodel, + GtkTreePath *arg1, + GtkTreeViewColumn *arg2, + gpointer user_data); + + +/* convert_pixels_to_time + * + * Convert from window pixel and time interval to an absolute time. + */ +static inline void convert_pixels_to_time( + gint width, + guint x, + TimeWindow time_window, + LttTime *time) +{ + double time_d; + + time_d = time_window.time_width_double; + time_d = time_d / (double)width * (double)x; + *time = ltt_time_from_double(time_d); + *time = ltt_time_add(time_window.start_time, *time); +} + + +static inline void convert_time_to_pixels( + TimeWindow time_window, + LttTime time, + int width, + guint *x) +{ + double time_d; +#ifdef EXTRA_CHECK + g_assert(ltt_time_compare(window_time_begin, time) <= 0 && + ltt_time_compare(window_time_end, time) >= 0); +#endif //EXTRA_CHECK + + time = ltt_time_sub(time, time_window.start_time); + + time_d = ltt_time_to_double(time); + + if(time_window.time_width_double == 0.0) { + g_assert(time_d == 0.0); + *x = 0; + } else { + *x = (guint)(time_d / time_window.time_width_double * (double)width); + } + +} + + + +#endif // _DRAWING_H diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/drawitem.c b/ltt/branches/poly/lttv/modules/gui/resourceview/drawitem.c new file mode 100644 index 00000000..d97628b0 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/drawitem.c @@ -0,0 +1,465 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + + +/****************************************************************************** + * drawitem.c + * + * This file contains methods responsible for drawing a generic type of data + * in a drawable. Doing this generically will permit user defined drawing + * behavior in a later time. + * + * This file provides an API which is meant to be reusable for all viewers that + * need to show information in line, icon, text, background or point form in + * a drawable area having time for x axis. The y axis, in the control flow + * viewer case, is corresponding to the different processes, but it can be + * reused integrally for cpu, and eventually locks, buffers, network + * interfaces... What will differ between the viewers is the precise + * information which interests us. We may think that the most useful + * information for control flow are some specific events, like schedule + * change, and processes'states. It may differ for a cpu viewer : the + * interesting information could be more the execution mode of each cpu. + * This API in meant to make viewer's writers life easier : it will become + * a simple choice of icons and line types for the precise information + * the viewer has to provide (agremented with keeping supplementary records + * and modifying slightly the DrawContext to suit the needs.) + * + * We keep each data type in attributes, keys to specific information + * being formed from the GQuark corresponding to the information received. + * (facilities / facility_name / events / eventname.) + * (cpus/cpu_name, process_states/ps_name, + * execution_modes/em_name, execution_submodes/es_name). + * The goal is then to provide a generic way to print information on the + * screen for all this different information. + * + * Information can be printed as + * + * - text (text + color + size + position (over or under line) + * - icon (icon filename, corresponding to a loaded icon, accessible through + * a GQuark. Icons are loaded statically at the guiControlFlow level during + * module initialization and can be added on the fly if not present in the + * GQuark.) The habitual place for xpm icons is in + * ${prefix}/share/LinuxTraceToolkit.) + position (over or under line) + * - line (color, width, style) + * - Arc (big points) (color, size) + * - background color (color) + * + * An item is a leaf of the attributes tree. It is, in that case, including + * all kind of events categories we can have. It then associates each category + * with one or more actions (drawing something) or nothing. + * + * Each item has an array of hooks (hook list). Each hook represents an + * operation to perform. We seek the array each time we want to + * draw an item. We execute each operation in order. An operation type + * is associated with each hook to permit user listing and modification + * of these operations. The operation type is also used to find the + * corresponding priority for the sorting. Operation type and priorities + * are enum and a static int table. + * + * The array has to be sorted by priority each time we add a task in it. + * A priority is associated with each operation type. It permits + * to perform background color selection before line or text drawing. We also + * draw lines before text, so the text appears over the lines. + * + * Executing all the arrays of operations for a specific event (which + * implies information for state, event, cpu, execution mode and submode) + * has to be done in a same DrawContext. The goal there is to keep the offset + * of the text and icons over and under the middle line, so a specific + * event could be printed as ( R Si 0 for running, scheduled in, cpu 0 ), + * text being easy to replace with icons. The DrawContext is passed as + * call_data for the operation hooks. + * + * We use the lttv global attributes to keep track of the loaded icons. + * If we need an icon, we look for it in the icons / icon name pathname. + * If found, we use the pointer to it. If not, we load the pixmap in + * memory and set the pointer to the GdkPixmap in the attributes. The + * structure pointed to contains the pixmap and the mask bitmap. + * + * Author : Mathieu Desnoyers, October 2003 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "drawing.h" +#include "drawitem.h" + + +#define MAX_PATH_LEN 256 + +/* drawing hook functions */ +gboolean draw_text( void *hook_data, void *call_data) +{ + PropertiesText *properties = (PropertiesText*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *font_desc;// = pango_font_description_new(); + PangoRectangle ink_rect; + + layout = draw_context->pango_layout; + + context = pango_layout_get_context(layout); + font_desc = pango_context_get_font_description(context); + + pango_font_description_set_size(font_desc, properties->size*PANGO_SCALE); + pango_layout_context_changed(layout); + + pango_layout_set_text(layout, properties->text, -1); + pango_layout_get_pixel_extents(layout, &ink_rect, NULL); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = ink_rect.width; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_layout_with_colors(draw_context->drawable, + draw_context->gc, + x, + y, + layout, properties->foreground, properties->background); + + return 0; +} + + +/* To speed up the process, search in already loaded icons list first. Only + * load it if not present. + */ +gboolean draw_icon( void *hook_data, void *call_data) +{ + PropertiesIcon *properties = (PropertiesIcon*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); + LttvAttributeValue value; + gchar icon_name[MAX_PATH_LEN] = "icons/"; + IconStruct *icon_info; + + strcat(icon_name, properties->icon_name); + + g_assert(lttv_iattribute_find_by_path(attributes, icon_name, + LTTV_POINTER, &value)); + if(unlikely(*(value.v_pointer) == NULL)) + { + *(value.v_pointer) = icon_info = g_new(IconStruct,1); + + icon_info->pixmap = gdk_pixmap_create_from_xpm(draw_context->drawable, + &icon_info->mask, NULL, properties->icon_name); + } + else + { + icon_info = *(value.v_pointer); + } + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->width; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) { + gdk_gc_set_clip_mask(draw_context->gc, icon_info->mask); + + gdk_gc_set_clip_origin( + draw_context->gc, + x, + y); + gdk_draw_drawable(draw_context->drawable, + draw_context->gc, + icon_info->pixmap, + 0, 0, + x, + y, + properties->width, properties->height); + + gdk_gc_set_clip_origin(draw_context->gc, 0, 0); + gdk_gc_set_clip_mask(draw_context->gc, NULL); + } + return 0; +} + +gboolean draw_line( void *hook_data, void *call_data) +{ + PropertiesLine *properties = (PropertiesLine*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, &properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, &properties->color); + gdk_gc_set_line_attributes( draw_context->gc, + properties->line_width, + properties->style, + GDK_CAP_BUTT, + GDK_JOIN_MITER); + //g_critical("DRAWING LINE : x1: %i, y1: %i, x2:%i, y2:%i", + // draw_context->previous->middle->x, + // draw_context->previous->middle->y, + // draw_context->drawinfo.middle.x, + // draw_context->drawinfo.middle.y); + + gint x_begin=0, x_end=0, y=0; + + x_begin = draw_context->drawinfo.start.x; + x_end = draw_context->drawinfo.end.x; + + switch(properties->y) { + case OVER: + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + y = draw_context->drawinfo.y.under; + break; + } + + drawing_draw_line( + NULL, draw_context->drawable, + x_begin, + y, + x_end, + y, + draw_context->gc); + + return 0; +} + +gboolean draw_arc( void *hook_data, void *call_data) +{ + PropertiesArc *properties = (PropertiesArc*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + gint x=0, y=0; + gint *offset=NULL; + gboolean enough_space = FALSE; + gint width = properties->size; + + switch(properties->position.x) { + case POS_START: + x = draw_context->drawinfo.start.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.start.offset.over; + x += draw_context->drawinfo.start.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.start.offset.middle; + x += draw_context->drawinfo.start.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.start.offset.under; + x += draw_context->drawinfo.start.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x + width <= draw_context->drawinfo.end.x)) { + enough_space = TRUE; + *offset += width; + } + break; + case POS_END: + x = draw_context->drawinfo.end.x; + switch(properties->position.y) { + case OVER: + offset = &draw_context->drawinfo.end.offset.over; + x += draw_context->drawinfo.end.offset.over; + y = draw_context->drawinfo.y.over; + break; + case MIDDLE: + offset = &draw_context->drawinfo.end.offset.middle; + x += draw_context->drawinfo.end.offset.middle; + y = draw_context->drawinfo.y.middle; + break; + case UNDER: + offset = &draw_context->drawinfo.end.offset.under; + x += draw_context->drawinfo.end.offset.under; + y = draw_context->drawinfo.y.under; + break; + } + /* verify if there is enough space to draw */ + if(unlikely(x - width >= draw_context->drawinfo.start.x)) { + enough_space = TRUE; + *offset -= width; + } + break; + } + + if(unlikely(enough_space)) + gdk_draw_arc(draw_context->drawable, draw_context->gc, + properties->filled, + x, + y, + properties->size, properties->size, 0, 360*64); + + return 0; +} + +gboolean draw_bg( void *hook_data, void *call_data) +{ + PropertiesBG *properties = (PropertiesBG*)hook_data; + DrawContext *draw_context = (DrawContext*)call_data; + + gdk_gc_set_foreground(draw_context->gc, properties->color); + //gdk_gc_set_rgb_fg_color(draw_context->gc, properties->color); + + //g_critical("DRAWING RECT : x: %i, y: %i, w:%i, h:%i, val1 :%i, val2:%i ", + // draw_context->previous->over->x, + // draw_context->previous->over->y, + // draw_context->drawinfo.over.x - draw_context->previous->over->x, + // draw_context->previous->under->y-draw_context->previous->over->y, + // draw_context->drawinfo.over.x, + // draw_context->previous->over->x); + gdk_draw_rectangle(draw_context->drawable, draw_context->gc, + TRUE, + draw_context->drawinfo.start.x, + draw_context->drawinfo.y.over, + draw_context->drawinfo.end.x - draw_context->drawinfo.start.x, + draw_context->drawinfo.y.under - draw_context->drawinfo.y.over); + + return 0; +} + + diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/drawitem.h b/ltt/branches/poly/lttv/modules/gui/resourceview/drawitem.h new file mode 100644 index 00000000..28fdc183 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/drawitem.h @@ -0,0 +1,279 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + +#ifndef _DRAW_ITEM_H +#define _DRAW_ITEM_H + +#include + +typedef struct _DrawContext DrawContext; +typedef struct _DrawInfo DrawInfo; +typedef struct _ItemInfo ItemInfo; + +typedef struct _IconStruct IconStruct; + +typedef struct _DrawOperation DrawOperation; + + +typedef struct _PropertiesText PropertiesText; +typedef struct _PropertiesIcon PropertiesIcon; +typedef struct _PropertiesLine PropertiesLine; +typedef struct _PropertiesArc PropertiesArc; +typedef struct _PropertiesBG PropertiesBG; + +typedef enum _DrawableItems DrawableItems; +enum _DrawableItems { + ITEM_TEXT, ITEM_ICON, ITEM_LINE, ITEM_POINT, ITEM_BACKGROUND +}; + +typedef enum _RelPosX { + POS_START, POS_END +} RelPosX; + +typedef enum _RelPosY { + OVER, MIDDLE, UNDER +} RelPosY; + + +/* The DrawContext keeps information about the current drawing position and + * the previous one, so we can use both to draw lines. + * + * over : position for drawing over the middle line. + * middle : middle line position. + * under : position for drawing under the middle line. + * + * the modify_* are used to take into account that we should go forward + * when we draw a text, an arc or an icon, while it's unneeded when we + * draw a line or background. + * + * The modify_* positions are altered by the draw item functions. + * + */ + + +struct _DrawContext { + GdkDrawable *drawable; + GdkGC *gc; + PangoLayout *pango_layout; + + struct { + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } start; + + struct { + gint x; + struct { + gint over; + gint middle; + gint under; + } offset; + } end; + + struct { + gint over; + gint middle; + gint under; + } y; + + } drawinfo; +}; + + + + +/* + * Structure used to keep information about icons. + */ +struct _IconStruct { + GdkPixmap *pixmap; + GdkBitmap *mask; +}; + + +/* + * The Item element is only used so the DrawOperation is modifiable by users. + * During drawing, only the Hook is needed. + */ +struct _DrawOperation { + DrawableItems item; + LttvHooks *hook; +}; +#if 0 +/* + * We define here each items that can be drawn, together with their + * associated priority. Many item types can have the same priority, + * it's only used for quicksorting the operations when we add a new one + * to the array of operations to perform. Lower priorities are executed + * first. So, for example, we may want to give background color a value + * of 10 while a line would have 20, so the background color, which + * is in fact a rectangle, does not hide the line. + */ + +static int Items_Priorities[] = { + 50, /* ITEM_TEXT */ + 40, /* ITEM_ICON */ + 20, /* ITEM_LINE */ + 30, /* ITEM_POINT */ + 10 /* ITEM_BACKGROUND */ +}; +#endif //0 + +/* + * Here are the different structures describing each item type that can be + * drawn. They contain the information necessary to draw the item : not the + * position (this is provided by the DrawContext), but the text, icon name, + * line width, color; all the properties of the specific items. + */ + +struct _PropertiesText { + GdkColor *foreground; + GdkColor *background; + gint size; + gchar *text; + struct { + RelPosX x; + RelPosY y; + } position; +}; + + +struct _PropertiesIcon { + gchar *icon_name; + gint width; + gint height; + struct { + RelPosX x; + RelPosY y; + } position; +}; + +struct _PropertiesLine { + GdkColor color; + gint line_width; + GdkLineStyle style; + RelPosY y; +}; + +struct _PropertiesArc { + GdkColor *color; + gint size; /* We force circle by width = height */ + gboolean filled; + struct { + RelPosX x; + RelPosY y; + } position; +}; + +struct _PropertiesBG { + GdkColor *color; +}; + + + +void draw_item( GdkDrawable *drawable, + gint x, + gint y, + LttvTraceState *ts, + LttvTracefileState *tfs, + LttvIAttribute *attributes); + +/* + * The tree of attributes used to store drawing operations goes like this : + * + * event_types/ + * "facility-event_type" + * cpus/ + * "cpu name" + * mode_types/ + * "execution mode"/ + * submodes/ + * "submode" + * process_states/ + * "state name" + * + * So if, for example, we want to add a hook to get called each time we + * receive an event that is in state LTTV_STATE_SYSCALL, we put the + * pointer to the GArray of DrawOperation in + * process_states/ "name associated with LTTV_STATE_SYSCALL" + */ + + +#if 0 +/* + * The add_operation has to do a quick sort by priority to keep the operations + * in the right order. + */ +void add_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The del_operation seeks the array present at pathname (if any) and + * removes the DrawOperation if present. It returns 0 on success, -1 + * if it fails. + */ +gint del_operation( LttvIAttribute *attributes, + gchar *pathname, + DrawOperation *operation); + +/* + * The clean_operations removes all operations present at a pathname. + * returns 0 on success, -1 if it fails. + */ +gint clean_operations( LttvIAttribute *attributes, + gchar *pathname ); + + +/* + * The list_operations gives a pointer to the operation array associated + * with the pathname. It will be NULL if no operation is present. + */ +void list_operations( LttvIAttribute *attributes, + gchar *pathname, + GArray **operation); + + + +/* + * exec_operation executes the operations if present in the attributes, or + * do nothing if not present. + */ +void exec_operations( LttvIAttribute *attributes, + gchar *pathname); +#endif //0 + +/* + * Here follow the prototypes of the hook functions used to draw the + * different items. + */ + +gboolean draw_text( void *hook_data, void *call_data); +gboolean draw_icon( void *hook_data, void *call_data); +gboolean draw_line( void *hook_data, void *call_data); +gboolean draw_arc( void *hook_data, void *call_data); +gboolean draw_bg( void *hook_data, void *call_data); + + +#endif // _DRAW_ITEM_H diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/eventhooks.c b/ltt/branches/poly/lttv/modules/gui/resourceview/eventhooks.c new file mode 100644 index 00000000..e87f7edb --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/eventhooks.c @@ -0,0 +1,2763 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + +/***************************************************************************** + * Hooks to be called by the main window * + *****************************************************************************/ + + +/* Event hooks are the drawing hooks called during traceset read. They draw the + * icons, text, lines and background color corresponding to the events read. + * + * Two hooks are used for drawing : before_schedchange and after_schedchange hooks. The + * before_schedchange is called before the state update that occurs with an event and + * the after_schedchange hook is called after this state update. + * + * The before_schedchange hooks fulfill the task of drawing the visible objects that + * corresponds to the data accumulated by the after_schedchange hook. + * + * The after_schedchange hook accumulates the data that need to be shown on the screen + * (items) into a queue. Then, the next before_schedchange hook will draw what that + * queue contains. That's the Right Way (TM) of drawing items on the screen, + * because we need to draw the background first (and then add icons, text, ... + * over it), but we only know the length of a background region once the state + * corresponding to it is over, which happens to be at the next before_schedchange + * hook. + * + * We also have a hook called at the end of a chunk to draw the information left + * undrawn in each process queue. We use the current time as end of + * line/background. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +//#define PANGO_ENABLE_BACKEND +#include +#include +#include +#include +#include +#include + +//#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include "eventhooks.h" +#include "cfv.h" +#include "processlist.h" +#include "drawing.h" + + +#define MAX_PATH_LEN 256 +#define STATE_LINE_WIDTH 4 +#define COLLISION_POSITION(height) (((height - STATE_LINE_WIDTH)/2) -3) + +extern GSList *g_legend_list; + + +/* Action to do when background computation completed. + * + * Wait for all the awaited computations to be over. + */ + +static gint background_ready(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData *)hook_data; + LttvTrace *trace = (LttvTrace*)call_data; + + control_flow_data->background_info_waiting--; + + if(control_flow_data->background_info_waiting == 0) { + g_message("control flow viewer : background computation data ready."); + + drawing_clear(control_flow_data->drawing); + processlist_clear(control_flow_data->process_list); + gtk_widget_set_size_request( + control_flow_data->drawing->drawing_area, + -1, processlist_get_height(control_flow_data->process_list)); + redraw_notify(control_flow_data, NULL); + } + + return 0; +} + + +/* Request background computation. Verify if it is in progress or ready first. + * Only for each trace in the tab's traceset. + */ +static void request_background_data(ControlFlowData *control_flow_data) +{ + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(control_flow_data->tab); + gint num_traces = lttv_traceset_number(tsc->ts); + gint i; + LttvTrace *trace; + LttvTraceState *tstate; + + LttvHooks *background_ready_hook = + lttv_hooks_new(); + lttv_hooks_add(background_ready_hook, background_ready, control_flow_data, + LTTV_PRIO_DEFAULT); + control_flow_data->background_info_waiting = 0; + + for(i=0;its, i); + tstate = LTTV_TRACE_STATE(tsc->traces[i]); + + if(lttvwindowtraces_get_ready(g_quark_from_string("state"),trace)==FALSE + && !tstate->has_precomputed_states) { + + if(lttvwindowtraces_get_in_progress(g_quark_from_string("state"), + trace) == FALSE) { + /* We first remove requests that could have been done for the same + * information. Happens when two viewers ask for it before servicing + * starts. + */ + if(!lttvwindowtraces_background_request_find(trace, "state")) + lttvwindowtraces_background_request_queue( + main_window_get_widget(control_flow_data->tab), trace, "state"); + lttvwindowtraces_background_notify_queue(control_flow_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + control_flow_data->background_info_waiting++; + } else { /* in progress */ + + lttvwindowtraces_background_notify_current(control_flow_data, + trace, + ltt_time_infinite, + NULL, + background_ready_hook); + control_flow_data->background_info_waiting++; + } + } else { + /* Data ready. By its nature, this viewer doesn't need to have + * its data ready hook called there, because a background + * request is always linked with a redraw. + */ + } + + } + + lttv_hooks_destroy(background_ready_hook); +} + + + + +/** + * Event Viewer's constructor hook + * + * This constructor is given as a parameter to the menuitem and toolbar button + * registration. It creates the list. + * @param tab A pointer to the parent tab. + * @return The widget created. + */ +GtkWidget * +h_guicontrolflow(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + Tab *tab = ptab->tab; + g_info("h_guicontrolflow, %p", tab); + ControlFlowData *control_flow_data = guicontrolflow(ptab); + + control_flow_data->tab = tab; + + // Unreg done in the GuiControlFlow_Destructor + lttvwindow_register_traceset_notify(tab, + traceset_notify, + control_flow_data); + + lttvwindow_register_time_window_notify(tab, + update_time_window_hook, + control_flow_data); + lttvwindow_register_current_time_notify(tab, + update_current_time_hook, + control_flow_data); + lttvwindow_register_redraw_notify(tab, + redraw_notify, + control_flow_data); + lttvwindow_register_continue_notify(tab, + continue_notify, + control_flow_data); + request_background_data(control_flow_data); + + + return guicontrolflow_get_widget(control_flow_data) ; + +} + +void legend_destructor(GtkWindow *legend) +{ + g_legend_list = g_slist_remove(g_legend_list, legend); +} + +/* Create a popup legend */ +GtkWidget * +h_legend(LttvPlugin *plugin) +{ + LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); + Tab *tab = ptab->tab; + g_info("h_legend, %p", tab); + + GtkWindow *legend = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL)); + + g_legend_list = g_slist_append( + g_legend_list, + legend); + + g_object_set_data_full( + G_OBJECT(legend), + "legend", + legend, + (GDestroyNotify)legend_destructor); + + gtk_window_set_title(legend, "Control Flow View Legend"); + + GtkWidget *pixmap = create_pixmap(GTK_WIDGET(legend), "lttv-color-list.png"); + + // GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixmap( + // GDK_PIXMAP(pixmap), NULL)); + + gtk_container_add(GTK_CONTAINER(legend), GTK_WIDGET(pixmap)); + + gtk_widget_show(GTK_WIDGET(pixmap)); + gtk_widget_show(GTK_WIDGET(legend)); + + + return NULL; /* This is a popup window */ +} + + +int event_selected_hook(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + guint *event_number = (guint*) call_data; + + g_debug("DEBUG : event selected by main window : %u", *event_number); + + return 0; +} + +/* Function that selects the color of status&exemode line */ +static inline PropertiesLine prepare_s_e_line(LttvProcessState *process) +{ + PropertiesLine prop_line; + prop_line.line_width = STATE_LINE_WIDTH; + prop_line.style = GDK_LINE_SOLID; + prop_line.y = MIDDLE; + //GdkColormap *colormap = gdk_colormap_get_system(); + + if(process->state->s == LTTV_STATE_RUN) { + if(process->state->t == LTTV_STATE_USER_MODE) + prop_line.color = drawing_colors[COL_RUN_USER_MODE]; + else if(process->state->t == LTTV_STATE_SYSCALL) + prop_line.color = drawing_colors[COL_RUN_SYSCALL]; + else if(process->state->t == LTTV_STATE_TRAP) + prop_line.color = drawing_colors[COL_RUN_TRAP]; + else if(process->state->t == LTTV_STATE_IRQ) + prop_line.color = drawing_colors[COL_RUN_IRQ]; + else if(process->state->t == LTTV_STATE_SOFT_IRQ) + prop_line.color = drawing_colors[COL_RUN_SOFT_IRQ]; + else if(process->state->t == LTTV_STATE_MODE_UNKNOWN) + prop_line.color = drawing_colors[COL_MODE_UNKNOWN]; + else + g_assert(FALSE); /* RUNNING MODE UNKNOWN */ + } else if(process->state->s == LTTV_STATE_WAIT) { + /* We don't show if we wait while in user mode, trap, irq or syscall */ + prop_line.color = drawing_colors[COL_WAIT]; + } else if(process->state->s == LTTV_STATE_WAIT_CPU) { + /* We don't show if we wait for CPU while in user mode, trap, irq + * or syscall */ + prop_line.color = drawing_colors[COL_WAIT_CPU]; + } else if(process->state->s == LTTV_STATE_ZOMBIE) { + prop_line.color = drawing_colors[COL_ZOMBIE]; + } else if(process->state->s == LTTV_STATE_WAIT_FORK) { + prop_line.color = drawing_colors[COL_WAIT_FORK]; + } else if(process->state->s == LTTV_STATE_EXIT) { + prop_line.color = drawing_colors[COL_EXIT]; + } else if(process->state->s == LTTV_STATE_UNNAMED) { + prop_line.color = drawing_colors[COL_UNNAMED]; + } else { + g_critical("unknown state : %s", g_quark_to_string(process->state->s)); + g_assert(FALSE); /* UNKNOWN STATE */ + } + + return prop_line; + +} + + +/* before_schedchange_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + + +int before_schedchange_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + gint target_pid_saved = tfc->target_pid; + + LttTime evtime = ltt_event_time(e); + LttvFilter *filter = control_flow_data->filter; + + /* we are in a schedchange, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + + guint pid_out; + guint pid_in; + { + pid_out = ltt_event_get_long_unsigned(e, thf->f1); + pid_in = ltt_event_get_long_unsigned(e, thf->f2); + } + + tfc->target_pid = pid_out; + if(!filter || !filter->head || + lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) { + /* For the pid_out */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + /* unknown state, bad current pid */ + if(process->pid != pid_out) + process = lttv_state_find_process(ts, + tfs->cpu, pid_out); + + if(process != NULL) { + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + hashed_process_data = processlist_get_process_data(process_list, + pid_out, + process->cpu, + &birth, + trace_num); + if(hashed_process_data == NULL) + { + g_assert(pid_out == 0 || pid_out != process->ppid); + /* Process not present */ + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid_out, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0) + { + if(hashed_process_data->x.middle_marked == FALSE) { + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used) + { + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + } + + tfc->target_pid = pid_in; + if(!filter || !filter->head || + lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) { + /* For the pid_in */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ + LttvProcessState *process; + process = lttv_state_find_process(ts, + tfs->cpu, pid_in); + guint trace_num = ts->parent.index; + + if(process != NULL) { + /* Well, the process existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + hashed_process_data = processlist_get_process_data(process_list, + pid_in, + tfs->cpu, + &birth, + trace_num); + if(hashed_process_data == NULL) + { + g_assert(pid_in == 0 || pid_in != process->ppid); + /* Process not present */ + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid_in, + process->tgid, + tfs->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + + } + //We could set the current process and hash here, but will be done + //by after schedchange hook + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0) + { + if(hashed_process_data->x.middle_marked == FALSE) { + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used) + { + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + } + + + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } else + g_warning("Cannot find pin_in in schedchange %u", pid_in); + } + tfc->target_pid = target_pid_saved; + return 0; + + + + + /* Text dump */ +#ifdef DONTSHOW + GString *string = g_string_new("");; + gboolean field_names = TRUE, state = TRUE; + + lttv_event_to_string(e, tfc->tf, string, TRUE, field_names, tfs); + g_string_append_printf(string,"\n"); + + if(state) { + g_string_append_printf(string, " %s", + g_quark_to_string(tfs->process->state->s)); + } + + g_info("%s",string->str); + + g_string_free(string, TRUE); + + /* End of text dump */ +#endif //DONTSHOW + +} + +/* after_schedchange_hook + * + * The draw after hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_schedchange_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + LttvProcessState *process_in; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data_in = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + guint pid_in; + { + guint pid_out; + pid_out = ltt_event_get_long_unsigned(e, thf->f1); + pid_in = ltt_event_get_long_unsigned(e, thf->f2); + } + + + /* Find process pid_in in the list... */ + //process_in = lttv_state_find_process(ts, ANY_CPU, pid_in); + //process_in = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + process_in = ts->running_process[cpu]; + /* It should exist, because we are after the state update. */ +#ifdef EXTRA_CHECK + g_assert(process_in != NULL); +#endif //EXTRA_CHECK + birth = process_in->creation_time; + + hashed_process_data_in = processlist_get_process_data(process_list, + pid_in, + process_in->cpu, + &birth, + trace_num); + if(hashed_process_data_in == NULL) + { + g_assert(pid_in == 0 || pid_in != process_in->ppid); + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + /* Process not present */ + processlist_add(process_list, + drawing, + pid_in, + process_in->tgid, + process_in->cpu, + process_in->ppid, + &birth, + trace_num, + process_in->name, + process_in->brand, + &pl_height, + &process_info, + &hashed_process_data_in); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process_in->cpu] = + hashed_process_data_in; + + if(ltt_time_compare(hashed_process_data_in->next_good_time, + evtime) <= 0) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint new_x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + + if(hashed_process_data_in->x.middle != new_x) { + hashed_process_data_in->x.middle = new_x; + hashed_process_data_in->x.middle_used = FALSE; + hashed_process_data_in->x.middle_marked = FALSE; + } + } + return 0; +} + + + + +/* before_execmode_hook + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ + + +int before_execmode_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* we are in a execmode, before the state update. We must draw the + * items corresponding to the state before it changes : now is the right + * time to do it. + */ + /* For the pid */ + //LttvProcessState *process = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + guint pid = process->pid; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + ProcessInfo *process_info; + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + + DrawContext draw_context; + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; +} + +/* before_process_exit_hook + * + * Draw lines for process event. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ + + +int before_process_exit_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + //LttvProcessState *process = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + guint pid = process->pid; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + g_assert(process != NULL); + + birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + + return 0; + +} + + + +/* before_process_release_hook + * + * Draw lines for process event. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ + + +int before_process_release_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + guint trace_num = ts->parent.index; + + guint pid; + { + pid = ltt_event_get_long_unsigned(e, thf->f1); + } + + /* Add process to process list (if not present) */ + /* Don't care about the process if it's not in the state hash already : + * that means a process that has never done anything in the trace and + * unknown suddently gets destroyed : no state meaningful to show. */ + LttvProcessState *process = lttv_state_find_process(ts, ANY_CPU, pid); + + if(process != NULL) { + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + birth = process->creation_time; + + /* Cannot use current process : this event happens on another process, + * action done by the parent. */ + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(likely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) > 0)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + } else { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint x; + + convert_time_to_pixels( + time_window, + evtime, + width, + &x); + + + /* Jump over draw if we are at the same x position */ + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) + { + if(unlikely(hashed_process_data->x.middle_marked == FALSE)) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(hashed_process_data->pixmap, + drawing->gc, + x, + COLLISION_POSITION(hashed_process_data->height)); + hashed_process_data->x.middle_marked = TRUE; + } + /* jump */ + } else { + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; + + { + /* Draw the line */ + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + } + /* become the last x position */ + hashed_process_data->x.middle = x; + hashed_process_data->x.middle_used = TRUE; + hashed_process_data->x.middle_marked = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + + return 0; +} + + + + + +/* after_process_fork_hook + * + * Create the processlist entry for the child process. Put the last + * position in x at the current time value. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_process_fork_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + guint child_pid; + { + child_pid = ltt_event_get_long_unsigned(e, thf->f2); + } + + /* Add process to process list (if not present) */ + LttvProcessState *process_child; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data_child = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + /* Find child in the list... */ + process_child = lttv_state_find_process(ts, ANY_CPU, child_pid); + /* It should exist, because we are after the state update. */ + g_assert(process_child != NULL); + + birth = process_child->creation_time; + guint trace_num = ts->parent.index; + + /* Cannot use current process, because this action is done by the parent + * on its child. */ + hashed_process_data_child = processlist_get_process_data(process_list, + child_pid, + process_child->cpu, + &birth, + trace_num); + if(likely(hashed_process_data_child == NULL)) + { + g_assert(child_pid == 0 || child_pid != process_child->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + child_pid, + process_child->tgid, + process_child->cpu, + process_child->ppid, + &birth, + trace_num, + process_child->name, + process_child->brand, + &pl_height, + &process_info, + &hashed_process_data_child); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } else { + processlist_set_ppid(process_list, process_child->ppid, + hashed_process_data_child); + processlist_set_tgid(process_list, process_child->tgid, + hashed_process_data_child); + } + + + if(likely(ltt_time_compare(hashed_process_data_child->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint new_x; + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + + if(likely(hashed_process_data_child->x.over != new_x)) { + hashed_process_data_child->x.over = new_x; + hashed_process_data_child->x.over_used = FALSE; + hashed_process_data_child->x.over_marked = FALSE; + } + if(likely(hashed_process_data_child->x.middle != new_x)) { + hashed_process_data_child->x.middle = new_x; + hashed_process_data_child->x.middle_used = FALSE; + hashed_process_data_child->x.middle_marked = FALSE; + } + if(likely(hashed_process_data_child->x.under != new_x)) { + hashed_process_data_child->x.under = new_x; + hashed_process_data_child->x.under_used = FALSE; + hashed_process_data_child->x.under_marked = FALSE; + } + } + return 0; +} + + + +/* after_process_exit_hook + * + * Create the processlist entry for the child process. Put the last + * position in x at the current time value. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_process_exit_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + //LttvProcessState *process = tfs->process; + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + + /* It should exist, because we are after the state update. */ + g_assert(process != NULL); + + guint pid = process->pid; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + + ProcessList *process_list = control_flow_data->process_list; + + birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL) ){ + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + ProcessInfo *process_info; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + if(unlikely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + guint new_x; + convert_time_to_pixels( + time_window, + evtime, + width, + &new_x); + if(unlikely(hashed_process_data->x.middle != new_x)) { + hashed_process_data->x.middle = new_x; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + } + } + + return 0; +} + + +/* Get the filename of the process to print */ +int after_fs_exec_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + guint pid = process->pid; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + ProcessInfo *process_info; + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + processlist_set_name(process_list, process->name, hashed_process_data); + + return 0; + +} + +/* Get the filename of the process to print */ +int after_user_generic_thread_brand_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + guint cpu = tfs->cpu; + guint trace_num = ts->parent.index; + LttvProcessState *process = ts->running_process[cpu]; + g_assert(process != NULL); + + guint pid = process->pid; + + /* Well, the process_out existed : we must get it in the process hash + * or add it, and draw its items. + */ + /* Add process to process list (if not present) */ + guint pl_height = 0; + HashedProcessData *hashed_process_data = NULL; + ProcessList *process_list = control_flow_data->process_list; + LttTime birth = process->creation_time; + + if(likely(process_list->current_hash_data[trace_num][cpu] != NULL)) { + hashed_process_data = process_list->current_hash_data[trace_num][cpu]; + } else { + hashed_process_data = processlist_get_process_data(process_list, + pid, + process->cpu, + &birth, + trace_num); + if(unlikely(hashed_process_data == NULL)) + { + g_assert(pid == 0 || pid != process->ppid); + ProcessInfo *process_info; + /* Process not present */ + Drawing_t *drawing = control_flow_data->drawing; + processlist_add(process_list, + drawing, + pid, + process->tgid, + process->cpu, + process->ppid, + &birth, + trace_num, + process->name, + process->brand, + &pl_height, + &process_info, + &hashed_process_data); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } + /* Set the current process */ + process_list->current_hash_data[trace_num][process->cpu] = + hashed_process_data; + } + + processlist_set_brand(process_list, process->brand, hashed_process_data); + + return 0; + +} + + +/* after_event_enum_process_hook + * + * Create the processlist entry for the child process. Put the last + * position in x at the current time value. + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int after_event_enum_process_hook(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + guint first_cpu, nb_cpus, cpu; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + /* Add process to process list (if not present) */ + LttvProcessState *process_in; + LttTime birth; + guint pl_height = 0; + HashedProcessData *hashed_process_data_in = NULL; + + ProcessList *process_list = control_flow_data->process_list; + guint trace_num = ts->parent.index; + + guint pid_in; + { + pid_in = ltt_event_get_long_unsigned(e, thf->f1); + } + + if(pid_in == 0) { + first_cpu = 0; + nb_cpus = ltt_trace_get_num_cpu(ts->parent.t); + } else { + first_cpu = ANY_CPU; + nb_cpus = ANY_CPU+1; + } + + for(cpu = first_cpu; cpu < nb_cpus; cpu++) { + /* Find process pid_in in the list... */ + process_in = lttv_state_find_process(ts, cpu, pid_in); + //process_in = tfs->process; + //guint cpu = tfs->cpu; + //guint trace_num = ts->parent.index; + //process_in = ts->running_process[cpu]; + /* It should exist, because we are after the state update. */ + #ifdef EXTRA_CHECK + //g_assert(process_in != NULL); + #endif //EXTRA_CHECK + birth = process_in->creation_time; + + hashed_process_data_in = processlist_get_process_data(process_list, + pid_in, + process_in->cpu, + &birth, + trace_num); + if(hashed_process_data_in == NULL) + { + if(pid_in != 0 && pid_in == process_in->ppid) + g_critical("TEST %u , %u", pid_in, process_in->ppid); + g_assert(pid_in == 0 || pid_in != process_in->ppid); + ProcessInfo *process_info; + Drawing_t *drawing = control_flow_data->drawing; + /* Process not present */ + processlist_add(process_list, + drawing, + pid_in, + process_in->tgid, + process_in->cpu, + process_in->ppid, + &birth, + trace_num, + process_in->name, + process_in->brand, + &pl_height, + &process_info, + &hashed_process_data_in); + gtk_widget_set_size_request(drawing->drawing_area, + -1, + pl_height); + gtk_widget_queue_draw(drawing->drawing_area); + } else { + processlist_set_name(process_list, process_in->name, + hashed_process_data_in); + processlist_set_ppid(process_list, process_in->ppid, + hashed_process_data_in); + processlist_set_tgid(process_list, process_in->tgid, + hashed_process_data_in); + } + } + return 0; +} + + +gint update_time_window_hook(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + ProcessList *process_list = control_flow_data->process_list; + + const TimeWindowNotifyData *time_window_nofify_data = + ((const TimeWindowNotifyData *)call_data); + + TimeWindow *old_time_window = + time_window_nofify_data->old_time_window; + TimeWindow *new_time_window = + time_window_nofify_data->new_time_window; + + /* Update the ruler */ + drawing_update_ruler(control_flow_data->drawing, + new_time_window); + + + /* Two cases : zoom in/out or scrolling */ + + /* In order to make sure we can reuse the old drawing, the scale must + * be the same and the new time interval being partly located in the + * currently shown time interval. (reuse is only for scrolling) + */ + + g_info("Old time window HOOK : %lu, %lu to %lu, %lu", + old_time_window->start_time.tv_sec, + old_time_window->start_time.tv_nsec, + old_time_window->time_width.tv_sec, + old_time_window->time_width.tv_nsec); + + g_info("New time window HOOK : %lu, %lu to %lu, %lu", + new_time_window->start_time.tv_sec, + new_time_window->start_time.tv_nsec, + new_time_window->time_width.tv_sec, + new_time_window->time_width.tv_nsec); + + if( new_time_window->time_width.tv_sec == old_time_window->time_width.tv_sec + && new_time_window->time_width.tv_nsec == old_time_window->time_width.tv_nsec) + { + /* Same scale (scrolling) */ + g_info("scrolling"); + LttTime *ns = &new_time_window->start_time; + LttTime *nw = &new_time_window->time_width; + LttTime *os = &old_time_window->start_time; + LttTime *ow = &old_time_window->time_width; + LttTime old_end = old_time_window->end_time; + LttTime new_end = new_time_window->end_time; + //if(nsdrawing->width; + convert_time_to_pixels( + *old_time_window, + *ns, + width, + &x); + + /* Copy old data to new location */ + copy_pixmap_region(process_list, + NULL, + control_flow_data->drawing->drawing_area->style->black_gc, + NULL, + x, 0, + 0, 0, + control_flow_data->drawing->width-x+SAFETY, -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_begin = control_flow_data->drawing->width-x; + else + drawing->damage_begin = 0; + + drawing->damage_end = control_flow_data->drawing->width; + + /* Clear the data request background, but not SAFETY */ + rectangle_pixmap(process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin+SAFETY, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1); + gtk_widget_queue_draw(drawing->drawing_area); + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + + /* Get new data for the rest. */ + drawing_data_request(control_flow_data->drawing, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, + control_flow_data->drawing->height); + } else { + //if(nsdrawing->width; + convert_time_to_pixels( + *new_time_window, + *os, + width, + &x); + + /* Copy old data to new location */ + copy_pixmap_region (process_list, + NULL, + control_flow_data->drawing->drawing_area->style->black_gc, + NULL, + 0, 0, + x, 0, + -1, -1); + + if(drawing->damage_begin == drawing->damage_end) + drawing->damage_end = x; + else + drawing->damage_end = + control_flow_data->drawing->width; + + drawing->damage_begin = 0; + + rectangle_pixmap (process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, // do not overlap + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + + + /* Get new data for the rest. */ + drawing_data_request(control_flow_data->drawing, + drawing->damage_begin, 0, + drawing->damage_end - drawing->damage_begin, + control_flow_data->drawing->height); + + } else { + if(ltt_time_compare(*ns,*os) == 0) + { + g_info("not scrolling"); + } else { + g_info("scrolling far"); + /* Cannot reuse any part of the screen : far jump */ + + + rectangle_pixmap (process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + control_flow_data->drawing->width+SAFETY, // do not overlap + -1); + + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = control_flow_data->drawing->width; + + drawing_data_request(control_flow_data->drawing, + 0, 0, + control_flow_data->drawing->width, + control_flow_data->drawing->height); + + } + } + } + } else { + /* Different scale (zoom) */ + g_info("zoom"); + + rectangle_pixmap (process_list, + control_flow_data->drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + control_flow_data->drawing->width+SAFETY, // do not overlap + -1); + + //gtk_widget_queue_draw_area (drawing->drawing_area, + // 0,0, + // control_flow_data->drawing->width, + // control_flow_data->drawing->height); + gtk_widget_queue_draw(drawing->drawing_area); + + drawing->damage_begin = 0; + drawing->damage_end = control_flow_data->drawing->width; + + drawing_data_request(control_flow_data->drawing, + 0, 0, + control_flow_data->drawing->width, + control_flow_data->drawing->height); + } + + /* Update directly when scrolling */ + gdk_window_process_updates(control_flow_data->drawing->drawing_area->window, + TRUE); + + return 0; +} + +gint traceset_notify(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + + if(unlikely(drawing->gc == NULL)) { + return FALSE; + } + if(drawing->dotted_gc == NULL) { + return FALSE; + } + + drawing_clear(control_flow_data->drawing); + processlist_clear(control_flow_data->process_list); + gtk_widget_set_size_request( + control_flow_data->drawing->drawing_area, + -1, processlist_get_height(control_flow_data->process_list)); + redraw_notify(control_flow_data, NULL); + + request_background_data(control_flow_data); + + return FALSE; +} + +gint redraw_notify(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + GtkWidget *widget = drawing->drawing_area; + + drawing->damage_begin = 0; + drawing->damage_end = drawing->width; + + /* fun feature, to be separated someday... */ + drawing_clear(control_flow_data->drawing); + processlist_clear(control_flow_data->process_list); + gtk_widget_set_size_request( + control_flow_data->drawing->drawing_area, + -1, processlist_get_height(control_flow_data->process_list)); + // Clear the images + rectangle_pixmap (control_flow_data->process_list, + widget->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, + -1); + + gtk_widget_queue_draw(drawing->drawing_area); + + if(drawing->damage_begin < drawing->damage_end) + { + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end-drawing->damage_begin, + drawing->height); + } + + //gtk_widget_queue_draw_area(drawing->drawing_area, + // 0,0, + // drawing->width, + // drawing->height); + return FALSE; + +} + + +gint continue_notify(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*) hook_data; + Drawing_t *drawing = control_flow_data->drawing; + + //g_assert(widget->allocation.width == drawing->damage_end); + + if(drawing->damage_begin < drawing->damage_end) + { + drawing_data_request(drawing, + drawing->damage_begin, + 0, + drawing->damage_end-drawing->damage_begin, + drawing->height); + } + + return FALSE; +} + + +gint update_current_time_hook(void *hook_data, void *call_data) +{ + ControlFlowData *control_flow_data = (ControlFlowData*)hook_data; + Drawing_t *drawing = control_flow_data->drawing; + + LttTime current_time = *((LttTime*)call_data); + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + + LttTime time_begin = time_window.start_time; + LttTime width = time_window.time_width; + LttTime half_width; + { + guint64 time_ll = ltt_time_to_uint64(width); + time_ll = time_ll >> 1; /* divide by two */ + half_width = ltt_time_from_uint64(time_ll); + } + LttTime time_end = ltt_time_add(time_begin, width); + + LttvTracesetContext * tsc = + lttvwindow_get_traceset_context(control_flow_data->tab); + + LttTime trace_start = tsc->time_span.start_time; + LttTime trace_end = tsc->time_span.end_time; + + g_info("New current time HOOK : %lu, %lu", current_time.tv_sec, + current_time.tv_nsec); + + + + /* If current time is inside time interval, just move the highlight + * bar */ + + /* Else, we have to change the time interval. We have to tell it + * to the main window. */ + /* The time interval change will take care of placing the current + * time at the center of the visible area, or nearest possible if we are + * at one end of the trace. */ + + + if(ltt_time_compare(current_time, time_begin) < 0) + { + TimeWindow new_time_window; + + if(ltt_time_compare(current_time, + ltt_time_add(trace_start,half_width)) < 0) + time_begin = trace_start; + else + time_begin = ltt_time_sub(current_time,half_width); + + new_time_window.start_time = time_begin; + new_time_window.time_width = width; + new_time_window.time_width_double = ltt_time_to_double(width); + new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(control_flow_data->tab, new_time_window); + } + else if(ltt_time_compare(current_time, time_end) > 0) + { + TimeWindow new_time_window; + + if(ltt_time_compare(current_time, ltt_time_sub(trace_end, half_width)) > 0) + time_begin = ltt_time_sub(trace_end,width); + else + time_begin = ltt_time_sub(current_time,half_width); + + new_time_window.start_time = time_begin; + new_time_window.time_width = width; + new_time_window.time_width_double = ltt_time_to_double(width); + new_time_window.end_time = ltt_time_add(time_begin, width); + + lttvwindow_report_time_window(control_flow_data->tab, new_time_window); + + } + gtk_widget_queue_draw(control_flow_data->drawing->drawing_area); + + /* Update directly when scrolling */ + gdk_window_process_updates(control_flow_data->drawing->drawing_area->window, + TRUE); + + return 0; +} + +typedef struct _ClosureData { + EventsRequest *events_request; + LttvTracesetState *tss; + LttTime end_time; + guint x_end; +} ClosureData; + + +void draw_closure(gpointer key, gpointer value, gpointer user_data) +{ + ProcessInfo *process_info = (ProcessInfo*)key; + HashedProcessData *hashed_process_data = (HashedProcessData*)value; + ClosureData *closure_data = (ClosureData*)user_data; + + EventsRequest *events_request = closure_data->events_request; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracesetState *tss = closure_data->tss; + LttvTracesetContext *tsc = (LttvTracesetContext*)tss; + + LttTime evtime = closure_data->end_time; + + gboolean dodraw = TRUE; + + { + /* For the process */ + /* First, check if the current process is in the state computation + * process list. If it is there, that means we must add it right now and + * draw items from the beginning of the read for it. If it is not + * present, it's a new process and it was not present : it will + * be added after the state update. */ +#ifdef EXTRA_CHECK + g_assert(lttv_traceset_number(tsc->ts) > 0); +#endif //EXTRA_CHECK + LttvTraceContext *tc = tsc->traces[process_info->trace_num]; + LttvTraceState *ts = (LttvTraceState*)tc; + +#if 0 + //FIXME : optimize data structures. + LttvTracefileState *tfs; + LttvTracefileContext *tfc; + guint i; + for(i=0;itracefiles->len;i++) { + tfc = g_array_index(tc->tracefiles, LttvTracefileContext*, i); + if(ltt_tracefile_name(tfc->tf) == LTT_NAME_CPU + && tfs->cpu == process_info->cpu) + break; + + } + g_assert(itracefiles->len); + tfs = LTTV_TRACEFILE_STATE(tfc); +#endif //0 + // LttvTracefileState *tfs = + // (LttvTracefileState*)tsc->traces[process_info->trace_num]-> + // tracefiles[process_info->cpu]; + + LttvProcessState *process; + process = lttv_state_find_process(ts, process_info->cpu, + process_info->pid); + + if(unlikely(process != NULL)) { + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,NULL,NULL, + tc->t,NULL,process,tc)) + dodraw = FALSE; + + /* Only draw for processes that are currently in the trace states */ + + ProcessList *process_list = control_flow_data->process_list; +#ifdef EXTRA_CHECK + /* Should be alike when background info is ready */ + if(control_flow_data->background_info_waiting==0) + g_assert(ltt_time_compare(process->creation_time, + process_info->birth) == 0); +#endif //EXTRA_CHECK + + /* Now, the process is in the state hash and our own process hash. + * We definitely can draw the items related to the ending state. + */ + + if(unlikely(ltt_time_compare(hashed_process_data->next_good_time, + evtime) <= 0)) + { + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + +#ifdef EXTRA_CHECK + if(ltt_time_compare(evtime, time_window.start_time) == -1 + || ltt_time_compare(evtime, time_window.end_time) == 1) + return; +#endif //EXTRA_CHECK + Drawing_t *drawing = control_flow_data->drawing; + guint width = drawing->width; + + guint x = closure_data->x_end; + + DrawContext draw_context; + + /* Now create the drawing context that will be used to draw + * items related to the last state. */ + draw_context.drawable = hashed_process_data->pixmap; + draw_context.gc = drawing->gc; + draw_context.pango_layout = drawing->pango_layout; + draw_context.drawinfo.end.x = x; + + draw_context.drawinfo.y.over = 1; + draw_context.drawinfo.y.middle = (hashed_process_data->height/2); + draw_context.drawinfo.y.under = hashed_process_data->height; + + draw_context.drawinfo.start.offset.over = 0; + draw_context.drawinfo.start.offset.middle = 0; + draw_context.drawinfo.start.offset.under = 0; + draw_context.drawinfo.end.offset.over = 0; + draw_context.drawinfo.end.offset.middle = 0; + draw_context.drawinfo.end.offset.under = 0; +#if 0 + /* Jump over draw if we are at the same x position */ + if(x == hashed_process_data->x.over) + { + /* jump */ + } else { + draw_context.drawinfo.start.x = hashed_process_data->x.over; + /* Draw the line */ + PropertiesLine prop_line = prepare_execmode_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + + hashed_process_data->x.over = x; + } +#endif //0 + + if(unlikely(x == hashed_process_data->x.middle && + hashed_process_data->x.middle_used)) { +#if 0 /* do not mark closure : not missing information */ + if(hashed_process_data->x.middle_marked == FALSE) { + /* Draw collision indicator */ + gdk_gc_set_foreground(drawing->gc, &drawing_colors[COL_WHITE]); + gdk_draw_point(drawing->pixmap, + drawing->gc, + x, + y+(height/2)-3); + hashed_process_data->x.middle_marked = TRUE; + } +#endif //0 + /* Jump */ + } else { + draw_context.drawinfo.start.x = hashed_process_data->x.middle; + /* Draw the line */ + if(dodraw) { + PropertiesLine prop_line = prepare_s_e_line(process); + draw_line((void*)&prop_line, (void*)&draw_context); + } + + /* become the last x position */ + if(likely(x != hashed_process_data->x.middle)) { + hashed_process_data->x.middle = x; + /* but don't use the pixel */ + hashed_process_data->x.middle_used = FALSE; + + /* Calculate the next good time */ + convert_pixels_to_time(width, x+1, time_window, + &hashed_process_data->next_good_time); + } + } + } + } + } + return; +} + +int before_chunk(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + ControlFlowData *cfd = (ControlFlowData*)events_request->viewer_data; +#if 0 + /* Desactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(cfd->process_list->list_store), + TRACE_COLUMN, + GTK_SORT_ASCENDING); +#endif //0 + drawing_chunk_begin(events_request, tss); + + return 0; +} + +int before_request(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + drawing_data_request_begin(events_request, tss); + + return 0; +} + + +/* + * after request is necessary in addition of after chunk in order to draw + * lines until the end of the screen. after chunk just draws lines until + * the last event. + * + * for each process + * draw closing line + * expose + */ +int after_request(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + + ProcessList *process_list = control_flow_data->process_list; + LttTime end_time = events_request->end_time; + + ClosureData closure_data; + closure_data.events_request = (EventsRequest*)hook_data; + closure_data.tss = tss; + closure_data.end_time = end_time; + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + guint width = control_flow_data->drawing->width; + convert_time_to_pixels( + time_window, + end_time, + width, + &closure_data.x_end); + + + /* Draw last items */ + g_hash_table_foreach(process_list->process_hash, draw_closure, + (void*)&closure_data); + + + /* Request expose */ + drawing_request_expose(events_request, tss, end_time); + return 0; +} + +/* + * for each process + * draw closing line + * expose + */ +int after_chunk(void *hook_data, void *call_data) +{ + EventsRequest *events_request = (EventsRequest*)hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + LttvTracesetState *tss = (LttvTracesetState*)call_data; + LttvTracesetContext *tsc = (LttvTracesetContext*)call_data; + LttvTracefileContext *tfc = lttv_traceset_context_get_current_tfc(tsc); + LttTime end_time; + + ProcessList *process_list = control_flow_data->process_list; + guint i; + LttvTraceset *traceset = tsc->ts; + guint nb_trace = lttv_traceset_number(traceset); + + /* Only execute when called for the first trace's events request */ + if(!process_list->current_hash_data) return; + + for(i = 0 ; i < nb_trace ; i++) { + g_free(process_list->current_hash_data[i]); + } + g_free(process_list->current_hash_data); + process_list->current_hash_data = NULL; + + if(tfc != NULL) + end_time = LTT_TIME_MIN(tfc->timestamp, events_request->end_time); + else /* end of traceset, or position now out of request : end */ + end_time = events_request->end_time; + + ClosureData closure_data; + closure_data.events_request = (EventsRequest*)hook_data; + closure_data.tss = tss; + closure_data.end_time = end_time; + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + guint width = control_flow_data->drawing->width; + convert_time_to_pixels( + time_window, + end_time, + width, + &closure_data.x_end); + + /* Draw last items */ + g_hash_table_foreach(process_list->process_hash, draw_closure, + (void*)&closure_data); +#if 0 + /* Reactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(control_flow_data->process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + update_index_to_pixmap(control_flow_data->process_list); + /* Request a full expose : drawing scrambled */ + gtk_widget_queue_draw(control_flow_data->drawing->drawing_area); +#endif //0 + /* Request expose (updates damages zone also) */ + drawing_request_expose(events_request, tss, end_time); + + return 0; +} + +/* after_statedump_end + * + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context. + * + * This function adds items to be drawn in a queue for each process. + * + */ +int before_statedump_end(void *hook_data, void *call_data) +{ + LttvTraceHookByFacility *thf = (LttvTraceHookByFacility*)hook_data; + EventsRequest *events_request = (EventsRequest*)thf->hook_data; + ControlFlowData *control_flow_data = events_request->viewer_data; + + LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; + + LttvTracefileState *tfs = (LttvTracefileState *)call_data; + + LttvTraceState *ts = (LttvTraceState *)tfc->t_context; + + LttvTracesetState *tss = (LttvTracesetState*)tfc->t_context->ts_context; + ProcessList *process_list = control_flow_data->process_list; + + LttEvent *e; + e = ltt_tracefile_get_event(tfc->tf); + + LttvFilter *filter = control_flow_data->filter; + if(filter != NULL && filter->head != NULL) + if(!lttv_filter_tree_parse(filter->head,e,tfc->tf, + tfc->t_context->t,tfc,NULL,NULL)) + return FALSE; + + LttTime evtime = ltt_event_time(e); + + ClosureData closure_data; + closure_data.events_request = events_request; + closure_data.tss = tss; + closure_data.end_time = evtime; + + TimeWindow time_window = + lttvwindow_get_time_window(control_flow_data->tab); + guint width = control_flow_data->drawing->width; + convert_time_to_pixels( + time_window, + evtime, + width, + &closure_data.x_end); + + /* Draw last items */ + g_hash_table_foreach(process_list->process_hash, draw_closure, + (void*)&closure_data); +#if 0 + /* Reactivate sort */ + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(control_flow_data->process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + update_index_to_pixmap(control_flow_data->process_list); + /* Request a full expose : drawing scrambled */ + gtk_widget_queue_draw(control_flow_data->drawing->drawing_area); +#endif //0 + /* Request expose (updates damages zone also) */ + drawing_request_expose(events_request, tss, evtime); + + return 0; +} diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/eventhooks.h b/ltt/branches/poly/lttv/modules/gui/resourceview/eventhooks.h new file mode 100644 index 00000000..88376123 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/eventhooks.h @@ -0,0 +1,123 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + +/* eventhooks.h defines the hooks that are given to processTrace as parameter. + * These hooks call the drawing API to draw the information on the screen, + * using information from Context, but mostly state (running, waiting...). + */ + + +#ifndef _EVENT_HOOKS_H +#define _EVENT_HOOKS_H + +#include +#include +#include + +#include "processlist.h" +#include "drawing.h" +#include "cfv.h" + + +/* Structure used to store and use information relative to one events refresh + * request. Typically filled in by the expose event callback, then passed to the + * library call, then used by the drawing hooks. Then, once all the events are + * sent, it is freed by the hook called after the reading. + */ +//typedef struct _EventRequest +//{ +// ControlFlowData *control_flow_data; +// LttTime time_begin, time_end; +// gint x_begin, x_end; + /* Fill the Events_Context during the initial expose, before calling for + * events. + */ + //GArray Events_Context; //FIXME +//} EventRequest ; + + + + + +void send_test_data(ProcessList *process_list, Drawing_t *drawing); + +GtkWidget *h_guicontrolflow(LttvPlugin *plugin); + +GtkWidget *h_legend(LttvPlugin *plugin); + +int event_selected_hook(void *hook_data, void *call_data); + +/* + * The draw event hook is called by the reading API to have a + * particular event drawn on the screen. + * @param hook_data ControlFlowData structure of the viewer. + * @param call_data Event context with state. + * + * This function basically draw lines and icons. Two types of lines are drawn : + * one small (3 pixels?) representing the state of the process and the second + * type is thicker (10 pixels?) representing on which CPU a process is running + * (and this only in running state). + * + * Extremums of the lines : + * x_min : time of the last event context for this process kept in memory. + * x_max : time of the current event. + * y : middle of the process in the process list. The process is found in the + * list, therefore is it's position in pixels. + * + * The choice of lines'color is defined by the context of the last event for this + * process. + */ +int before_schedchange_hook(void *hook_data, void *call_data); +int after_schedchange_hook(void *hook_data, void *call_data); +int before_execmode_hook(void *hook_data, void *call_data); +int after_execmode_hook(void *hook_data, void *call_data); + + +int before_process_exit_hook(void *hook_data, void *call_data); +int before_process_release_hook(void *hook_data, void *call_data); +int after_process_exit_hook(void *hook_data, void *call_data); +int after_process_fork_hook(void *hook_data, void *call_data); +int after_fs_exec_hook(void *hook_data, void *call_data); +int after_user_generic_thread_brand_hook(void *hook_data, void *call_data); +int after_event_enum_process_hook(void *hook_data, void *call_data); + +#if 0 +int before_process_hook(void *hook_data, void *call_data); +int after_process_hook(void *hook_data, void *call_data); +#endif //0 + +void draw_closure(gpointer key, gpointer value, gpointer user_data); + +int before_chunk(void *hook_data, void *call_data); +int after_chunk(void *hook_data, void *call_data); +int before_request(void *hook_data, void *call_data); +int after_request(void *hook_data, void *call_data); +int before_statedump_end(void *hook_data, void *call_data); + + + +gint update_time_window_hook(void *hook_data, void *call_data); +gint update_current_time_hook(void *hook_data, void *call_data); +gint traceset_notify(void *hook_data, void *call_data); +gint redraw_notify(void *hook_data, void *call_data); +gint continue_notify(void *hook_data, void *call_data); + +void legend_destructor(GtkWindow *legend); + +#endif // _EVENT_HOOKS_H diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/hGuiControlFlowInsert.xpm b/ltt/branches/poly/lttv/modules/gui/resourceview/hGuiControlFlowInsert.xpm new file mode 100644 index 00000000..db4b7275 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/hGuiControlFlowInsert.xpm @@ -0,0 +1,28 @@ +/* XPM */ +static char * hGuiControlFlowInsert_xpm[] = { +"22 22 3 1", +" c None", +". c #0DF904", +"+ c #F90404", +" ", +" . ", +" .. ", +" ... ", +" .... ", +" ... ", +" .. ", +" . ", +" ", +"++++++++++............", +"++++++++++............", +" ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ++++++ ", +" ", +"..........++++++++++++", +"..........++++++++++++", +" "}; diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/hLegendInsert.xpm b/ltt/branches/poly/lttv/modules/gui/resourceview/hLegendInsert.xpm new file mode 100644 index 00000000..a6ff0f39 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/hLegendInsert.xpm @@ -0,0 +1,45 @@ +/* XPM */ +static char * hLegendInsert_xpm[] = { +"22 22 20 1", +" c None", +". c #0DF904", +"+ c #000000", +"@ c #000500", +"# c #000B00", +"$ c #034001", +"% c #000200", +"& c #0CF403", +"* c #0BD603", +"= c #034901", +"- c #F90404", +"; c #0AC503", +"> c #000F00", +", c #034601", +"' c #0CF503", +") c #D60303", +"! c #001000", +"~ c #044E01", +"{ c #0CF203", +"] c #E40303", +" ", +" . ", +" .. ", +" ++@#$ ", +" ++++++% ", +" + &*=++ ", +" .. ++ ", +" . ++ ", +" ++ ", +"----------;>+,'.......", +"---------)!+~{........", +" +++ ", +" ++]---- ", +" ++----- ", +" ++----- ", +" ------ ", +" ++----- ", +" ++----- ", +" ", +"..........------------", +"..........------------", +" "}; diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/lttv_plugin_cfv.c b/ltt/branches/poly/lttv/modules/gui/resourceview/lttv_plugin_cfv.c new file mode 100644 index 00000000..d8b005ab --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/lttv_plugin_cfv.c @@ -0,0 +1,84 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * 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. + */ + + +#include "lttv_plugin_cfv.h" +#include +#include "drawing.h" + +/* + * forward definitions + */ + +/* + * Implementation + */ + +static void cfv_update_filter(LttvPlugin *parent, LttvFilter *filter) +{ + LttvPluginCFV *self = LTTV_PLUGIN_CFV(parent); + g_message("In CFV update filter."); + lttv_filter_destroy(self->cfd->filter); + self->cfd->filter = filter; + redraw_notify(self->cfd, NULL); +} + + +static void +lttv_plugin_cfv_class_init (LttvPluginCFVClass *klass) +{ + LttvPluginClass *parent_klass; + parent_klass = &klass->parent; + parent_klass->update_filter = cfv_update_filter; + g_type_class_add_private (klass, sizeof (ControlFlowData)); +} + + +static void +lttv_plugin_cfv_init (GTypeInstance *instance, gpointer g_class) +{ + LttvPluginCFV *self = LTTV_PLUGIN_CFV (instance); + self->cfd = G_TYPE_INSTANCE_GET_PRIVATE (self, + LTTV_TYPE_PLUGIN_CFV, ControlFlowData); +} + + +GType +lttv_plugin_cfv_get_type (void) +{ + static GType type = 0; + if (type == 0) { + static const GTypeInfo info = { + sizeof (LttvPluginCFVClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + lttv_plugin_cfv_class_init, /* class_init */ + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (LttvPluginCFV), + 0, /* n_preallocs */ + lttv_plugin_cfv_init /* instance_init */ + }; + type = g_type_register_static (G_TYPE_OBJECT, + "LttvPluginCFVType", + &info, 0); + } + return type; +} + + diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/lttv_plugin_cfv.h b/ltt/branches/poly/lttv/modules/gui/resourceview/lttv_plugin_cfv.h new file mode 100644 index 00000000..45445da9 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/lttv_plugin_cfv.h @@ -0,0 +1,63 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2006 Mathieu Desnoyers + * + * 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. + */ + +#ifndef LTTV_PLUGIN_CFV_H +#define LTTV_PLUGIN_CFV_H + +#include +#include +#include "cfv.h" + +/* + * Type macros. + */ + +#define LTTV_TYPE_PLUGIN_CFV (lttv_plugin_cfv_get_type ()) +#define LTTV_PLUGIN_CFV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFV)) +#define LTTV_PLUGIN_CFV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFVClass)) +#define LTTV_IS_PLUGIN_CFV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LTTV_TYPE_PLUGIN_CFV)) +#define LTTV_IS_PLUGIN_CFV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LTTV_TYPE_PLUGIN_CFV)) +#define LTTV_PLUGIN_CFV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LTTV_TYPE_PLUGIN_CFV, LttvPluginCFVClass)) + +typedef struct _LttvPluginCFV LttvPluginCFV; +typedef struct _LttvPluginCFVClass LttvPluginCFVClass; + +struct _LttvPluginCFV { + LttvPlugin parent; + + /* instance members */ + ControlFlowData *cfd; + + /* private */ +}; + +struct _LttvPluginCFVClass { + LttvPluginClass parent; + + /* class members */ +}; + +/* used by LTTV_PLUGIN_TAB_TYPE */ +GType lttv_plugin_cfv_get_type (void); + +/* + * Method definitions. + */ + + +#endif diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/module.c b/ltt/branches/poly/lttv/modules/gui/resourceview/module.c new file mode 100644 index 00000000..8165ccea --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/module.c @@ -0,0 +1,95 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2007 Pierre-Marc Fournier + * + * 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. + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "cfv.h" +#include "lttv_plugin_cfv.h" +#include "eventhooks.h" + +#include "hGuiControlFlowInsert.xpm" +#include "hLegendInsert.xpm" + +GQuark LTT_NAME_CPU; + +/** Array containing instanced objects. Used when module is unloaded */ +GSList *g_control_flow_data_list = NULL ; + +GSList *g_legend_list = NULL ; + +/***************************************************************************** + * Functions for module loading/unloading * + *****************************************************************************/ +/** + * plugin's init function + * + * This function initializes the Control Flow Viewer functionnality through the + * gtkTraceSet API. + */ +static void init() { + + g_info("Resource usage viewer init()"); + + /* Register the toolbar insert button and menu entry*/ + lttvwindow_register_constructor("resourceview", + "/", + "Insert Resource Viewer", + hGuiControlFlowInsert_xpm, + "Insert Resource Viewer", + h_guicontrolflow); + + + LTT_NAME_CPU = g_quark_from_string("/cpu"); +} + +void destroy_walk(gpointer data, gpointer user_data) +{ + g_info("Walk destroy Resource Viewer"); + guicontrolflow_destructor_full((LttvPluginCFV*)data); +} + + +/** + * plugin's destroy function + * + * This function releases the memory reserved by the module and unregisters + * everything that has been registered in the gtkTraceSet API. + */ +static void destroy() { + g_info("GUI resource viewer destroy()"); + + g_slist_foreach(g_control_flow_data_list, destroy_walk, NULL ); + + g_slist_free(g_control_flow_data_list); + + /* Unregister the toolbar insert button and menu entry */ + lttvwindow_unregister_constructor(h_guicontrolflow); +} + + +LTTV_MODULE("resourceview", "Resource viewer", \ + "Graphical module to view usage of resources", \ + init, destroy, "lttvwindow") diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/processlist.c b/ltt/branches/poly/lttv/modules/gui/resourceview/processlist.c new file mode 100644 index 00000000..04be3d21 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/processlist.c @@ -0,0 +1,781 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include "processlist.h" +#include "drawing.h" +#include "drawitem.h" + +#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) + +/* Preallocated Size of the index_to_pixmap array */ +#define ALLOCATE_PROCESSES 1000 + +/***************************************************************************** + * Methods to synchronize process list * + *****************************************************************************/ + + +gint process_sort_func ( GtkTreeModel *model, + GtkTreeIter *it_a, + GtkTreeIter *it_b, + gpointer user_data) +{ + gchar *a_name; + gchar *a_brand; + guint a_pid, a_tgid, a_ppid, a_cpu; + gulong a_birth_s, a_birth_ns; + guint a_trace; + + gchar *b_name; + gchar *b_brand; + guint b_pid, b_tgid, b_ppid, b_cpu; + gulong b_birth_s, b_birth_ns; + guint b_trace; + + gtk_tree_model_get(model, + it_a, + PROCESS_COLUMN, &a_name, + BRAND_COLUMN, &a_brand, + PID_COLUMN, &a_pid, + TGID_COLUMN, &a_tgid, + PPID_COLUMN, &a_ppid, + CPU_COLUMN, &a_cpu, + BIRTH_S_COLUMN, &a_birth_s, + BIRTH_NS_COLUMN, &a_birth_ns, + TRACE_COLUMN, &a_trace, + -1); + + gtk_tree_model_get(model, + it_b, + PROCESS_COLUMN, &b_name, + BRAND_COLUMN, &b_brand, + PID_COLUMN, &b_pid, + TGID_COLUMN, &b_tgid, + PPID_COLUMN, &b_ppid, + CPU_COLUMN, &b_cpu, + BIRTH_S_COLUMN, &b_birth_s, + BIRTH_NS_COLUMN, &b_birth_ns, + TRACE_COLUMN, &b_trace, + -1); + + + /* Order by PID */ + if(a_pid == 0 && b_pid == 0) { + /* If 0, order by CPU */ + if(a_cpu > b_cpu) return 1; + if(a_cpu < b_cpu) return -1; + + } else { /* if not 0, order by pid */ + + if(a_pid > b_pid) return 1; + if(a_pid < b_pid) return -1; + } + + /* Order by birth second */ + + if(a_birth_s > b_birth_s) return 1; + if(a_birth_s < b_birth_s) return -1; + + + /* Order by birth nanosecond */ + if(a_birth_ns > b_birth_ns) return 1; + if(a_birth_ns < b_birth_ns) return -1; + + /* Order by trace_num */ + if(a_trace > b_trace) return 1; + if(a_trace < b_trace) return -1; + + return 0; + +} + +static guint process_list_hash_fct(gconstpointer key) +{ + guint pid = ((const ProcessInfo*)key)->pid; + return ((pid>>8 ^ pid>>4 ^ pid>>2 ^ pid) ^ ((const ProcessInfo*)key)->cpu); +} + +/* If hash is good, should be different */ +static gboolean process_list_equ_fct(gconstpointer a, gconstpointer b) +{ + const ProcessInfo *pa = (const ProcessInfo*)a; + const ProcessInfo *pb = (const ProcessInfo*)b; + + gboolean ret = TRUE; + + if(likely(pa->pid != pb->pid)) + ret = FALSE; + if(likely((pa->pid == 0 && (pa->cpu != pb->cpu)))) + ret = FALSE; + if(unlikely(ltt_time_compare(pa->birth, pb->birth) != 0)) + ret = FALSE; + if(unlikely(pa->trace_num != pb->trace_num)) + ret = FALSE; + + return ret; +} + +void destroy_hash_key(gpointer key); + +void destroy_hash_data(gpointer data); + + +gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) +{ + ControlFlowData *control_flow_data = + (ControlFlowData*)g_object_get_data( + G_OBJECT(widget), + "control_flow_data"); + Drawing_t *drawing = control_flow_data->drawing; + unsigned int cell_height = + get_cell_height(GTK_TREE_VIEW(control_flow_data->process_list->process_list_widget)); + + switch(event->direction) { + case GDK_SCROLL_UP: + gtk_adjustment_set_value(control_flow_data->v_adjust, + gtk_adjustment_get_value(control_flow_data->v_adjust) - cell_height); + break; + case GDK_SCROLL_DOWN: + gtk_adjustment_set_value(control_flow_data->v_adjust, + gtk_adjustment_get_value(control_flow_data->v_adjust) + cell_height); + break; + default: + g_error("should only scroll up and down."); + } + return TRUE; +} + + +static void update_index_to_pixmap_each(ProcessInfo *key, + HashedProcessData *value, + ProcessList *process_list) +{ + guint array_index = processlist_get_index_from_data(process_list, value); + + g_assert(array_index < process_list->index_to_pixmap->len); + + GdkPixmap **pixmap = + (GdkPixmap**)&g_ptr_array_index(process_list->index_to_pixmap, array_index); + + *pixmap = value->pixmap; +} + + +void update_index_to_pixmap(ProcessList *process_list) +{ + g_ptr_array_set_size(process_list->index_to_pixmap, + g_hash_table_size(process_list->process_hash)); + g_hash_table_foreach(process_list->process_hash, + (GHFunc)update_index_to_pixmap_each, + process_list); +} + + +static void update_pixmap_size_each(ProcessInfo *key, + HashedProcessData *value, + guint width) +{ + GdkPixmap *old_pixmap = value->pixmap; + + value->pixmap = + gdk_pixmap_new(old_pixmap, + width, + value->height, + -1); + + gdk_pixmap_unref(old_pixmap); +} + + +void update_pixmap_size(ProcessList *process_list, guint width) +{ + g_hash_table_foreach(process_list->process_hash, + (GHFunc)update_pixmap_size_each, + (gpointer)width); +} + + +typedef struct _CopyPixmap { + GdkDrawable *dest; + GdkGC *gc; + GdkDrawable *src; + gint xsrc, ysrc, xdest, ydest, width, height; +} CopyPixmap; + +static void copy_pixmap_region_each(ProcessInfo *key, + HashedProcessData *value, + CopyPixmap *cp) +{ + GdkPixmap *src = cp->src; + GdkPixmap *dest = cp->dest; + + if(dest == NULL) + dest = value->pixmap; + if(src == NULL) + src = value->pixmap; + + gdk_draw_drawable (dest, + cp->gc, + src, + cp->xsrc, cp->ysrc, + cp->xdest, cp->ydest, + cp->width, cp->height); +} + + + + +void copy_pixmap_region(ProcessList *process_list, GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height) +{ + CopyPixmap cp = { dest, gc, src, xsrc, ysrc, xdest, ydest, width, height }; + + g_hash_table_foreach(process_list->process_hash, + (GHFunc)copy_pixmap_region_each, + &cp); +} + + + +typedef struct _RectanglePixmap { + gboolean filled; + gint x, y, width, height; + GdkGC *gc; +} RectanglePixmap; + +static void rectangle_pixmap_each(ProcessInfo *key, + HashedProcessData *value, + RectanglePixmap *rp) +{ + if(rp->height == -1) + rp->height = value->height; + + gdk_draw_rectangle (value->pixmap, + rp->gc, + rp->filled, + rp->x, rp->y, + rp->width, rp->height); +} + + + + +void rectangle_pixmap(ProcessList *process_list, GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height) +{ + RectanglePixmap rp = { filled, x, y, width, height, gc }; + + g_hash_table_foreach(process_list->process_hash, + (GHFunc)rectangle_pixmap_each, + &rp); +} + + +/* Renders each pixmaps into on big drawable */ +void copy_pixmap_to_screen(ProcessList *process_list, + GdkDrawable *dest, + GdkGC *gc, + gint x, gint y, + gint width, gint height) +{ + if(process_list->index_to_pixmap->len == 0) return; + guint cell_height = process_list->cell_height; + + /* Get indexes */ + gint begin = floor(y/(double)cell_height); + gint end = MIN(ceil((y+height)/(double)cell_height), + process_list->index_to_pixmap->len); + gint i; + + for(i=begin; iindex_to_pixmap->len); + /* Render the pixmap to the screen */ + GdkPixmap *pixmap = + //(GdkPixmap*)g_ptr_array_index(process_list->index_to_pixmap, i); + GDK_PIXMAP(g_ptr_array_index(process_list->index_to_pixmap, i)); + + gdk_draw_drawable (dest, + gc, + pixmap, + x, 0, + x, i*cell_height, + width, cell_height); + + } + + +} + + + + + + + + + +ProcessList *processlist_construct(void) +{ + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + ProcessList* process_list = g_new(ProcessList,1); + + process_list->number_of_process = 0; + + process_list->current_hash_data = NULL; + + /* Create the Process list */ + process_list->list_store = gtk_list_store_new ( N_COLUMNS, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_UINT, + G_TYPE_ULONG, + G_TYPE_ULONG, + G_TYPE_UINT); + + + process_list->process_list_widget = + gtk_tree_view_new_with_model + (GTK_TREE_MODEL (process_list->list_store)); + + g_object_unref (G_OBJECT (process_list->list_store)); + + gtk_tree_sortable_set_default_sort_func( + GTK_TREE_SORTABLE(process_list->list_store), + process_sort_func, + NULL, + NULL); + + + gtk_tree_sortable_set_sort_column_id( + GTK_TREE_SORTABLE(process_list->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + + process_list->process_hash = g_hash_table_new_full( + process_list_hash_fct, process_list_equ_fct, + destroy_hash_key, destroy_hash_data + ); + + + gtk_tree_view_set_headers_visible( + GTK_TREE_VIEW(process_list->process_list_widget), TRUE); + + /* Create a column, associating the "text" attribute of the + * cell_renderer to the first column of the model */ + /* Columns alignment : 0.0 : Left 0.5 : Center 1.0 : Right */ + renderer = gtk_cell_renderer_text_new (); + process_list->renderer = renderer; + + gint vertical_separator; + gtk_widget_style_get (GTK_WIDGET (process_list->process_list_widget), + "vertical-separator", &vertical_separator, + NULL); + gtk_cell_renderer_get_size(renderer, + GTK_WIDGET(process_list->process_list_widget), + NULL, + NULL, + NULL, + NULL, + &process_list->cell_height); + +#if GTK_CHECK_VERSION(2,4,15) + guint ypad; + g_object_get(G_OBJECT(renderer), "ypad", &ypad, NULL); + + process_list->cell_height += ypad; +#endif + process_list->cell_height += vertical_separator; + + + column = gtk_tree_view_column_new_with_attributes ( "Process", + renderer, + "text", + PROCESS_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + process_list->button = column->button; + + column = gtk_tree_view_column_new_with_attributes ( "Brand", + renderer, + "text", + BRAND_COLUMN, + NULL); + gtk_tree_view_column_set_alignment (column, 0.0); + gtk_tree_view_column_set_fixed_width (column, 45); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "PID", + renderer, + "text", + PID_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "TGID", + renderer, + "text", + TGID_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "PPID", + renderer, + "text", + PPID_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "CPU", + renderer, + "text", + CPU_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "Birth sec", + renderer, + "text", + BIRTH_S_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + //gtk_tree_view_column_set_visible(column, 0); + // + column = gtk_tree_view_column_new_with_attributes ( "Birth nsec", + renderer, + "text", + BIRTH_NS_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + column = gtk_tree_view_column_new_with_attributes ( "TRACE", + renderer, + "text", + TRACE_COLUMN, + NULL); + gtk_tree_view_append_column ( + GTK_TREE_VIEW (process_list->process_list_widget), column); + + + //gtk_tree_view_column_set_visible(column, 0); + + g_object_set_data_full( + G_OBJECT(process_list->process_list_widget), + "process_list_Data", + process_list, + (GDestroyNotify)processlist_destroy); + + process_list->index_to_pixmap = g_ptr_array_sized_new(ALLOCATE_PROCESSES); + + return process_list; +} + +void processlist_destroy(ProcessList *process_list) +{ + g_debug("processlist_destroy %p", process_list); + g_hash_table_destroy(process_list->process_hash); + process_list->process_hash = NULL; + g_ptr_array_free(process_list->index_to_pixmap, TRUE); + + g_free(process_list); + g_debug("processlist_destroy end"); +} + +static gboolean remove_hash_item(ProcessInfo *process_info, + HashedProcessData *hashed_process_data, + ProcessList *process_list) +{ + GtkTreeIter iter; + + iter = hashed_process_data->y_iter; + + gtk_list_store_remove (process_list->list_store, &iter); + gdk_pixmap_unref(hashed_process_data->pixmap); + + if(likely(process_list->current_hash_data != NULL)) { + if(likely(hashed_process_data == + process_list->current_hash_data[process_info->trace_num][process_info->cpu])) + process_list->current_hash_data[process_info->trace_num][process_info->cpu] = NULL; + } + return TRUE; /* remove the element from the hash table */ +} + +void processlist_clear(ProcessList *process_list) +{ + g_info("processlist_clear %p", process_list); + + g_hash_table_foreach_remove(process_list->process_hash, + (GHRFunc)remove_hash_item, + (gpointer)process_list); + process_list->number_of_process = 0; + update_index_to_pixmap(process_list); +} + + +GtkWidget *processlist_get_widget(ProcessList *process_list) +{ + return process_list->process_list_widget; +} + + +void destroy_hash_key(gpointer key) +{ + g_free(key); +} + +void destroy_hash_data(gpointer data) +{ + g_free(data); +} + + +void processlist_set_name(ProcessList *process_list, + GQuark name, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + PROCESS_COLUMN, g_quark_to_string(name), + -1); +} + +void processlist_set_brand(ProcessList *process_list, + GQuark brand, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + BRAND_COLUMN, g_quark_to_string(brand), + -1); +} + +void processlist_set_tgid(ProcessList *process_list, + guint tgid, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + TGID_COLUMN, tgid, + -1); +} + +void processlist_set_ppid(ProcessList *process_list, + guint ppid, + HashedProcessData *hashed_process_data) +{ + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + PPID_COLUMN, ppid, + -1); +} + + +int processlist_add( ProcessList *process_list, + Drawing_t *drawing, + guint pid, + guint tgid, + guint cpu, + guint ppid, + LttTime *birth, + guint trace_num, + GQuark name, + GQuark brand, + guint *height, + ProcessInfo **pm_process_info, + HashedProcessData **pm_hashed_process_data) +{ + ProcessInfo *Process_Info = g_new(ProcessInfo, 1); + HashedProcessData *hashed_process_data = g_new(HashedProcessData, 1); + *pm_hashed_process_data = hashed_process_data; + *pm_process_info = Process_Info; + + Process_Info->pid = pid; + Process_Info->tgid = tgid; + if(pid == 0) + Process_Info->cpu = cpu; + else + Process_Info->cpu = 0; + Process_Info->ppid = ppid; + Process_Info->birth = *birth; + Process_Info->trace_num = trace_num; + + /* When we create it from before state update, we are sure that the + * last event occured before the beginning of the global area. + * + * If it is created after state update, this value (0) will be + * overriden by the new state before anything is drawn. + */ + hashed_process_data->x.over = 0; + hashed_process_data->x.over_used = FALSE; + hashed_process_data->x.over_marked = FALSE; + hashed_process_data->x.middle = 0; + hashed_process_data->x.middle_used = FALSE; + hashed_process_data->x.middle_marked = FALSE; + hashed_process_data->x.under = 0; + hashed_process_data->x.under_used = FALSE; + hashed_process_data->x.under_marked = FALSE; + hashed_process_data->next_good_time = ltt_time_zero; + + /* Add a new row to the model */ + gtk_list_store_append ( process_list->list_store, + &hashed_process_data->y_iter); + + gtk_list_store_set ( process_list->list_store, &hashed_process_data->y_iter, + PROCESS_COLUMN, g_quark_to_string(name), + BRAND_COLUMN, g_quark_to_string(brand), + PID_COLUMN, pid, + TGID_COLUMN, tgid, + PPID_COLUMN, ppid, + CPU_COLUMN, cpu, + BIRTH_S_COLUMN, birth->tv_sec, + BIRTH_NS_COLUMN, birth->tv_nsec, + TRACE_COLUMN, trace_num, + -1); + //gtk_tree_view_set_model(GTK_TREE_VIEW(process_list->process_list_widget), + // GTK_TREE_MODEL(process_list->list_store)); + //gtk_container_resize_children(GTK_CONTAINER(process_list->process_list_widget)); + + g_hash_table_insert(process_list->process_hash, + (gpointer)Process_Info, + (gpointer)hashed_process_data); + + process_list->number_of_process++; + + hashed_process_data->height = process_list->cell_height; + + g_assert(hashed_process_data->height != 0); + + *height = hashed_process_data->height * process_list->number_of_process; + + hashed_process_data->pixmap = + gdk_pixmap_new(drawing->drawing_area->window, + drawing->alloc_width, + hashed_process_data->height, + -1); + + // Clear the image + gdk_draw_rectangle (hashed_process_data->pixmap, + drawing->drawing_area->style->black_gc, + TRUE, + 0, 0, + drawing->alloc_width, + hashed_process_data->height); + + update_index_to_pixmap(process_list); + + + return 0; +} + +int processlist_remove( ProcessList *process_list, + guint pid, + guint cpu, + LttTime *birth, + guint trace_num) +{ + ProcessInfo process_info; + HashedProcessData *hashed_process_data; + GtkTreeIter iter; + + process_info.pid = pid; + if(pid == 0) + process_info.cpu = cpu; + else + process_info.cpu = 0; + process_info.birth = *birth; + process_info.trace_num = trace_num; + + + hashed_process_data = + (HashedProcessData*)g_hash_table_lookup( + process_list->process_hash, + &process_info); + if(likely(hashed_process_data != NULL)) + { + iter = hashed_process_data->y_iter; + + gtk_list_store_remove (process_list->list_store, &iter); + + g_hash_table_remove(process_list->process_hash, + &process_info); + + if(likely(process_list->current_hash_data != NULL)) { + if(likely(hashed_process_data == process_list->current_hash_data[trace_num][cpu])) { + process_list->current_hash_data[trace_num][cpu] = NULL; + } + } + + gdk_pixmap_unref(hashed_process_data->pixmap); + + update_index_to_pixmap(process_list); + + process_list->number_of_process--; + + return 0; + } else { + return 1; + } +} + + +#if 0 +static inline guint get_cpu_number_from_name(GQuark name) +{ + const gchar *string; + char *begin; + guint cpu; + + string = g_quark_to_string(name); + + begin = strrchr(string, '/'); + begin++; + + g_assert(begin != '\0'); + + cpu = strtoul(begin, NULL, 10); + + return cpu; +} +#endif //0 diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/processlist.h b/ltt/branches/poly/lttv/modules/gui/resourceview/processlist.h new file mode 100644 index 00000000..d9484603 --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/processlist.h @@ -0,0 +1,271 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + + + +#ifndef _PROCESS_LIST_H +#define _PROCESS_LIST_H + +#include +#include +#include +#include + +#include "drawitem.h" + +/* The process list + * + * Tasks : + * Create a process list + * contains the data for the process list + * tells the height of the process list widget + * provides methods to add/remove process from the list + * note : the sync with drawing is left to the caller. + * provides helper function to convert a process unique identifier to + * pixels (in height). + * + */ + + +/* Enumeration of the columns */ +enum +{ + PROCESS_COLUMN, + BRAND_COLUMN, + PID_COLUMN, + TGID_COLUMN, + PPID_COLUMN, + CPU_COLUMN, + BIRTH_S_COLUMN, + BIRTH_NS_COLUMN, + TRACE_COLUMN, + N_COLUMNS +}; + + +typedef struct _ProcessInfo { + + guint pid; + guint tgid; + guint cpu; + guint ppid; + LttTime birth; + guint trace_num; + + // gint height_cache; + +} ProcessInfo; + +typedef struct _HashedProcessData { + + GdkPixmap *pixmap; // Pixmap slice containing drawing buffer for the PID + gint height; // height of the pixmap + GtkTreeIter y_iter; // Access quickly to y pos. + // DrawContext *draw_context; + /* Information on current drawing */ + struct { + guint over; + gboolean over_used; /* inform the user that information is incomplete */ + gboolean over_marked; /* inform the user that information is incomplete */ + guint middle; + gboolean middle_used; /* inform the user that information is incomplete */ + gboolean middle_marked;/* inform the user that information is incomplete */ + guint under; + gboolean under_used; /* inform the user that information is incomplete */ + gboolean under_marked; /* inform the user that information is incomplete */ + } x; /* last x position saved by after state update */ + + LttTime next_good_time; /* precalculate the next time where the next + pixel is.*/ + +} HashedProcessData; + +struct _ProcessList { + + GtkWidget *process_list_widget; + GtkListStore *list_store; + GtkWidget *button; /* one button of the tree view */ + GtkCellRenderer *renderer; + + /* A hash table by PID to speed up process position find in the list */ + GHashTable *process_hash; + + guint number_of_process; + gint cell_height; + + /* Current process pointer, one per cpu, one per trace */ + HashedProcessData ***current_hash_data; + + /* Array containing index -> pixmap correspondance. Must be updated + * every time the process list is reordered, process added or removed */ + GPtrArray * index_to_pixmap; + +}; + + +typedef struct _ProcessList ProcessList; + + +#ifndef TYPE_DRAWING_T_DEFINED +#define TYPE_DRAWING_T_DEFINED +typedef struct _Drawing_t Drawing_t; +#endif //TYPE_DRAWING_T_DEFINED + +ProcessList *processlist_construct(void); +void processlist_destroy(ProcessList *process_list); +GtkWidget *processlist_get_widget(ProcessList *process_list); + +void processlist_clear(ProcessList *process_list); + +// out : success (0) and height +/* CPU num is only used for PID 0 */ +int processlist_add(ProcessList *process_list, Drawing_t * drawing, + guint pid, guint tgid, guint cpu, guint ppid, + LttTime *birth, guint trace_num, GQuark name, GQuark brand, guint *height, + ProcessInfo **process_info, + HashedProcessData **hashed_process_data); +// out : success (0) and height +int processlist_remove(ProcessList *process_list, guint pid, guint cpu, + LttTime *birth, guint trace_num); + +/* Set the name of a process */ +void processlist_set_name(ProcessList *process_list, + GQuark name, + HashedProcessData *hashed_process_data); + +void processlist_set_brand(ProcessList *process_list, + GQuark brand, + HashedProcessData *hashed_process_data); + +/* Set the ppid of a process */ +void processlist_set_tgid(ProcessList *process_list, + guint tgid, + HashedProcessData *hashed_process_data); +void processlist_set_ppid(ProcessList *process_list, + guint ppid, + HashedProcessData *hashed_process_data); + + +/* Synchronize the list at the left and the drawing */ +void update_index_to_pixmap(ProcessList *process_list); + +/* Update the width of each pixmap buffer for each process */ +void update_pixmap_size(ProcessList *process_list, guint width); + + +/* Put src and/or dest to NULL to copy from/to the each PID specific pixmap */ +void copy_pixmap_region(ProcessList *process_list, GdkDrawable *dest, + GdkGC *gc, GdkDrawable *src, + gint xsrc, gint ysrc, + gint xdest, gint ydest, gint width, gint height); + +/* If height is -1, the height of each pixmap is used */ +void rectangle_pixmap(ProcessList *process_list, GdkGC *gc, + gboolean filled, gint x, gint y, gint width, gint height); + +/* Renders each pixmaps into on big drawable */ +void copy_pixmap_to_screen(ProcessList *process_list, + GdkDrawable *dest, + GdkGC *gc, + gint x, gint y, + gint width, gint height); + + +static inline gint get_cell_height(GtkTreeView *TreeView) +{ + gint height; + GtkTreeViewColumn *column = gtk_tree_view_get_column(TreeView, 0); + + gtk_tree_view_column_cell_get_size(column, NULL, NULL, NULL, NULL, &height); + + gint vertical_separator; + gtk_widget_style_get (GTK_WIDGET (TreeView), + "vertical-separator", &vertical_separator, + NULL); + height += vertical_separator; + + return height; +} + +static inline guint processlist_get_height(ProcessList *process_list) +{ + return process_list->cell_height * process_list->number_of_process ; +} + + +static inline HashedProcessData *processlist_get_process_data( + ProcessList *process_list, + guint pid, guint cpu, LttTime *birth, guint trace_num) +{ + ProcessInfo process_info; + + process_info.pid = pid; + if(pid == 0) + process_info.cpu = cpu; + else + process_info.cpu = ANY_CPU; + process_info.birth = *birth; + process_info.trace_num = trace_num; + + return (HashedProcessData*)g_hash_table_lookup( + process_list->process_hash, + &process_info); +} + + +static inline gint processlist_get_pixels_from_data( ProcessList *process_list, + HashedProcessData *hashed_process_data, + guint *y, + guint *height) +{ + gint *path_indices; + GtkTreePath *tree_path; + + tree_path = gtk_tree_model_get_path((GtkTreeModel*)process_list->list_store, + &hashed_process_data->y_iter); + path_indices = gtk_tree_path_get_indices (tree_path); + + *height = get_cell_height((GtkTreeView*)process_list->process_list_widget); + *y = *height * path_indices[0]; + gtk_tree_path_free(tree_path); + + return 0; + +} + +static inline guint processlist_get_index_from_data(ProcessList *process_list, + HashedProcessData *hashed_process_data) +{ + gint *path_indices; + GtkTreePath *tree_path; + guint ret; + + tree_path = gtk_tree_model_get_path((GtkTreeModel*)process_list->list_store, + &hashed_process_data->y_iter); + path_indices = gtk_tree_path_get_indices (tree_path); + + ret = path_indices[0]; + + gtk_tree_path_free(tree_path); + + return ret; +} + + + +#endif // _PROCESS_LIST_H diff --git a/ltt/branches/poly/lttv/modules/gui/resourceview/test.c b/ltt/branches/poly/lttv/modules/gui/resourceview/test.c new file mode 100644 index 00000000..8a71a52e --- /dev/null +++ b/ltt/branches/poly/lttv/modules/gui/resourceview/test.c @@ -0,0 +1,578 @@ +/* This file is part of the Linux Trace Toolkit viewer + * Copyright (C) 2003-2004 Mathieu Desnoyers + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +static void destroy_cb( GtkWidget *widget, + gpointer data ) +{ + gtk_main_quit (); +} + + + +int main(int argc, char **argv) +{ + GtkWidget *Window; + GtkWidget *CF_Viewer; + GtkWidget *VBox_V; + GtkWidget *HScroll_VC; + ControlFlowData *control_flow_data; + guint ev_sel = 444 ; + /* Horizontal scrollbar and it's adjustment */ + GtkWidget *VScroll_VC; + GtkAdjustment *v_adjust ; + + /* Initialize i18n support */ + gtk_set_locale (); + + /* Initialize the widget set */ + gtk_init (&argc, &argv); + + init(); + + Window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_window_set_title (GTK_WINDOW (Window), ("Test Window")); + + g_signal_connect (G_OBJECT (Window), "destroy", + G_CALLBACK (destroy_cb), NULL); + + + VBox_V = gtk_vbox_new(0, 0); + gtk_container_add (GTK_CONTAINER (Window), VBox_V); + + //ListViewer = hGuiEvents(Window); + //gtk_box_pack_start(GTK_BOX(VBox_V), ListViewer, TRUE, TRUE, 0); + + //ListViewer = hGuiEvents(Window); + //gtk_box_pack_start(GTK_BOX(VBox_V), ListViewer, FALSE, TRUE, 0); + + control_flow_data = guicontrolflow(); + CF_Viewer = control_flow_data->scrolled_window; + gtk_box_pack_start(GTK_BOX(VBox_V), CF_Viewer, TRUE, TRUE, 0); + + /* Create horizontal scrollbar and pack it */ + HScroll_VC = gtk_hscrollbar_new(NULL); + gtk_box_pack_start(GTK_BOX(VBox_V), HScroll_VC, FALSE, TRUE, 0); + + + gtk_widget_show (HScroll_VC); + gtk_widget_show (VBox_V); + gtk_widget_show (Window); + + //Event_Selected_Hook(control_flow_data, &ev_sel); + + gtk_main (); + + g_critical("main loop finished"); + + //h_guievents_destructor(ListViewer); + + //g_critical("GuiEvents Destructor finished"); + destroy(); + + return 0; +} + + + +void add_test_process(ControlFlowData *control_flow_data) +{ + GtkTreeIter iter; + int i; + gchar *process[] = { "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" }; + + for(i=0; inumber_of_process; i++) + { + /* Add a new row to the model */ + gtk_list_store_append (control_flow_data->list_store, &iter); + gtk_list_store_set ( control_flow_data->list_store, &iter, + PROCESS_COLUMN, process[i], + -1); + } + +} + + + + + + +void test_draw(ControlFlowData *control_flow_data) +{ + /* Draw event states using available height, Number of process, cell height + * (don't forget to remove two pixels at beginning and end). + * For horizontal : use width, Time_Begin, Time_End. + * This function calls the reading library to get the draw_hook called + * for the desired period of time. */ + + drawingAreaInfo *drawing_Area_Info = &control_flow_data->drawing_Area_Info; + + +} + +#ifdef DEBUG +void test_draw() { + gint cell_height = get_cell_height(GTK_TREE_VIEW(control_flow_data->process_list_widget)); + GdkGC *GC = gdk_gc_new(widget->window); + GdkColor color = CF_Colors[GREEN]; + + gdk_color_alloc (gdk_colormap_get_system () , &color); + + g_critical("expose"); + + /* When redrawing, use widget->allocation.width to get the width of + * drawable area. */ + control_flow_data->drawing_Area_Info.width = widget->allocation.width; + + test_draw(control_flow_data); + + gdk_gc_copy(GC,widget->style->black_gc); + gdk_gc_set_foreground(GC,&color); + + //gdk_draw_arc (widget->window, + // widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + // TRUE, + // //0, 0, widget->allocation.width, widget->allocation.height, + // 0, 0, widget->allocation.width, + // control_flow_data->drawing_Area_Info.height, + // 0, 64 * 360); + + + //drawing_Area_Init(control_flow_data); + + // 2 pixels for the box around the drawing area, 1 pixel for off-by-one + // (starting from 0) + //gdk_gc_copy (&GC, widget->style->fg_gc[GTK_WIDGET_STATE (widget)]); + + gdk_gc_set_line_attributes(GC,12, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + + gdk_draw_line (widget->window, + GC, + 0, (cell_height-1)/2, + widget->allocation.width, (cell_height-1)/2); + + color = CF_Colors[BLUE]; + + gdk_color_alloc (gdk_colormap_get_system () , &color); + + gdk_gc_set_foreground(GC,&color); + + + gdk_gc_set_line_attributes(GC,3, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + + gdk_draw_line (widget->window, + GC, + 0, (cell_height-1)/2, + widget->allocation.width,(cell_height-1)/2); + + + + + + + g_object_unref(GC); + + //gdk_colormap_alloc_colors(gdk_colormap_get_system(), TRUE, + + //gdk_gc_set_line_attributes(GC,5, GDK_LINE_SOLID, GDK_CAP_NOT_LAST,GDK_JOIN_MITER); + //gdk_gc_set_foreground(GC, + + //gdk_draw_line (widget->window, + // GC, + // 0, (2*cell_height)-2-1, + // 50, (2*cell_height)-2-1); + +} +#endif //DEBUG + + +/* Event_Hook.c tests */ + +void test_draw_item(Drawing_t *drawing, + GdkPixmap *pixmap) +{ + PropertiesIcon properties_icon; + DrawContext draw_context; + + DrawInfo current, previous; + ItemInfo over, middle, under, modify_over, modify_middle, modify_under; + + int i=0,j=0; + + //for(i=0; i<1024;i=i+15) + { + // for(j=0;j<768;j=j+15) + { + over.x = i; + over.y = j; + + current.modify_over = &over; + + draw_context.drawable = pixmap; + draw_context.gc = drawing->drawing_area->style->black_gc; + + draw_context.current = ¤t; + draw_context.previous = NULL; + + properties_icon.icon_name = g_new(char, MAX_PATH_LEN); + strncpy(properties_icon.icon_name, + "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/mini-display.xpm", + MAX_PATH_LEN); + properties_icon.width = -1; + properties_icon.height = -1; + properties_icon.position = OVER; + draw_icon(&properties_icon, &draw_context); + g_free(properties_icon.icon_name); + } + } + +} + +#ifdef NOTUSE +/* NOTE : no drawing data should be sent there, since the drawing widget + * has not been initialized */ +void send_test_drawing(ProcessList *process_list, + Drawing_t *drawing, + GdkPixmap *pixmap, + gint x, gint y, // y not used here? + gint width, + gint height) // height won't be used here ? +{ + int i,j; + ProcessInfo Process_Info = {10000, 12000, 55600}; + //ProcessInfo Process_Info = {156, 14000, 55500}; + GtkTreeRowReference *row_ref; + PangoContext *context; + PangoLayout *layout; + PangoFontDescription *FontDesc;// = pango_font_description_new(); + gint Font_Size; + + //icon + //GdkBitmap *mask = g_new(GdkBitmap, 1); + //GdkPixmap *icon_pixmap = g_new(GdkPixmap, 1); + GdkGC * gc; + // rectangle + GdkColor color = { 0, 0xffff, 0x0000, 0x0000 }; + + gc = gdk_gc_new(pixmap); + /* Sent text data */ + layout = gtk_widget_create_pango_layout(drawing->drawing_area, + NULL); + context = pango_layout_get_context(layout); + FontDesc = pango_context_get_font_description(context); + Font_Size = pango_font_description_get_size(FontDesc); + pango_font_description_set_size(FontDesc, Font_Size-3*PANGO_SCALE); + + + + + LttTime birth; + birth.tv_sec = 12000; + birth.tv_nsec = 55500; + g_info("we have : x : %u, y : %u, width : %u, height : %u", x, y, width, height); + processlist_get_process_pixels(process_list, + 1, + &birth, + &y, + &height); + + g_info("we draw : x : %u, y : %u, width : %u, height : %u", x, y, width, height); + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + pango_layout_set_text(layout, "Test", -1); + gdk_draw_layout(pixmap, drawing->drawing_area->style->black_gc, + 0, y+height, layout); + + birth.tv_sec = 14000; + birth.tv_nsec = 55500; + + processlist_get_process_pixels(process_list, + 156, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_info("y : %u, height : %u", y, height); + + + + birth.tv_sec = 12000; + birth.tv_nsec = 55700; + + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + + /* Draw rectangle (background color) */ + gdk_gc_copy(gc, drawing->drawing_area->style->black_gc); + gdk_gc_set_rgb_fg_color(gc, &color); + gdk_draw_rectangle(pixmap, gc, + TRUE, + x, y, width, height); + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + + /* Draw arc */ + gdk_draw_arc(pixmap, drawing->drawing_area->style->black_gc, + TRUE, 100, y, height/2, height/2, 0, 360*64); + + g_info("y : %u, height : %u", y, height); + + for(i=0; i<10; i++) + { + birth.tv_sec = i*12000; + birth.tv_nsec = i*55700; + + processlist_get_process_pixels(process_list, + i, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_critical("y : %u, height : %u", y, height); + + } + + birth.tv_sec = 12000; + birth.tv_nsec = 55600; + + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + + + drawing_draw_line( + drawing, pixmap, x, + y+(height/2), x + width, y+(height/2), + drawing->drawing_area->style->black_gc); + + g_info("y : %u, height : %u", y, height); + + + /* IMPORTANT : This action uses the cpu heavily! */ + //icon_pixmap = gdk_pixmap_create_from_xpm(pixmap, &mask, NULL, +// "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/move_message.xpm"); + // "/home/compudj/local/share/LinuxTraceToolkit/pixmaps/mini-display.xpm"); + + // gdk_gc_set_clip_mask(drawing->drawing_area->style->black_gc, mask); + +// for(i=x;idrawing_area->style->black_gc); +// gdk_gc_set_clip_origin(drawing->drawing_area->style->black_gc, i, j); +// gdk_draw_drawable(pixmap, +// drawing->drawing_area->style->black_gc, +// icon_pixmap, +// 0, 0, i, j, -1, -1); + +// } +// } + + test_draw_item(drawing,pixmap); + + //gdk_gc_set_clip_origin(drawing->drawing_area->style->black_gc, 0, 0); + //gdk_gc_set_clip_mask(drawing->drawing_area->style->black_gc, NULL); + + //g_free(icon_pixmap); + //g_free(mask); + + + + + + + pango_font_description_set_size(FontDesc, Font_Size); + g_object_unref(layout); + g_free(gc); +} + +void send_test_process(ProcessList *process_list, Drawing_t *drawing) +{ + guint height, y; + int i; + ProcessInfo Process_Info = {10000, 12000, 55600}; + //ProcessInfo Process_Info = {156, 14000, 55500}; + GtkTreeRowReference *row_ref; + + LttTime birth; + + if(process_list->Test_Process_Sent) return; + + birth.tv_sec = 12000; + birth.tv_nsec = 55500; + + processlist_add(process_list, + 1, + &birth, + &y); + processlist_get_process_pixels(process_list, + 1, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + birth.tv_sec = 14000; + birth.tv_nsec = 55500; + + processlist_add(process_list, + 156, + &birth, + &y); + processlist_get_process_pixels(process_list, + 156, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + birth.tv_sec = 12000; + birth.tv_nsec = 55700; + + processlist_add(process_list, + 10, + &birth, + &height); + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + //drawing_insert_square( drawing, height, 5); + + for(i=0; i<10; i++) + { + birth.tv_sec = i*12000; + birth.tv_nsec = i*55700; + + processlist_add(process_list, + i, + &birth, + &height); + processlist_get_process_pixels(process_list, + i, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + // g_critical("y : %u, height : %u", y, height); + + } + //g_critical("height : %u", height); + + birth.tv_sec = 12000; + birth.tv_nsec = 55600; + + processlist_add(process_list, + 10, + &birth, + &y); + processlist_get_process_pixels(process_list, + 10, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + processlist_add(process_list, + 10000, + &birth, + &height); + processlist_get_process_pixels(process_list, + 10000, + &birth, + &y, + &height); + drawing_insert_square( drawing, y, height); + + //g_critical("y : %u, height : %u", y, height); + + //drawing_insert_square( drawing, height, 5); + //g_critical("height : %u", height); + + + processlist_get_process_pixels(process_list, + 10000, + &birth, + &y, &height); + processlist_remove( process_list, + 10000, + &birth); + + drawing_remove_square( drawing, y, height); + + if(row_ref = + (GtkTreeRowReference*)g_hash_table_lookup( + process_list->process_hash, + &Process_Info)) + { + g_critical("key found"); + g_critical("position in the list : %s", + gtk_tree_path_to_string ( + gtk_tree_row_reference_get_path( + (GtkTreeRowReference*)row_ref) + )); + + } + + process_list->Test_Process_Sent = TRUE; + +} +#endif//NOTUSE +