1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 Mathieu Desnoyers
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 #include "xenoltt_threadlist.h"
31 #include "xenoltt_drawing.h"
32 #include "xenoltt_drawitem.h"
34 #define g_info(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format)
35 #define g_debug(format...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format)
37 /* Preallocated Size of the index_to_pixmap array */
38 #define ALLOCATE_PROCESSES 1000
40 /*****************************************************************************
41 * Methods to synchronize process list *
42 *****************************************************************************/
45 gint
thread_sort_func ( GtkTreeModel
*model
,
51 guint a_prio
, a_period
, a_cpu
;
52 gulong a_birth_s
, a_birth_ns
;
56 guint b_prio
, b_period
, b_cpu
;
57 gulong b_birth_s
, b_birth_ns
;
60 gtk_tree_model_get(model
,
62 THREAD_COLUMN
, &a_name
,
64 PERIOD_COLUMN
, &a_period
,
66 BIRTH_S_COLUMN
, &a_birth_s
,
67 BIRTH_NS_COLUMN
, &a_birth_ns
,
68 TRACE_COLUMN
, &a_trace
,
71 gtk_tree_model_get(model
,
73 THREAD_COLUMN
, &b_name
,
75 PERIOD_COLUMN
, &b_period
,
77 BIRTH_S_COLUMN
, &b_birth_s
,
78 BIRTH_NS_COLUMN
, &b_birth_ns
,
79 TRACE_COLUMN
, &b_trace
,
83 /* Order by PRIORITY */
84 if(a_prio
== 0 && b_prio
== 0) {
85 if(a_prio
> b_prio
) return -1;
86 if(a_prio
< b_prio
) return 1;
89 /* Order by birth second */
91 if(a_birth_s
> b_birth_s
) return 1;
92 if(a_birth_s
< b_birth_s
) return -1;
95 /* Order by birth nanosecond */
96 if(a_birth_ns
> b_birth_ns
) return 1;
97 if(a_birth_ns
< b_birth_ns
) return -1;
99 /* Order by trace_num */
100 if(a_trace
> b_trace
) return 1;
101 if(a_trace
< b_trace
) return -1;
107 static guint
thread_list_hash_fct(gconstpointer key
)
109 guint address
= ((const XenoThreadInfo
*)key
)->address
;
110 return ((address
>>8 ^ address
>>4 ^ address
>>2 ^ address
) ^ ((const XenoThreadInfo
*)key
)->cpu
);
113 /* If hash is good, should be different */
114 static gboolean
thread_list_equ_fct(gconstpointer a
, gconstpointer b
)
116 const XenoThreadInfo
*pa
= (const XenoThreadInfo
*)a
;
117 const XenoThreadInfo
*pb
= (const XenoThreadInfo
*)b
;
121 if(likely(pa
->address
!= pb
->address
))
123 if(likely((pa
->address
== 0)))
125 if(likely((pa
->address
== 0 && (pa
->cpu
!= pb
->cpu
))))
127 if(unlikely(ltt_time_compare(pa
->thread_birth
, pb
->thread_birth
) != 0))
129 if(unlikely(pa
->trace_num
!= pb
->trace_num
))
135 void destroy_hash_key(gpointer key
);
137 void destroy_hash_data(gpointer data
);
140 gboolean
scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
, gpointer data
)
142 XenoLTTData
*xenoltt_data
=
143 (XenoLTTData
*)g_object_get_data(
146 XenoLtt_Drawing_t
*drawing
= xenoltt_data
->drawing
;
147 unsigned int cell_height
=
148 get_cell_height(GTK_TREE_VIEW(xenoltt_data
->thread_list
->thread_list_widget
));
150 switch(event
->direction
) {
152 gtk_adjustment_set_value(xenoltt_data
->v_adjust
,
153 gtk_adjustment_get_value(xenoltt_data
->v_adjust
) - cell_height
);
155 case GDK_SCROLL_DOWN
:
156 gtk_adjustment_set_value(xenoltt_data
->v_adjust
,
157 gtk_adjustment_get_value(xenoltt_data
->v_adjust
) + cell_height
);
160 g_error("should only scroll up and down.");
166 static void update_index_to_pixmap_each(XenoThreadInfo
*key
,
167 HashedThreadData
*value
,
168 ThreadList
*thread_list
)
170 guint array_index
= threadlist_get_index_from_data(thread_list
, value
);
172 g_assert(array_index
< thread_list
->index_to_pixmap
->len
);
175 (GdkPixmap
**)&g_ptr_array_index(thread_list
->index_to_pixmap
, array_index
);
177 *pixmap
= value
->pixmap
;
181 void update_index_to_pixmap(ThreadList
*thread_list
)
183 g_ptr_array_set_size(thread_list
->index_to_pixmap
,
184 g_hash_table_size(thread_list
->thread_hash
));
185 g_hash_table_foreach(thread_list
->thread_hash
,
186 (GHFunc
)update_index_to_pixmap_each
,
191 static void update_pixmap_size_each(XenoThreadInfo
*key
,
192 HashedThreadData
*value
,
195 GdkPixmap
*old_pixmap
= value
->pixmap
;
198 gdk_pixmap_new(old_pixmap
,
203 gdk_pixmap_unref(old_pixmap
);
207 void update_pixmap_size(ThreadList
*thread_list
, guint width
)
209 g_hash_table_foreach(thread_list
->thread_hash
,
210 (GHFunc
)update_pixmap_size_each
,
215 typedef struct _CopyPixmap
{
219 gint xsrc
, ysrc
, xdest
, ydest
, width
, height
;
222 static void copy_pixmap_region_each(XenoThreadInfo
*key
,
223 HashedThreadData
*value
,
226 GdkPixmap
*src
= cp
->src
;
227 GdkPixmap
*dest
= cp
->dest
;
230 dest
= value
->pixmap
;
234 gdk_draw_drawable (dest
,
238 cp
->xdest
, cp
->ydest
,
239 cp
->width
, cp
->height
);
245 void copy_pixmap_region(ThreadList
*thread_list
, GdkDrawable
*dest
,
246 GdkGC
*gc
, GdkDrawable
*src
,
247 gint xsrc
, gint ysrc
,
248 gint xdest
, gint ydest
, gint width
, gint height
)
250 CopyPixmap cp
= { dest
, gc
, src
, xsrc
, ysrc
, xdest
, ydest
, width
, height
};
252 g_hash_table_foreach(thread_list
->thread_hash
,
253 (GHFunc
)copy_pixmap_region_each
,
259 typedef struct _RectanglePixmap
{
261 gint x
, y
, width
, height
;
265 static void rectangle_pixmap_each(XenoThreadInfo
*key
,
266 HashedThreadData
*value
,
270 rp
->height
= value
->height
;
272 gdk_draw_rectangle (value
->pixmap
,
276 rp
->width
, rp
->height
);
282 void rectangle_pixmap(ThreadList
*thread_list
, GdkGC
*gc
,
283 gboolean filled
, gint x
, gint y
, gint width
, gint height
)
285 RectanglePixmap rp
= { filled
, x
, y
, width
, height
, gc
};
287 g_hash_table_foreach(thread_list
->thread_hash
,
288 (GHFunc
)rectangle_pixmap_each
,
293 /* Renders each pixmaps into on big drawable */
294 void copy_pixmap_to_screen(ThreadList
*thread_list
,
298 gint width
, gint height
)
300 if(thread_list
->index_to_pixmap
->len
== 0) return;
301 guint cell_height
= thread_list
->cell_height
;
304 gint begin
= floor(y
/(double)cell_height
);
305 gint end
= MIN(ceil((y
+height
)/(double)cell_height
),
306 thread_list
->index_to_pixmap
->len
);
309 for(i
=begin
; i
<end
; i
++) {
310 g_assert(i
<thread_list
->index_to_pixmap
->len
);
311 /* Render the pixmap to the screen */
313 //(GdkPixmap*)g_ptr_array_index(thread_list->index_to_pixmap, i);
314 GDK_PIXMAP(g_ptr_array_index(thread_list
->index_to_pixmap
, i
));
316 gdk_draw_drawable (dest
,
336 ThreadList
*threadlist_construct(void)
338 GtkTreeViewColumn
*column
;
339 GtkCellRenderer
*renderer
;
341 ThreadList
* thread_list
= g_new(ThreadList
,1);
343 thread_list
->number_of_thread
= 0;
345 thread_list
->current_hash_data
= NULL
;
347 /* Create the Xenomai Thread list */
348 thread_list
->list_store
= gtk_list_store_new ( N_COLUMNS
,
358 thread_list
->thread_list_widget
=
359 gtk_tree_view_new_with_model
360 (GTK_TREE_MODEL (thread_list
->list_store
));
362 g_object_unref (G_OBJECT (thread_list
->list_store
));
364 gtk_tree_sortable_set_default_sort_func(
365 GTK_TREE_SORTABLE(thread_list
->list_store
),
371 gtk_tree_sortable_set_sort_column_id(
372 GTK_TREE_SORTABLE(thread_list
->list_store
),
373 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID
,
377 thread_list
->thread_hash
= g_hash_table_new_full(
378 thread_list_hash_fct
, thread_list_equ_fct
,
379 destroy_hash_key
, destroy_hash_data
383 gtk_tree_view_set_headers_visible(
384 GTK_TREE_VIEW(thread_list
->thread_list_widget
), TRUE
);
386 /* Create a column, associating the "text" attribute of the
387 * cell_renderer to the first column of the model */
388 /* Columns alignment : 0.0 : Left 0.5 : Center 1.0 : Right */
389 renderer
= gtk_cell_renderer_text_new ();
390 thread_list
->renderer
= renderer
;
392 gint vertical_separator
;
393 gtk_widget_style_get (GTK_WIDGET (thread_list
->thread_list_widget
),
394 "vertical-separator", &vertical_separator
,
396 gtk_cell_renderer_get_size(renderer
,
397 GTK_WIDGET(thread_list
->thread_list_widget
),
402 &thread_list
->cell_height
);
404 #if GTK_CHECK_VERSION(2,4,15)
406 g_object_get(G_OBJECT(renderer
), "ypad", &ypad
, NULL
);
408 thread_list
->cell_height
+= ypad
;
410 thread_list
->cell_height
+= vertical_separator
;
413 /* Column 1 representing the Xenomai Task name */
414 column
= gtk_tree_view_column_new_with_attributes ( "Task",
419 gtk_tree_view_column_set_alignment (column
, 0.0);
420 gtk_tree_view_column_set_fixed_width (column
, 45);
421 gtk_tree_view_append_column (
422 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
424 thread_list
->button
= column
->button
;
426 /* Column 1 representing the priority of the task */
427 column
= gtk_tree_view_column_new_with_attributes ( "Priority",
432 gtk_tree_view_append_column (
433 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
435 // gtk_tree_view_column_set_visible(column, 0);
437 /* Column 1 representing the period of the task */
438 column
= gtk_tree_view_column_new_with_attributes ( "Period",
443 gtk_tree_view_append_column (
444 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
446 // gtk_tree_view_column_set_visible(column, 0);
448 column
= gtk_tree_view_column_new_with_attributes ( "CPU",
453 gtk_tree_view_append_column (
454 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
456 column
= gtk_tree_view_column_new_with_attributes ( "Birth sec",
461 gtk_tree_view_append_column (
462 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
464 //gtk_tree_view_column_set_visible(column, 0);
466 column
= gtk_tree_view_column_new_with_attributes ( "Birth nsec",
471 gtk_tree_view_append_column (
472 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
474 column
= gtk_tree_view_column_new_with_attributes ( "TRACE",
479 gtk_tree_view_append_column (
480 GTK_TREE_VIEW (thread_list
->thread_list_widget
), column
);
483 //gtk_tree_view_column_set_visible(column, 0);
485 g_object_set_data_full(
486 G_OBJECT(thread_list
->thread_list_widget
),
489 (GDestroyNotify
)threadlist_destroy
);
491 thread_list
->index_to_pixmap
= g_ptr_array_sized_new(ALLOCATE_PROCESSES
);
496 void threadlist_destroy(ThreadList
*thread_list
)
498 g_debug("threadlist_destroy %p", thread_list
);
499 g_hash_table_destroy(thread_list
->thread_hash
);
500 thread_list
->thread_hash
= NULL
;
501 g_ptr_array_free(thread_list
->index_to_pixmap
, TRUE
);
504 g_debug("threadlist_destroy end");
507 static gboolean
remove_hash_item(XenoThreadInfo
*thread_Info
,
508 HashedThreadData
*hashed_thread_data
,
509 ThreadList
*thread_list
)
513 iter
= hashed_thread_data
->y_iter
;
515 gtk_list_store_remove (thread_list
->list_store
, &iter
);
516 gdk_pixmap_unref(hashed_thread_data
->pixmap
);
518 if(likely(thread_list
->current_hash_data
!= NULL
)) {
519 if(likely(hashed_thread_data
==
520 thread_list
->current_hash_data
[thread_Info
->trace_num
][thread_Info
->cpu
]))
521 thread_list
->current_hash_data
[thread_Info
->trace_num
][thread_Info
->cpu
] = NULL
;
523 return TRUE
; /* remove the element from the hash table */
526 void threadlist_clear(ThreadList
*thread_list
)
528 g_info("threadlist_clear %p", thread_list
);
530 g_hash_table_foreach_remove(thread_list
->thread_hash
,
531 (GHRFunc
)remove_hash_item
,
532 (gpointer
)thread_list
);
533 thread_list
->number_of_thread
= 0;
534 update_index_to_pixmap(thread_list
);
538 GtkWidget
*threadlist_get_widget(ThreadList
*thread_list
)
540 return thread_list
->thread_list_widget
;
544 void destroy_hash_key(gpointer key
)
549 void destroy_hash_data(gpointer data
)
555 void threadlist_set_name(ThreadList
*thread_list
,
557 HashedThreadData
*hashed_thread_data
)
559 gtk_list_store_set ( thread_list
->list_store
, &hashed_thread_data
->y_iter
,
560 THREAD_COLUMN
, g_quark_to_string(name
),
564 void threadlist_set_prio(ThreadList
*thread_list
,
566 HashedThreadData
*hashed_thread_data
)
568 gtk_list_store_set ( thread_list
->list_store
, &hashed_thread_data
->y_iter
,
573 void threadlist_set_period(ThreadList
*thread_list
,
575 HashedThreadData
*hashed_thread_data
)
577 gtk_list_store_set ( thread_list
->list_store
, &hashed_thread_data
->y_iter
,
578 PERIOD_COLUMN
, period
,
583 int threadlist_add( ThreadList
*thread_list
,
584 XenoLtt_Drawing_t
*drawing
,
589 LttTime
*thread_birth
,
593 XenoThreadInfo
**pm_thread_Info
,
594 HashedThreadData
**pm_hashed_thread_data
)
596 XenoThreadInfo
*thread_Info
= g_new(XenoThreadInfo
, 1);
597 HashedThreadData
*hashed_thread_data
= g_new(HashedThreadData
, 1);
598 *pm_hashed_thread_data
= hashed_thread_data
;
599 *pm_thread_Info
= thread_Info
;
601 thread_Info
->address
= address
;
602 thread_Info
->prio
= prio
;
604 thread_Info
->cpu
= cpu
;
606 thread_Info
->cpu
= 0;
607 thread_Info
->period
= period
;
608 thread_Info
->thread_birth
= *thread_birth
;
609 thread_Info
->trace_num
= trace_num
;
611 /* When we create it from before state update, we are sure that the
612 * last event occured before the beginning of the global area.
614 * If it is created after state update, this value (0) will be
615 * overriden by the new state before anything is drawn.
617 hashed_thread_data
->x
.over
= 0;
618 hashed_thread_data
->x
.over_used
= FALSE
;
619 hashed_thread_data
->x
.over_marked
= FALSE
;
620 hashed_thread_data
->x
.middle
= 0;
621 hashed_thread_data
->x
.middle_used
= FALSE
;
622 hashed_thread_data
->x
.middle_marked
= FALSE
;
623 hashed_thread_data
->x
.under
= 0;
624 hashed_thread_data
->x
.under_used
= FALSE
;
625 hashed_thread_data
->x
.under_marked
= FALSE
;
626 hashed_thread_data
->next_good_time
= ltt_time_zero
;
627 /* Add a new row to the model */
628 gtk_list_store_append ( thread_list
->list_store
,
629 &hashed_thread_data
->y_iter
);
631 gtk_list_store_set ( thread_list
->list_store
, &hashed_thread_data
->y_iter
,
632 THREAD_COLUMN
, g_quark_to_string(name
),
634 PERIOD_COLUMN
, period
,
636 BIRTH_S_COLUMN
, thread_birth
->tv_sec
,
637 BIRTH_NS_COLUMN
, thread_birth
->tv_nsec
,
638 TRACE_COLUMN
, trace_num
,
641 g_hash_table_insert(thread_list
->thread_hash
,
642 (gpointer
)thread_Info
,
643 (gpointer
)hashed_thread_data
);
645 thread_list
->number_of_thread
++;
647 hashed_thread_data
->height
= thread_list
->cell_height
;
649 g_assert(hashed_thread_data
->height
!= 0);
651 *height
= hashed_thread_data
->height
* thread_list
->number_of_thread
;
653 hashed_thread_data
->pixmap
=
654 gdk_pixmap_new(drawing
->drawing_area
->window
,
655 drawing
->alloc_width
,
656 hashed_thread_data
->height
,
660 gdk_draw_rectangle (hashed_thread_data
->pixmap
,
661 drawing
->drawing_area
->style
->black_gc
,
664 drawing
->alloc_width
,
665 hashed_thread_data
->height
);
667 update_index_to_pixmap(thread_list
);
673 int threadlist_remove( ThreadList
*thread_list
,
676 LttTime
*thread_birth
,
679 XenoThreadInfo thread_Info
;
680 HashedThreadData
*hashed_thread_data
;
683 thread_Info
.address
= address
;
685 thread_Info
.cpu
= cpu
;
688 thread_Info
.thread_birth
= *thread_birth
;
689 thread_Info
.trace_num
= trace_num
;
693 (HashedThreadData
*)g_hash_table_lookup(
694 thread_list
->thread_hash
,
696 if(likely(hashed_thread_data
!= NULL
))
698 iter
= hashed_thread_data
->y_iter
;
700 gtk_list_store_remove (thread_list
->list_store
, &iter
);
702 g_hash_table_remove(thread_list
->thread_hash
,
705 if(likely(thread_list
->current_hash_data
!= NULL
)) {
706 if(likely(hashed_thread_data
== thread_list
->current_hash_data
[trace_num
][cpu
])) {
707 thread_list
->current_hash_data
[trace_num
][cpu
] = NULL
;
711 gdk_pixmap_unref(hashed_thread_data
->pixmap
);
713 update_index_to_pixmap(thread_list
);
715 thread_list
->number_of_thread
--;
725 static inline guint
get_cpu_number_from_name(GQuark name
)
731 string
= g_quark_to_string(name
);
733 begin
= strrchr(string
, '/');
736 g_assert(begin
!= '\0');
738 cpu
= strtoul(begin
, NULL
, 10);