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,
18 /******************************************************************
19 Each field of the interrupt viewer is summarized as follows:
25 - Frequency (Hz): the number of interrupts per second (Hz).
26 We compute the total number of interrupts. Then
27 we divide it by the time interval.
29 - Total Duration (nsec): the sum of each interrupt duration in nsec.
30 For a given Irq ID, we sum the duration of each interrupt
31 to give us the total duration
34 *******************************************************************/
45 #include <ltt/event.h>
46 #include <ltt/trace.h>
47 #include <lttv/module.h>
48 #include <lttv/hook.h>
49 #include <lttv/tracecontext.h>
50 #include <lttv/state.h>
51 #include <lttv/filter.h>
52 #include <lttvwindow/lttvwindow.h>
53 #include <lttvwindow/lttv_plugin_tab.h>
56 #include "hTutorialInsert.xpm"
58 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
59 #define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
66 guint TotalNumberOfInterrupts
;
67 LttTime total_duration
;
83 /** Array containing instanced objects. Used when module is unloaded */
84 static GSList
*interrupt_data_list
= NULL
;
87 #define TRACE_NUMBER 0
89 typedef struct _InterruptEventData
92 /* Graphical Widgets */
93 GtkWidget
* ScrollWindow
;
94 GtkListStore
*ListStore
;
97 GtkTreeSelection
*SelectionTree
;
99 Tab
* tab
; /* tab that contains this plug-in*/
100 LttvPluginTab
* ptab
;
101 LttvHooks
* event_hooks
;
102 LttvHooks
* hooks_trace_after
;
103 LttvHooks
* hooks_trace_before
;
104 TimeWindow time_window
;
105 LttvHooksById
* event_by_id_hooks
;
108 } InterruptEventData
;
111 /* Function prototypes */
113 static gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
);
114 static GtkWidget
*interrupts(LttvPlugin
*plugin
);
115 static InterruptEventData
*system_info(LttvPluginTab
*ptab
);
116 void interrupt_destructor(InterruptEventData
*event_viewer_data
);
117 static void request_event(InterruptEventData
*event_data
);
118 static guint64
get_interrupt_id(LttEvent
*e
);
119 static gboolean
trace_header(void *hook_data
, void *call_data
);
120 static gboolean
interrupt_display (void *hook_data
, void *call_data
);
121 static void calcul_duration(LttTime time_exit
, guint cpu_id
, InterruptEventData
*event_data
);
122 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*interrupt_counters
);
123 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
);
124 static gboolean
irq_exit_callback(void *hook_data
, void *call_data
);
125 static void InterruptFree(InterruptEventData
*event_viewer_data
);
126 static int FrequencyInHZ(gint NumerofInterruptions
, TimeWindow time_window
);
127 /* Enumeration of the columns */
142 * This is the entry point of the viewer.
147 g_info("interrupts: init()");
149 lttvwindow_register_constructor("tutorial",
151 "Insert Interrupts View",
153 "Insert Interrupts View",
162 static GtkWidget
*interrupts(LttvPlugin
*plugin
)
164 LttvPluginTab
*ptab
= LTTV_PLUGIN_TAB(plugin
);
165 InterruptEventData
* event_data
= system_info(ptab
) ;
167 return event_data
->Hbox
;
173 * This function initializes the Event Viewer functionnality through the
176 InterruptEventData
*system_info(LttvPluginTab
*ptab
)
179 GtkTreeViewColumn
*column
;
180 GtkCellRenderer
*renderer
;
181 InterruptEventData
* event_viewer_data
= g_new(InterruptEventData
,1) ;
182 Tab
*tab
= ptab
->tab
;
184 event_viewer_data
->tab
= tab
;
185 event_viewer_data
->ptab
= ptab
;
187 /*Get the current time frame from the main window */
188 event_viewer_data
->time_window
= lttvwindow_get_time_window(tab
);
189 event_viewer_data
->IrqExit
= g_array_new(FALSE
, FALSE
, sizeof(Irq
));
190 event_viewer_data
->IrqEntry
= g_array_new(FALSE
, FALSE
, sizeof(irq_entry
));
192 /*Create tha main window for the viewer */
193 event_viewer_data
->ScrollWindow
= gtk_scrolled_window_new (NULL
, NULL
);
194 gtk_widget_show (event_viewer_data
->ScrollWindow
);
195 gtk_scrolled_window_set_policy(
196 GTK_SCROLLED_WINDOW(event_viewer_data
->ScrollWindow
),
197 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
199 /* Create a model for storing the data list */
200 event_viewer_data
->ListStore
= gtk_list_store_new (
201 N_COLUMNS
, /* Total number of columns */
202 G_TYPE_INT
, /* CPUID */
203 G_TYPE_INT
, /* IRQ_ID */
204 G_TYPE_INT
, /* Frequency */
205 G_TYPE_UINT64
/* Duration */
208 event_viewer_data
->TreeView
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data
->ListStore
));
210 g_object_unref (G_OBJECT (event_viewer_data
->ListStore
));
212 renderer
= gtk_cell_renderer_text_new ();
213 column
= gtk_tree_view_column_new_with_attributes ("CPU ID",
215 "text", CPUID_COLUMN
,
217 gtk_tree_view_column_set_alignment (column
, 0.0);
218 gtk_tree_view_column_set_fixed_width (column
, 45);
219 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
222 renderer
= gtk_cell_renderer_text_new ();
223 column
= gtk_tree_view_column_new_with_attributes ("IRQ ID",
225 "text", IRQ_ID_COLUMN
,
227 gtk_tree_view_column_set_alignment (column
, 0.0);
228 gtk_tree_view_column_set_fixed_width (column
, 220);
229 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
231 renderer
= gtk_cell_renderer_text_new ();
232 column
= gtk_tree_view_column_new_with_attributes ("Frequency (HZ)",
234 "text", FREQUENCY_COLUMN
,
236 gtk_tree_view_column_set_alignment (column
, 1.0);
237 gtk_tree_view_column_set_fixed_width (column
, 220);
238 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
240 renderer
= gtk_cell_renderer_text_new ();
241 column
= gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)",
243 "text", DURATION_COLUMN
,
245 gtk_tree_view_column_set_alignment (column
, 0.0);
246 gtk_tree_view_column_set_fixed_width (column
, 145);
247 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
249 event_viewer_data
->SelectionTree
= gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data
->TreeView
));
250 gtk_tree_selection_set_mode (event_viewer_data
->SelectionTree
, GTK_SELECTION_SINGLE
);
252 gtk_container_add (GTK_CONTAINER (event_viewer_data
->ScrollWindow
), event_viewer_data
->TreeView
);
254 event_viewer_data
->Hbox
= gtk_hbox_new(0, 0);
255 gtk_box_pack_start(GTK_BOX(event_viewer_data
->Hbox
), event_viewer_data
->ScrollWindow
, TRUE
, TRUE
, 0);
257 gtk_widget_show(event_viewer_data
->Hbox
);
258 gtk_widget_show(event_viewer_data
->TreeView
);
260 interrupt_data_list
= g_slist_append(interrupt_data_list
, event_viewer_data
);
262 /* Registration for time notification */
263 lttvwindow_register_time_window_notify(tab
,
264 interrupt_update_time_window
,
267 g_object_set_data_full(G_OBJECT(event_viewer_data
->Hbox
),
270 (GDestroyNotify
) InterruptFree
);
273 request_event(event_viewer_data
);
274 return event_viewer_data
;
279 * For each trace in the traceset, this function:
280 * - registers a callback function to each hook
281 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
282 * - calls lttvwindow_events_request() to request data in a specific
283 * time interval to the main window
286 static void request_event(InterruptEventData
*event_data
)
288 guint i
, k
, l
, nb_trace
;
298 EventsRequest
*events_request
;
300 LttvTraceHookByFacility
*thf
;
302 LttvTracesetContext
*tsc
= lttvwindow_get_traceset_context(event_data
->tab
);
305 /* Get the traceset */
306 LttvTraceset
*traceset
= tsc
->ts
;
308 nb_trace
= lttv_traceset_number(traceset
);
310 /* There are many traces in a traceset. Iteration for each trace. */
311 for(i
= 0; i
<MIN(TRACE_NUMBER
+1, nb_trace
);i
++)
313 events_request
= g_new(EventsRequest
, 1);
315 hooks
= g_array_new(FALSE
, FALSE
, sizeof(LttvTraceHook
));
317 hooks
= g_array_set_size(hooks
, 2);
319 event_data
->hooks_trace_before
= lttv_hooks_new();
321 /* Registers a hook function */
322 lttv_hooks_add(event_data
->hooks_trace_before
, trace_header
, event_data
, LTTV_PRIO_DEFAULT
);
324 event_data
->hooks_trace_after
= lttv_hooks_new();
325 /* Registers a hook function */
326 lttv_hooks_add(event_data
->hooks_trace_after
, interrupt_display
, event_data
, LTTV_PRIO_DEFAULT
);
327 /* Get a trace state */
328 ts
= (LttvTraceState
*)tsc
->traces
[i
];
329 /* Create event_by_Id hooks */
330 event_data
->event_by_id_hooks
= lttv_hooks_by_id_new();
332 /*Register event_by_id_hooks with a callback function*/
333 ret
= lttv_trace_find_hook(ts
->parent
.t
,
334 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_ENTRY
,
335 LTT_FIELD_IRQ_ID
, 0, 0,
338 &g_array_index(hooks
, LttvTraceHook
, 0));
340 ret
= lttv_trace_find_hook(ts
->parent
.t
,
341 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_EXIT
,
342 LTT_FIELD_IRQ_ID
, 0, 0,
345 &g_array_index(hooks
, LttvTraceHook
, 1));
349 /*iterate through the facility list*/
350 for(k
= 0 ; k
< hooks
->len
; k
++)
352 hook
= &g_array_index(hooks
, LttvTraceHook
, k
);
353 for(l
=0; l
<hook
->fac_list
->len
; l
++)
355 thf
= g_array_index(hook
->fac_list
, LttvTraceHookByFacility
*, l
);
356 lttv_hooks_add(lttv_hooks_by_id_find(event_data
->event_by_id_hooks
, thf
->id
),
364 /* Initalize the EventsRequest structure */
365 events_request
->owner
= event_data
;
366 events_request
->viewer_data
= event_data
;
367 events_request
->servicing
= FALSE
;
368 events_request
->start_time
= event_data
->time_window
.start_time
;
369 events_request
->start_position
= NULL
;
370 events_request
->stop_flag
= FALSE
;
371 events_request
->end_time
= event_data
->time_window
.end_time
;
372 events_request
->num_events
= G_MAXUINT
;
373 events_request
->end_position
= NULL
;
374 events_request
->trace
= i
;
376 events_request
->hooks
= hooks
;
378 events_request
->before_chunk_traceset
= NULL
;
379 events_request
->before_chunk_trace
= event_data
->hooks_trace_before
;
380 events_request
->before_chunk_tracefile
= NULL
;
381 events_request
->event
= NULL
;
382 events_request
->event_by_id
= event_data
->event_by_id_hooks
;
383 events_request
->after_chunk_tracefile
= NULL
;
384 events_request
->after_chunk_trace
= NULL
;
385 events_request
->after_chunk_traceset
= NULL
;
386 events_request
->before_request
= NULL
;
387 events_request
->after_request
= event_data
->hooks_trace_after
;
389 lttvwindow_events_request(event_data
->tab
, events_request
);
395 * This function is called whenever an irq_entry event occurs.
398 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
)
404 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
405 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
406 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
407 GArray
* IrqEntry
= event_data
->IrqEntry
;
408 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
409 event_time
= ltt_event_time(e
);
410 cpu_id
= ltt_event_cpu_id(e
);
412 if ((ltt_time_compare(event_time
,event_data
->time_window
.start_time
) == TRUE
) &&
413 (ltt_time_compare(event_data
->time_window
.end_time
,event_time
) == TRUE
))
415 entry
.id
=get_interrupt_id(e
);
416 entry
.cpu_id
= cpu_id
;
417 entry
.event_time
= event_time
;
418 g_array_append_val (IrqEntry
, entry
);
424 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
425 * Refer to the print.c file for howto extract data from a dynamic structure.
427 static guint64
get_interrupt_id(LttEvent
*e
)
430 LttEventType
*event_type
;
434 event_type
= ltt_event_eventtype(e
);
435 num_fields
= ltt_eventtype_num_fields(event_type
);
436 for(i
= 0 ; i
< num_fields
-1 ; i
++)
438 field
= ltt_eventtype_field(event_type
, i
);
439 irq_id
= ltt_event_get_long_unsigned(e
,field
);
445 * This function is called whenever an irq_exit event occurs.
448 gboolean
irq_exit_callback(void *hook_data
, void *call_data
)
452 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
453 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
454 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
455 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
456 LttEventType
*type
= ltt_event_eventtype(e
);
457 event_time
= ltt_event_time(e
);
458 cpu_id
= ltt_event_cpu_id(e
);
459 if ((ltt_time_compare(event_time
,event_data
->time_window
.start_time
) == TRUE
) &&
460 (ltt_time_compare(event_data
->time_window
.end_time
,event_time
) == TRUE
))
462 calcul_duration( event_time
, cpu_id
, event_data
);
469 * This function calculates the duration of an interrupt.
472 static void calcul_duration(LttTime time_exit
, guint cpu_id
,InterruptEventData
*event_data
){
477 GArray
*IrqExit
= event_data
->IrqExit
;
478 GArray
*IrqEntry
= event_data
->IrqEntry
;
479 for(i
= 0; i
< IrqEntry
->len
; i
++){
480 element
= &g_array_index(IrqEntry
,irq_entry
,i
);
481 if(element
->cpu_id
== cpu_id
)
483 sum_interrupt_data(element
,time_exit
, IrqExit
);
484 g_array_remove_index(IrqEntry
, i
);
490 * This function calculates the total duration of an interrupt.
493 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*IrqExit
)
499 gboolean notFound
= FALSE
;
500 memset ((void*)&irq
, 0,sizeof(Irq
));
503 if(IrqExit
->len
== NO_ITEMS
)
505 irq
.cpu_id
= e
->cpu_id
;
507 irq
.TotalNumberOfInterrupts
++;
508 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
509 g_array_append_val (IrqExit
, irq
);
512 for(i
= 0; i
< IrqExit
->len
; i
++)
514 element
= &g_array_index(IrqExit
, Irq
, i
);
515 if(element
->id
== e
->id
){
517 duration
= ltt_time_sub(time_exit
, e
->event_time
);
518 element
->total_duration
= ltt_time_add(element
->total_duration
, duration
);
519 element
->TotalNumberOfInterrupts
++;
525 irq
.cpu_id
= e
->cpu_id
;
527 irq
.TotalNumberOfInterrupts
++;
528 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
529 g_array_append_val (IrqExit
, irq
);
535 * This function displays the result on the viewer
538 static gboolean
interrupt_display(void *hook_data
, void *call_data
)
543 LttTime average_duration
;
548 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
549 GArray
*IrqExit
= event_data
->IrqExit
;
550 gtk_list_store_clear(event_data
->ListStore
);
551 for(i
= 0; i
< IrqExit
->len
; i
++)
554 element
= g_array_index(IrqExit
,Irq
,i
);
555 real_data
= element
.total_duration
.tv_sec
;
556 real_data
*= NANOSECONDS_PER_SECOND
;
557 real_data
+= element
.total_duration
.tv_nsec
;
559 FrequencyHZ
= FrequencyInHZ(element
.TotalNumberOfInterrupts
,event_data
->time_window
);
561 gtk_list_store_append (event_data
->ListStore
, &iter
);
563 gtk_list_store_set (event_data
->ListStore
, &iter
,
564 CPUID_COLUMN
, element
.cpu_id
,
565 IRQ_ID_COLUMN
, element
.id
,
566 FREQUENCY_COLUMN
, FrequencyHZ
,
567 DURATION_COLUMN
, real_data
,
572 if(event_data
->IrqExit
->len
)
573 g_array_remove_range (event_data
->IrqExit
,0,event_data
->IrqExit
->len
);
575 if(event_data
->IrqEntry
->len
)
576 g_array_remove_range (event_data
->IrqEntry
,0,event_data
->IrqEntry
->len
);
581 * This function converts the number of interrupts over a time window to
584 static int FrequencyInHZ(gint NumerofInterruptions
, TimeWindow time_window
)
586 guint64 frequencyHz
= 0;
587 double timeSec
; // time in second
589 result
= ltt_time_to_double(time_window
.time_width
);
590 timeSec
= (result
/NANOSECONDS_PER_SECOND
); //time in second
591 frequencyHz
= NumerofInterruptions
/ timeSec
;
597 * This function is called by the main window
598 * when the time interval needs to be updated.
600 gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
)
602 InterruptEventData
*event_data
= (InterruptEventData
*) hook_data
;
603 const TimeWindowNotifyData
*time_window_nofify_data
= ((const TimeWindowNotifyData
*)call_data
);
604 event_data
->time_window
= *time_window_nofify_data
->new_time_window
;
605 g_info("interrupts: interrupt_update_time_window()\n");
606 Tab
*tab
= event_data
->tab
;
607 lttvwindow_events_request_remove_all(tab
, event_data
);
608 request_event(event_data
);
613 gboolean
trace_header(void *hook_data
, void *call_data
)
616 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
617 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
623 void interrupt_destroy_walk(gpointer data
, gpointer user_data
)
625 g_info("interrupt_destroy_walk");
626 interrupt_destructor((InterruptEventData
*)data
);
630 void interrupt_destructor(InterruptEventData
*event_viewer_data
)
632 /* May already been done by GTK window closing */
633 g_info("enter interrupt_destructor \n");
634 if(GTK_IS_WIDGET(event_viewer_data
->Hbox
))
636 gtk_widget_destroy(event_viewer_data
->Hbox
);
641 This function is called when the viewer is destroyed to free hooks and memory
643 static void InterruptFree(InterruptEventData
*event_viewer_data
)
645 Tab
*tab
= event_viewer_data
->tab
;
648 g_array_free(event_viewer_data
->IrqExit
, TRUE
);
650 g_array_free(event_viewer_data
->IrqEntry
, TRUE
);
652 lttvwindow_unregister_time_window_notify(tab
, interrupt_update_time_window
, event_viewer_data
);
654 lttvwindow_events_request_remove_all(event_viewer_data
->tab
,
657 interrupt_data_list
= g_slist_remove(interrupt_data_list
, event_viewer_data
);
664 * plugin's destroy function
666 * This function releases the memory reserved by the module and unregisters
667 * everything that has been registered in the gtkTraceSet API.
669 static void destroy() {
670 g_info("Destroy interrupts");
671 g_slist_foreach(interrupt_data_list
, interrupt_destroy_walk
, NULL
);
672 g_slist_free(interrupt_data_list
);
673 lttvwindow_unregister_constructor(interrupts
);
677 LTTV_MODULE("tutorial", "interrupts info view", \
678 "Graphical module to display interrupts performance", \
679 init
, destroy
, "lttvwindow")