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>
47 #include <ltt/trace.h>
48 #include <ltt/facility.h>
49 #include <lttv/module.h>
50 #include <lttv/hook.h>
51 #include <lttv/tracecontext.h>
52 #include <lttv/state.h>
53 #include <lttv/filter.h>
54 #include <lttvwindow/lttvwindow.h>
57 #include "hTutorialInsert.xpm"
59 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
60 #define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
67 guint TotalNumberOfInterrupts
;
68 LttTime total_duration
;
84 /** Array containing instanced objects. Used when module is unloaded */
85 static GSList
*interrupt_data_list
= NULL
;
88 #define TRACE_NUMBER 0
90 typedef struct _InterruptEventData
93 /* Graphical Widgets */
94 GtkWidget
* ScrollWindow
;
95 GtkListStore
*ListStore
;
98 GtkTreeSelection
*SelectionTree
;
100 Tab
* tab
; /* tab that contains this plug-in*/
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(Tab
*tab
);
115 static InterruptEventData
*system_info(Tab
*tab
);
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(Tab
* tab
)
165 InterruptEventData
* event_data
= system_info(tab
) ;
167 return event_data
->Hbox
;
173 * This function initializes the Event Viewer functionnality through the
176 InterruptEventData
*system_info(Tab
*tab
)
179 GtkTreeViewColumn
*column
;
180 GtkCellRenderer
*renderer
;
181 InterruptEventData
* event_viewer_data
= g_new(InterruptEventData
,1) ;
184 event_viewer_data
->tab
= tab
;
186 /*Get the current time frame from the main window */
187 event_viewer_data
->time_window
= lttvwindow_get_time_window(tab
);
188 event_viewer_data
->IrqExit
= g_array_new(FALSE
, FALSE
, sizeof(Irq
));
189 event_viewer_data
->IrqEntry
= g_array_new(FALSE
, FALSE
, sizeof(irq_entry
));
191 /*Create tha main window for the viewer */
192 event_viewer_data
->ScrollWindow
= gtk_scrolled_window_new (NULL
, NULL
);
193 gtk_widget_show (event_viewer_data
->ScrollWindow
);
194 gtk_scrolled_window_set_policy(
195 GTK_SCROLLED_WINDOW(event_viewer_data
->ScrollWindow
),
196 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
198 /* Create a model for storing the data list */
199 event_viewer_data
->ListStore
= gtk_list_store_new (
200 N_COLUMNS
, /* Total number of columns */
201 G_TYPE_INT
, /* CPUID */
202 G_TYPE_INT
, /* IRQ_ID */
203 G_TYPE_INT
, /* Frequency */
204 G_TYPE_UINT64
/* Duration */
207 event_viewer_data
->TreeView
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data
->ListStore
));
209 g_object_unref (G_OBJECT (event_viewer_data
->ListStore
));
211 renderer
= gtk_cell_renderer_text_new ();
212 column
= gtk_tree_view_column_new_with_attributes ("CPU ID",
214 "text", CPUID_COLUMN
,
216 gtk_tree_view_column_set_alignment (column
, 0.0);
217 gtk_tree_view_column_set_fixed_width (column
, 45);
218 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
221 renderer
= gtk_cell_renderer_text_new ();
222 column
= gtk_tree_view_column_new_with_attributes ("IRQ ID",
224 "text", IRQ_ID_COLUMN
,
226 gtk_tree_view_column_set_alignment (column
, 0.0);
227 gtk_tree_view_column_set_fixed_width (column
, 220);
228 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
230 renderer
= gtk_cell_renderer_text_new ();
231 column
= gtk_tree_view_column_new_with_attributes ("Frequency (HZ)",
233 "text", FREQUENCY_COLUMN
,
235 gtk_tree_view_column_set_alignment (column
, 1.0);
236 gtk_tree_view_column_set_fixed_width (column
, 220);
237 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
239 renderer
= gtk_cell_renderer_text_new ();
240 column
= gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)",
242 "text", DURATION_COLUMN
,
244 gtk_tree_view_column_set_alignment (column
, 0.0);
245 gtk_tree_view_column_set_fixed_width (column
, 145);
246 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
248 event_viewer_data
->SelectionTree
= gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data
->TreeView
));
249 gtk_tree_selection_set_mode (event_viewer_data
->SelectionTree
, GTK_SELECTION_SINGLE
);
251 gtk_container_add (GTK_CONTAINER (event_viewer_data
->ScrollWindow
), event_viewer_data
->TreeView
);
253 event_viewer_data
->Hbox
= gtk_hbox_new(0, 0);
254 gtk_box_pack_start(GTK_BOX(event_viewer_data
->Hbox
), event_viewer_data
->ScrollWindow
, TRUE
, TRUE
, 0);
256 gtk_widget_show(event_viewer_data
->Hbox
);
257 gtk_widget_show(event_viewer_data
->TreeView
);
259 interrupt_data_list
= g_slist_append(interrupt_data_list
, event_viewer_data
);
261 /* Registration for time notification */
262 lttvwindow_register_time_window_notify(tab
,
263 interrupt_update_time_window
,
266 g_object_set_data_full(G_OBJECT(event_viewer_data
->Hbox
),
269 (GDestroyNotify
) InterruptFree
);
272 request_event(event_viewer_data
);
273 return event_viewer_data
;
278 * For each trace in the traceset, this function:
279 * - registers a callback function to each hook
280 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
281 * - calls lttvwindow_events_request() to request data in a specific
282 * time interval to the main window
285 static void request_event(InterruptEventData
*event_data
)
287 guint i
, k
, l
, nb_trace
;
297 EventsRequest
*events_request
;
299 LttvTraceHookByFacility
*thf
;
301 LttvTracesetContext
*tsc
= lttvwindow_get_traceset_context(event_data
->tab
);
304 /* Get the traceset */
305 LttvTraceset
*traceset
= tsc
->ts
;
307 nb_trace
= lttv_traceset_number(traceset
);
309 /* There are many traces in a traceset. Iteration for each trace. */
310 for(i
= 0; i
<MIN(TRACE_NUMBER
+1, nb_trace
);i
++)
312 events_request
= g_new(EventsRequest
, 1);
314 hooks
= g_array_new(FALSE
, FALSE
, sizeof(LttvTraceHook
));
316 hooks
= g_array_set_size(hooks
, 2);
318 event_data
->hooks_trace_before
= lttv_hooks_new();
320 /* Registers a hook function */
321 lttv_hooks_add(event_data
->hooks_trace_before
, trace_header
, event_data
, LTTV_PRIO_DEFAULT
);
323 event_data
->hooks_trace_after
= lttv_hooks_new();
324 /* Registers a hook function */
325 lttv_hooks_add(event_data
->hooks_trace_after
, interrupt_display
, event_data
, LTTV_PRIO_DEFAULT
);
326 /* Get a trace state */
327 ts
= (LttvTraceState
*)tsc
->traces
[i
];
328 /* Create event_by_Id hooks */
329 event_data
->event_by_id_hooks
= lttv_hooks_by_id_new();
331 /*Register event_by_id_hooks with a callback function*/
332 ret
= lttv_trace_find_hook(ts
->parent
.t
,
333 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_ENTRY
,
334 LTT_FIELD_IRQ_ID
, 0, 0,
337 &g_array_index(hooks
, LttvTraceHook
, 0));
339 ret
= lttv_trace_find_hook(ts
->parent
.t
,
340 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_EXIT
,
341 LTT_FIELD_IRQ_ID
, 0, 0,
344 &g_array_index(hooks
, LttvTraceHook
, 1));
348 /*iterate through the facility list*/
349 for(k
= 0 ; k
< hooks
->len
; k
++)
351 hook
= &g_array_index(hooks
, LttvTraceHook
, k
);
352 for(l
=0; l
<hook
->fac_list
->len
; l
++)
354 thf
= g_array_index(hook
->fac_list
, LttvTraceHookByFacility
*, l
);
355 lttv_hooks_add(lttv_hooks_by_id_find(event_data
->event_by_id_hooks
, thf
->id
),
363 /* Initalize the EventsRequest structure */
364 events_request
->owner
= event_data
;
365 events_request
->viewer_data
= event_data
;
366 events_request
->servicing
= FALSE
;
367 events_request
->start_time
= event_data
->time_window
.start_time
;
368 events_request
->start_position
= NULL
;
369 events_request
->stop_flag
= FALSE
;
370 events_request
->end_time
= event_data
->time_window
.end_time
;
371 events_request
->num_events
= G_MAXUINT
;
372 events_request
->end_position
= NULL
;
373 events_request
->trace
= i
;
375 events_request
->hooks
= hooks
;
377 events_request
->before_chunk_traceset
= NULL
;
378 events_request
->before_chunk_trace
= event_data
->hooks_trace_before
;
379 events_request
->before_chunk_tracefile
= NULL
;
380 events_request
->event
= NULL
;
381 events_request
->event_by_id
= event_data
->event_by_id_hooks
;
382 events_request
->after_chunk_tracefile
= NULL
;
383 events_request
->after_chunk_trace
= NULL
;
384 events_request
->after_chunk_traceset
= NULL
;
385 events_request
->before_request
= NULL
;
386 events_request
->after_request
= event_data
->hooks_trace_after
;
388 lttvwindow_events_request(event_data
->tab
, events_request
);
394 * This function is called whenever an irq_entry event occurs.
397 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
)
403 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
404 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
405 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
406 GArray
* IrqEntry
= event_data
->IrqEntry
;
407 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
408 event_time
= ltt_event_time(e
);
409 cpu_id
= ltt_event_cpu_id(e
);
411 if ((ltt_time_compare(event_time
,event_data
->time_window
.start_time
) == TRUE
) &&
412 (ltt_time_compare(event_data
->time_window
.end_time
,event_time
) == TRUE
))
414 entry
.id
=get_interrupt_id(e
);
415 entry
.cpu_id
= cpu_id
;
416 entry
.event_time
= event_time
;
417 g_array_append_val (IrqEntry
, entry
);
423 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
424 * Refer to the print.c file for howto extract data from a dynamic structure.
426 static guint64
get_interrupt_id(LttEvent
*e
)
429 LttEventType
*event_type
;
433 event_type
= ltt_event_eventtype(e
);
434 num_fields
= ltt_eventtype_num_fields(event_type
);
435 for(i
= 0 ; i
< num_fields
-1 ; i
++)
437 field
= ltt_eventtype_field(event_type
, i
);
438 irq_id
= ltt_event_get_long_unsigned(e
,field
);
444 * This function is called whenever an irq_exit event occurs.
447 gboolean
irq_exit_callback(void *hook_data
, void *call_data
)
451 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
452 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
453 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
454 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
455 LttEventType
*type
= ltt_event_eventtype(e
);
456 event_time
= ltt_event_time(e
);
457 cpu_id
= ltt_event_cpu_id(e
);
458 if ((ltt_time_compare(event_time
,event_data
->time_window
.start_time
) == TRUE
) &&
459 (ltt_time_compare(event_data
->time_window
.end_time
,event_time
) == TRUE
))
461 calcul_duration( event_time
, cpu_id
, event_data
);
468 * This function calculates the duration of an interrupt.
471 static void calcul_duration(LttTime time_exit
, guint cpu_id
,InterruptEventData
*event_data
){
476 GArray
*IrqExit
= event_data
->IrqExit
;
477 GArray
*IrqEntry
= event_data
->IrqEntry
;
478 for(i
= 0; i
< IrqEntry
->len
; i
++){
479 element
= &g_array_index(IrqEntry
,irq_entry
,i
);
480 if(element
->cpu_id
== cpu_id
)
482 sum_interrupt_data(element
,time_exit
, IrqExit
);
483 g_array_remove_index(IrqEntry
, i
);
489 * This function calculates the total duration of an interrupt.
492 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*IrqExit
)
498 gboolean notFound
= FALSE
;
499 memset ((void*)&irq
, 0,sizeof(Irq
));
502 if(IrqExit
->len
== NO_ITEMS
)
504 irq
.cpu_id
= e
->cpu_id
;
506 irq
.TotalNumberOfInterrupts
++;
507 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
508 g_array_append_val (IrqExit
, irq
);
511 for(i
= 0; i
< IrqExit
->len
; i
++)
513 element
= &g_array_index(IrqExit
, Irq
, i
);
514 if(element
->id
== e
->id
){
516 duration
= ltt_time_sub(time_exit
, e
->event_time
);
517 element
->total_duration
= ltt_time_add(element
->total_duration
, duration
);
518 element
->TotalNumberOfInterrupts
++;
524 irq
.cpu_id
= e
->cpu_id
;
526 irq
.TotalNumberOfInterrupts
++;
527 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
528 g_array_append_val (IrqExit
, irq
);
534 * This function displays the result on the viewer
537 static gboolean
interrupt_display(void *hook_data
, void *call_data
)
542 LttTime average_duration
;
547 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
548 GArray
*IrqExit
= event_data
->IrqExit
;
549 gtk_list_store_clear(event_data
->ListStore
);
550 for(i
= 0; i
< IrqExit
->len
; i
++)
553 element
= g_array_index(IrqExit
,Irq
,i
);
554 real_data
= element
.total_duration
.tv_sec
;
555 real_data
*= NANOSECONDS_PER_SECOND
;
556 real_data
+= element
.total_duration
.tv_nsec
;
558 FrequencyHZ
= FrequencyInHZ(element
.TotalNumberOfInterrupts
,event_data
->time_window
);
560 gtk_list_store_append (event_data
->ListStore
, &iter
);
562 gtk_list_store_set (event_data
->ListStore
, &iter
,
563 CPUID_COLUMN
, element
.cpu_id
,
564 IRQ_ID_COLUMN
, element
.id
,
565 FREQUENCY_COLUMN
, FrequencyHZ
,
566 DURATION_COLUMN
, real_data
,
571 if(event_data
->IrqExit
->len
)
572 g_array_remove_range (event_data
->IrqExit
,0,event_data
->IrqExit
->len
);
574 if(event_data
->IrqEntry
->len
)
575 g_array_remove_range (event_data
->IrqEntry
,0,event_data
->IrqEntry
->len
);
580 * This function converts the number of interrupts over a time window to
583 static int FrequencyInHZ(gint NumerofInterruptions
, TimeWindow time_window
)
585 guint64 frequencyHz
= 0;
586 double timeSec
; // time in second
588 result
= ltt_time_to_double(time_window
.time_width
);
589 timeSec
= (result
/NANOSECONDS_PER_SECOND
); //time in second
590 frequencyHz
= NumerofInterruptions
/ timeSec
;
596 * This function is called by the main window
597 * when the time interval needs to be updated.
599 gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
)
601 InterruptEventData
*event_data
= (InterruptEventData
*) hook_data
;
602 const TimeWindowNotifyData
*time_window_nofify_data
= ((const TimeWindowNotifyData
*)call_data
);
603 event_data
->time_window
= *time_window_nofify_data
->new_time_window
;
604 g_info("interrupts: interrupt_update_time_window()\n");
605 Tab
*tab
= event_data
->tab
;
606 lttvwindow_events_request_remove_all(tab
, event_data
);
607 request_event(event_data
);
612 gboolean
trace_header(void *hook_data
, void *call_data
)
615 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
616 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
622 void interrupt_destroy_walk(gpointer data
, gpointer user_data
)
624 g_info("interrupt_destroy_walk");
625 interrupt_destructor((InterruptEventData
*)data
);
629 void interrupt_destructor(InterruptEventData
*event_viewer_data
)
631 /* May already been done by GTK window closing */
632 g_info("enter interrupt_destructor \n");
633 if(GTK_IS_WIDGET(event_viewer_data
->Hbox
))
635 gtk_widget_destroy(event_viewer_data
->Hbox
);
640 This function is called when the viewer is destroyed to free hooks and memory
642 static void InterruptFree(InterruptEventData
*event_viewer_data
)
644 Tab
*tab
= event_viewer_data
->tab
;
647 g_array_free(event_viewer_data
->IrqExit
, TRUE
);
649 g_array_free(event_viewer_data
->IrqEntry
, TRUE
);
651 lttvwindow_unregister_time_window_notify(tab
, interrupt_update_time_window
, event_viewer_data
);
653 lttvwindow_events_request_remove_all(event_viewer_data
->tab
,
656 interrupt_data_list
= g_slist_remove(interrupt_data_list
, event_viewer_data
);
663 * plugin's destroy function
665 * This function releases the memory reserved by the module and unregisters
666 * everything that has been registered in the gtkTraceSet API.
668 static void destroy() {
669 g_info("Destroy interrupts");
670 g_slist_foreach(interrupt_data_list
, interrupt_destroy_walk
, NULL
);
671 g_slist_free(interrupt_data_list
);
672 lttvwindow_unregister_constructor(interrupts
);
676 LTTV_MODULE("tutorial", "interrupts info view", \
677 "Graphical module to display interrupts performance", \
678 init
, destroy
, "lttvwindow")