Commit | Line | Data |
---|---|---|
0a4ed526 | 1 | /* This file is part of the Linux Trace Toolkit viewer |
2 | * Copyright (C) 2005 Peter Ho | |
3 | * | |
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; | |
7 | * | |
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. | |
12 | * | |
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 | |
b9ce0bad YB |
15 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
16 | * MA 02110-1301, USA. | |
0a4ed526 | 17 | */ |
18 | /****************************************************************** | |
19 | Each field of the interrupt viewer is summarized as follows: | |
20 | ||
21 | - CPUID: processor ID | |
22 | ||
23 | - IrqId: IRQ ID | |
24 | ||
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. | |
28 | ||
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 | |
32 | ||
33 | ||
34 | *******************************************************************/ | |
35 | ||
36 | ||
37 | #include <math.h> | |
38 | #include <glib.h> | |
39 | #include <gtk/gtk.h> | |
40 | #include <gdk/gdk.h> | |
41 | #include <stdio.h> | |
42 | #include <stdlib.h> | |
43 | #include <string.h> | |
44 | #include <ltt/ltt.h> | |
45 | #include <ltt/event.h> | |
0a4ed526 | 46 | #include <ltt/trace.h> |
0a4ed526 | 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> | |
e433e6d6 | 53 | #include <lttvwindow/lttv_plugin_tab.h> |
0a4ed526 | 54 | #include <ltt/time.h> |
55 | ||
56 | #include "hTutorialInsert.xpm" | |
57 | ||
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) | |
60 | #define NO_ITEMS 0 | |
61 | ||
62 | typedef struct | |
63 | { | |
64 | guint cpu_id; | |
65 | guint id; | |
66 | guint TotalNumberOfInterrupts; | |
67 | LttTime total_duration; | |
68 | }Irq; | |
69 | ||
70 | typedef struct | |
71 | { | |
72 | guint id; | |
73 | guint cpu_id; | |
74 | LttTime event_time; | |
75 | }irq_entry; | |
76 | ||
77 | enum type_t | |
78 | { | |
79 | IRQ_ENTRY, | |
80 | IRQ_EXIT | |
81 | }; | |
82 | ||
83 | /** Array containing instanced objects. Used when module is unloaded */ | |
84 | static GSList *interrupt_data_list = NULL ; | |
85 | ||
86 | ||
87 | #define TRACE_NUMBER 0 | |
88 | ||
89 | typedef struct _InterruptEventData | |
90 | { | |
91 | ||
92 | /* Graphical Widgets */ | |
93 | GtkWidget * ScrollWindow; | |
94 | GtkListStore *ListStore; | |
95 | GtkWidget *Hbox; | |
96 | GtkWidget *TreeView; | |
97 | GtkTreeSelection *SelectionTree; | |
98 | ||
99 | Tab * tab; /* tab that contains this plug-in*/ | |
e433e6d6 | 100 | LttvPluginTab * ptab; |
0a4ed526 | 101 | LttvHooks * event_hooks; |
102 | LttvHooks * hooks_trace_after; | |
103 | LttvHooks * hooks_trace_before; | |
104 | TimeWindow time_window; | |
105 | LttvHooksById * event_by_id_hooks; | |
106 | GArray *IrqExit; | |
107 | GArray *IrqEntry; | |
108 | } InterruptEventData ; | |
109 | ||
110 | ||
111 | /* Function prototypes */ | |
112 | ||
113 | static gboolean interrupt_update_time_window(void * hook_data, void * call_data); | |
e433e6d6 | 114 | static GtkWidget *interrupts(LttvPlugin *plugin); |
115 | static InterruptEventData *system_info(LttvPluginTab *ptab); | |
0a4ed526 | 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 */ | |
128 | enum{ | |
129 | CPUID_COLUMN, | |
130 | IRQ_ID_COLUMN, | |
131 | FREQUENCY_COLUMN, | |
132 | DURATION_COLUMN, | |
133 | N_COLUMNS | |
134 | }; | |
135 | ||
136 | ||
137 | ||
138 | /** | |
139 | * init function | |
140 | * | |
141 | * | |
142 | * This is the entry point of the viewer. | |
143 | * | |
144 | */ | |
145 | static void init() | |
146 | { | |
147 | g_info("interrupts: init()"); | |
148 | ||
149 | lttvwindow_register_constructor("tutorial", | |
150 | "/", | |
151 | "Insert Interrupts View", | |
152 | hTutorialInsert_xpm, | |
153 | "Insert Interrupts View", | |
154 | interrupts); | |
155 | } | |
156 | ||
157 | ||
158 | /** | |
159 | * Constructor hook | |
160 | * | |
161 | */ | |
e433e6d6 | 162 | static GtkWidget *interrupts(LttvPlugin *plugin) |
0a4ed526 | 163 | { |
e433e6d6 | 164 | LttvPluginTab *ptab = LTTV_PLUGIN_TAB(plugin); |
165 | InterruptEventData* event_data = system_info(ptab) ; | |
0a4ed526 | 166 | if(event_data) |
167 | return event_data->Hbox; | |
168 | else | |
169 | return NULL; | |
170 | } | |
171 | ||
172 | /** | |
173 | * This function initializes the Event Viewer functionnality through the | |
174 | * GTK API. | |
175 | */ | |
e433e6d6 | 176 | InterruptEventData *system_info(LttvPluginTab *ptab) |
0a4ed526 | 177 | { |
178 | LttTime end; | |
179 | GtkTreeViewColumn *column; | |
180 | GtkCellRenderer *renderer; | |
181 | InterruptEventData* event_viewer_data = g_new(InterruptEventData,1) ; | |
e433e6d6 | 182 | Tab *tab = ptab->tab; |
0a4ed526 | 183 | |
184 | event_viewer_data->tab = tab; | |
e433e6d6 | 185 | event_viewer_data->ptab = ptab; |
0a4ed526 | 186 | |
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)); | |
191 | ||
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); | |
198 | ||
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 */ | |
206 | ); | |
207 | ||
208 | event_viewer_data->TreeView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->ListStore)); | |
209 | ||
210 | g_object_unref (G_OBJECT (event_viewer_data->ListStore)); | |
211 | ||
212 | renderer = gtk_cell_renderer_text_new (); | |
213 | column = gtk_tree_view_column_new_with_attributes ("CPU ID", | |
214 | renderer, | |
215 | "text", CPUID_COLUMN, | |
216 | NULL); | |
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); | |
220 | ||
221 | ||
222 | renderer = gtk_cell_renderer_text_new (); | |
223 | column = gtk_tree_view_column_new_with_attributes ("IRQ ID", | |
224 | renderer, | |
225 | "text", IRQ_ID_COLUMN, | |
226 | NULL); | |
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); | |
230 | ||
231 | renderer = gtk_cell_renderer_text_new (); | |
232 | column = gtk_tree_view_column_new_with_attributes ("Frequency (HZ)", | |
233 | renderer, | |
234 | "text", FREQUENCY_COLUMN, | |
235 | NULL); | |
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); | |
239 | ||
240 | renderer = gtk_cell_renderer_text_new (); | |
241 | column = gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)", | |
242 | renderer, | |
243 | "text", DURATION_COLUMN, | |
244 | NULL); | |
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); | |
248 | ||
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); | |
251 | ||
252 | gtk_container_add (GTK_CONTAINER (event_viewer_data->ScrollWindow), event_viewer_data->TreeView); | |
253 | ||
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); | |
256 | ||
257 | gtk_widget_show(event_viewer_data->Hbox); | |
258 | gtk_widget_show(event_viewer_data->TreeView); | |
259 | ||
260 | interrupt_data_list = g_slist_append(interrupt_data_list, event_viewer_data); | |
261 | ||
262 | /* Registration for time notification */ | |
263 | lttvwindow_register_time_window_notify(tab, | |
264 | interrupt_update_time_window, | |
265 | event_viewer_data); | |
266 | ||
267 | g_object_set_data_full(G_OBJECT(event_viewer_data->Hbox), | |
268 | "event_data", | |
269 | event_viewer_data, | |
270 | (GDestroyNotify) InterruptFree); | |
271 | ||
272 | ||
273 | request_event(event_viewer_data ); | |
274 | return event_viewer_data; | |
275 | } | |
276 | ||
277 | /** | |
278 | * | |
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 | |
284 | * | |
285 | */ | |
286 | static void request_event(InterruptEventData *event_data ) | |
287 | { | |
288 | guint i, k, l, nb_trace; | |
289 | ||
290 | LttvTraceHook *hook; | |
291 | ||
292 | guint ret; | |
293 | ||
294 | LttvTraceState *ts; | |
295 | ||
296 | GArray *hooks; | |
297 | ||
298 | EventsRequest *events_request; | |
299 | ||
300 | LttvTraceHookByFacility *thf; | |
301 | ||
302 | LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab); | |
303 | ||
304 | ||
305 | /* Get the traceset */ | |
306 | LttvTraceset *traceset = tsc->ts; | |
307 | ||
308 | nb_trace = lttv_traceset_number(traceset); | |
309 | ||
310 | /* There are many traces in a traceset. Iteration for each trace. */ | |
311 | for(i = 0; i<MIN(TRACE_NUMBER+1, nb_trace);i++) | |
312 | { | |
313 | events_request = g_new(EventsRequest, 1); | |
314 | ||
315 | hooks = g_array_new(FALSE, FALSE, sizeof(LttvTraceHook)); | |
316 | ||
317 | hooks = g_array_set_size(hooks, 2); | |
318 | ||
319 | event_data->hooks_trace_before = lttv_hooks_new(); | |
320 | ||
321 | /* Registers a hook function */ | |
322 | lttv_hooks_add(event_data->hooks_trace_before, trace_header, event_data, LTTV_PRIO_DEFAULT); | |
323 | ||
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(); | |
331 | ||
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, | |
336 | irq_entry_callback, | |
337 | events_request, | |
338 | &g_array_index(hooks, LttvTraceHook, 0)); | |
339 | ||
340 | ret = lttv_trace_find_hook(ts->parent.t, | |
341 | LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT, | |
342 | LTT_FIELD_IRQ_ID, 0, 0, | |
343 | irq_exit_callback, | |
344 | events_request, | |
345 | &g_array_index(hooks, LttvTraceHook, 1)); | |
346 | ||
347 | g_assert(!ret); | |
348 | ||
349 | /*iterate through the facility list*/ | |
350 | for(k = 0 ; k < hooks->len; k++) | |
351 | { | |
352 | hook = &g_array_index(hooks, LttvTraceHook, k); | |
353 | for(l=0; l<hook->fac_list->len; l++) | |
354 | { | |
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), | |
357 | thf->h, | |
358 | event_data, | |
359 | LTTV_PRIO_DEFAULT); | |
360 | ||
361 | } | |
362 | } | |
363 | ||
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; | |
375 | ||
376 | events_request->hooks = hooks; | |
377 | ||
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; | |
388 | ||
389 | lttvwindow_events_request(event_data->tab, events_request); | |
390 | } | |
391 | ||
392 | } | |
393 | ||
394 | /** | |
395 | * This function is called whenever an irq_entry event occurs. | |
396 | * | |
397 | */ | |
398 | static gboolean irq_entry_callback(void *hook_data, void *call_data) | |
399 | { | |
400 | ||
401 | LttTime event_time; | |
402 | unsigned cpu_id; | |
403 | irq_entry entry; | |
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); | |
411 | ||
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)) | |
414 | { | |
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); | |
419 | } | |
420 | return FALSE; | |
421 | } | |
422 | ||
423 | /** | |
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. | |
426 | */ | |
427 | static guint64 get_interrupt_id(LttEvent *e) | |
428 | { | |
429 | guint i, num_fields; | |
430 | LttEventType *event_type; | |
431 | LttField *element; | |
432 | LttField *field; | |
433 | guint64 irq_id; | |
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++) | |
437 | { | |
438 | field = ltt_eventtype_field(event_type, i); | |
439 | irq_id = ltt_event_get_long_unsigned(e,field); | |
440 | } | |
441 | return irq_id; | |
442 | ||
443 | } | |
444 | /** | |
445 | * This function is called whenever an irq_exit event occurs. | |
446 | * | |
447 | */ | |
448 | gboolean irq_exit_callback(void *hook_data, void *call_data) | |
449 | { | |
450 | LttTime event_time; | |
451 | unsigned cpu_id; | |
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)) | |
461 | { | |
462 | calcul_duration( event_time, cpu_id, event_data); | |
463 | ||
464 | } | |
465 | return FALSE; | |
466 | } | |
467 | ||
468 | /** | |
469 | * This function calculates the duration of an interrupt. | |
470 | * | |
471 | */ | |
472 | static void calcul_duration(LttTime time_exit, guint cpu_id,InterruptEventData *event_data){ | |
473 | ||
474 | gint i, irq_id; | |
475 | irq_entry *element; | |
476 | LttTime duration; | |
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) | |
482 | { | |
483 | sum_interrupt_data(element,time_exit, IrqExit); | |
484 | g_array_remove_index(IrqEntry, i); | |
485 | break; | |
486 | } | |
487 | } | |
488 | } | |
489 | /** | |
490 | * This function calculates the total duration of an interrupt. | |
491 | * | |
492 | */ | |
493 | static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *IrqExit) | |
494 | { | |
495 | Irq irq; | |
496 | Irq *element; | |
497 | guint i; | |
498 | LttTime duration; | |
499 | gboolean notFound = FALSE; | |
500 | memset ((void*)&irq, 0,sizeof(Irq)); | |
501 | ||
502 | ||
503 | if(IrqExit->len == NO_ITEMS) | |
504 | { | |
505 | irq.cpu_id = e->cpu_id; | |
506 | irq.id = e->id; | |
507 | irq.TotalNumberOfInterrupts++; | |
508 | irq.total_duration = ltt_time_sub(time_exit, e->event_time); | |
509 | g_array_append_val (IrqExit, irq); | |
510 | } | |
511 | else{ | |
512 | for(i = 0; i < IrqExit->len; i++) | |
513 | { | |
514 | element = &g_array_index(IrqExit, Irq, i); | |
515 | if(element->id == e->id){ | |
516 | notFound = TRUE; | |
517 | duration = ltt_time_sub(time_exit, e->event_time); | |
518 | element->total_duration = ltt_time_add(element->total_duration, duration); | |
519 | element->TotalNumberOfInterrupts++; | |
520 | } | |
521 | } | |
522 | ||
523 | if(!notFound) | |
524 | { | |
525 | irq.cpu_id = e->cpu_id; | |
526 | irq.id = e->id; | |
527 | irq.TotalNumberOfInterrupts++; | |
528 | irq.total_duration = ltt_time_sub(time_exit, e->event_time); | |
529 | g_array_append_val (IrqExit, irq); | |
530 | } | |
531 | } | |
532 | } | |
533 | ||
534 | /** | |
535 | * This function displays the result on the viewer | |
536 | * | |
537 | */ | |
538 | static gboolean interrupt_display(void *hook_data, void *call_data) | |
539 | { | |
540 | ||
541 | gint i; | |
542 | Irq element; | |
543 | LttTime average_duration; | |
544 | GtkTreeIter iter; | |
545 | guint64 real_data; | |
546 | int FrequencyHZ = 0; | |
547 | ||
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++) | |
552 | { | |
553 | ||
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; | |
558 | ||
559 | FrequencyHZ = FrequencyInHZ(element.TotalNumberOfInterrupts,event_data->time_window); | |
560 | ||
561 | gtk_list_store_append (event_data->ListStore, &iter); | |
562 | ||
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, | |
568 | -1); | |
569 | ||
570 | } | |
571 | ||
572 | if(event_data->IrqExit->len) | |
573 | g_array_remove_range (event_data->IrqExit,0,event_data->IrqExit->len); | |
574 | ||
575 | if(event_data->IrqEntry->len) | |
576 | g_array_remove_range (event_data->IrqEntry,0,event_data->IrqEntry->len); | |
577 | return FALSE; | |
578 | } | |
579 | ||
580 | /** | |
581 | * This function converts the number of interrupts over a time window to | |
582 | * frequency in HZ | |
583 | */ | |
584 | static int FrequencyInHZ(gint NumerofInterruptions, TimeWindow time_window) | |
585 | { | |
586 | guint64 frequencyHz = 0; | |
587 | double timeSec; // time in second | |
588 | double result; | |
589 | result = ltt_time_to_double(time_window.time_width); | |
590 | timeSec = (result/NANOSECONDS_PER_SECOND); //time in second | |
591 | frequencyHz = NumerofInterruptions / timeSec; | |
592 | return frequencyHz; | |
593 | } | |
594 | ||
595 | ||
596 | /* | |
597 | * This function is called by the main window | |
598 | * when the time interval needs to be updated. | |
599 | **/ | |
600 | gboolean interrupt_update_time_window(void * hook_data, void * call_data) | |
601 | { | |
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 ); | |
609 | return FALSE; | |
610 | } | |
611 | ||
612 | ||
613 | gboolean trace_header(void *hook_data, void *call_data) | |
614 | { | |
615 | ||
616 | InterruptEventData *event_data = (InterruptEventData *)hook_data; | |
617 | LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; | |
618 | LttEvent *e; | |
619 | LttTime event_time; | |
620 | return FALSE; | |
621 | } | |
622 | ||
623 | void interrupt_destroy_walk(gpointer data, gpointer user_data) | |
624 | { | |
625 | g_info("interrupt_destroy_walk"); | |
626 | interrupt_destructor((InterruptEventData*)data); | |
627 | ||
628 | } | |
629 | ||
630 | void interrupt_destructor(InterruptEventData *event_viewer_data) | |
631 | { | |
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)) | |
635 | { | |
636 | gtk_widget_destroy(event_viewer_data->Hbox); | |
637 | } | |
638 | } | |
639 | ||
640 | /** | |
641 | This function is called when the viewer is destroyed to free hooks and memory | |
642 | */ | |
643 | static void InterruptFree(InterruptEventData *event_viewer_data) | |
644 | { | |
645 | Tab *tab = event_viewer_data->tab; | |
646 | if(tab != NULL) | |
647 | { | |
648 | g_array_free(event_viewer_data->IrqExit, TRUE); | |
649 | ||
650 | g_array_free(event_viewer_data->IrqEntry, TRUE); | |
651 | ||
652 | lttvwindow_unregister_time_window_notify(tab, interrupt_update_time_window, event_viewer_data); | |
653 | ||
654 | lttvwindow_events_request_remove_all(event_viewer_data->tab, | |
655 | event_viewer_data); | |
656 | ||
657 | interrupt_data_list = g_slist_remove(interrupt_data_list, event_viewer_data); | |
658 | ||
659 | } | |
660 | ||
661 | } | |
662 | ||
663 | /** | |
664 | * plugin's destroy function | |
665 | * | |
666 | * This function releases the memory reserved by the module and unregisters | |
667 | * everything that has been registered in the gtkTraceSet API. | |
668 | */ | |
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); | |
674 | ||
675 | } | |
676 | ||
677 | LTTV_MODULE("tutorial", "interrupts info view", \ | |
678 | "Graphical module to display interrupts performance", \ | |
679 | init, destroy, "lttvwindow") |