1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2005 Peter Ho
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
29 #include <ltt/event.h>
31 #include <ltt/trace.h>
32 #include <ltt/facility.h>
33 #include <lttv/module.h>
34 #include <lttv/hook.h>
35 #include <lttv/tracecontext.h>
36 #include <lttv/state.h>
37 #include <lttv/filter.h>
38 #include <lttvwindow/lttvwindow.h>
41 #include "hInterruptsInsert.xpm"
43 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
44 #define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
51 LttTime total_duration
;
65 /** Array containing instanced objects. Used when module is unloaded */
66 static GSList
*interrupt_data_list
= NULL
;
69 #define TRACE_NUMBER 0
71 typedef struct _InterruptEventData
{
73 /*Graphical Widgets */
74 GtkWidget
* ScrollWindow
;
75 GtkListStore
*ListStore
;
78 GtkTreeSelection
*SelectionTree
;
80 Tab
* tab
; /* tab that contains this plug-in*/
81 LttvHooks
* event_hooks
;
82 LttvHooks
* hooks_trace_after
;
83 LttvHooks
* hooks_trace_before
;
84 TimeWindow time_window
;
85 LttvHooksById
* event_by_id_hooks
;
86 GArray
*interrupt_counters
;
87 GArray
*active_irq_entry
;
88 } InterruptEventData
;
91 /* Function prototypes */
93 static gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
);
94 static GtkWidget
*interrupts(Tab
*tab
);
95 static InterruptEventData
*system_info(Tab
*tab
);
96 void interrupt_destructor(InterruptEventData
*event_viewer_data
);
97 static void request_event(InterruptEventData
*event_data
);
98 static guint64
get_interrupt_id(LttEvent
*e
);
99 static gboolean
trace_header(void *hook_data
, void *call_data
);
100 static gboolean
interrupt_display (void *hook_data
, void *call_data
);
101 static void calcul_duration(LttTime time_exit
, guint cpu_id
, InterruptEventData
*event_data
);
102 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*interrupt_counters
);
103 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
);
104 static gboolean
irq_exit_callback(void *hook_data
, void *call_data
);
106 /* Enumeration of the columns */
121 * This is the entry point of the viewer.
125 g_info("interrupts: init()");
126 lttvwindow_register_constructor("interrupts",
128 "Insert Interrupts View",
129 hInterruptsInsert_xpm
,
130 "Insert Interrupts View",
140 static GtkWidget
*interrupts(Tab
* tab
)
143 InterruptEventData
* event_data
= system_info(tab
) ;
145 return event_data
->Hbox
;
151 * This function initializes the Event Viewer functionnality through the
154 InterruptEventData
*system_info(Tab
*tab
)
157 GtkTreeViewColumn
*column
;
158 GtkCellRenderer
*renderer
;
159 InterruptEventData
* event_viewer_data
= g_new(InterruptEventData
,1) ;
162 event_viewer_data
->tab
= tab
;
164 /*Get the current time frame from the main window */
165 event_viewer_data
->time_window
= lttvwindow_get_time_window(tab
);
166 event_viewer_data
->interrupt_counters
= g_array_new(FALSE
, FALSE
, sizeof(Irq
));
167 event_viewer_data
->active_irq_entry
= g_array_new(FALSE
, FALSE
, sizeof(irq_entry
));
168 /*Create tha main window for the viewer */
169 event_viewer_data
->ScrollWindow
= gtk_scrolled_window_new (NULL
, NULL
);
170 gtk_widget_show (event_viewer_data
->ScrollWindow
);
171 gtk_scrolled_window_set_policy(
172 GTK_SCROLLED_WINDOW(event_viewer_data
->ScrollWindow
),
173 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
/*GTK_POLICY_NEVER*/);
175 /* Create a model for storing the data list */
176 event_viewer_data
->ListStore
= gtk_list_store_new (
177 N_COLUMNS
, /* Total number of columns */
178 G_TYPE_INT
, /* CPUID */
179 G_TYPE_INT
, /* IRQ_ID */
180 G_TYPE_INT
, /* Frequency */
181 G_TYPE_UINT64
/* Duration */
184 event_viewer_data
->TreeView
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data
->ListStore
));
186 g_object_unref (G_OBJECT (event_viewer_data
->ListStore
));
188 renderer
= gtk_cell_renderer_text_new ();
189 column
= gtk_tree_view_column_new_with_attributes ("CPUID",
191 "text", CPUID_COLUMN
,
193 gtk_tree_view_column_set_alignment (column
, 0.0);
194 gtk_tree_view_column_set_fixed_width (column
, 45);
195 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
198 renderer
= gtk_cell_renderer_text_new ();
199 column
= gtk_tree_view_column_new_with_attributes ("IrqId",
201 "text", IRQ_ID_COLUMN
,
203 gtk_tree_view_column_set_alignment (column
, 0.0);
204 gtk_tree_view_column_set_fixed_width (column
, 220);
205 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
207 renderer
= gtk_cell_renderer_text_new ();
208 column
= gtk_tree_view_column_new_with_attributes ("Frequency",
210 "text", FREQUENCY_COLUMN
,
212 gtk_tree_view_column_set_alignment (column
, 1.0);
213 gtk_tree_view_column_set_fixed_width (column
, 220);
214 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
216 renderer
= gtk_cell_renderer_text_new ();
217 column
= gtk_tree_view_column_new_with_attributes ("Duration (nsec)",
219 "text", DURATION_COLUMN
,
221 gtk_tree_view_column_set_alignment (column
, 0.0);
222 gtk_tree_view_column_set_fixed_width (column
, 145);
223 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
225 event_viewer_data
->SelectionTree
= gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data
->TreeView
));
226 gtk_tree_selection_set_mode (event_viewer_data
->SelectionTree
, GTK_SELECTION_SINGLE
);
228 gtk_container_add (GTK_CONTAINER (event_viewer_data
->ScrollWindow
), event_viewer_data
->TreeView
);
230 event_viewer_data
->Hbox
= gtk_hbox_new(0, 0);
231 gtk_box_pack_start(GTK_BOX(event_viewer_data
->Hbox
), event_viewer_data
->ScrollWindow
, TRUE
, TRUE
, 0);
233 gtk_widget_show(event_viewer_data
->Hbox
);
234 gtk_widget_show(event_viewer_data
->TreeView
);
236 interrupt_data_list
= g_slist_append(interrupt_data_list
, event_viewer_data
);
237 /* Registration for time notification */
238 lttvwindow_register_time_window_notify(tab
,
239 interrupt_update_time_window
,
243 request_event(event_viewer_data
);
244 return event_viewer_data
;
249 * For each trace in the traceset, this function:
250 * - registers a callback function to each hook
251 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
252 * - calls lttvwindow_events_request() to request data in a specific
253 * time interval to the main window
256 static void request_event(InterruptEventData
*event_data
)
258 guint i
, k
, l
, nb_trace
;
268 EventsRequest
*events_request
;
270 LttvTraceHookByFacility
*thf
;
272 LttvTracesetContext
*tsc
= lttvwindow_get_traceset_context(event_data
->tab
);
275 /* Get the traceset */
276 LttvTraceset
*traceset
= tsc
->ts
;
278 nb_trace
= lttv_traceset_number(traceset
);
280 /* There are many traces in a traceset. Iteration for each trace. */
281 for(i
= 0; i
<MIN(TRACE_NUMBER
+1, nb_trace
);i
++)
283 events_request
= g_new(EventsRequest
, 1);
285 hooks
= g_array_new(FALSE
, FALSE
, sizeof(LttvTraceHook
));
287 hooks
= g_array_set_size(hooks
, 2);
289 event_data
->hooks_trace_before
= lttv_hooks_new();
291 /* Registers a hook function */
292 lttv_hooks_add(event_data
->hooks_trace_before
, trace_header
, event_data
, LTTV_PRIO_DEFAULT
);
294 event_data
->hooks_trace_after
= lttv_hooks_new();
295 /* Registers a hook function */
296 lttv_hooks_add(event_data
->hooks_trace_after
, interrupt_display
, event_data
, LTTV_PRIO_DEFAULT
);
297 /* Get a trace state */
298 ts
= (LttvTraceState
*)tsc
->traces
[i
];
299 /* Create event_by_Id hooks */
300 event_data
->event_by_id_hooks
= lttv_hooks_by_id_new();
302 /*Register event_by_id_hooks with a callback function*/
303 ret
= lttv_trace_find_hook(ts
->parent
.t
,
304 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_ENTRY
,
305 LTT_FIELD_IRQ_ID
, 0, 0,
308 &g_array_index(hooks
, LttvTraceHook
, 0));
310 ret
= lttv_trace_find_hook(ts
->parent
.t
,
311 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_EXIT
,
312 LTT_FIELD_IRQ_ID
, 0, 0,
315 &g_array_index(hooks
, LttvTraceHook
, 1));
318 /*iterate through the facility list*/
319 for(k
= 0 ; k
< hooks
->len
; k
++)
321 hook
= &g_array_index(hooks
, LttvTraceHook
, k
);
322 for(l
=0; l
<hook
->fac_list
->len
; l
++)
324 thf
= g_array_index(hook
->fac_list
, LttvTraceHookByFacility
*, l
);
325 lttv_hooks_add(lttv_hooks_by_id_find(event_data
->event_by_id_hooks
, thf
->id
),
332 /* Initalize the EventsRequest structure */
333 events_request
->owner
= event_data
;
334 events_request
->viewer_data
= event_data
;
335 events_request
->servicing
= FALSE
;
336 events_request
->start_time
= event_data
->time_window
.start_time
;
337 events_request
->start_position
= NULL
;
338 events_request
->stop_flag
= FALSE
;
339 events_request
->end_time
= event_data
->time_window
.end_time
;
340 events_request
->num_events
= G_MAXUINT
;
341 events_request
->end_position
= NULL
;
342 events_request
->trace
= i
;
344 events_request
->hooks
= hooks
;
346 events_request
->before_chunk_traceset
= NULL
;
347 events_request
->before_chunk_trace
= event_data
->hooks_trace_before
;
348 events_request
->before_chunk_tracefile
= NULL
;
349 events_request
->event
= NULL
;
350 events_request
->event_by_id
= event_data
->event_by_id_hooks
;
351 events_request
->after_chunk_tracefile
= NULL
;
352 events_request
->after_chunk_trace
= NULL
;
353 events_request
->after_chunk_traceset
= NULL
;
354 events_request
->before_request
= NULL
;
355 events_request
->after_request
= event_data
->hooks_trace_after
;
357 lttvwindow_events_request(event_data
->tab
, events_request
);
363 * This function is called whenever an irq_entry event occurs.
366 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
)
372 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
373 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
374 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
375 GArray
* active_irq_entry
= event_data
->active_irq_entry
;
376 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
377 event_time
= ltt_event_time(e
);
378 cpu_id
= ltt_event_cpu_id(e
);
381 entry
.id
=get_interrupt_id(e
);
382 entry
.cpu_id
= cpu_id
;
383 entry
.event_time
= event_time
;
384 g_array_append_val (active_irq_entry
, entry
);
390 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
391 * Refer to the print.c file for howto extract data from a dynamic structure.
393 static guint64
get_interrupt_id(LttEvent
*e
)
396 LttEventType
*event_type
;
400 event_type
= ltt_event_eventtype(e
);
401 num_fields
= ltt_eventtype_num_fields(event_type
);
402 for(i
= 0 ; i
< num_fields
-1 ; i
++)
404 field
= ltt_eventtype_field(event_type
, i
);
405 irq_id
= ltt_event_get_long_unsigned(e
,field
);
411 * This function is called whenever an irq_exit event occurs.
414 gboolean
irq_exit_callback(void *hook_data
, void *call_data
)
418 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
419 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
420 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
421 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
422 LttEventType
*type
= ltt_event_eventtype(e
);
423 event_time
= ltt_event_time(e
);
424 cpu_id
= ltt_event_cpu_id(e
);
426 calcul_duration( event_time
, cpu_id
, event_data
);
431 * This function calculates the duration of an interrupt.
434 static void calcul_duration(LttTime time_exit
, guint cpu_id
,InterruptEventData
*event_data
){
439 GArray
*interrupt_counters
= event_data
->interrupt_counters
;
440 GArray
*active_irq_entry
= event_data
->active_irq_entry
;
441 for(i
= 0; i
< active_irq_entry
->len
; i
++)
443 element
= &g_array_index(active_irq_entry
,irq_entry
,i
);
444 if(element
->cpu_id
== cpu_id
)
446 sum_interrupt_data(element
,time_exit
, interrupt_counters
);
447 g_array_remove_index(active_irq_entry
, i
);
453 * This function calculates the total duration of an interrupt.
456 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*interrupt_counters
){
461 gboolean notFound
= FALSE
;
462 memset ((void*)&irq
, 0,sizeof(Irq
));
465 if(interrupt_counters
->len
== NO_ITEMS
)
467 irq
.cpu_id
= e
->cpu_id
;
470 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
471 g_array_append_val (interrupt_counters
, irq
);
475 for(i
= 0; i
< interrupt_counters
->len
; i
++)
477 element
= &g_array_index(interrupt_counters
,Irq
,i
);
478 if(element
->id
== e
->id
){
480 duration
= ltt_time_sub(time_exit
, e
->event_time
);
481 element
->total_duration
= ltt_time_add(element
->total_duration
, duration
);
482 element
->frequency
++;
487 irq
.cpu_id
= e
->cpu_id
;
490 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
491 g_array_append_val (interrupt_counters
, irq
);
497 * This function displays the result on the viewer
500 static gboolean
interrupt_display(void *hook_data
, void *call_data
){
504 LttTime average_duration
;
507 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
508 GArray
*interrupt_counters
= event_data
->interrupt_counters
;
509 gtk_list_store_clear(event_data
->ListStore
);
510 for(i
= 0; i
< interrupt_counters
->len
; i
++)
512 element
= g_array_index(interrupt_counters
,Irq
,i
);
513 real_data
= element
.total_duration
.tv_sec
;
514 real_data
*= NANOSECONDS_PER_SECOND
;
515 real_data
+= element
.total_duration
.tv_nsec
;
516 gtk_list_store_append (event_data
->ListStore
, &iter
);
517 gtk_list_store_set (event_data
->ListStore
, &iter
,
518 CPUID_COLUMN
, element
.cpu_id
,
519 IRQ_ID_COLUMN
, element
.id
,
520 FREQUENCY_COLUMN
, element
.frequency
,
521 DURATION_COLUMN
, real_data
,
526 if(event_data
->interrupt_counters
->len
)
528 g_array_remove_range (event_data
->interrupt_counters
,0,event_data
->interrupt_counters
->len
);
531 if(event_data
->active_irq_entry
->len
)
534 g_array_remove_range (event_data
->active_irq_entry
,0,event_data
->active_irq_entry
->len
);
539 * This function is called by the main window
540 * when the time interval needs to be updated.
542 gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
)
544 InterruptEventData
*event_data
= (InterruptEventData
*) hook_data
;
545 const TimeWindowNotifyData
*time_window_nofify_data
= ((const TimeWindowNotifyData
*)call_data
);
546 event_data
->time_window
= *time_window_nofify_data
->new_time_window
;
547 g_info("interrupts: interrupt_update_time_window()\n");
548 Tab
*tab
= event_data
->tab
;
549 lttvwindow_events_request_remove_all(tab
, event_data
);
550 request_event(event_data
);
555 gboolean
trace_header(void *hook_data
, void *call_data
)
558 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
559 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
565 void interrupt_destroy_walk(gpointer data
, gpointer user_data
)
567 g_info("interrupt_destroy_walk");
568 interrupt_destructor((InterruptEventData
*)data
);
572 void interrupt_destructor(InterruptEventData
*event_viewer_data
)
574 /* May already been done by GTK window closing */
575 g_info("enter interrupt_destructor \n");
576 if(GTK_IS_WIDGET(event_viewer_data
->Hbox
))
578 gtk_widget_destroy(event_viewer_data
->Hbox
);
583 * plugin's destroy function
585 * This function releases the memory reserved by the module and unregisters
586 * everything that has been registered in the gtkTraceSet API.
588 static void destroy() {
589 g_info("Destroy interrupts");
590 g_slist_foreach(interrupt_data_list
, interrupt_destroy_walk
, NULL
);
591 g_slist_free(interrupt_data_list
);
592 lttvwindow_unregister_constructor(interrupts
);
596 LTTV_MODULE("interrupts", "interrupts info view", \
597 "Graphical module to display interrupts performance", \
598 init
, destroy
, "lttvwindow")