1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 Michel Dagenais
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,
24 #include <lttv/tracecontext.h>
25 #include <ltt/event.h>
26 #include <ltt/facility.h>
27 #include <ltt/trace.h>
31 #define min(a,b) (((a)<(b))?(a):(b))
34 gint
compare_tracefile(gconstpointer a
, gconstpointer b
)
38 const LttvTracefileContext
*trace_a
= (const LttvTracefileContext
*)a
;
39 const LttvTracefileContext
*trace_b
= (const LttvTracefileContext
*)b
;
41 if(likely(trace_a
!= trace_b
)) {
42 comparison
= ltt_time_compare(trace_a
->timestamp
, trace_b
->timestamp
);
43 if(unlikely(comparison
== 0)) {
44 if(trace_a
->index
< trace_b
->index
) comparison
= -1;
45 else if(trace_a
->index
> trace_b
->index
) comparison
= 1;
46 else if(trace_a
->t_context
->index
< trace_b
->t_context
->index
)
48 else if(trace_a
->t_context
->index
> trace_b
->t_context
->index
)
55 struct _LttvTracesetContextPosition
{
56 GArray
*ep
; /* Array of LttEventPosition */
57 GArray
*tfc
; /* Array of corresponding
59 LttTime timestamp
; /* Current time at the saved position */
60 /* If ltt_time_infinite : no position is
61 * set, else, a position is set (may be end
62 * of trace, with ep->len == 0) */
65 void lttv_context_init(LttvTracesetContext
*self
, LttvTraceset
*ts
)
67 LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->init(self
, ts
);
71 void lttv_context_fini(LttvTracesetContext
*self
)
73 LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->fini(self
);
78 lttv_context_new_traceset_context(LttvTracesetContext
*self
)
80 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_traceset_context(self
);
87 lttv_context_new_trace_context(LttvTracesetContext
*self
)
89 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_trace_context(self
);
93 LttvTracefileContext
*
94 lttv_context_new_tracefile_context(LttvTracesetContext
*self
)
96 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_tracefile_context(self
);
99 /****************************************************************************
100 * lttv_traceset_context_compute_time_span
102 * Keep the time span is sync with on the fly addition and removal of traces
103 * in a trace set. It must be called each time a trace is added/removed from
104 * the traceset. It could be more efficient to call it only once a bunch
105 * of traces are loaded, but the calculation is not long, so it's not
108 * Author : Xang Xiu Yang
109 ***************************************************************************/
110 static void lttv_traceset_context_compute_time_span(
111 LttvTracesetContext
*self
,
112 TimeInterval
*time_span
)
114 LttvTraceset
* traceset
= self
->ts
;
115 int numTraces
= lttv_traceset_number(traceset
);
118 LttvTraceContext
*tc
;
121 time_span
->start_time
.tv_sec
= 0;
122 time_span
->start_time
.tv_nsec
= 0;
123 time_span
->end_time
.tv_sec
= 0;
124 time_span
->end_time
.tv_nsec
= 0;
126 for(i
=0; i
<numTraces
;i
++){
127 tc
= self
->traces
[i
];
130 ltt_trace_time_span_get(trace
, &s
, &e
);
133 time_span
->start_time
= s
;
134 time_span
->end_time
= e
;
136 if(s
.tv_sec
< time_span
->start_time
.tv_sec
137 || (s
.tv_sec
== time_span
->start_time
.tv_sec
138 && s
.tv_nsec
< time_span
->start_time
.tv_nsec
))
139 time_span
->start_time
= s
;
140 if(e
.tv_sec
> time_span
->end_time
.tv_sec
141 || (e
.tv_sec
== time_span
->end_time
.tv_sec
142 && e
.tv_nsec
> time_span
->end_time
.tv_nsec
))
143 time_span
->end_time
= e
;
148 static void init_tracefile_context(LttTracefile
*tracefile
,
149 LttvTraceContext
*tc
)
151 LttvTracefileContext
*tfc
;
152 LttvTracesetContext
*tsc
= tc
->ts_context
;
154 tfc
= LTTV_TRACESET_CONTEXT_GET_CLASS(tsc
)->new_tracefile_context(tsc
);
156 tfc
->index
= tc
->tracefiles
->len
;
157 tc
->tracefiles
= g_array_append_val(tc
->tracefiles
, tfc
);
162 tfc
->event
= lttv_hooks_new();
163 tfc
->event_by_id
= lttv_hooks_by_id_new();
164 tfc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
169 init(LttvTracesetContext
*self
, LttvTraceset
*ts
)
173 LttvTraceContext
*tc
;
175 GData
**tracefiles_groups
;
177 struct compute_tracefile_group_args args
;
179 nb_trace
= lttv_traceset_number(ts
);
181 self
->traces
= g_new(LttvTraceContext
*, nb_trace
);
182 self
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
183 self
->ts_a
= lttv_traceset_attribute(ts
);
184 self
->sync_position
= lttv_traceset_context_position_new();
185 for(i
= 0 ; i
< nb_trace
; i
++) {
186 tc
= LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_trace_context(self
);
187 self
->traces
[i
] = tc
;
189 tc
->ts_context
= self
;
191 tc
->vt
= lttv_traceset_get(ts
, i
);
192 tc
->t
= lttv_trace(tc
->vt
);
193 tc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
194 tc
->t_a
= lttv_trace_attribute(tc
->vt
);
195 tc
->tracefiles
= g_array_sized_new(FALSE
, TRUE
,
196 sizeof(LttvTracefileContext
*), 10);
198 tracefiles_groups
= ltt_trace_get_tracefiles_groups(tc
->t
);
199 if(tracefiles_groups
!= NULL
) {
200 args
.func
= (ForEachTraceFileFunc
)init_tracefile_context
;
203 g_datalist_foreach(tracefiles_groups
,
204 (GDataForeachFunc
)compute_tracefile_group
,
209 nb_control
= ltt_trace_control_tracefile_number(tc
->t
);
210 nb_per_cpu
= ltt_trace_per_cpu_tracefile_number(tc
->t
);
211 nb_tracefile
= nb_control
+ nb_per_cpu
;
212 tc
->tracefiles
= g_new(LttvTracefileContext
*, nb_tracefile
);
214 for(j
= 0 ; j
< nb_tracefile
; j
++) {
215 tfc
= LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_tracefile_context(self
);
216 tc
->tracefiles
[j
] = tfc
;
221 tfc
->tf
= ltt_trace_control_tracefile_get(tc
->t
, j
);
224 tfc
->control
= FALSE
;
225 tfc
->tf
= ltt_trace_per_cpu_tracefile_get(tc
->t
, j
- nb_control
);
229 tfc
->e
= ltt_event_new();
230 tfc
->event
= lttv_hooks_new();
231 tfc
->event_by_id
= lttv_hooks_by_id_new();
232 tfc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
237 self
->pqueue
= g_tree_new(compare_tracefile
);
238 lttv_process_traceset_seek_time(self
, ltt_time_zero
);
239 lttv_traceset_context_compute_time_span(self
, &self
->time_span
);
244 void fini(LttvTracesetContext
*self
)
246 guint i
, j
, nb_trace
, nb_tracefile
;
248 LttvTraceContext
*tc
;
250 LttvTracefileContext
**tfc
;
252 LttvTraceset
*ts
= self
->ts
;
254 g_tree_destroy(self
->pqueue
);
255 g_object_unref(self
->a
);
256 lttv_traceset_context_position_destroy(self
->sync_position
);
258 nb_trace
= lttv_traceset_number(ts
);
260 for(i
= 0 ; i
< nb_trace
; i
++) {
261 tc
= self
->traces
[i
];
263 g_object_unref(tc
->a
);
265 nb_tracefile
= tc
->tracefiles
->len
;
267 for(j
= 0 ; j
< nb_tracefile
; j
++) {
268 tfc
= &g_array_index(tc
->tracefiles
, LttvTracefileContext
*, j
);
269 lttv_hooks_destroy((*tfc
)->event
);
270 lttv_hooks_by_id_destroy((*tfc
)->event_by_id
);
271 g_object_unref((*tfc
)->a
);
272 g_object_unref(*tfc
);
274 g_array_free(tc
->tracefiles
, TRUE
);
277 g_free(self
->traces
);
281 void lttv_traceset_context_add_hooks(LttvTracesetContext
*self
,
282 LttvHooks
*before_traceset
,
283 LttvHooks
*before_trace
,
284 LttvHooks
*before_tracefile
,
286 LttvHooksById
*event_by_id
)
288 LttvTraceset
*ts
= self
->ts
;
292 LttvTraceContext
*tc
;
294 lttv_hooks_call(before_traceset
, self
);
296 nb_trace
= lttv_traceset_number(ts
);
298 for(i
= 0 ; i
< nb_trace
; i
++) {
299 tc
= self
->traces
[i
];
300 lttv_trace_context_add_hooks(tc
,
309 void lttv_traceset_context_remove_hooks(LttvTracesetContext
*self
,
310 LttvHooks
*after_traceset
,
311 LttvHooks
*after_trace
,
312 LttvHooks
*after_tracefile
,
314 LttvHooksById
*event_by_id
)
317 LttvTraceset
*ts
= self
->ts
;
321 LttvTraceContext
*tc
;
323 nb_trace
= lttv_traceset_number(ts
);
325 for(i
= 0 ; i
< nb_trace
; i
++) {
326 tc
= self
->traces
[i
];
327 lttv_trace_context_remove_hooks(tc
,
334 lttv_hooks_call(after_traceset
, self
);
339 void lttv_trace_context_add_hooks(LttvTraceContext
*self
,
340 LttvHooks
*before_trace
,
341 LttvHooks
*before_tracefile
,
343 LttvHooksById
*event_by_id
)
345 guint i
, nb_tracefile
;
347 LttvTracefileContext
**tfc
;
349 lttv_hooks_call(before_trace
, self
);
351 nb_tracefile
= self
->tracefiles
->len
;
353 for(i
= 0 ; i
< nb_tracefile
; i
++) {
354 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
355 lttv_tracefile_context_add_hooks(*tfc
,
364 void lttv_trace_context_remove_hooks(LttvTraceContext
*self
,
365 LttvHooks
*after_trace
,
366 LttvHooks
*after_tracefile
,
368 LttvHooksById
*event_by_id
)
370 guint i
, nb_tracefile
;
372 LttvTracefileContext
**tfc
;
374 nb_tracefile
= self
->tracefiles
->len
;
376 for(i
= 0 ; i
< nb_tracefile
; i
++) {
377 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
378 lttv_tracefile_context_remove_hooks(*tfc
,
384 lttv_hooks_call(after_trace
, self
);
387 void lttv_tracefile_context_add_hooks(LttvTracefileContext
*self
,
388 LttvHooks
*before_tracefile
,
390 LttvHooksById
*event_by_id
)
396 lttv_hooks_call(before_tracefile
, self
);
397 lttv_hooks_add_list(self
->event
, event
);
398 if(event_by_id
!= NULL
) {
399 for(i
= 0; i
< event_by_id
->array
->len
; i
++) {
400 index
= g_array_index(event_by_id
->array
, guint
, i
);
401 hook
= lttv_hooks_by_id_find(self
->event_by_id
, index
);
402 lttv_hooks_add_list(hook
, lttv_hooks_by_id_get(event_by_id
, index
));
407 void lttv_tracefile_context_remove_hooks(LttvTracefileContext
*self
,
408 LttvHooks
*after_tracefile
,
410 LttvHooksById
*event_by_id
)
416 lttv_hooks_remove_list(self
->event
, event
);
417 if(event_by_id
!= NULL
) {
418 for(i
= 0; i
< event_by_id
->array
->len
; i
++) {
419 index
= g_array_index(event_by_id
->array
, guint
, i
);
420 hook
= lttv_hooks_by_id_get(self
->event_by_id
, index
);
422 lttv_hooks_remove_list(hook
, lttv_hooks_by_id_get(event_by_id
, index
));
426 lttv_hooks_call(after_tracefile
, self
);
431 void lttv_tracefile_context_add_hooks_by_id(LttvTracefileContext
*tfc
,
433 LttvHooks
*event_by_id
)
436 h
= lttv_hooks_by_id_find(tfc
->event_by_id
, i
);
437 lttv_hooks_add_list(h
, event_by_id
);
440 void lttv_tracefile_context_remove_hooks_by_id(LttvTracefileContext
*tfc
,
443 lttv_hooks_by_id_remove(tfc
->event_by_id
, i
);
446 static LttvTracesetContext
*
447 new_traceset_context(LttvTracesetContext
*self
)
449 return g_object_new(LTTV_TRACESET_CONTEXT_TYPE
, NULL
);
453 static LttvTraceContext
*
454 new_trace_context(LttvTracesetContext
*self
)
456 return g_object_new(LTTV_TRACE_CONTEXT_TYPE
, NULL
);
460 static LttvTracefileContext
*
461 new_tracefile_context(LttvTracesetContext
*self
)
463 return g_object_new(LTTV_TRACEFILE_CONTEXT_TYPE
, NULL
);
468 traceset_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
470 /* Be careful of anything which would not work well with shallow copies */
475 traceset_context_finalize (LttvTracesetContext
*self
)
477 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACESET_CONTEXT_TYPE
)))
478 ->finalize(G_OBJECT(self
));
483 traceset_context_class_init (LttvTracesetContextClass
*klass
)
485 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
487 gobject_class
->finalize
= (void (*)(GObject
*self
))traceset_context_finalize
;
490 klass
->new_traceset_context
= new_traceset_context
;
491 klass
->new_trace_context
= new_trace_context
;
492 klass
->new_tracefile_context
= new_tracefile_context
;
497 lttv_traceset_context_get_type(void)
499 static GType type
= 0;
501 static const GTypeInfo info
= {
502 sizeof (LttvTracesetContextClass
),
503 NULL
, /* base_init */
504 NULL
, /* base_finalize */
505 (GClassInitFunc
) traceset_context_class_init
, /* class_init */
506 NULL
, /* class_finalize */
507 NULL
, /* class_data */
508 sizeof (LttvTracesetContext
),
510 (GInstanceInitFunc
) traceset_context_instance_init
, /* instance_init */
511 NULL
/* Value handling */
514 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTracesetContextType",
522 trace_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
524 /* Be careful of anything which would not work well with shallow copies */
529 trace_context_finalize (LttvTraceContext
*self
)
531 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACE_CONTEXT_TYPE
)))->
532 finalize(G_OBJECT(self
));
537 trace_context_class_init (LttvTraceContextClass
*klass
)
539 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
541 gobject_class
->finalize
= (void (*)(GObject
*self
)) trace_context_finalize
;
546 lttv_trace_context_get_type(void)
548 static GType type
= 0;
550 static const GTypeInfo info
= {
551 sizeof (LttvTraceContextClass
),
552 NULL
, /* base_init */
553 NULL
, /* base_finalize */
554 (GClassInitFunc
) trace_context_class_init
, /* class_init */
555 NULL
, /* class_finalize */
556 NULL
, /* class_data */
557 sizeof (LttvTraceContext
),
559 (GInstanceInitFunc
) trace_context_instance_init
, /* instance_init */
560 NULL
/* Value handling */
563 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTraceContextType",
571 tracefile_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
573 /* Be careful of anything which would not work well with shallow copies */
578 tracefile_context_finalize (LttvTracefileContext
*self
)
580 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACEFILE_CONTEXT_TYPE
)))
581 ->finalize(G_OBJECT(self
));
586 tracefile_context_class_init (LttvTracefileContextClass
*klass
)
588 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
590 gobject_class
->finalize
= (void (*)(GObject
*self
))tracefile_context_finalize
;
595 lttv_tracefile_context_get_type(void)
597 static GType type
= 0;
599 static const GTypeInfo info
= {
600 sizeof (LttvTracefileContextClass
),
601 NULL
, /* base_init */
602 NULL
, /* base_finalize */
603 (GClassInitFunc
) tracefile_context_class_init
, /* class_init */
604 NULL
, /* class_finalize */
605 NULL
, /* class_data */
606 sizeof (LttvTracefileContext
),
608 (GInstanceInitFunc
) tracefile_context_instance_init
, /* instance_init */
609 NULL
/* Value handling */
612 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTracefileContextType",
620 static gboolean
get_first(gpointer key
, gpointer value
, gpointer user_data
) {
621 g_assert(key
== value
);
622 *((LttvTracefileContext
**)user_data
) = (LttvTracefileContext
*)value
;
627 // Test to see if pqueue is traversed in the right order.
628 static LttTime test_time
;
630 static gboolean
test_tree(gpointer key
, gpointer value
, gpointer user_data
) {
632 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)key
;
634 g_debug("Tracefile name %s, time %lu.%lu, tfi %u, ti %u",
635 g_quark_to_string(ltt_tracefile_name(tfc
->tf
)),
636 tfc
->timestamp
.tv_sec
, tfc
->timestamp
.tv_nsec
,
637 tfc
->index
, tfc
->t_context
->index
);
639 if(user_data
!= NULL
) {
640 if(((LttvTracefileContext
*)user_data
) == (LttvTracefileContext
*)value
) {
641 g_assert(compare_tracefile(user_data
, value
) == 0);
643 g_assert(compare_tracefile(user_data
, value
) != 0);
645 g_assert(ltt_time_compare(test_time
, tfc
->timestamp
) <= 0);
646 test_time
.tv_sec
= tfc
->timestamp
.tv_sec
;
647 test_time
.tv_nsec
= tfc
->timestamp
.tv_nsec
;
650 //g_assert(((LttvTracefileContext *)user_data) != (LttvTracefileContext *)value);
657 void lttv_process_traceset_begin(LttvTracesetContext
*self
,
658 LttvHooks
*before_traceset
,
659 LttvHooks
*before_trace
,
660 LttvHooks
*before_tracefile
,
662 LttvHooksById
*event_by_id
)
665 /* simply add hooks in context. _before hooks are called by add_hooks. */
666 /* It calls all before_traceset, before_trace, and before_tracefile hooks. */
667 lttv_traceset_context_add_hooks(self
,
676 enum read_state
{ LAST_NONE
, LAST_OK
, LAST_EMPTY
};
678 /* Note : a _middle must be preceded from a _seek or another middle */
679 guint
lttv_process_traceset_middle(LttvTracesetContext
*self
,
682 const LttvTracesetContextPosition
*end_position
)
684 GTree
*pqueue
= self
->pqueue
;
686 guint fac_id
, ev_id
, id
;
688 LttvTracefileContext
*tfc
;
696 enum read_state last_read_state
= LAST_NONE
;
698 gboolean last_ret
= FALSE
; /* return value of the last hook list called */
700 /* Get the next event from the pqueue, call its hooks,
701 reinsert in the pqueue the following event from the same tracefile
702 unless the tracefile is finished or the event is later than the
707 g_tree_foreach(pqueue
, get_first
, &tfc
);
708 /* End of traceset : tfc is NULL */
709 if(unlikely(tfc
== NULL
))
715 * - the maximum number of events specified?
716 * - the end position ?
718 * then the read is finished. We leave the queue in the same state and
722 if(unlikely(last_ret
== TRUE
||
723 ((count
>= nb_events
) && (nb_events
!= G_MAXULONG
)) ||
724 (end_position
!=NULL
&<tv_traceset_context_ctx_pos_compare(self
,
725 end_position
) == 0)||
726 ltt_time_compare(end
, tfc
->timestamp
) <= 0))
731 /* Get the tracefile with an event for the smallest time found. If two
732 or more tracefiles have events for the same time, hope that lookup
733 and remove are consistent. */
736 test_time
.tv_sec
= 0;
737 test_time
.tv_nsec
= 0;
738 g_debug("test tree before remove");
739 g_tree_foreach(pqueue
, test_tree
, tfc
);
741 g_tree_remove(pqueue
, tfc
);
744 test_time
.tv_sec
= 0;
745 test_time
.tv_nsec
= 0;
746 g_debug("test tree after remove");
747 g_tree_foreach(pqueue
, test_tree
, tfc
);
750 e
= ltt_tracefile_get_event(tfc
->tf
);
752 if(last_read_state
!= LAST_EMPTY
) {
753 /* Only call hooks if the last read has given an event or if we are at the
754 * first pass (not if last read returned end of tracefile) */
757 fac_id
= ltt_event_facility_id(e
);
758 ev_id
= ltt_event_eventtype_id(e
);
759 id
= GET_HOOK_ID(fac_id
, ev_id
);
760 last_ret
= lttv_hooks_call_merge(tfc
->event
, tfc
,
761 lttv_hooks_by_id_get(tfc
->event_by_id
, id
), tfc
);
764 read_ret
= ltt_tracefile_read(tfc
->tf
);
766 if(likely(!read_ret
)) {
767 //g_debug("An event is ready");
768 tfc
->timestamp
= ltt_event_time(e
);
769 g_assert(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) != 0);
770 g_tree_insert(pqueue
, tfc
, tfc
);
772 test_time
.tv_sec
= 0;
773 test_time
.tv_nsec
= 0;
774 g_debug("test tree after event ready");
775 g_tree_foreach(pqueue
, test_tree
, NULL
);
778 last_read_state
= LAST_OK
;
780 tfc
->timestamp
= ltt_time_infinite
;
782 if(read_ret
== ERANGE
) {
783 last_read_state
= LAST_EMPTY
;
784 g_debug("End of trace");
786 g_error("Error happened in lttv_process_traceset_middle");
792 void lttv_process_traceset_end(LttvTracesetContext
*self
,
793 LttvHooks
*after_traceset
,
794 LttvHooks
*after_trace
,
795 LttvHooks
*after_tracefile
,
797 LttvHooksById
*event_by_id
)
799 /* Remove hooks from context. _after hooks are called by remove_hooks. */
800 /* It calls all after_traceset, after_trace, and after_tracefile hooks. */
801 lttv_traceset_context_remove_hooks(self
,
809 /* Subtile modification :
810 * if tracefile has no event at or after the time requested, it is not put in
811 * the queue, as the next read would fail.
813 * Don't forget to empty the traceset pqueue before calling this.
815 void lttv_process_trace_seek_time(LttvTraceContext
*self
, LttTime start
)
817 guint i
, nb_tracefile
;
821 LttvTracefileContext
**tfc
;
823 nb_tracefile
= self
->tracefiles
->len
;
825 GTree
*pqueue
= self
->ts_context
->pqueue
;
827 for(i
= 0 ; i
< nb_tracefile
; i
++) {
828 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
830 //g_tree_remove(pqueue, *tfc);
832 ret
= ltt_tracefile_seek_time((*tfc
)->tf
, start
);
833 if(ret
== EPERM
) g_error("error in lttv_process_trace_seek_time seek");
835 if(ret
== 0) { /* not ERANGE especially */
836 (*tfc
)->timestamp
= ltt_event_time(ltt_tracefile_get_event((*tfc
)->tf
));
837 g_assert(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0);
838 g_tree_insert(pqueue
, (*tfc
), (*tfc
));
840 (*tfc
)->timestamp
= ltt_time_infinite
;
844 test_time
.tv_sec
= 0;
845 test_time
.tv_nsec
= 0;
846 g_debug("test tree after seek_time");
847 g_tree_foreach(pqueue
, test_tree
, NULL
);
855 void lttv_process_traceset_seek_time(LttvTracesetContext
*self
, LttTime start
)
859 LttvTraceContext
*tc
;
861 g_tree_destroy(self
->pqueue
);
862 self
->pqueue
= g_tree_new(compare_tracefile
);
864 nb_trace
= lttv_traceset_number(self
->ts
);
865 for(i
= 0 ; i
< nb_trace
; i
++) {
866 tc
= self
->traces
[i
];
867 lttv_process_trace_seek_time(tc
, start
);
872 gboolean
lttv_process_traceset_seek_position(LttvTracesetContext
*self
,
873 const LttvTracesetContextPosition
*pos
)
877 /* If a position is set, seek the traceset to this position */
878 if(ltt_time_compare(pos
->timestamp
, ltt_time_infinite
) != 0) {
879 g_tree_destroy(self
->pqueue
);
880 self
->pqueue
= g_tree_new(compare_tracefile
);
882 for(i
=0;i
<pos
->ep
->len
; i
++) {
883 LttEventPosition
**ep
= &g_array_index(pos
->ep
, LttEventPosition
*, i
);
884 LttvTracefileContext
**tfc
=
885 &g_array_index(pos
->tfc
, LttvTracefileContext
*, i
);
887 if(ltt_tracefile_seek_position((*tfc
)->tf
, *ep
) != 0)
889 (*tfc
)->timestamp
= ltt_event_time(ltt_tracefile_get_event((*tfc
)->tf
));
890 g_assert(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0);
891 g_tree_insert(self
->pqueue
, (*tfc
), (*tfc
));
893 (*tfc
)->timestamp
= ltt_time_infinite
;
898 test_time
.tv_sec
= 0;
899 test_time
.tv_nsec
= 0;
900 g_debug("test tree after seek_position");
901 g_tree_foreach(self
->pqueue
, test_tree
, NULL
);
912 find_field(LttEventType
*et
, const GQuark field
)
923 if(field
== 0) return NULL
;
925 f
= ltt_eventtype_field(et
);
926 t
= ltt_eventtype_type(et
);
927 g_assert(ltt_type_class(t
) == LTT_STRUCT
);
928 nb
= ltt_type_member_number(t
);
929 for(i
= 0 ; i
< nb
; i
++) {
930 ltt_type_member_type(t
, i
, &name
);
931 if(name
== field
) break;
934 return ltt_field_member(f
, i
);
937 LttvTraceHookByFacility
*lttv_trace_hook_get_fac(LttvTraceHook
*th
,
940 return &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, facility_id
);
943 /* Get the first facility corresponding to the name. As the types must be
944 * compatible, it is relevant to use the field name and sizes of the first
945 * facility to create data structures and assume the data will be compatible
946 * thorough the trace */
947 LttvTraceHookByFacility
*lttv_trace_hook_get_first(LttvTraceHook
*th
)
949 g_assert(th
->fac_list
->len
> 0);
950 return g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, 0);
954 /* Returns 0 on success, -1 if fails. */
956 lttv_trace_find_hook(LttTrace
*t
, GQuark facility
, GQuark event
,
957 GQuark field1
, GQuark field2
, GQuark field3
, LttvHook h
, gpointer hook_data
,
962 LttEventType
*et
, *first_et
;
966 guint i
, fac_id
, ev_id
;
968 LttvTraceHookByFacility
*thf
, *first_thf
;
970 facilities
= ltt_trace_facility_get_by_name(t
, facility
);
972 if(unlikely(facilities
== NULL
)) goto facility_error
;
974 th
->fac_index
= g_array_sized_new(FALSE
, TRUE
,
975 sizeof(LttvTraceHookByFacility
),
977 th
->fac_index
= g_array_set_size(th
->fac_index
, NUM_FACILITIES
);
979 th
->fac_list
= g_array_sized_new(FALSE
, TRUE
,
980 sizeof(LttvTraceHookByFacility
*),
982 th
->fac_list
= g_array_set_size(th
->fac_list
, facilities
->len
);
984 fac_id
= g_array_index(facilities
, guint
, 0);
985 f
= ltt_trace_get_facility_by_num(t
, fac_id
);
987 et
= ltt_facility_eventtype_get_by_name(f
, event
);
988 if(unlikely(et
== NULL
)) goto event_error
;
990 thf
= &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, fac_id
);
991 g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, 0) = thf
;
993 ev_id
= ltt_eventtype_id(et
);
996 thf
->id
= GET_HOOK_ID(fac_id
, ev_id
);
997 thf
->f1
= find_field(et
, field1
);
998 thf
->f2
= find_field(et
, field2
);
999 thf
->f3
= find_field(et
, field3
);
1000 thf
->hook_data
= hook_data
;
1005 /* Check for type compatibility too */
1006 for(i
=1;i
<facilities
->len
;i
++) {
1007 fac_id
= g_array_index(facilities
, guint
, i
);
1008 f
= ltt_trace_get_facility_by_num(t
, fac_id
);
1010 et
= ltt_facility_eventtype_get_by_name(f
, event
);
1011 if(unlikely(et
== NULL
)) goto event_error
;
1013 thf
= &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, fac_id
);
1014 g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, i
) = thf
;
1015 ev_id
= ltt_eventtype_id(et
);
1017 thf
->id
= GET_HOOK_ID(fac_id
, ev_id
);
1018 thf
->f1
= find_field(et
, field1
);
1019 if(check_fields_compatibility(first_et
, et
,
1020 first_thf
->f1
, thf
->f1
))
1023 thf
->f2
= find_field(et
, field2
);
1024 if(check_fields_compatibility(first_et
, et
,
1025 first_thf
->f2
, thf
->f2
))
1028 thf
->f3
= find_field(et
, field3
);
1029 if(check_fields_compatibility(first_et
, et
,
1030 first_thf
->f3
, thf
->f3
))
1032 thf
->hook_data
= hook_data
;
1040 g_error("Event type does not exist for event %s",
1041 g_quark_to_string(event
));
1044 g_error("No %s facility", g_quark_to_string(facility
));
1047 g_array_free(th
->fac_index
, TRUE
);
1048 g_array_free(th
->fac_list
, TRUE
);
1049 th
->fac_index
= NULL
;
1050 th
->fac_list
= NULL
;
1054 void lttv_trace_hook_destroy(LttvTraceHook
*th
)
1056 g_array_free(th
->fac_index
, TRUE
);
1057 g_array_free(th
->fac_list
, TRUE
);
1061 LttvTracesetContextPosition
*lttv_traceset_context_position_new()
1063 LttvTracesetContextPosition
*pos
= g_new(LttvTracesetContextPosition
,1);
1064 pos
->ep
= g_array_sized_new(FALSE
, TRUE
, sizeof(LttEventPosition
*),
1066 pos
->tfc
= g_array_sized_new(FALSE
, TRUE
, sizeof(LttvTracefileContext
*),
1068 pos
->timestamp
= ltt_time_infinite
;
1072 /* Save all positions, the ones with infinite time will have NULL
1074 void lttv_traceset_context_position_save(const LttvTracesetContext
*self
,
1075 LttvTracesetContextPosition
*pos
)
1078 guint num_traces
= lttv_traceset_number(self
->ts
);
1080 pos
->tfc
= g_array_set_size(pos
->tfc
, 0);
1081 pos
->ep
= g_array_set_size(pos
->ep
, 0);
1083 pos
->timestamp
= ltt_time_infinite
;
1085 for(i
=0; i
<num_traces
;i
++) {
1086 GArray
* tracefiles
= self
->traces
[i
]->tracefiles
;
1088 guint num_tracefiles
= tracefiles
->len
;
1090 for(j
=0;j
<num_tracefiles
;j
++) {
1091 LttvTracefileContext
**tfc
= &g_array_index(tracefiles
,
1092 LttvTracefileContext
*, j
);
1094 LttEvent
*event
= ltt_tracefile_get_event((*tfc
)->tf
);
1095 LttEventPosition
*ep
;
1097 if(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0) {
1098 ep
= ltt_event_position_new();
1099 ltt_event_position(event
, ep
);
1100 if(ltt_time_compare((*tfc
)->timestamp
, pos
->timestamp
) < 0)
1101 pos
->timestamp
= (*tfc
)->timestamp
;
1105 g_array_append_val(pos
->tfc
, *tfc
);
1106 g_array_append_val(pos
->ep
, ep
);
1112 void lttv_traceset_context_position_destroy(LttvTracesetContextPosition
*pos
)
1115 LttEventPosition
**ep
;
1117 for(i
=0;i
<pos
->ep
->len
;i
++) {
1118 ep
= &g_array_index(pos
->ep
, LttEventPosition
*, i
);
1122 g_array_free(pos
->ep
, TRUE
);
1123 g_array_free(pos
->tfc
, TRUE
);
1127 void lttv_traceset_context_position_copy(LttvTracesetContextPosition
*dest
,
1128 const LttvTracesetContextPosition
*src
)
1131 LttEventPosition
**src_ep
, **dest_ep
;
1133 dest
->ep
= g_array_set_size(dest
->ep
, src
->ep
->len
);
1134 dest
->tfc
= g_array_set_size(dest
->tfc
, src
->tfc
->len
);
1136 for(i
=0;i
<src
->ep
->len
;i
++) {
1137 src_ep
= &g_array_index(src
->ep
, LttEventPosition
*, i
);
1138 dest_ep
= &g_array_index(dest
->ep
, LttEventPosition
*, i
);
1139 if(*src_ep
!= NULL
) {
1140 *dest_ep
= ltt_event_position_new();
1141 ltt_event_position_copy(
1147 for(i
=0;i
<src
->tfc
->len
;i
++) {
1148 g_array_index(dest
->tfc
, LttvTracefileContext
*, i
) =
1149 g_array_index(src
->tfc
, LttvTracefileContext
*, i
);
1151 dest
->timestamp
= src
->timestamp
;
1154 gint
lttv_traceset_context_ctx_pos_compare(const LttvTracesetContext
*self
,
1155 const LttvTracesetContextPosition
*pos
)
1160 if(pos
->ep
->len
== 0) {
1161 if(lttv_traceset_number(self
->ts
) == 0) return 0;
1164 if(lttv_traceset_number(self
->ts
) == 0)
1167 for(i
=0;i
<pos
->ep
->len
;i
++) {
1168 LttEventPosition
*ep
= g_array_index(pos
->ep
, LttEventPosition
*, i
);
1169 LttvTracefileContext
*tfc
=
1170 g_array_index(pos
->tfc
, LttvTracefileContext
*, i
);
1173 if(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) != 0) {
1177 if(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) == 0) {
1180 LttEvent
*event
= ltt_tracefile_get_event(tfc
->tf
);
1182 ret
= ltt_event_position_compare((LttEventPosition
*)event
,
1186 if(ret
!= 0) return ret
;
1193 gint
lttv_traceset_context_pos_pos_compare(
1194 const LttvTracesetContextPosition
*pos1
,
1195 const LttvTracesetContextPosition
*pos2
)
1200 if(pos1
->ep
->len
== 0) {
1201 if(pos2
->ep
->len
== 0) return 0;
1204 if(pos2
->ep
->len
== 0)
1207 for(i
=0;i
<pos1
->ep
->len
;i
++) {
1208 LttEventPosition
*ep1
= g_array_index(pos1
->ep
, LttEventPosition
*, i
);
1209 LttvTracefileContext
*tfc1
= g_array_index(pos1
->tfc
,
1210 LttvTracefileContext
*, i
);
1213 for(j
=0;j
<pos2
->ep
->len
;j
++) {
1214 LttEventPosition
*ep2
= g_array_index(pos2
->ep
, LttEventPosition
*, j
);
1215 LttvTracefileContext
*tfc2
= g_array_index(pos2
->tfc
,
1216 LttvTracefileContext
*, j
);
1219 ret
= ltt_event_position_compare(ep1
, ep2
);
1223 if(ret
!= 0) return ret
;
1227 for(j
=0;j
<pos2
->ep
->len
;j
++) {
1228 LttEventPosition
*ep2
= g_array_index(pos2
->ep
, LttEventPosition
*, j
);
1229 LttvTracefileContext
*tfc2
= g_array_index(pos2
->tfc
,
1230 LttvTracefileContext
*, j
);
1232 if(ep2
!= NULL
) ret
= 1;
1241 LttTime
lttv_traceset_context_position_get_time(
1242 const LttvTracesetContextPosition
*pos
)
1244 return pos
->timestamp
;
1248 LttvTracefileContext
*lttv_traceset_context_get_current_tfc(LttvTracesetContext
*self
)
1250 GTree
*pqueue
= self
->pqueue
;
1251 LttvTracefileContext
*tfc
= NULL
;
1253 g_tree_foreach(pqueue
, get_first
, &tfc
);
1258 /* lttv_process_traceset_synchronize_tracefiles
1260 * Use the sync_position field of the trace set context to synchronize each
1261 * tracefile with the previously saved position.
1263 * If no previous position has been saved, it simply does nothing.
1265 void lttv_process_traceset_synchronize_tracefiles(LttvTracesetContext
*tsc
)
1267 g_assert(lttv_process_traceset_seek_position(tsc
, tsc
->sync_position
) == 0);
1273 void lttv_process_traceset_get_sync_data(LttvTracesetContext
*tsc
)
1275 lttv_traceset_context_position_save(tsc
, tsc
->sync_position
);
1278 struct seek_back_data
{
1279 guint first_event
; /* Index of the first event in the array : we will always
1280 overwrite at this position : this is a circular array.
1283 GPtrArray
*array
; /* array of LttvTracesetContextPositions pointers */
1286 static gint
seek_back_event_hook(void *hook_data
, void* call_data
)
1288 struct seek_back_data
*sd
= (struct seek_back_data
*)hook_data
;
1289 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
1290 LttvTracesetContext
*tsc
= tfc
->t_context
->ts_context
;
1291 LttvTracesetContextPosition
*pos
;
1293 if(sd
->events_found
< sd
->array
->len
) {
1294 pos
= (LttvTracesetContextPosition
*)g_ptr_array_index (sd
->array
,
1297 pos
= (LttvTracesetContextPosition
*)g_ptr_array_index (sd
->array
,
1301 lttv_traceset_context_position_save(tsc
, pos
);
1303 if(sd
->first_event
>= sd
->array
->len
- 1) sd
->first_event
= 0;
1304 else sd
->first_event
++;
1306 sd
->events_found
= min(sd
->array
->len
, sd
->events_found
+ 1);
1312 /* Seek back n events back from the current position.
1315 * @self The trace set context
1316 * @n number of events to jump over
1317 * @first_offset The initial offset value used. Hint : try about 100000ns.
1319 * Return value : the number of events found (might be lower than the number
1320 * requested if beginning of traceset is reached).
1322 * The first search will go back first_offset and try to find the last n events
1323 * matching the filter. If there are not enough, it will try to go back from the
1324 * new trace point from first_offset*2, and so on, until beginning of trace or n
1327 * Note : this function does not take in account the LttvFilter : use the
1328 * similar function found in state.c instead.
1330 * Note2 : the caller must make sure that the LttvTracesetContext does not
1331 * contain any hook, as process_traceset_middle is used in this routine.
1333 guint
lttv_process_traceset_seek_n_backward(LttvTracesetContext
*self
,
1334 guint n
, LttTime first_offset
)
1337 LttvTracesetContextPosition
*next_iter_end_pos
=
1338 lttv_traceset_context_position_new();
1339 LttvTracesetContextPosition
*end_pos
= lttv_traceset_context_position_new();
1341 LttTime time_offset
;
1342 struct seek_back_data sd
;
1343 LttvHooks
*hooks
= lttv_hooks_new();
1346 sd
.events_found
= 0;
1347 sd
.array
= g_ptr_array_sized_new(n
);
1348 g_ptr_array_set_size(sd
.array
, n
);
1350 g_ptr_array_index (sd
.array
, i
) = lttv_traceset_context_position_new();
1353 lttv_traceset_context_position_save(self
, next_iter_end_pos
);
1354 /* Get the current time from which we will offset */
1355 time
= lttv_traceset_context_position_get_time(next_iter_end_pos
);
1356 /* the position saved might be end of traceset... */
1357 if(ltt_time_compare(time
, self
->time_span
.end_time
) > 0) {
1358 time
= self
->time_span
.end_time
;
1360 time_offset
= first_offset
;
1362 lttv_hooks_add(hooks
, seek_back_event_hook
, &sd
, LTTV_PRIO_DEFAULT
);
1364 lttv_process_traceset_begin(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1367 /* stop criteria : - n events found
1368 * - time < beginning of trace */
1369 if(ltt_time_compare(time
, self
->time_span
.start_time
) < 0) break;
1370 if(sd
.events_found
== n
) break;
1372 lttv_traceset_context_position_copy(end_pos
, next_iter_end_pos
);
1374 /* We must seek the traceset back to time - time_offset */
1375 /* this time becomes the new reference time */
1376 time
= ltt_time_sub(time
, time_offset
);
1378 lttv_process_traceset_seek_time(self
, time
);
1379 lttv_traceset_context_position_save(self
, next_iter_end_pos
);
1381 /* Process the traceset, calling a hook which adds events
1382 * to the array, overwriting the tail. It changes first_event and
1383 * events_found too. */
1384 /* We would like to have a clean context here : no other hook than our's */
1386 lttv_process_traceset_middle(self
, ltt_time_infinite
,
1387 G_MAXUINT
, end_pos
);
1389 time_offset
= ltt_time_mul(time_offset
, BACKWARD_SEEK_MUL
);
1392 lttv_traceset_context_position_destroy(end_pos
);
1393 lttv_traceset_context_position_destroy(next_iter_end_pos
);
1395 lttv_process_traceset_end(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1397 if(sd
.events_found
> 0) {
1398 /* Seek the traceset to the first event in the circular array */
1399 LttvTracesetContextPosition
*pos
=
1400 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
,
1402 g_assert(lttv_process_traceset_seek_position(self
, pos
) == 0);
1406 LttvTracesetContextPosition
*pos
=
1407 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, i
);
1408 lttv_traceset_context_position_destroy(pos
);
1410 g_ptr_array_free(sd
.array
, TRUE
);
1412 lttv_hooks_destroy(hooks
);
1414 return sd
.events_found
;
1418 struct seek_forward_data
{
1419 guint event_count
; /* event counter */
1420 guint n
; /* requested number of events to jump over */
1423 static gint
seek_forward_event_hook(void *hook_data
, void* call_data
)
1425 struct seek_forward_data
*sd
= (struct seek_forward_data
*)hook_data
;
1429 if(sd
->event_count
>= sd
->n
)
1435 /* Seek back n events forward from the current position
1438 * @self the trace set context
1439 * @n number of events to jump over
1441 * returns : the number of events jumped over (may be less than requested if end
1442 * of traceset reached) */
1443 guint
lttv_process_traceset_seek_n_forward(LttvTracesetContext
*self
,
1446 struct seek_forward_data sd
;
1449 LttvHooks
*hooks
= lttv_hooks_new();
1451 lttv_hooks_add(hooks
, seek_forward_event_hook
, &sd
, LTTV_PRIO_DEFAULT
);
1453 lttv_process_traceset_begin(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1455 /* it will end on the end of traceset, or the fact that the
1456 * hook returns TRUE.
1458 lttv_process_traceset_middle(self
, ltt_time_infinite
,
1461 /* Here, our position is either the end of traceset, or the exact position
1462 * after n events : leave it like this. */
1464 lttv_process_traceset_end(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1466 lttv_hooks_destroy(hooks
);
1468 return sd
.event_count
;