Add standard deviation
[lttv.git] / ltt / branches / poly / lttv / modules / gui / interrupts / interrupts.c
CommitLineData
5f5119ee 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#include <math.h>
22#include <glib.h>
23#include <gtk/gtk.h>
24#include <gdk/gdk.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <ltt/ltt.h>
29#include <ltt/event.h>
30#include <ltt/type.h>
31#include <ltt/trace.h>
32#include <ltt/facility.h>
33#include <lttv/module.h>
34#include <lttv/hook.h>
35#include <lttv/tracecontext.h>
36#include <lttv/state.h>
37#include <lttv/filter.h>
38#include <lttvwindow/lttvwindow.h>
39#include <ltt/time.h>
40
41#include "hInterruptsInsert.xpm"
42
43#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
44#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
45#define NO_ITEMS 0
46
47typedef struct {
48 guint cpu_id;
49 guint id;
50 guint frequency;
51 LttTime total_duration;
52}Irq;
53
54typedef struct {
55 guint id;
56 guint cpu_id;
57 LttTime event_time;
58}irq_entry;
59
60enum type_t {
61 IRQ_ENTRY,
62 IRQ_EXIT
63};
64
65/** Array containing instanced objects. Used when module is unloaded */
66static GSList *interrupt_data_list = NULL ;
67
68
f2ec7aa9 69#define TRACE_NUMBER 0
70
5f5119ee 71typedef struct _InterruptEventData {
72
b3e7b125 73 /*Graphical Widgets */
74 GtkWidget * ScrollWindow;
75 GtkListStore *ListStore;
76 GtkWidget *Hbox;
77 GtkWidget *TreeView;
78 GtkTreeSelection *SelectionTree;
79
80 Tab * tab; /* tab that contains this plug-in*/
5f5119ee 81 LttvHooks * event_hooks;
82 LttvHooks * hooks_trace_after;
83 LttvHooks * hooks_trace_before;
b3e7b125 84 TimeWindow time_window;
f2ec7aa9 85 LttvHooksById * event_by_id_hooks;
5f5119ee 86 GArray *interrupt_counters;
87 GArray *active_irq_entry ;
88} InterruptEventData ;
89
f2ec7aa9 90
b3e7b125 91/* Function prototypes */
5f5119ee 92
93static gboolean interrupt_update_time_window(void * hook_data, void * call_data);
f2ec7aa9 94static GtkWidget *interrupts(Tab *tab);
95static InterruptEventData *system_info(Tab *tab);
5f5119ee 96void interrupt_destructor(InterruptEventData *event_viewer_data);
f2ec7aa9 97static void request_event(InterruptEventData *event_data );
98static guint64 get_interrupt_id(LttEvent *e);
5f5119ee 99static gboolean trace_header(void *hook_data, void *call_data);
f2ec7aa9 100static gboolean interrupt_display (void *hook_data, void *call_data);
5f5119ee 101static void calcul_duration(LttTime time_exit, guint cpu_id, InterruptEventData *event_data);
102static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *interrupt_counters);
f2ec7aa9 103static gboolean irq_entry_callback(void *hook_data, void *call_data);
104static gboolean irq_exit_callback(void *hook_data, void *call_data);
105
5f5119ee 106/* Enumeration of the columns */
107enum{
108 CPUID_COLUMN,
109 IRQ_ID_COLUMN,
110 FREQUENCY_COLUMN,
111 DURATION_COLUMN,
112 N_COLUMNS
113};
f2ec7aa9 114
115
116
117/**
118 * init function
119 *
120 *
121 * This is the entry point of the viewer.
122 *
123 */
124static void init() {
125 g_info("interrupts: init()");
126 lttvwindow_register_constructor("interrupts",
127 "/",
128 "Insert Interrupts View",
129 hInterruptsInsert_xpm,
130 "Insert Interrupts View",
131 interrupts);
132
133}
134
5f5119ee 135
5f5119ee 136/**
f2ec7aa9 137 * Constructor hook
5f5119ee 138 *
5f5119ee 139 */
f2ec7aa9 140static GtkWidget *interrupts(Tab * tab)
141{
b3e7b125 142
5f5119ee 143 InterruptEventData* event_data = system_info(tab) ;
144 if(event_data)
b3e7b125 145 return event_data->Hbox;
5f5119ee 146 else
147 return NULL;
148}
149
f2ec7aa9 150/**
151 * This function initializes the Event Viewer functionnality through the
152 * GTK API.
153 */
5f5119ee 154InterruptEventData *system_info(Tab *tab)
155{
156 LttTime end;
157 GtkTreeViewColumn *column;
158 GtkCellRenderer *renderer;
159 InterruptEventData* event_viewer_data = g_new(InterruptEventData,1) ;
f2ec7aa9 160
b3e7b125 161
5f5119ee 162 event_viewer_data->tab = tab;
f2ec7aa9 163
164 /*Get the current time frame from the main window */
5f5119ee 165 event_viewer_data->time_window = lttvwindow_get_time_window(tab);
166 event_viewer_data->interrupt_counters = g_array_new(FALSE, FALSE, sizeof(Irq));
167 event_viewer_data->active_irq_entry = g_array_new(FALSE, FALSE, sizeof(irq_entry));
f2ec7aa9 168 /*Create tha main window for the viewer */
b3e7b125 169 event_viewer_data->ScrollWindow = gtk_scrolled_window_new (NULL, NULL);
170 gtk_widget_show (event_viewer_data->ScrollWindow);
5f5119ee 171 gtk_scrolled_window_set_policy(
b3e7b125 172 GTK_SCROLLED_WINDOW(event_viewer_data->ScrollWindow),
5f5119ee 173 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC/*GTK_POLICY_NEVER*/);
174
175 /* Create a model for storing the data list */
b3e7b125 176 event_viewer_data->ListStore = gtk_list_store_new (
5f5119ee 177 N_COLUMNS, /* Total number of columns */
178 G_TYPE_INT, /* CPUID */
179 G_TYPE_INT, /* IRQ_ID */
180 G_TYPE_INT, /* Frequency */
181 G_TYPE_UINT64 /* Duration */
182 );
183
b3e7b125 184 event_viewer_data->TreeView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->ListStore));
5f5119ee 185
b3e7b125 186 g_object_unref (G_OBJECT (event_viewer_data->ListStore));
5f5119ee 187
188 renderer = gtk_cell_renderer_text_new ();
189 column = gtk_tree_view_column_new_with_attributes ("CPUID",
190 renderer,
191 "text", CPUID_COLUMN,
192 NULL);
193 gtk_tree_view_column_set_alignment (column, 0.0);
194 gtk_tree_view_column_set_fixed_width (column, 45);
b3e7b125 195 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
5f5119ee 196
197
198 renderer = gtk_cell_renderer_text_new ();
199 column = gtk_tree_view_column_new_with_attributes ("IrqId",
200 renderer,
201 "text", IRQ_ID_COLUMN,
202 NULL);
203 gtk_tree_view_column_set_alignment (column, 0.0);
204 gtk_tree_view_column_set_fixed_width (column, 220);
b3e7b125 205 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
5f5119ee 206
207 renderer = gtk_cell_renderer_text_new ();
208 column = gtk_tree_view_column_new_with_attributes ("Frequency",
209 renderer,
210 "text", FREQUENCY_COLUMN,
211 NULL);
212 gtk_tree_view_column_set_alignment (column, 1.0);
213 gtk_tree_view_column_set_fixed_width (column, 220);
b3e7b125 214 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
5f5119ee 215
216 renderer = gtk_cell_renderer_text_new ();
217 column = gtk_tree_view_column_new_with_attributes ("Duration (nsec)",
218 renderer,
219 "text", DURATION_COLUMN,
220 NULL);
221 gtk_tree_view_column_set_alignment (column, 0.0);
222 gtk_tree_view_column_set_fixed_width (column, 145);
b3e7b125 223 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
5f5119ee 224
b3e7b125 225 event_viewer_data->SelectionTree = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data->TreeView));
226 gtk_tree_selection_set_mode (event_viewer_data->SelectionTree, GTK_SELECTION_SINGLE);
5f5119ee 227
b3e7b125 228 gtk_container_add (GTK_CONTAINER (event_viewer_data->ScrollWindow), event_viewer_data->TreeView);
5f5119ee 229
b3e7b125 230 event_viewer_data->Hbox = gtk_hbox_new(0, 0);
231 gtk_box_pack_start(GTK_BOX(event_viewer_data->Hbox), event_viewer_data->ScrollWindow, TRUE, TRUE, 0);
5f5119ee 232
b3e7b125 233 gtk_widget_show(event_viewer_data->Hbox);
234 gtk_widget_show(event_viewer_data->TreeView);
5f5119ee 235
b3e7b125 236 interrupt_data_list = g_slist_append(interrupt_data_list, event_viewer_data);
f2ec7aa9 237 /* Registration for time notification */
b3e7b125 238 lttvwindow_register_time_window_notify(tab,
239 interrupt_update_time_window,
240 event_viewer_data);
f2ec7aa9 241
242
243 request_event(event_viewer_data );
5f5119ee 244 return event_viewer_data;
245}
b3e7b125 246
f2ec7aa9 247/**
248 *
249 * For each trace in the traceset, this function:
250 * - registers a callback function to each hook
251 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
252 * - calls lttvwindow_events_request() to request data in a specific
253 * time interval to the main window
254 *
255 */
256static void request_event(InterruptEventData *event_data )
257{
258 guint i, k, l, nb_trace;
259
260 LttvTraceHook *hook;
261
262 guint ret;
263
264 LttvTraceState *ts;
265
266 GArray *hooks;
267
268 EventsRequest *events_request;
269
270 LttvTraceHookByFacility *thf;
271
272 LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab);
5f5119ee 273
f2ec7aa9 274
275 /* Get the traceset */
276 LttvTraceset *traceset = tsc->ts;
5f5119ee 277
f2ec7aa9 278 nb_trace = lttv_traceset_number(traceset);
279
280 /* There are many traces in a traceset. Iteration for each trace. */
281 for(i = 0; i<MIN(TRACE_NUMBER+1, nb_trace);i++)
282 {
283 events_request = g_new(EventsRequest, 1);
284
285 hooks = g_array_new(FALSE, FALSE, sizeof(LttvTraceHook));
286
287 hooks = g_array_set_size(hooks, 2);
b3e7b125 288
f2ec7aa9 289 event_data->hooks_trace_before = lttv_hooks_new();
290
291 /* Registers a hook function */
292 lttv_hooks_add(event_data->hooks_trace_before, trace_header, event_data, LTTV_PRIO_DEFAULT);
293
294 event_data->hooks_trace_after = lttv_hooks_new();
295 /* Registers a hook function */
296 lttv_hooks_add(event_data->hooks_trace_after, interrupt_display, event_data, LTTV_PRIO_DEFAULT);
297 /* Get a trace state */
298 ts = (LttvTraceState *)tsc->traces[i];
299 /* Create event_by_Id hooks */
300 event_data->event_by_id_hooks = lttv_hooks_by_id_new();
b3e7b125 301
f2ec7aa9 302 /*Register event_by_id_hooks with a callback function*/
303 ret = lttv_trace_find_hook(ts->parent.t,
304 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY,
305 LTT_FIELD_IRQ_ID, 0, 0,
306 irq_entry_callback,
307 events_request,
308 &g_array_index(hooks, LttvTraceHook, 0));
309
310 ret = lttv_trace_find_hook(ts->parent.t,
311 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT,
312 LTT_FIELD_IRQ_ID, 0, 0,
313 irq_exit_callback,
314 events_request,
315 &g_array_index(hooks, LttvTraceHook, 1));
316
317 g_assert(!ret);
318 /*iterate through the facility list*/
319 for(k = 0 ; k < hooks->len; k++)
320 {
321 hook = &g_array_index(hooks, LttvTraceHook, k);
322 for(l=0; l<hook->fac_list->len; l++)
323 {
324 thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l);
325 lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, thf->id),
326 thf->h,
327 event_data,
328 LTTV_PRIO_DEFAULT);
329
330 }
331 }
332 /* Initalize the EventsRequest structure */
333 events_request->owner = event_data;
334 events_request->viewer_data = event_data;
335 events_request->servicing = FALSE;
336 events_request->start_time = event_data->time_window.start_time;
337 events_request->start_position = NULL;
338 events_request->stop_flag = FALSE;
339 events_request->end_time = event_data->time_window.end_time;
340 events_request->num_events = G_MAXUINT;
341 events_request->end_position = NULL;
342 events_request->trace = i;
343
344 events_request->hooks = hooks;
345
346 events_request->before_chunk_traceset = NULL;
347 events_request->before_chunk_trace = event_data->hooks_trace_before;
348 events_request->before_chunk_tracefile= NULL;
349 events_request->event = NULL;
350 events_request->event_by_id = event_data->event_by_id_hooks;
351 events_request->after_chunk_tracefile = NULL;
231a6432 352 events_request->after_chunk_trace = NULL;
f2ec7aa9 353 events_request->after_chunk_traceset = NULL;
231a6432 354 events_request->before_request = NULL;
f2ec7aa9 355 events_request->after_request = event_data->hooks_trace_after;
356
357 lttvwindow_events_request(event_data->tab, events_request);
358 }
359
b3e7b125 360}
5f5119ee 361
f2ec7aa9 362/**
363 * This function is called whenever an irq_entry event occurs.
364 *
365 */
366static gboolean irq_entry_callback(void *hook_data, void *call_data)
367{
368
b3e7b125 369 LttTime event_time;
b3e7b125 370 unsigned cpu_id;
371 irq_entry entry;
b3e7b125 372 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
373 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
f2ec7aa9 374 InterruptEventData *event_data = (InterruptEventData *)hook_data;
375 GArray* active_irq_entry = event_data->active_irq_entry;
376 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
b3e7b125 377 event_time = ltt_event_time(e);
b3e7b125 378 cpu_id = ltt_event_cpu_id(e);
f2ec7aa9 379
231a6432 380
381 entry.id =get_interrupt_id(e);
382 entry.cpu_id = cpu_id;
383 entry.event_time = event_time;
384 g_array_append_val (active_irq_entry, entry);
385
f2ec7aa9 386 return FALSE;
5f5119ee 387}
388
f2ec7aa9 389/**
390 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
391 * Refer to the print.c file for howto extract data from a dynamic structure.
392 */
393static guint64 get_interrupt_id(LttEvent *e)
5f5119ee 394{
f2ec7aa9 395 guint i, num_fields;
396 LttEventType *event_type;
397 LttField *element;
398 LttField *field;
399 guint64 irq_id;
400 event_type = ltt_event_eventtype(e);
401 num_fields = ltt_eventtype_num_fields(event_type);
402 for(i = 0 ; i < num_fields-1 ; i++)
403 {
404 field = ltt_eventtype_field(event_type, i);
405 irq_id = ltt_event_get_long_unsigned(e,field);
5f5119ee 406 }
407 return irq_id;
5f5119ee 408
f2ec7aa9 409}
410/**
411 * This function is called whenever an irq_exit event occurs.
412 *
413 */
414gboolean irq_exit_callback(void *hook_data, void *call_data)
415{
416 LttTime event_time;
417 unsigned cpu_id;
5f5119ee 418 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
f2ec7aa9 419 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
420 InterruptEventData *event_data = (InterruptEventData *)hook_data;
421 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
422 LttEventType *type = ltt_event_eventtype(e);
423 event_time = ltt_event_time(e);
424 cpu_id = ltt_event_cpu_id(e);
231a6432 425
426 calcul_duration( event_time, cpu_id, event_data);
427 return FALSE;
5f5119ee 428}
429
f2ec7aa9 430/**
431 * This function calculates the duration of an interrupt.
432 *
433 */
434static void calcul_duration(LttTime time_exit, guint cpu_id,InterruptEventData *event_data){
5f5119ee 435
f2ec7aa9 436 gint i, irq_id;
437 irq_entry *element;
438 LttTime duration;
439 GArray *interrupt_counters = event_data->interrupt_counters;
440 GArray *active_irq_entry = event_data->active_irq_entry;
231a6432 441 for(i = 0; i < active_irq_entry->len; i++)
442 {
f2ec7aa9 443 element = &g_array_index(active_irq_entry,irq_entry,i);
231a6432 444 if(element->cpu_id == cpu_id)
445 {
f2ec7aa9 446 sum_interrupt_data(element,time_exit, interrupt_counters);
447 g_array_remove_index(active_irq_entry, i);
448 break;
449 }
450 }
5f5119ee 451}
f2ec7aa9 452/**
453 * This function calculates the total duration of an interrupt.
454 *
455 */
5f5119ee 456static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *interrupt_counters){
457 Irq irq;
458 Irq *element;
459 guint i;
460 LttTime duration;
461 gboolean notFound = FALSE;
462 memset ((void*)&irq, 0,sizeof(Irq));
463
231a6432 464 /*first time*/
465 if(interrupt_counters->len == NO_ITEMS)
466 {
5f5119ee 467 irq.cpu_id = e->cpu_id;
468 irq.id = e->id;
469 irq.frequency++;
470 irq.total_duration = ltt_time_sub(time_exit, e->event_time);
471 g_array_append_val (interrupt_counters, irq);
472 }
231a6432 473 else
474 {
475 for(i = 0; i < interrupt_counters->len; i++)
476 {
5f5119ee 477 element = &g_array_index(interrupt_counters,Irq,i);
478 if(element->id == e->id){
479 notFound = TRUE;
480 duration = ltt_time_sub(time_exit, e->event_time);
481 element->total_duration = ltt_time_add(element->total_duration, duration);
482 element->frequency++;
483 }
484 }
231a6432 485 if(!notFound)
486 {
5f5119ee 487 irq.cpu_id = e->cpu_id;
488 irq.id = e->id;
489 irq.frequency++;
490 irq.total_duration = ltt_time_sub(time_exit, e->event_time);
491 g_array_append_val (interrupt_counters, irq);
492 }
493 }
494}
231a6432 495
f2ec7aa9 496/**
497 * This function displays the result on the viewer
498 *
499 */
500static gboolean interrupt_display(void *hook_data, void *call_data){
5f5119ee 501
f2ec7aa9 502 gint i;
503 Irq element;
504 LttTime average_duration;
505 GtkTreeIter iter;
506 guint64 real_data;
507 InterruptEventData *event_data = (InterruptEventData *)hook_data;
508 GArray *interrupt_counters = event_data->interrupt_counters;
509 gtk_list_store_clear(event_data->ListStore);
231a6432 510 for(i = 0; i < interrupt_counters->len; i++)
511 {
f2ec7aa9 512 element = g_array_index(interrupt_counters,Irq,i);
513 real_data = element.total_duration.tv_sec;
514 real_data *= NANOSECONDS_PER_SECOND;
515 real_data += element.total_duration.tv_nsec;
516 gtk_list_store_append (event_data->ListStore, &iter);
517 gtk_list_store_set (event_data->ListStore, &iter,
518 CPUID_COLUMN, element.cpu_id,
519 IRQ_ID_COLUMN, element.id,
520 FREQUENCY_COLUMN, element.frequency,
521 DURATION_COLUMN, real_data,
522 -1);
231a6432 523
f2ec7aa9 524 }
231a6432 525
f2ec7aa9 526 if(event_data->interrupt_counters->len)
231a6432 527 {
f2ec7aa9 528 g_array_remove_range (event_data->interrupt_counters,0,event_data->interrupt_counters->len);
231a6432 529 }
530
f2ec7aa9 531 if(event_data->active_irq_entry->len)
231a6432 532 {
533
f2ec7aa9 534 g_array_remove_range (event_data->active_irq_entry,0,event_data->active_irq_entry->len);
231a6432 535 }
f2ec7aa9 536 return FALSE;
5f5119ee 537}
f2ec7aa9 538/*
539 * This function is called by the main window
540 * when the time interval needs to be updated.
541 **/
542gboolean interrupt_update_time_window(void * hook_data, void * call_data)
543{
5f5119ee 544 InterruptEventData *event_data = (InterruptEventData *) hook_data;
545 const TimeWindowNotifyData *time_window_nofify_data = ((const TimeWindowNotifyData *)call_data);
546 event_data->time_window = *time_window_nofify_data->new_time_window;
547 g_info("interrupts: interrupt_update_time_window()\n");
548 Tab *tab = event_data->tab;
549 lttvwindow_events_request_remove_all(tab, event_data);
f2ec7aa9 550 request_event(event_data );
551 return FALSE;
5f5119ee 552}
553
f2ec7aa9 554
555gboolean trace_header(void *hook_data, void *call_data)
556{
557
558 InterruptEventData *event_data = (InterruptEventData *)hook_data;
559 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
f2ec7aa9 560 LttEvent *e;
561 LttTime event_time;
562 return FALSE;
5f5119ee 563}
564
f2ec7aa9 565void interrupt_destroy_walk(gpointer data, gpointer user_data)
566{
5f5119ee 567 g_info("interrupt_destroy_walk");
568 interrupt_destructor((InterruptEventData*)data);
569
570}
571
f2ec7aa9 572void interrupt_destructor(InterruptEventData *event_viewer_data)
573{
574 /* May already been done by GTK window closing */
575 g_info("enter interrupt_destructor \n");
576 if(GTK_IS_WIDGET(event_viewer_data->Hbox))
577 {
578 gtk_widget_destroy(event_viewer_data->Hbox);
579 }
580}
581
5f5119ee 582/**
583 * plugin's destroy function
584 *
585 * This function releases the memory reserved by the module and unregisters
586 * everything that has been registered in the gtkTraceSet API.
587 */
588static void destroy() {
589 g_info("Destroy interrupts");
590 g_slist_foreach(interrupt_data_list, interrupt_destroy_walk, NULL );
591 g_slist_free(interrupt_data_list);
592 lttvwindow_unregister_constructor(interrupts);
593
594}
595
596LTTV_MODULE("interrupts", "interrupts info view", \
597 "Graphical module to display interrupts performance", \
598 init, destroy, "lttvwindow")
This page took 0.054201 seconds and 4 git commands to generate.