add source code for the tutorial
[lttv.git] / ltt / branches / poly / lttv / modules / gui / tutorial / tutorial.c
CommitLineData
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
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
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>
46#include <ltt/type.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 <ltt/time.h>
56
57#include "hTutorialInsert.xpm"
58
59#define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
60#define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
61#define NO_ITEMS 0
62
63typedef struct
64{
65 guint cpu_id;
66 guint id;
67 guint TotalNumberOfInterrupts;
68 LttTime total_duration;
69}Irq;
70
71typedef struct
72{
73 guint id;
74 guint cpu_id;
75 LttTime event_time;
76}irq_entry;
77
78enum type_t
79{
80 IRQ_ENTRY,
81 IRQ_EXIT
82};
83
84/** Array containing instanced objects. Used when module is unloaded */
85static GSList *interrupt_data_list = NULL ;
86
87
88#define TRACE_NUMBER 0
89
90typedef struct _InterruptEventData
91{
92
93 /* Graphical Widgets */
94 GtkWidget * ScrollWindow;
95 GtkListStore *ListStore;
96 GtkWidget *Hbox;
97 GtkWidget *TreeView;
98 GtkTreeSelection *SelectionTree;
99
100 Tab * tab; /* tab that contains this plug-in*/
101 LttvHooks * event_hooks;
102 LttvHooks * hooks_trace_after;
103 LttvHooks * hooks_trace_before;
104 TimeWindow time_window;
105 LttvHooksById * event_by_id_hooks;
106 GArray *IrqExit;
107 GArray *IrqEntry;
108} InterruptEventData ;
109
110
111/* Function prototypes */
112
113static gboolean interrupt_update_time_window(void * hook_data, void * call_data);
114static GtkWidget *interrupts(Tab *tab);
115static InterruptEventData *system_info(Tab *tab);
116void interrupt_destructor(InterruptEventData *event_viewer_data);
117static void request_event(InterruptEventData *event_data );
118static guint64 get_interrupt_id(LttEvent *e);
119static gboolean trace_header(void *hook_data, void *call_data);
120static gboolean interrupt_display (void *hook_data, void *call_data);
121static void calcul_duration(LttTime time_exit, guint cpu_id, InterruptEventData *event_data);
122static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *interrupt_counters);
123static gboolean irq_entry_callback(void *hook_data, void *call_data);
124static gboolean irq_exit_callback(void *hook_data, void *call_data);
125static void InterruptFree(InterruptEventData *event_viewer_data);
126static int FrequencyInHZ(gint NumerofInterruptions, TimeWindow time_window);
127/* Enumeration of the columns */
128enum{
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 */
145static 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 */
162static GtkWidget *interrupts(Tab * tab)
163{
164
165 InterruptEventData* event_data = system_info(tab) ;
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 */
176InterruptEventData *system_info(Tab *tab)
177{
178 LttTime end;
179 GtkTreeViewColumn *column;
180 GtkCellRenderer *renderer;
181 InterruptEventData* event_viewer_data = g_new(InterruptEventData,1) ;
182
183
184 event_viewer_data->tab = tab;
185
186 /*Get the current time frame from the main window */
187 event_viewer_data->time_window = lttvwindow_get_time_window(tab);
188 event_viewer_data->IrqExit = g_array_new(FALSE, FALSE, sizeof(Irq));
189 event_viewer_data->IrqEntry = g_array_new(FALSE, FALSE, sizeof(irq_entry));
190
191 /*Create tha main window for the viewer */
192 event_viewer_data->ScrollWindow = gtk_scrolled_window_new (NULL, NULL);
193 gtk_widget_show (event_viewer_data->ScrollWindow);
194 gtk_scrolled_window_set_policy(
195 GTK_SCROLLED_WINDOW(event_viewer_data->ScrollWindow),
196 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
197
198 /* Create a model for storing the data list */
199 event_viewer_data->ListStore = gtk_list_store_new (
200 N_COLUMNS, /* Total number of columns */
201 G_TYPE_INT, /* CPUID */
202 G_TYPE_INT, /* IRQ_ID */
203 G_TYPE_INT, /* Frequency */
204 G_TYPE_UINT64 /* Duration */
205 );
206
207 event_viewer_data->TreeView = gtk_tree_view_new_with_model (GTK_TREE_MODEL (event_viewer_data->ListStore));
208
209 g_object_unref (G_OBJECT (event_viewer_data->ListStore));
210
211 renderer = gtk_cell_renderer_text_new ();
212 column = gtk_tree_view_column_new_with_attributes ("CPU ID",
213 renderer,
214 "text", CPUID_COLUMN,
215 NULL);
216 gtk_tree_view_column_set_alignment (column, 0.0);
217 gtk_tree_view_column_set_fixed_width (column, 45);
218 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
219
220
221 renderer = gtk_cell_renderer_text_new ();
222 column = gtk_tree_view_column_new_with_attributes ("IRQ ID",
223 renderer,
224 "text", IRQ_ID_COLUMN,
225 NULL);
226 gtk_tree_view_column_set_alignment (column, 0.0);
227 gtk_tree_view_column_set_fixed_width (column, 220);
228 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
229
230 renderer = gtk_cell_renderer_text_new ();
231 column = gtk_tree_view_column_new_with_attributes ("Frequency (HZ)",
232 renderer,
233 "text", FREQUENCY_COLUMN,
234 NULL);
235 gtk_tree_view_column_set_alignment (column, 1.0);
236 gtk_tree_view_column_set_fixed_width (column, 220);
237 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
238
239 renderer = gtk_cell_renderer_text_new ();
240 column = gtk_tree_view_column_new_with_attributes ("Total Duration (nsec)",
241 renderer,
242 "text", DURATION_COLUMN,
243 NULL);
244 gtk_tree_view_column_set_alignment (column, 0.0);
245 gtk_tree_view_column_set_fixed_width (column, 145);
246 gtk_tree_view_append_column (GTK_TREE_VIEW (event_viewer_data->TreeView), column);
247
248 event_viewer_data->SelectionTree = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_viewer_data->TreeView));
249 gtk_tree_selection_set_mode (event_viewer_data->SelectionTree, GTK_SELECTION_SINGLE);
250
251 gtk_container_add (GTK_CONTAINER (event_viewer_data->ScrollWindow), event_viewer_data->TreeView);
252
253 event_viewer_data->Hbox = gtk_hbox_new(0, 0);
254 gtk_box_pack_start(GTK_BOX(event_viewer_data->Hbox), event_viewer_data->ScrollWindow, TRUE, TRUE, 0);
255
256 gtk_widget_show(event_viewer_data->Hbox);
257 gtk_widget_show(event_viewer_data->TreeView);
258
259 interrupt_data_list = g_slist_append(interrupt_data_list, event_viewer_data);
260
261 /* Registration for time notification */
262 lttvwindow_register_time_window_notify(tab,
263 interrupt_update_time_window,
264 event_viewer_data);
265
266 g_object_set_data_full(G_OBJECT(event_viewer_data->Hbox),
267 "event_data",
268 event_viewer_data,
269 (GDestroyNotify) InterruptFree);
270
271
272 request_event(event_viewer_data );
273 return event_viewer_data;
274}
275
276/**
277 *
278 * For each trace in the traceset, this function:
279 * - registers a callback function to each hook
280 * - calls lttv_trace_find_hook() registers a hook function to event_by_id_hooks
281 * - calls lttvwindow_events_request() to request data in a specific
282 * time interval to the main window
283 *
284 */
285static void request_event(InterruptEventData *event_data )
286{
287 guint i, k, l, nb_trace;
288
289 LttvTraceHook *hook;
290
291 guint ret;
292
293 LttvTraceState *ts;
294
295 GArray *hooks;
296
297 EventsRequest *events_request;
298
299 LttvTraceHookByFacility *thf;
300
301 LttvTracesetContext *tsc = lttvwindow_get_traceset_context(event_data->tab);
302
303
304 /* Get the traceset */
305 LttvTraceset *traceset = tsc->ts;
306
307 nb_trace = lttv_traceset_number(traceset);
308
309 /* There are many traces in a traceset. Iteration for each trace. */
310 for(i = 0; i<MIN(TRACE_NUMBER+1, nb_trace);i++)
311 {
312 events_request = g_new(EventsRequest, 1);
313
314 hooks = g_array_new(FALSE, FALSE, sizeof(LttvTraceHook));
315
316 hooks = g_array_set_size(hooks, 2);
317
318 event_data->hooks_trace_before = lttv_hooks_new();
319
320 /* Registers a hook function */
321 lttv_hooks_add(event_data->hooks_trace_before, trace_header, event_data, LTTV_PRIO_DEFAULT);
322
323 event_data->hooks_trace_after = lttv_hooks_new();
324 /* Registers a hook function */
325 lttv_hooks_add(event_data->hooks_trace_after, interrupt_display, event_data, LTTV_PRIO_DEFAULT);
326 /* Get a trace state */
327 ts = (LttvTraceState *)tsc->traces[i];
328 /* Create event_by_Id hooks */
329 event_data->event_by_id_hooks = lttv_hooks_by_id_new();
330
331 /*Register event_by_id_hooks with a callback function*/
332 ret = lttv_trace_find_hook(ts->parent.t,
333 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY,
334 LTT_FIELD_IRQ_ID, 0, 0,
335 irq_entry_callback,
336 events_request,
337 &g_array_index(hooks, LttvTraceHook, 0));
338
339 ret = lttv_trace_find_hook(ts->parent.t,
340 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT,
341 LTT_FIELD_IRQ_ID, 0, 0,
342 irq_exit_callback,
343 events_request,
344 &g_array_index(hooks, LttvTraceHook, 1));
345
346 g_assert(!ret);
347
348 /*iterate through the facility list*/
349 for(k = 0 ; k < hooks->len; k++)
350 {
351 hook = &g_array_index(hooks, LttvTraceHook, k);
352 for(l=0; l<hook->fac_list->len; l++)
353 {
354 thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l);
355 lttv_hooks_add(lttv_hooks_by_id_find(event_data->event_by_id_hooks, thf->id),
356 thf->h,
357 event_data,
358 LTTV_PRIO_DEFAULT);
359
360 }
361 }
362
363 /* Initalize the EventsRequest structure */
364 events_request->owner = event_data;
365 events_request->viewer_data = event_data;
366 events_request->servicing = FALSE;
367 events_request->start_time = event_data->time_window.start_time;
368 events_request->start_position = NULL;
369 events_request->stop_flag = FALSE;
370 events_request->end_time = event_data->time_window.end_time;
371 events_request->num_events = G_MAXUINT;
372 events_request->end_position = NULL;
373 events_request->trace = i;
374
375 events_request->hooks = hooks;
376
377 events_request->before_chunk_traceset = NULL;
378 events_request->before_chunk_trace = event_data->hooks_trace_before;
379 events_request->before_chunk_tracefile= NULL;
380 events_request->event = NULL;
381 events_request->event_by_id = event_data->event_by_id_hooks;
382 events_request->after_chunk_tracefile = NULL;
383 events_request->after_chunk_trace = NULL;
384 events_request->after_chunk_traceset = NULL;
385 events_request->before_request = NULL;
386 events_request->after_request = event_data->hooks_trace_after;
387
388 lttvwindow_events_request(event_data->tab, events_request);
389 }
390
391}
392
393/**
394 * This function is called whenever an irq_entry event occurs.
395 *
396 */
397static gboolean irq_entry_callback(void *hook_data, void *call_data)
398{
399
400 LttTime event_time;
401 unsigned cpu_id;
402 irq_entry entry;
403 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
404 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
405 InterruptEventData *event_data = (InterruptEventData *)hook_data;
406 GArray* IrqEntry = event_data->IrqEntry;
407 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
408 event_time = ltt_event_time(e);
409 cpu_id = ltt_event_cpu_id(e);
410
411 if ((ltt_time_compare(event_time,event_data->time_window.start_time) == TRUE) &&
412 (ltt_time_compare(event_data->time_window.end_time,event_time) == TRUE))
413 {
414 entry.id =get_interrupt_id(e);
415 entry.cpu_id = cpu_id;
416 entry.event_time = event_time;
417 g_array_append_val (IrqEntry, entry);
418 }
419 return FALSE;
420}
421
422/**
423 * This function gets the id of the interrupt. The id is stored in a dynamic structure.
424 * Refer to the print.c file for howto extract data from a dynamic structure.
425 */
426static guint64 get_interrupt_id(LttEvent *e)
427{
428 guint i, num_fields;
429 LttEventType *event_type;
430 LttField *element;
431 LttField *field;
432 guint64 irq_id;
433 event_type = ltt_event_eventtype(e);
434 num_fields = ltt_eventtype_num_fields(event_type);
435 for(i = 0 ; i < num_fields-1 ; i++)
436 {
437 field = ltt_eventtype_field(event_type, i);
438 irq_id = ltt_event_get_long_unsigned(e,field);
439 }
440 return irq_id;
441
442}
443/**
444 * This function is called whenever an irq_exit event occurs.
445 *
446 */
447gboolean irq_exit_callback(void *hook_data, void *call_data)
448{
449 LttTime event_time;
450 unsigned cpu_id;
451 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
452 LttvTracefileState *tfs = (LttvTracefileState *)call_data;
453 InterruptEventData *event_data = (InterruptEventData *)hook_data;
454 LttEvent *e = ltt_tracefile_get_event(tfc->tf);
455 LttEventType *type = ltt_event_eventtype(e);
456 event_time = ltt_event_time(e);
457 cpu_id = ltt_event_cpu_id(e);
458 if ((ltt_time_compare(event_time,event_data->time_window.start_time) == TRUE) &&
459 (ltt_time_compare(event_data->time_window.end_time,event_time) == TRUE))
460 {
461 calcul_duration( event_time, cpu_id, event_data);
462
463 }
464 return FALSE;
465}
466
467/**
468 * This function calculates the duration of an interrupt.
469 *
470 */
471static void calcul_duration(LttTime time_exit, guint cpu_id,InterruptEventData *event_data){
472
473 gint i, irq_id;
474 irq_entry *element;
475 LttTime duration;
476 GArray *IrqExit = event_data->IrqExit;
477 GArray *IrqEntry = event_data->IrqEntry;
478 for(i = 0; i < IrqEntry->len; i++){
479 element = &g_array_index(IrqEntry,irq_entry,i);
480 if(element->cpu_id == cpu_id)
481 {
482 sum_interrupt_data(element,time_exit, IrqExit);
483 g_array_remove_index(IrqEntry, i);
484 break;
485 }
486 }
487}
488/**
489 * This function calculates the total duration of an interrupt.
490 *
491 */
492static void sum_interrupt_data(irq_entry *e, LttTime time_exit, GArray *IrqExit)
493{
494 Irq irq;
495 Irq *element;
496 guint i;
497 LttTime duration;
498 gboolean notFound = FALSE;
499 memset ((void*)&irq, 0,sizeof(Irq));
500
501
502 if(IrqExit->len == NO_ITEMS)
503 {
504 irq.cpu_id = e->cpu_id;
505 irq.id = e->id;
506 irq.TotalNumberOfInterrupts++;
507 irq.total_duration = ltt_time_sub(time_exit, e->event_time);
508 g_array_append_val (IrqExit, irq);
509 }
510 else{
511 for(i = 0; i < IrqExit->len; i++)
512 {
513 element = &g_array_index(IrqExit, Irq, i);
514 if(element->id == e->id){
515 notFound = TRUE;
516 duration = ltt_time_sub(time_exit, e->event_time);
517 element->total_duration = ltt_time_add(element->total_duration, duration);
518 element->TotalNumberOfInterrupts++;
519 }
520 }
521
522 if(!notFound)
523 {
524 irq.cpu_id = e->cpu_id;
525 irq.id = e->id;
526 irq.TotalNumberOfInterrupts++;
527 irq.total_duration = ltt_time_sub(time_exit, e->event_time);
528 g_array_append_val (IrqExit, irq);
529 }
530 }
531}
532
533/**
534 * This function displays the result on the viewer
535 *
536 */
537static gboolean interrupt_display(void *hook_data, void *call_data)
538{
539
540 gint i;
541 Irq element;
542 LttTime average_duration;
543 GtkTreeIter iter;
544 guint64 real_data;
545 int FrequencyHZ = 0;
546
547 InterruptEventData *event_data = (InterruptEventData *)hook_data;
548 GArray *IrqExit = event_data->IrqExit;
549 gtk_list_store_clear(event_data->ListStore);
550 for(i = 0; i < IrqExit->len; i++)
551 {
552
553 element = g_array_index(IrqExit,Irq,i);
554 real_data = element.total_duration.tv_sec;
555 real_data *= NANOSECONDS_PER_SECOND;
556 real_data += element.total_duration.tv_nsec;
557
558 FrequencyHZ = FrequencyInHZ(element.TotalNumberOfInterrupts,event_data->time_window);
559
560 gtk_list_store_append (event_data->ListStore, &iter);
561
562 gtk_list_store_set (event_data->ListStore, &iter,
563 CPUID_COLUMN, element.cpu_id,
564 IRQ_ID_COLUMN, element.id,
565 FREQUENCY_COLUMN, FrequencyHZ,
566 DURATION_COLUMN, real_data,
567 -1);
568
569 }
570
571 if(event_data->IrqExit->len)
572 g_array_remove_range (event_data->IrqExit,0,event_data->IrqExit->len);
573
574 if(event_data->IrqEntry->len)
575 g_array_remove_range (event_data->IrqEntry,0,event_data->IrqEntry->len);
576 return FALSE;
577}
578
579/**
580 * This function converts the number of interrupts over a time window to
581 * frequency in HZ
582 */
583static int FrequencyInHZ(gint NumerofInterruptions, TimeWindow time_window)
584{
585 guint64 frequencyHz = 0;
586 double timeSec; // time in second
587 double result;
588 result = ltt_time_to_double(time_window.time_width);
589 timeSec = (result/NANOSECONDS_PER_SECOND); //time in second
590 frequencyHz = NumerofInterruptions / timeSec;
591 return frequencyHz;
592}
593
594
595/*
596 * This function is called by the main window
597 * when the time interval needs to be updated.
598 **/
599gboolean interrupt_update_time_window(void * hook_data, void * call_data)
600{
601 InterruptEventData *event_data = (InterruptEventData *) hook_data;
602 const TimeWindowNotifyData *time_window_nofify_data = ((const TimeWindowNotifyData *)call_data);
603 event_data->time_window = *time_window_nofify_data->new_time_window;
604 g_info("interrupts: interrupt_update_time_window()\n");
605 Tab *tab = event_data->tab;
606 lttvwindow_events_request_remove_all(tab, event_data);
607 request_event(event_data );
608 return FALSE;
609}
610
611
612gboolean trace_header(void *hook_data, void *call_data)
613{
614
615 InterruptEventData *event_data = (InterruptEventData *)hook_data;
616 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
617 LttEvent *e;
618 LttTime event_time;
619 return FALSE;
620}
621
622void interrupt_destroy_walk(gpointer data, gpointer user_data)
623{
624 g_info("interrupt_destroy_walk");
625 interrupt_destructor((InterruptEventData*)data);
626
627}
628
629void interrupt_destructor(InterruptEventData *event_viewer_data)
630{
631 /* May already been done by GTK window closing */
632 g_info("enter interrupt_destructor \n");
633 if(GTK_IS_WIDGET(event_viewer_data->Hbox))
634 {
635 gtk_widget_destroy(event_viewer_data->Hbox);
636 }
637}
638
639/**
640 This function is called when the viewer is destroyed to free hooks and memory
641*/
642static void InterruptFree(InterruptEventData *event_viewer_data)
643{
644 Tab *tab = event_viewer_data->tab;
645 if(tab != NULL)
646 {
647 g_array_free(event_viewer_data->IrqExit, TRUE);
648
649 g_array_free(event_viewer_data->IrqEntry, TRUE);
650
651 lttvwindow_unregister_time_window_notify(tab, interrupt_update_time_window, event_viewer_data);
652
653 lttvwindow_events_request_remove_all(event_viewer_data->tab,
654 event_viewer_data);
655
656 interrupt_data_list = g_slist_remove(interrupt_data_list, event_viewer_data);
657
658 }
659
660}
661
662/**
663 * plugin's destroy function
664 *
665 * This function releases the memory reserved by the module and unregisters
666 * everything that has been registered in the gtkTraceSet API.
667 */
668static void destroy() {
669 g_info("Destroy interrupts");
670 g_slist_foreach(interrupt_data_list, interrupt_destroy_walk, NULL );
671 g_slist_free(interrupt_data_list);
672 lttvwindow_unregister_constructor(interrupts);
673
674}
675
676LTTV_MODULE("tutorial", "interrupts info view", \
677 "Graphical module to display interrupts performance", \
678 init, destroy, "lttvwindow")
This page took 0.046567 seconds and 4 git commands to generate.