add InterruptFree()
[lttv.git] / ltt / branches / poly / lttv / modules / gui / interrupts / interrupts.c
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
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
17 */
18
19 /******************************************************************
20
21 The standard deviation calculation is based on:
22 http://en.wikipedia.org/wiki/Standard_deviation
23
24 Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2))
25
26 To compute the standard deviation, we need to make EventRequests to LTTV. In
27 the first EventRequest, we compute the average duration (Xa) and the
28 frequency (N) of each IrqID. We store the information calculated in the first
29 EventRequest in an array called FirstRequestIrqExit.
30 In the second EventRequest, we compute the Sum ((xi -Xa)^2) and store this information
31 in a array called SumArray. The function CalculateDurationStandardDeviation() uses FirstRequestIrqExit
32 and SumArray arrays to calculate the standard deviation.
33
34
35
36 CPUID: processor ID
37
38 IrqId: IRQ ID
39
40 Frequency (Hz): the number of interruptions per second (Hz)
41
42 Total Duration (nsec): the sum of each interrupt duration in nsec
43
44 Duration standard deviation (nsec): taken from http://en.wikipedia.org/wiki/Standard_deviation
45 Duration Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) where
46 N: number of interrupts
47 xi: duration of an interrupt (nsec)
48 Xa: average duration (nsec)
49
50 Max IRQ handler duration (nsec) [time interval]: the longest IRQ handler duration in nsec.
51
52 Average period (nsec): 1/frequency
53
54
55 Period Standard_deviation = sqrt(1/N Sum ((xi -Xa)^2)) where
56 N: number of interrupts
57 xi: duration of an interrupt
58 Xa: 1/frequency
59
60 *******************************************************************/
61
62
63
64 #include <math.h>
65 #include <glib.h>
66 #include <gtk/gtk.h>
67 #include <gdk/gdk.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <math.h>
71 #include <string.h>
72 #include <ltt/ltt.h>
73 #include <ltt/event.h>
74 #include <ltt/type.h>
75 #include <ltt/trace.h>
76 #include <ltt/facility.h>
77 #include <lttv/module.h>
78 #include <lttv/hook.h>
79 #include <lttv/tracecontext.h>
80 #include <lttv/state.h>
81 #include <lttv/filter.h>
82 #include <lttvwindow/lttvwindow.h>
83 #include <ltt/time.h>
84
85 #include "hInterruptsInsert.xpm"
86
87 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
88 #define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
89 #define NO_ITEMS 0
90
91 typedef struct
92 {
93 LttTime duration;
94 LttTime start_time;
95 LttTime end_time;
96 }MaxDuration;
97
98 typedef struct {
99 guint cpu_id;
100 guint id;
101 guint frequency;
102 LttTime total_duration;
103 guint average_duration;
104 MaxDuration max_irq_handler;
105
106 }Irq;
107
108 typedef struct {
109 guint id;
110 guint cpu_id;
111 LttTime event_time;
112 }irq_entry;
113
114
115 typedef struct
116 {
117 guint irqId;
118 guint frequency;
119 guint64 sumOfDurations;
120 guint64 sumOfPeriods;
121
122 }SumId;
123
124 enum type_t {
125 IRQ_ENTRY,
126 IRQ_EXIT
127 };
128
129 /** Array containing instanced objects. Used when module is unloaded */
130 static GSList *interrupt_data_list = NULL ;
131
132
133 #define TRACE_NUMBER 0
134
135 typedef struct _InterruptEventData {
136
137 /*Graphical Widgets */
138 GtkWidget * ScrollWindow;
139 GtkListStore *ListStore;
140 GtkWidget *Hbox;
141 GtkWidget *TreeView;
142 GtkTreeSelection *SelectionTree;
143
144 Tab * tab; /* tab that contains this plug-in*/
145 LttvHooks * event_hooks;
146 LttvHooks * hooks_trace_after;
147 LttvHooks * hooks_trace_before;
148 TimeWindow time_window;
149 LttvHooksById * event_by_id_hooks;
150 GArray *FirstRequestIrqExit;
151 GArray *FirstRequestIrqEntry;
152 GArray *SecondRequestIrqEntry;
153 GArray *SecondRequestIrqExit;
154 GArray *SumArray;
155
156 } InterruptEventData ;
157
158
159 /* Function prototypes */
160
161 static gboolean interrupt_update_time_window(void * hook_data, void * call_data);
162 static GtkWidget *interrupts(Tab *tab);
163 static InterruptEventData *system_info(Tab *tab);
164 void interrupt_destructor(InterruptEventData *event_viewer_data);
165 static void FirstRequest(InterruptEventData *event_data );
166 static guint64 get_interrupt_id(LttEvent *e);
167 static gboolean trace_header(void *hook_data, void *call_data);
168 static gboolean DisplayViewer (void *hook_data, void *call_data);
169 static void CalculateData(LttTime time_exit, guint cpu_id, InterruptEventData *event_data);
170 static void TotalDurationMaxIrqDuration(irq_entry *e, LttTime time_exit, GArray *FirstRequestIrqExit);
171 static gboolean FirstRequestIrqEntryCallback(void *hook_data, void *call_data);
172 static gboolean FirstRequestIrqExitCallback(void *hook_data, void *call_data);
173 static gboolean SecondRequest(void *hook_data, void *call_data);
174 static void CalculateAverageDurationForEachIrqId(InterruptEventData *event_data);
175 static gboolean SecondRequestIrqEntryCallback(void *hook_data, void *call_data);
176 static gboolean SecondRequestIrqExitCallback(void *hook_data, void *call_data);
177 static void CalculateXi(LttEvent *event, InterruptEventData *event_data);
178 static void SumItems(gint irq_id, LttTime Xi, InterruptEventData *event_data);
179 static int CalculateDurationStandardDeviation(gint id, InterruptEventData *event_data);
180 static int CalculatePeriodStandardDeviation(gint id, InterruptEventData *event_data);
181 static int FrequencyInHZ(gint frequency, TimeWindow time_window);
182 static guint64 CalculatePeriodInnerPart(guint Xi, guint FrequencyHZ);
183 static void InterruptFree(InterruptEventData *event_viewer_data);
184 /* Enumeration of the columns */
185 enum{
186 CPUID_COLUMN,
187 IRQ_ID_COLUMN,
188 FREQUENCY_COLUMN,
189 DURATION_COLUMN,
190 DURATION_STANDARD_DEV_COLUMN,
191 MAX_IRQ_HANDLER_COLUMN,
192 AVERAGE_PERIOD,
193 PERIOD_STANDARD_DEV_COLUMN,
194 N_COLUMNS
195 };
196
197
198
199 /**
200 * init function
201 *
202 *
203 * This is the entry point of the viewer.
204 *
205 */
206 static void init() {
207 g_info("interrupts: init()");
208 lttvwindow_register_constructor("interrupts",
209 "/",
210 "Insert Interrupts View",
211 hInterruptsInsert_xpm,
212 "Insert Interrupts View",
213 interrupts);
214
215 }
216
217
218 /**
219 * Constructor hook
220 *
221 */
222 static GtkWidget *interrupts(Tab * tab)
223 {
224
225 InterruptEventData* event_data = system_info(tab) ;
226 if(event_data)
227 return event_data->Hbox;
228 else
229 return NULL;
230 }
231
232 /**
233 * This function initializes the Event Viewer functionnality through the
234 * GTK API.
235 */
236 InterruptEventData *system_info(Tab *tab)
237 {
238
239 LttTime end;
240 GtkTreeViewColumn *column;
241 GtkCellRenderer *renderer;
242 InterruptEventData* event_viewer_data = g_new(InterruptEventData,1) ;
243
244 event_viewer_data->tab = tab;
245
246 /*Get the current time frame from the main window */
247 event_viewer_data->time_window = lttvwindow_get_time_window(tab);
248
249 event_viewer_data->FirstRequestIrqExit = g_array_new(FALSE, FALSE, sizeof(Irq));
250 event_viewer_data->FirstRequestIrqEntry = g_array_new(FALSE, FALSE, sizeof(irq_entry));
251
252 event_viewer_data->SecondRequestIrqEntry = g_array_new(FALSE, FALSE, sizeof(irq_entry));
253 event_viewer_data->SecondRequestIrqExit = g_array_new(FALSE, FALSE, sizeof(Irq));
254
255 event_viewer_data->SumArray = g_array_new(FALSE, FALSE, sizeof(SumId));
256
257
258 /*Create tha main window for the viewer */
259 event_viewer_data->ScrollWindow = gtk_scrolled_window_new (NULL, NULL);
260 gtk_widget_show (event_viewer_data->ScrollWindow);
261 gtk_scrolled_window_set_policy(
262 GTK_SCROLLED_WINDOW(event_viewer_data->ScrollWindow),
263 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC/*GTK_POLICY_NEVER*/);
264
265 /* Create a model for storing the data list */
266 event_viewer_data->ListStore = gtk_list_store_new (
267 N_COLUMNS, /* Total number of columns */
268 G_TYPE_INT, /* CPUID */
269 G_TYPE_INT, /* IRQ_ID */
270 G_TYPE_INT, /* Frequency */
271 G_TYPE_UINT64, /* Duration */
272 G_TYPE_INT, /* standard deviation */
273 G_TYPE_STRING, /* Max IRQ handler */
274 G_TYPE_INT, /* Average period */
275 G_TYPE_INT /* period standard deviation */
276 );
277
278 event_viewer_data->TreeView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->ListStore));
279
280 g_object_unref (G_OBJECT (event_viewer_data->ListStore));
281
282 renderer = gtk_cell_renderer_text_new ();
283 column = gtk_tree_view_column_new_with_attributes ("CPUID",
284 renderer,
285 "text", CPUID_COLUMN,
286 NULL);
287 gtk_tree_view_column_set_alignment (column, 0.0);
288 gtk_tree_view_column_set_fixed_width (column, 45);
289 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
290
291
292 renderer = gtk_cell_renderer_text_new ();
293 column = gtk_tree_view_column_new_with_attributes ("IrqId",
294 renderer,
295 "text", IRQ_ID_COLUMN,
296 NULL);
297 gtk_tree_view_column_set_alignment (column, 0.0);
298 gtk_tree_view_column_set_fixed_width (column, 220);
299 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
300
301 renderer = gtk_cell_renderer_text_new ();
302 column = gtk_tree_view_column_new_with_attributes ("Frequency (Hz)",
303 renderer,
304 "text", FREQUENCY_COLUMN,
305 NULL);
306 gtk_tree_view_column_set_alignment (column, 1.0);
307 gtk_tree_view_column_set_fixed_width (column, 220);
308 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
309
310 renderer = gtk_cell_renderer_text_new ();
311 column = gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)",
312 renderer,
313 "text", DURATION_COLUMN,
314 NULL);
315 gtk_tree_view_column_set_alignment (column, 0.0);
316 gtk_tree_view_column_set_fixed_width (column, 145);
317 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
318
319
320 renderer = gtk_cell_renderer_text_new ();
321 column = gtk_tree_view_column_new_with_attributes ("Duration standard deviation (nsec)",
322 renderer,
323 "text", DURATION_STANDARD_DEV_COLUMN,
324 NULL);
325 gtk_tree_view_column_set_alignment (column, 0.0);
326 gtk_tree_view_column_set_fixed_width (column, 200);
327 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
328
329 renderer = gtk_cell_renderer_text_new ();
330 column = gtk_tree_view_column_new_with_attributes ("Max IRQ handler duration (nsec) [time interval]",
331 renderer,
332 "text", MAX_IRQ_HANDLER_COLUMN,
333 NULL);
334 gtk_tree_view_column_set_alignment (column, 0.0);
335 gtk_tree_view_column_set_fixed_width (column, 250);
336 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
337
338 renderer = gtk_cell_renderer_text_new ();
339 column = gtk_tree_view_column_new_with_attributes (" Average period (nsec)",
340 renderer,
341 "text", AVERAGE_PERIOD,
342 NULL);
343 gtk_tree_view_column_set_alignment (column, 0.0);
344 gtk_tree_view_column_set_fixed_width (column, 200);
345 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
346
347 renderer = gtk_cell_renderer_text_new ();
348 column = gtk_tree_view_column_new_with_attributes ("Period standard deviation (nsec)",
349 renderer,
350 "text", PERIOD_STANDARD_DEV_COLUMN,
351 NULL);
352 gtk_tree_view_column_set_alignment (column, 0.0);
353 gtk_tree_view_column_set_fixed_width (column, 200);
354 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
355
356
357
358 event_viewer_data->SelectionTree = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data->TreeView));
359 gtk_tree_selection_set_mode (event_viewer_data->SelectionTree, GTK_SELECTION_SINGLE);
360
361 gtk_container_add (GTK_CONTAINER (event_viewer_data->ScrollWindow), event_viewer_data->TreeView);
362
363 event_viewer_data->Hbox = gtk_hbox_new(0, 0);
364 gtk_box_pack_start(GTK_BOX(event_viewer_data->Hbox), event_viewer_data->ScrollWindow, TRUE, TRUE, 0);
365
366 gtk_widget_show(event_viewer_data->Hbox);
367 gtk_widget_show(event_viewer_data->TreeView);
368
369 interrupt_data_list = g_slist_append(interrupt_data_list, event_viewer_data);
370 /* Registration for time notification */
371 lttvwindow_register_time_window_notify(tab,
372 interrupt_update_time_window,
373 event_viewer_data);
374
375 g_object_set_data_full(G_OBJECT(event_viewer_data->Hbox),
376 "event_data",
377 event_viewer_data,
378 (GDestroyNotify) InterruptFree);
379
380 FirstRequest(event_viewer_data );
381 return event_viewer_data;
382 }
383
384
385 /**
386 *
387 * For each trace in the traceset, this function:
388 * - registers a callback function to each hook
389 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
390 * - calls lttvwindow_events_request() to request data in a specific
391 * time interval to the main window
392 *
393 */
394 static void FirstRequest(InterruptEventData *event_data )
395 {
396 guint i, k, l, nb_trace;
397
398 LttvTraceHook *hook;
399
400 guint ret;
401
402 LttvTraceState *ts;
403
404 GArray *hooks;
405
406 EventsRequest *events_request;
407
408 LttvTraceHookByFacility *thf;
409
410 LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab);
411
412
413 /* Get the traceset */
414 LttvTraceset *traceset = tsc->ts;
415
416 nb_trace = lttv_traceset_number(traceset);
417
418 /* There are many traces in a traceset. Iteration for each trace. */
419 for(i = 0; i<MIN(TRACE_NUMBER+1, nb_trace);i++)
420 {
421 events_request = g_new(EventsRequest, 1);
422
423 hooks = g_array_new(FALSE, FALSE, sizeof(LttvTraceHook));
424
425 hooks = g_array_set_size(hooks, 2);
426
427 event_data->hooks_trace_before = lttv_hooks_new();
428
429 /* Registers a hook function */
430 lttv_hooks_add(event_data->hooks_trace_before, trace_header, event_data, LTTV_PRIO_DEFAULT);
431
432 event_data->hooks_trace_after = lttv_hooks_new();
433
434 /* Registers a hook function */
435 lttv_hooks_add(event_data->hooks_trace_after, SecondRequest, event_data, LTTV_PRIO_DEFAULT);
436 /* Get a trace state */
437 ts = (LttvTraceState *)tsc->traces[i];
438 /* Create event_by_Id hooks */
439 event_data->event_by_id_hooks = lttv_hooks_by_id_new();
440
441 /*Register event_by_id_hooks with a callback function*/
442 ret = lttv_trace_find_hook(ts->parent.t,
443 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY,
444 LTT_FIELD_IRQ_ID, 0, 0,
445 FirstRequestIrqEntryCallback,
446 events_request,
447 &g_array_index(hooks, LttvTraceHook, 0));
448
449 ret = lttv_trace_find_hook(ts->parent.t,
450 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT,
451 LTT_FIELD_IRQ_ID, 0, 0,
452 FirstRequestIrqExitCallback,
453 events_request,
454 &g_array_index(hooks, LttvTraceHook, 1));
455
456 g_assert(!ret);
457 /*iterate through the facility list*/
458 for(k = 0 ; k < hooks->len; k++)
459 {
460 hook = &g_array_index(hooks, LttvTraceHook, k);
461 for(l=0; l<hook->fac_list->len; l++)
462 {
463 thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l);
464 lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, thf->id),
465 thf->h,
466 event_data,
467 LTTV_PRIO_DEFAULT);
468
469 }
470 }
471 /* Initalize the EventsRequest structure */
472 events_request->owner = event_data;
473 events_request->viewer_data = event_data;
474 events_request->servicing = FALSE;
475 events_request->start_time = event_data->time_window.start_time;
476 events_request->start_position = NULL;
477 events_request->stop_flag = FALSE;
478 events_request->end_time = event_data->time_window.end_time;
479 events_request->num_events = G_MAXUINT;
480 events_request->end_position = NULL;
481 events_request->trace = i;
482
483 events_request->hooks = hooks;
484
485 events_request->before_chunk_traceset = NULL;
486 events_request->before_chunk_trace = event_data->hooks_trace_before;
487 events_request->before_chunk_tracefile= NULL;
488 events_request->event = NULL;
489 events_request->event_by_id = event_data->event_by_id_hooks;
490 events_request->after_chunk_tracefile = NULL;
491 events_request->after_chunk_trace = NULL;
492 events_request->after_chunk_traceset = NULL;
493 events_request->before_request = NULL;
494 events_request->after_request = event_data->hooks_trace_after;
495
496 lttvwindow_events_request(event_data->tab, events_request);
497 }
498
499 }
500
501 /**
502 * This function is called whenever an irq_entry event occurs.
503 *
504 */
505 static gboolean FirstRequestIrqEntryCallback(void *hook_data, void *call_data)
506 {
507
508 LttTime event_time;
509 unsigned cpu_id;
510 irq_entry entry;
511 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
512 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
513 InterruptEventData *event_data = (InterruptEventData *)hook_data;
514 GArray* FirstRequestIrqEntry = event_data->FirstRequestIrqEntry;
515 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
516 event_time = ltt_event_time(e);
517 cpu_id = ltt_event_cpu_id(e);
518
519
520 entry.id =get_interrupt_id(e);
521 entry.cpu_id = cpu_id;
522 entry.event_time = event_time;
523 g_array_append_val (FirstRequestIrqEntry, entry);
524
525 return FALSE;
526 }
527
528 /**
529 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
530 * Refer to the print.c file for howto extract data from a dynamic structure.
531 */
532 static guint64 get_interrupt_id(LttEvent *e)
533 {
534 guint i, num_fields;
535 LttEventType *event_type;
536 LttField *element;
537 LttField *field;
538 guint64 irq_id;
539 event_type = ltt_event_eventtype(e);
540 num_fields = ltt_eventtype_num_fields(event_type);
541 for(i = 0 ; i < num_fields-1 ; i++)
542 {
543 field = ltt_eventtype_field(event_type, i);
544 irq_id = ltt_event_get_long_unsigned(e,field);
545 }
546 return irq_id;
547
548 }
549 /**
550 * This function is called whenever an irq_exit event occurs.
551 *
552 */
553 gboolean FirstRequestIrqExitCallback(void *hook_data, void *call_data)
554 {
555 LttTime event_time;
556 unsigned cpu_id;
557 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
558 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
559 InterruptEventData *event_data = (InterruptEventData *)hook_data;
560 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
561 LttEventType *type = ltt_event_eventtype(e);
562 event_time = ltt_event_time(e);
563 cpu_id = ltt_event_cpu_id(e);
564
565 CalculateData( event_time, cpu_id, event_data);
566
567 return FALSE;
568 }
569
570 /**
571 * This function calculates the duration of an interrupt.
572 *
573 */
574 static void CalculateData(LttTime time_exit, guint cpu_id,InterruptEventData *event_data)
575 {
576
577 gint i, irq_id;
578 irq_entry *element;
579 LttTime duration;
580 GArray *FirstRequestIrqExit = event_data->FirstRequestIrqExit;
581 GArray *FirstRequestIrqEntry = event_data->FirstRequestIrqEntry;
582 for(i = 0; i < FirstRequestIrqEntry->len; i++)
583 {
584 element = &g_array_index(FirstRequestIrqEntry,irq_entry,i);
585 if(element->cpu_id == cpu_id)
586 {
587 TotalDurationMaxIrqDuration(element,time_exit, FirstRequestIrqExit);
588 g_array_remove_index(FirstRequestIrqEntry, i);
589 break;
590 }
591 }
592 }
593
594 /**
595 * This function calculates the total duration of an interrupt and the longest Irq handler.
596 *
597 */
598 static void TotalDurationMaxIrqDuration(irq_entry *e, LttTime time_exit, GArray *FirstRequestIrqExit){
599 Irq irq;
600 Irq *element;
601 guint i;
602 LttTime duration;
603 gboolean notFound = FALSE;
604 memset ((void*)&irq, 0,sizeof(Irq));
605
606 /*first time*/
607 if(FirstRequestIrqExit->len == NO_ITEMS)
608 {
609 irq.cpu_id = e->cpu_id;
610 irq.id = e->id;
611 irq.frequency++;
612 irq.total_duration = ltt_time_sub(time_exit, e->event_time);
613
614 irq.max_irq_handler.start_time = e->event_time;
615 irq.max_irq_handler.end_time = time_exit;
616 irq.max_irq_handler.duration = ltt_time_sub(time_exit, e->event_time);
617
618 g_array_append_val (FirstRequestIrqExit, irq);
619 }
620 else
621 {
622 for(i = 0; i < FirstRequestIrqExit->len; i++)
623 {
624 element = &g_array_index(FirstRequestIrqExit,Irq,i);
625 if(element->id == e->id)
626 {
627 notFound = TRUE;
628 duration = ltt_time_sub(time_exit, e->event_time);
629 element->total_duration = ltt_time_add(element->total_duration, duration);
630 element->frequency++;
631 if(ltt_time_compare(duration,element->max_irq_handler.duration) > 0)
632 {
633 element->max_irq_handler.duration = duration;
634 element->max_irq_handler.start_time = e->event_time;
635 element->max_irq_handler.end_time = time_exit;
636 }
637 }
638 }
639 if(!notFound)
640 {
641 irq.cpu_id = e->cpu_id;
642 irq.id = e->id;
643 irq.frequency++;
644 irq.total_duration = ltt_time_sub(time_exit, e->event_time);
645
646 irq.max_irq_handler.start_time = e->event_time;
647 irq.max_irq_handler.end_time = time_exit;
648 irq.max_irq_handler.duration = ltt_time_sub(time_exit, e->event_time);
649
650 g_array_append_val (FirstRequestIrqExit, irq);
651 }
652 }
653 }
654
655 /**
656 * This function passes the second EventsRequest to LTTV
657 *
658 */
659 static gboolean SecondRequest(void *hook_data, void *call_data)
660 {
661
662 guint i, k, l, nb_trace;
663
664 LttvTraceHook *hook;
665
666 guint ret;
667
668 LttvTraceState *ts;
669
670 GArray *hooks;
671
672 EventsRequest *events_request;
673
674 LttvTraceHookByFacility *thf;
675
676 InterruptEventData *event_data = (InterruptEventData *)hook_data;
677
678 LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab);
679
680 CalculateAverageDurationForEachIrqId(event_data);
681
682 /* Get the traceset */
683 LttvTraceset *traceset = tsc->ts;
684
685 nb_trace = lttv_traceset_number(traceset);
686
687 /* There are many traces in a traceset. Iteration for each trace. */
688 for(i = 0; i<MIN(TRACE_NUMBER+1, nb_trace);i++)
689 {
690 events_request = g_new(EventsRequest, 1);
691
692 hooks = g_array_new(FALSE, FALSE, sizeof(LttvTraceHook));
693
694 hooks = g_array_set_size(hooks, 2);
695
696 event_data->hooks_trace_after = lttv_hooks_new();
697
698 /* Registers a hook function */
699 lttv_hooks_add(event_data->hooks_trace_after, DisplayViewer, event_data, LTTV_PRIO_DEFAULT);
700
701 /* Get a trace state */
702 ts = (LttvTraceState *)tsc->traces[i];
703 /* Create event_by_Id hooks */
704 event_data->event_by_id_hooks = lttv_hooks_by_id_new();
705
706 /*Register event_by_id_hooks with a callback function*/
707 ret = lttv_trace_find_hook(ts->parent.t,
708 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY,
709 LTT_FIELD_IRQ_ID, 0, 0,
710 SecondRequestIrqEntryCallback,
711 events_request,
712 &g_array_index(hooks, LttvTraceHook, 0));
713
714 ret = lttv_trace_find_hook(ts->parent.t,
715 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT,
716 LTT_FIELD_IRQ_ID, 0, 0,
717 SecondRequestIrqExitCallback,
718 events_request,
719 &g_array_index(hooks, LttvTraceHook, 1));
720
721 g_assert(!ret);
722
723 /* iterate through the facility list */
724 for(k = 0 ; k < hooks->len; k++)
725 {
726 hook = &g_array_index(hooks, LttvTraceHook, k);
727 for(l=0; l<hook->fac_list->len; l++)
728 {
729 thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l);
730 lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, thf->id),
731 thf->h,
732 event_data,
733 LTTV_PRIO_DEFAULT);
734
735 }
736 }
737 /* Initalize the EventsRequest structure */
738 events_request->owner = event_data;
739 events_request->viewer_data = event_data;
740 events_request->servicing = FALSE;
741 events_request->start_time = event_data->time_window.start_time;
742 events_request->start_position = NULL;
743 events_request->stop_flag = FALSE;
744 events_request->end_time = event_data->time_window.end_time;
745 events_request->num_events = G_MAXUINT;
746 events_request->end_position = NULL;
747 events_request->trace = i;
748
749 events_request->hooks = hooks;
750
751 events_request->before_chunk_traceset = NULL;
752 events_request->before_chunk_trace = NULL;
753 events_request->before_chunk_tracefile= NULL;
754 events_request->event = NULL;
755 events_request->event_by_id = event_data->event_by_id_hooks;
756 events_request->after_chunk_tracefile = NULL;
757 events_request->after_chunk_trace = NULL;
758 events_request->after_chunk_traceset = NULL;
759 events_request->before_request = NULL;
760 events_request->after_request = event_data->hooks_trace_after;
761
762 lttvwindow_events_request(event_data->tab, events_request);
763 }
764 return FALSE;
765 }
766
767 static void CalculateAverageDurationForEachIrqId(InterruptEventData *event_data)
768 {
769 guint64 real_data;
770 Irq *element;
771 gint i;
772 GArray* FirstRequestIrqExit = event_data->FirstRequestIrqExit;
773 for(i = 0; i < FirstRequestIrqExit->len; i++)
774 {
775 element = &g_array_index(FirstRequestIrqExit,Irq,i);
776 real_data = element->total_duration.tv_sec;
777 real_data *= NANOSECONDS_PER_SECOND;
778 real_data += element->total_duration.tv_nsec;
779 element->average_duration = real_data / element->frequency;
780 }
781
782 }
783
784 /**
785 * This function is called whenever an irq_entry event occurs. Use in the second request
786 *
787 */
788 static gboolean SecondRequestIrqEntryCallback(void *hook_data, void *call_data)
789 {
790
791 LttTime event_time;
792 unsigned cpu_id;
793 irq_entry entry;
794 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
795 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
796 InterruptEventData *event_data = (InterruptEventData *)hook_data;
797 GArray* SecondRequestIrqEntry = event_data->SecondRequestIrqEntry;
798 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
799 event_time = ltt_event_time(e);
800 cpu_id = ltt_event_cpu_id(e);
801
802
803 entry.id =get_interrupt_id(e);
804 entry.cpu_id = cpu_id;
805 entry.event_time = event_time;
806 g_array_append_val (SecondRequestIrqEntry, entry);
807
808 return FALSE;
809 }
810
811 /**
812 * This function is called whenever an irq_exit event occurs in the second request.
813 *
814 */
815 static gboolean SecondRequestIrqExitCallback(void *hook_data, void *call_data)
816 {
817
818 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
819 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
820 InterruptEventData *event_data = (InterruptEventData *)hook_data;
821 LttEvent *event = ltt_tracefile_get_event(tfc->tf);
822
823 CalculateXi(event, event_data);
824 return FALSE;
825 }
826
827
828 /**
829 * This function is called whenever an irq_exit event occurs in the second request.
830 *
831 */
832 static void CalculateXi(LttEvent *event_irq_exit, InterruptEventData *event_data)
833 {
834 gint i, irq_id;
835 irq_entry *element;
836 LttTime Xi;
837 LttTime exit_time;
838 unsigned cpu_id;
839
840 GArray *SecondRequestIrqExit = event_data->SecondRequestIrqExit;
841 GArray *SecondRequestIrqEntry = event_data->SecondRequestIrqEntry;
842 cpu_id = ltt_event_cpu_id(event_irq_exit);
843 for(i = 0; i < SecondRequestIrqEntry->len; i++)
844 {
845 element = &g_array_index(SecondRequestIrqEntry,irq_entry,i);
846 if(element->cpu_id == cpu_id)
847 {
848
849 /* time calculation */
850 exit_time = ltt_event_time(event_irq_exit);
851 Xi = ltt_time_sub(exit_time, element->event_time);
852 irq_id = element->id;
853
854 SumItems(irq_id, Xi,event_data);
855 g_array_remove_index(SecondRequestIrqEntry, i);
856 break;
857 }
858 }
859 }
860
861
862 /**
863 * This function computes the Sum ((xi -Xa)^2) and store the result in SumArray
864 *
865 */
866 static void SumItems(gint irq_id, LttTime Xi, InterruptEventData *event_data)
867 {
868 gint i;
869 guint Xi_in_ns;
870
871 gint duration_inner_part;
872 guint64 period_inner_part;
873 Irq *average;
874 SumId *sumItem;
875 SumId sum;
876 int FrequencyHZ = 0;
877 gboolean notFound = FALSE;
878 GArray *FirstRequestIrqExit = event_data->FirstRequestIrqExit;
879 GArray *SumArray = event_data->SumArray;
880 Xi_in_ns = Xi.tv_sec;
881 Xi_in_ns *= NANOSECONDS_PER_SECOND;
882 Xi_in_ns += Xi.tv_nsec;
883
884 for(i = 0; i < FirstRequestIrqExit->len; i++)
885 {
886 average = &g_array_index(FirstRequestIrqExit,Irq,i);
887 if(irq_id == average->id)
888 {
889 duration_inner_part = Xi_in_ns - average->average_duration;
890 FrequencyHZ = FrequencyInHZ(average->frequency, event_data->time_window);
891
892 sum.irqId = irq_id;
893 sum.frequency = average->frequency;
894 sum.sumOfDurations = pow (duration_inner_part , 2);
895 period_inner_part = CalculatePeriodInnerPart(Xi_in_ns, FrequencyHZ);
896 sum.sumOfPeriods = period_inner_part;
897 if(event_data->SumArray->len == NO_ITEMS)
898 {
899 g_array_append_val (SumArray, sum);
900 }
901 else
902 {
903 for(i = 0; i < SumArray->len; i++)
904 {
905 sumItem = &g_array_index(SumArray, SumId, i);
906 if(sumItem->irqId == irq_id)
907 {
908 notFound = TRUE;
909 sumItem->sumOfDurations += sum.sumOfDurations;
910 sumItem->sumOfPeriods += sum.sumOfPeriods;
911 }
912 }
913 if(!notFound)
914 {
915 g_array_append_val (SumArray, sum);
916 }
917
918 }
919
920 }
921 }
922 }
923
924 static guint64 CalculatePeriodInnerPart(guint Xi, guint FrequencyHZ)
925 {
926
927 double periodInSec; /*period in sec*/
928 int periodInNSec;
929 gint difference;
930 guint64 result;
931 periodInSec = (double)1/FrequencyHZ;
932 periodInSec *= NANOSECONDS_PER_SECOND;
933 periodInNSec = (int)periodInSec;
934
935 difference = Xi - periodInNSec;
936 result = pow (difference , 2);
937
938
939 return result;
940
941
942
943 }
944 /**
945 * This function displays the result on the viewer
946 *
947 */
948 static gboolean DisplayViewer(void *hook_data, void *call_data)
949 {
950
951 guint average;
952 gint i;
953 Irq element;
954 LttTime average_duration;
955 GtkTreeIter iter;
956 guint64 real_data;
957 guint maxIRQduration;
958 double periodInSec;
959 int periodInNsec;
960 char maxIrqHandler[80];
961 InterruptEventData *event_data = (InterruptEventData *)hook_data;
962 GArray *FirstRequestIrqExit = event_data->FirstRequestIrqExit;
963 int FrequencyHZ = 0;
964 periodInSec = 0;
965 gtk_list_store_clear(event_data->ListStore);
966 for(i = 0; i < FirstRequestIrqExit->len; i++)
967 {
968 element = g_array_index(FirstRequestIrqExit,Irq,i);
969 real_data = element.total_duration.tv_sec;
970 real_data *= NANOSECONDS_PER_SECOND;
971 real_data += element.total_duration.tv_nsec;
972
973
974 maxIRQduration = element.max_irq_handler.duration.tv_sec;
975 maxIRQduration *= NANOSECONDS_PER_SECOND;
976 maxIRQduration += element.max_irq_handler.duration.tv_nsec;
977
978 sprintf(maxIrqHandler, "%d [%d.%d - %d.%d]",maxIRQduration, element.max_irq_handler.start_time.tv_sec, \
979 element.max_irq_handler.start_time.tv_nsec, element.max_irq_handler.end_time.tv_sec, \
980 element.max_irq_handler.end_time.tv_nsec) ;
981 FrequencyHZ = FrequencyInHZ(element.frequency,event_data->time_window);
982
983 if(FrequencyHZ != 0)
984 {
985 periodInSec = (double)1/FrequencyHZ;
986 periodInSec *= NANOSECONDS_PER_SECOND;
987 periodInNsec = (int)periodInSec;
988 //printf("period1:%d\n", periodInNsec);
989 }
990
991 gtk_list_store_append (event_data->ListStore, &iter);
992 gtk_list_store_set (event_data->ListStore, &iter,
993 CPUID_COLUMN, element.cpu_id,
994 IRQ_ID_COLUMN, element.id,
995 FREQUENCY_COLUMN, FrequencyHZ,
996 DURATION_COLUMN, real_data,
997 DURATION_STANDARD_DEV_COLUMN, CalculateDurationStandardDeviation(element.id, event_data),
998 MAX_IRQ_HANDLER_COLUMN, maxIrqHandler,
999 AVERAGE_PERIOD , periodInNsec,
1000 PERIOD_STANDARD_DEV_COLUMN, CalculatePeriodStandardDeviation(element.id, event_data),
1001 -1);
1002
1003
1004 }
1005
1006
1007 if(event_data->FirstRequestIrqExit->len)
1008 {
1009 g_array_remove_range (event_data->FirstRequestIrqExit,0,event_data->FirstRequestIrqExit->len);
1010 }
1011
1012 if(event_data->FirstRequestIrqEntry->len)
1013 {
1014 g_array_remove_range (event_data->FirstRequestIrqEntry,0,event_data->FirstRequestIrqEntry->len);
1015 }
1016
1017 if(event_data->SecondRequestIrqEntry->len)
1018 {
1019 g_array_remove_range (event_data->SecondRequestIrqEntry,0,event_data->SecondRequestIrqEntry->len);
1020 }
1021
1022 if(event_data->SecondRequestIrqExit->len)
1023 {
1024 g_array_remove_range (event_data->SecondRequestIrqExit,0, event_data->SecondRequestIrqExit->len);
1025 }
1026
1027 if(event_data->SumArray->len)
1028 {
1029 g_array_remove_range (event_data->SumArray,0, event_data->SumArray->len);
1030 }
1031
1032 return FALSE;
1033 }
1034
1035
1036 /**
1037 * This function converts the number of interrupts over a time window to
1038 * frequency in HZ
1039 */
1040 static int FrequencyInHZ(gint frequency, TimeWindow time_window)
1041 {
1042 guint64 frequencyHz = 0;
1043 double timeSec; // time in second
1044 double result;
1045 result = ltt_time_to_double(time_window.time_width);
1046 timeSec = (result/NANOSECONDS_PER_SECOND); //time in second
1047 frequencyHz = frequency / timeSec;
1048 return frequencyHz;
1049 }
1050
1051 /**
1052 * This function calculates the duration standard deviation
1053 *
1054 */
1055 static int CalculateDurationStandardDeviation(gint id, InterruptEventData *event_data)
1056 {
1057 int i;
1058 SumId sumId;
1059 double inner_component;
1060 int deviation = 0;
1061 for(i = 0; i < event_data->SumArray->len; i++)
1062 {
1063 sumId = g_array_index(event_data->SumArray, SumId, i);
1064 if(id == sumId.irqId)
1065 {
1066 inner_component = sumId.sumOfDurations/ sumId.frequency;
1067 deviation = sqrt(inner_component);
1068 return deviation;
1069 }
1070 }
1071 return deviation;
1072 }
1073
1074
1075 /**
1076 * This function calculates the period standard deviation
1077 *
1078 */
1079 static int CalculatePeriodStandardDeviation(gint id, InterruptEventData *event_data)
1080 {
1081 int i;
1082 SumId sumId;
1083 guint64 inner_component;
1084 guint64 period_standard_deviation = 0;
1085 for(i = 0; i < event_data->SumArray->len; i++)
1086 {
1087 sumId = g_array_index(event_data->SumArray, SumId, i);
1088 if(id == sumId.irqId)
1089 {
1090 inner_component = sumId.sumOfPeriods / sumId.frequency;
1091 period_standard_deviation = sqrt(inner_component);
1092 }
1093 }
1094
1095 return period_standard_deviation;
1096 }
1097 /*
1098 * This function is called by the main window
1099 * when the time interval needs to be updated.
1100 **/
1101 gboolean interrupt_update_time_window(void * hook_data, void * call_data)
1102 {
1103 InterruptEventData *event_data = (InterruptEventData *) hook_data;
1104 const TimeWindowNotifyData *time_window_nofify_data = ((const TimeWindowNotifyData *)call_data);
1105 event_data->time_window = *time_window_nofify_data->new_time_window;
1106 g_info("interrupts: interrupt_update_time_window()\n");
1107 Tab *tab = event_data->tab;
1108 lttvwindow_events_request_remove_all(tab, event_data);
1109 FirstRequest(event_data );
1110 return FALSE;
1111 }
1112
1113
1114 gboolean trace_header(void *hook_data, void *call_data)
1115 {
1116
1117 InterruptEventData *event_data = (InterruptEventData *)hook_data;
1118 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
1119 LttEvent *e;
1120 LttTime event_time;
1121 return FALSE;
1122 }
1123
1124 void interrupt_destroy_walk(gpointer data, gpointer user_data)
1125 {
1126 g_info("interrupt_destroy_walk");
1127 InterruptEventData *event_data = (InterruptEventData*) data;
1128
1129
1130 interrupt_destructor((InterruptEventData*)data);
1131
1132 }
1133
1134
1135 void interrupt_destructor(InterruptEventData *event_viewer_data)
1136 {
1137 /* May already been done by GTK window closing */
1138 g_info("enter interrupt_destructor \n");
1139 if(GTK_IS_WIDGET(event_viewer_data->Hbox))
1140 {
1141 gtk_widget_destroy(event_viewer_data->Hbox);
1142 }
1143 }
1144
1145 /**
1146 This function is called when the viewer is destroyed to free hooks and memory
1147 */
1148 static void InterruptFree(InterruptEventData *event_viewer_data)
1149 {
1150 Tab *tab = event_viewer_data->tab;
1151 if(tab != NULL)
1152 {
1153
1154 g_array_free(event_viewer_data->FirstRequestIrqExit, TRUE);
1155 g_array_free(event_viewer_data->FirstRequestIrqEntry, TRUE);
1156 g_array_free(event_viewer_data->SecondRequestIrqEntry, TRUE);
1157 g_array_free(event_viewer_data->SecondRequestIrqExit, TRUE);
1158 g_array_free(event_viewer_data->SumArray, TRUE);
1159
1160 lttvwindow_unregister_time_window_notify(tab, interrupt_update_time_window, event_viewer_data);
1161
1162 lttvwindow_events_request_remove_all(event_viewer_data->tab,
1163 event_viewer_data);
1164
1165 interrupt_data_list = g_slist_remove(interrupt_data_list, event_viewer_data);
1166
1167 }
1168
1169 }
1170
1171 /**
1172 * plugin's destroy function
1173 *
1174 * This function releases the memory reserved by the module and unregisters
1175 * everything that has been registered in the gtkTraceSet API.
1176 */
1177 static void destroy()
1178 {
1179
1180 g_info("Destroy interrupts");
1181 g_slist_foreach(interrupt_data_list, interrupt_destroy_walk, NULL );
1182 g_slist_free(interrupt_data_list);
1183 lttvwindow_unregister_constructor(interrupts);
1184
1185 }
1186
1187 LTTV_MODULE("interrupts", "interrupts info view", \
1188 "Graphical module to display interrupts performance", \
1189 init, destroy, "lttvwindow")
This page took 0.054517 seconds and 4 git commands to generate.