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>
55 #include <lttvwindow/lttv_plugin_tab.h>
58 #include "hTutorialInsert.xpm"
60 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
61 #define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
68 guint TotalNumberOfInterrupts
;
69 LttTime total_duration
;
85 /** Array containing instanced objects. Used when module is unloaded */
86 static GSList
*interrupt_data_list
= NULL
;
89 #define TRACE_NUMBER 0
91 typedef struct _InterruptEventData
94 /* Graphical Widgets */
95 GtkWidget
* ScrollWindow
;
96 GtkListStore
*ListStore
;
99 GtkTreeSelection
*SelectionTree
;
101 Tab
* tab
; /* tab that contains this plug-in*/
102 LttvPluginTab
* ptab
;
103 LttvHooks
* event_hooks
;
104 LttvHooks
* hooks_trace_after
;
105 LttvHooks
* hooks_trace_before
;
106 TimeWindow time_window
;
107 LttvHooksById
* event_by_id_hooks
;
110 } InterruptEventData
;
113 /* Function prototypes */
115 static gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
);
116 static GtkWidget
*interrupts(LttvPlugin
*plugin
);
117 static InterruptEventData
*system_info(LttvPluginTab
*ptab
);
118 void interrupt_destructor(InterruptEventData
*event_viewer_data
);
119 static void request_event(InterruptEventData
*event_data
);
120 static guint64
get_interrupt_id(LttEvent
*e
);
121 static gboolean
trace_header(void *hook_data
, void *call_data
);
122 static gboolean
interrupt_display (void *hook_data
, void *call_data
);
123 static void calcul_duration(LttTime time_exit
, guint cpu_id
, InterruptEventData
*event_data
);
124 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*interrupt_counters
);
125 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
);
126 static gboolean
irq_exit_callback(void *hook_data
, void *call_data
);
127 static void InterruptFree(InterruptEventData
*event_viewer_data
);
128 static int FrequencyInHZ(gint NumerofInterruptions
, TimeWindow time_window
);
129 /* Enumeration of the columns */
144 * This is the entry point of the viewer.
149 g_info("interrupts: init()");
151 lttvwindow_register_constructor("tutorial",
153 "Insert Interrupts View",
155 "Insert Interrupts View",
164 static GtkWidget
*interrupts(LttvPlugin
*plugin
)
166 LttvPluginTab
*ptab
= LTTV_PLUGIN_TAB(plugin
);
167 InterruptEventData
* event_data
= system_info(ptab
) ;
169 return event_data
->Hbox
;
175 * This function initializes the Event Viewer functionnality through the
178 InterruptEventData
*system_info(LttvPluginTab
*ptab
)
181 GtkTreeViewColumn
*column
;
182 GtkCellRenderer
*renderer
;
183 InterruptEventData
* event_viewer_data
= g_new(InterruptEventData
,1) ;
184 Tab
*tab
= ptab
->tab
;
186 event_viewer_data
->tab
= tab
;
187 event_viewer_data
->ptab
= ptab
;
189 /*Get the current time frame from the main window */
190 event_viewer_data
->time_window
= lttvwindow_get_time_window(tab
);
191 event_viewer_data
->IrqExit
= g_array_new(FALSE
, FALSE
, sizeof(Irq
));
192 event_viewer_data
->IrqEntry
= g_array_new(FALSE
, FALSE
, sizeof(irq_entry
));
194 /*Create tha main window for the viewer */
195 event_viewer_data
->ScrollWindow
= gtk_scrolled_window_new (NULL
, NULL
);
196 gtk_widget_show (event_viewer_data
->ScrollWindow
);
197 gtk_scrolled_window_set_policy(
198 GTK_SCROLLED_WINDOW(event_viewer_data
->ScrollWindow
),
199 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
201 /* Create a model for storing the data list */
202 event_viewer_data
->ListStore
= gtk_list_store_new (
203 N_COLUMNS
, /* Total number of columns */
204 G_TYPE_INT
, /* CPUID */
205 G_TYPE_INT
, /* IRQ_ID */
206 G_TYPE_INT
, /* Frequency */
207 G_TYPE_UINT64
/* Duration */
210 event_viewer_data
->TreeView
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data
->ListStore
));
212 g_object_unref (G_OBJECT (event_viewer_data
->ListStore
));
214 renderer
= gtk_cell_renderer_text_new ();
215 column
= gtk_tree_view_column_new_with_attributes ("CPU ID",
217 "text", CPUID_COLUMN
,
219 gtk_tree_view_column_set_alignment (column
, 0.0);
220 gtk_tree_view_column_set_fixed_width (column
, 45);
221 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
224 renderer
= gtk_cell_renderer_text_new ();
225 column
= gtk_tree_view_column_new_with_attributes ("IRQ ID",
227 "text", IRQ_ID_COLUMN
,
229 gtk_tree_view_column_set_alignment (column
, 0.0);
230 gtk_tree_view_column_set_fixed_width (column
, 220);
231 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
233 renderer
= gtk_cell_renderer_text_new ();
234 column
= gtk_tree_view_column_new_with_attributes ("Frequency (HZ)",
236 "text", FREQUENCY_COLUMN
,
238 gtk_tree_view_column_set_alignment (column
, 1.0);
239 gtk_tree_view_column_set_fixed_width (column
, 220);
240 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
242 renderer
= gtk_cell_renderer_text_new ();
243 column
= gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)",
245 "text", DURATION_COLUMN
,
247 gtk_tree_view_column_set_alignment (column
, 0.0);
248 gtk_tree_view_column_set_fixed_width (column
, 145);
249 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data
->TreeView
), column
);
251 event_viewer_data
->SelectionTree
= gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data
->TreeView
));
252 gtk_tree_selection_set_mode (event_viewer_data
->SelectionTree
, GTK_SELECTION_SINGLE
);
254 gtk_container_add (GTK_CONTAINER (event_viewer_data
->ScrollWindow
), event_viewer_data
->TreeView
);
256 event_viewer_data
->Hbox
= gtk_hbox_new(0, 0);
257 gtk_box_pack_start(GTK_BOX(event_viewer_data
->Hbox
), event_viewer_data
->ScrollWindow
, TRUE
, TRUE
, 0);
259 gtk_widget_show(event_viewer_data
->Hbox
);
260 gtk_widget_show(event_viewer_data
->TreeView
);
262 interrupt_data_list
= g_slist_append(interrupt_data_list
, event_viewer_data
);
264 /* Registration for time notification */
265 lttvwindow_register_time_window_notify(tab
,
266 interrupt_update_time_window
,
269 g_object_set_data_full(G_OBJECT(event_viewer_data
->Hbox
),
272 (GDestroyNotify
) InterruptFree
);
275 request_event(event_viewer_data
);
276 return event_viewer_data
;
281 * For each trace in the traceset, this function:
282 * - registers a callback function to each hook
283 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
284 * - calls lttvwindow_events_request() to request data in a specific
285 * time interval to the main window
288 static void request_event(InterruptEventData
*event_data
)
290 guint i
, k
, l
, nb_trace
;
300 EventsRequest
*events_request
;
302 LttvTraceHookByFacility
*thf
;
304 LttvTracesetContext
*tsc
= lttvwindow_get_traceset_context(event_data
->tab
);
307 /* Get the traceset */
308 LttvTraceset
*traceset
= tsc
->ts
;
310 nb_trace
= lttv_traceset_number(traceset
);
312 /* There are many traces in a traceset. Iteration for each trace. */
313 for(i
= 0; i
<MIN(TRACE_NUMBER
+1, nb_trace
);i
++)
315 events_request
= g_new(EventsRequest
, 1);
317 hooks
= g_array_new(FALSE
, FALSE
, sizeof(LttvTraceHook
));
319 hooks
= g_array_set_size(hooks
, 2);
321 event_data
->hooks_trace_before
= lttv_hooks_new();
323 /* Registers a hook function */
324 lttv_hooks_add(event_data
->hooks_trace_before
, trace_header
, event_data
, LTTV_PRIO_DEFAULT
);
326 event_data
->hooks_trace_after
= lttv_hooks_new();
327 /* Registers a hook function */
328 lttv_hooks_add(event_data
->hooks_trace_after
, interrupt_display
, event_data
, LTTV_PRIO_DEFAULT
);
329 /* Get a trace state */
330 ts
= (LttvTraceState
*)tsc
->traces
[i
];
331 /* Create event_by_Id hooks */
332 event_data
->event_by_id_hooks
= lttv_hooks_by_id_new();
334 /*Register event_by_id_hooks with a callback function*/
335 ret
= lttv_trace_find_hook(ts
->parent
.t
,
336 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_ENTRY
,
337 LTT_FIELD_IRQ_ID
, 0, 0,
340 &g_array_index(hooks
, LttvTraceHook
, 0));
342 ret
= lttv_trace_find_hook(ts
->parent
.t
,
343 LTT_FACILITY_KERNEL
, LTT_EVENT_IRQ_EXIT
,
344 LTT_FIELD_IRQ_ID
, 0, 0,
347 &g_array_index(hooks
, LttvTraceHook
, 1));
351 /*iterate through the facility list*/
352 for(k
= 0 ; k
< hooks
->len
; k
++)
354 hook
= &g_array_index(hooks
, LttvTraceHook
, k
);
355 for(l
=0; l
<hook
->fac_list
->len
; l
++)
357 thf
= g_array_index(hook
->fac_list
, LttvTraceHookByFacility
*, l
);
358 lttv_hooks_add(lttv_hooks_by_id_find(event_data
->event_by_id_hooks
, thf
->id
),
366 /* Initalize the EventsRequest structure */
367 events_request
->owner
= event_data
;
368 events_request
->viewer_data
= event_data
;
369 events_request
->servicing
= FALSE
;
370 events_request
->start_time
= event_data
->time_window
.start_time
;
371 events_request
->start_position
= NULL
;
372 events_request
->stop_flag
= FALSE
;
373 events_request
->end_time
= event_data
->time_window
.end_time
;
374 events_request
->num_events
= G_MAXUINT
;
375 events_request
->end_position
= NULL
;
376 events_request
->trace
= i
;
378 events_request
->hooks
= hooks
;
380 events_request
->before_chunk_traceset
= NULL
;
381 events_request
->before_chunk_trace
= event_data
->hooks_trace_before
;
382 events_request
->before_chunk_tracefile
= NULL
;
383 events_request
->event
= NULL
;
384 events_request
->event_by_id
= event_data
->event_by_id_hooks
;
385 events_request
->after_chunk_tracefile
= NULL
;
386 events_request
->after_chunk_trace
= NULL
;
387 events_request
->after_chunk_traceset
= NULL
;
388 events_request
->before_request
= NULL
;
389 events_request
->after_request
= event_data
->hooks_trace_after
;
391 lttvwindow_events_request(event_data
->tab
, events_request
);
397 * This function is called whenever an irq_entry event occurs.
400 static gboolean
irq_entry_callback(void *hook_data
, void *call_data
)
406 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
407 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
408 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
409 GArray
* IrqEntry
= event_data
->IrqEntry
;
410 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
411 event_time
= ltt_event_time(e
);
412 cpu_id
= ltt_event_cpu_id(e
);
414 if ((ltt_time_compare(event_time
,event_data
->time_window
.start_time
) == TRUE
) &&
415 (ltt_time_compare(event_data
->time_window
.end_time
,event_time
) == TRUE
))
417 entry
.id
=get_interrupt_id(e
);
418 entry
.cpu_id
= cpu_id
;
419 entry
.event_time
= event_time
;
420 g_array_append_val (IrqEntry
, entry
);
426 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
427 * Refer to the print.c file for howto extract data from a dynamic structure.
429 static guint64
get_interrupt_id(LttEvent
*e
)
432 LttEventType
*event_type
;
436 event_type
= ltt_event_eventtype(e
);
437 num_fields
= ltt_eventtype_num_fields(event_type
);
438 for(i
= 0 ; i
< num_fields
-1 ; i
++)
440 field
= ltt_eventtype_field(event_type
, i
);
441 irq_id
= ltt_event_get_long_unsigned(e
,field
);
447 * This function is called whenever an irq_exit event occurs.
450 gboolean
irq_exit_callback(void *hook_data
, void *call_data
)
454 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
455 LttvTracefileState
*tfs
= (LttvTracefileState
*)call_data
;
456 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
457 LttEvent
*e
= ltt_tracefile_get_event(tfc
->tf
);
458 LttEventType
*type
= ltt_event_eventtype(e
);
459 event_time
= ltt_event_time(e
);
460 cpu_id
= ltt_event_cpu_id(e
);
461 if ((ltt_time_compare(event_time
,event_data
->time_window
.start_time
) == TRUE
) &&
462 (ltt_time_compare(event_data
->time_window
.end_time
,event_time
) == TRUE
))
464 calcul_duration( event_time
, cpu_id
, event_data
);
471 * This function calculates the duration of an interrupt.
474 static void calcul_duration(LttTime time_exit
, guint cpu_id
,InterruptEventData
*event_data
){
479 GArray
*IrqExit
= event_data
->IrqExit
;
480 GArray
*IrqEntry
= event_data
->IrqEntry
;
481 for(i
= 0; i
< IrqEntry
->len
; i
++){
482 element
= &g_array_index(IrqEntry
,irq_entry
,i
);
483 if(element
->cpu_id
== cpu_id
)
485 sum_interrupt_data(element
,time_exit
, IrqExit
);
486 g_array_remove_index(IrqEntry
, i
);
492 * This function calculates the total duration of an interrupt.
495 static void sum_interrupt_data(irq_entry
*e
, LttTime time_exit
, GArray
*IrqExit
)
501 gboolean notFound
= FALSE
;
502 memset ((void*)&irq
, 0,sizeof(Irq
));
505 if(IrqExit
->len
== NO_ITEMS
)
507 irq
.cpu_id
= e
->cpu_id
;
509 irq
.TotalNumberOfInterrupts
++;
510 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
511 g_array_append_val (IrqExit
, irq
);
514 for(i
= 0; i
< IrqExit
->len
; i
++)
516 element
= &g_array_index(IrqExit
, Irq
, i
);
517 if(element
->id
== e
->id
){
519 duration
= ltt_time_sub(time_exit
, e
->event_time
);
520 element
->total_duration
= ltt_time_add(element
->total_duration
, duration
);
521 element
->TotalNumberOfInterrupts
++;
527 irq
.cpu_id
= e
->cpu_id
;
529 irq
.TotalNumberOfInterrupts
++;
530 irq
.total_duration
= ltt_time_sub(time_exit
, e
->event_time
);
531 g_array_append_val (IrqExit
, irq
);
537 * This function displays the result on the viewer
540 static gboolean
interrupt_display(void *hook_data
, void *call_data
)
545 LttTime average_duration
;
550 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
551 GArray
*IrqExit
= event_data
->IrqExit
;
552 gtk_list_store_clear(event_data
->ListStore
);
553 for(i
= 0; i
< IrqExit
->len
; i
++)
556 element
= g_array_index(IrqExit
,Irq
,i
);
557 real_data
= element
.total_duration
.tv_sec
;
558 real_data
*= NANOSECONDS_PER_SECOND
;
559 real_data
+= element
.total_duration
.tv_nsec
;
561 FrequencyHZ
= FrequencyInHZ(element
.TotalNumberOfInterrupts
,event_data
->time_window
);
563 gtk_list_store_append (event_data
->ListStore
, &iter
);
565 gtk_list_store_set (event_data
->ListStore
, &iter
,
566 CPUID_COLUMN
, element
.cpu_id
,
567 IRQ_ID_COLUMN
, element
.id
,
568 FREQUENCY_COLUMN
, FrequencyHZ
,
569 DURATION_COLUMN
, real_data
,
574 if(event_data
->IrqExit
->len
)
575 g_array_remove_range (event_data
->IrqExit
,0,event_data
->IrqExit
->len
);
577 if(event_data
->IrqEntry
->len
)
578 g_array_remove_range (event_data
->IrqEntry
,0,event_data
->IrqEntry
->len
);
583 * This function converts the number of interrupts over a time window to
586 static int FrequencyInHZ(gint NumerofInterruptions
, TimeWindow time_window
)
588 guint64 frequencyHz
= 0;
589 double timeSec
; // time in second
591 result
= ltt_time_to_double(time_window
.time_width
);
592 timeSec
= (result
/NANOSECONDS_PER_SECOND
); //time in second
593 frequencyHz
= NumerofInterruptions
/ timeSec
;
599 * This function is called by the main window
600 * when the time interval needs to be updated.
602 gboolean
interrupt_update_time_window(void * hook_data
, void * call_data
)
604 InterruptEventData
*event_data
= (InterruptEventData
*) hook_data
;
605 const TimeWindowNotifyData
*time_window_nofify_data
= ((const TimeWindowNotifyData
*)call_data
);
606 event_data
->time_window
= *time_window_nofify_data
->new_time_window
;
607 g_info("interrupts: interrupt_update_time_window()\n");
608 Tab
*tab
= event_data
->tab
;
609 lttvwindow_events_request_remove_all(tab
, event_data
);
610 request_event(event_data
);
615 gboolean
trace_header(void *hook_data
, void *call_data
)
618 InterruptEventData
*event_data
= (InterruptEventData
*)hook_data
;
619 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
625 void interrupt_destroy_walk(gpointer data
, gpointer user_data
)
627 g_info("interrupt_destroy_walk");
628 interrupt_destructor((InterruptEventData
*)data
);
632 void interrupt_destructor(InterruptEventData
*event_viewer_data
)
634 /* May already been done by GTK window closing */
635 g_info("enter interrupt_destructor \n");
636 if(GTK_IS_WIDGET(event_viewer_data
->Hbox
))
638 gtk_widget_destroy(event_viewer_data
->Hbox
);
643 This function is called when the viewer is destroyed to free hooks and memory
645 static void InterruptFree(InterruptEventData
*event_viewer_data
)
647 Tab
*tab
= event_viewer_data
->tab
;
650 g_array_free(event_viewer_data
->IrqExit
, TRUE
);
652 g_array_free(event_viewer_data
->IrqEntry
, TRUE
);
654 lttvwindow_unregister_time_window_notify(tab
, interrupt_update_time_window
, event_viewer_data
);
656 lttvwindow_events_request_remove_all(event_viewer_data
->tab
,
659 interrupt_data_list
= g_slist_remove(interrupt_data_list
, event_viewer_data
);
666 * plugin's destroy function
668 * This function releases the memory reserved by the module and unregisters
669 * everything that has been registered in the gtkTraceSet API.
671 static void destroy() {
672 g_info("Destroy interrupts");
673 g_slist_foreach(interrupt_data_list
, interrupt_destroy_walk
, NULL
);
674 g_slist_free(interrupt_data_list
);
675 lttvwindow_unregister_constructor(interrupts
);
679 LTTV_MODULE("tutorial", "interrupts info view", \
680 "Graphical module to display interrupts performance", \
681 init
, destroy
, "lttvwindow")