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>
29 #include <lttv/filter.h>
32 #define min(a,b) (((a)<(b))?(a):(b))
35 gint
compare_tracefile(gconstpointer a
, gconstpointer b
)
39 const LttvTracefileContext
*trace_a
= (const LttvTracefileContext
*)a
;
40 const LttvTracefileContext
*trace_b
= (const LttvTracefileContext
*)b
;
42 if(likely(trace_a
!= trace_b
)) {
43 comparison
= ltt_time_compare(trace_a
->timestamp
, trace_b
->timestamp
);
44 if(unlikely(comparison
== 0)) {
45 if(trace_a
->index
< trace_b
->index
) comparison
= -1;
46 else if(trace_a
->index
> trace_b
->index
) comparison
= 1;
47 else if(trace_a
->t_context
->index
< trace_b
->t_context
->index
)
49 else if(trace_a
->t_context
->index
> trace_b
->t_context
->index
)
56 struct _LttvTracesetContextPosition
{
57 GArray
*ep
; /* Array of LttEventPosition */
58 GArray
*tfc
; /* Array of corresponding
60 LttTime timestamp
; /* Current time at the saved position */
61 /* If ltt_time_infinite : no position is
62 * set, else, a position is set (may be end
63 * of trace, with ep->len == 0) */
66 void lttv_context_init(LttvTracesetContext
*self
, LttvTraceset
*ts
)
68 LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->init(self
, ts
);
72 void lttv_context_fini(LttvTracesetContext
*self
)
74 LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->fini(self
);
79 lttv_context_new_traceset_context(LttvTracesetContext
*self
)
81 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_traceset_context(self
);
88 lttv_context_new_trace_context(LttvTracesetContext
*self
)
90 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_trace_context(self
);
94 LttvTracefileContext
*
95 lttv_context_new_tracefile_context(LttvTracesetContext
*self
)
97 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_tracefile_context(self
);
100 /****************************************************************************
101 * lttv_traceset_context_compute_time_span
103 * Keep the time span is sync with on the fly addition and removal of traces
104 * in a trace set. It must be called each time a trace is added/removed from
105 * the traceset. It could be more efficient to call it only once a bunch
106 * of traces are loaded, but the calculation is not long, so it's not
109 * Author : Xang Xiu Yang
110 ***************************************************************************/
111 static void lttv_traceset_context_compute_time_span(
112 LttvTracesetContext
*self
,
113 TimeInterval
*time_span
)
115 LttvTraceset
* traceset
= self
->ts
;
116 int numTraces
= lttv_traceset_number(traceset
);
119 LttvTraceContext
*tc
;
122 time_span
->start_time
.tv_sec
= 0;
123 time_span
->start_time
.tv_nsec
= 0;
124 time_span
->end_time
.tv_sec
= 0;
125 time_span
->end_time
.tv_nsec
= 0;
127 for(i
=0; i
<numTraces
;i
++){
128 tc
= self
->traces
[i
];
131 ltt_trace_time_span_get(trace
, &s
, &e
);
134 time_span
->start_time
= s
;
135 time_span
->end_time
= e
;
137 if(s
.tv_sec
< time_span
->start_time
.tv_sec
138 || (s
.tv_sec
== time_span
->start_time
.tv_sec
139 && s
.tv_nsec
< time_span
->start_time
.tv_nsec
))
140 time_span
->start_time
= s
;
141 if(e
.tv_sec
> time_span
->end_time
.tv_sec
142 || (e
.tv_sec
== time_span
->end_time
.tv_sec
143 && e
.tv_nsec
> time_span
->end_time
.tv_nsec
))
144 time_span
->end_time
= e
;
149 static void init_tracefile_context(LttTracefile
*tracefile
,
150 LttvTraceContext
*tc
)
152 LttvTracefileContext
*tfc
;
153 LttvTracesetContext
*tsc
= tc
->ts_context
;
155 tfc
= LTTV_TRACESET_CONTEXT_GET_CLASS(tsc
)->new_tracefile_context(tsc
);
157 tfc
->index
= tc
->tracefiles
->len
;
158 tc
->tracefiles
= g_array_append_val(tc
->tracefiles
, tfc
);
163 tfc
->event
= lttv_hooks_new();
164 tfc
->event_by_id
= lttv_hooks_by_id_new();
165 tfc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
170 init(LttvTracesetContext
*self
, LttvTraceset
*ts
)
174 LttvTraceContext
*tc
;
176 GData
**tracefiles_groups
;
178 struct compute_tracefile_group_args args
;
180 nb_trace
= lttv_traceset_number(ts
);
182 self
->traces
= g_new(LttvTraceContext
*, nb_trace
);
183 self
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
184 self
->ts_a
= lttv_traceset_attribute(ts
);
185 self
->sync_position
= lttv_traceset_context_position_new();
186 for(i
= 0 ; i
< nb_trace
; i
++) {
187 tc
= LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_trace_context(self
);
188 self
->traces
[i
] = tc
;
190 tc
->ts_context
= self
;
192 tc
->vt
= lttv_traceset_get(ts
, i
);
193 tc
->t
= lttv_trace(tc
->vt
);
194 tc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
195 tc
->t_a
= lttv_trace_attribute(tc
->vt
);
196 tc
->tracefiles
= g_array_sized_new(FALSE
, TRUE
,
197 sizeof(LttvTracefileContext
*), 10);
199 tracefiles_groups
= ltt_trace_get_tracefiles_groups(tc
->t
);
200 if(tracefiles_groups
!= NULL
) {
201 args
.func
= (ForEachTraceFileFunc
)init_tracefile_context
;
204 g_datalist_foreach(tracefiles_groups
,
205 (GDataForeachFunc
)compute_tracefile_group
,
210 nb_control
= ltt_trace_control_tracefile_number(tc
->t
);
211 nb_per_cpu
= ltt_trace_per_cpu_tracefile_number(tc
->t
);
212 nb_tracefile
= nb_control
+ nb_per_cpu
;
213 tc
->tracefiles
= g_new(LttvTracefileContext
*, nb_tracefile
);
215 for(j
= 0 ; j
< nb_tracefile
; j
++) {
216 tfc
= LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_tracefile_context(self
);
217 tc
->tracefiles
[j
] = tfc
;
222 tfc
->tf
= ltt_trace_control_tracefile_get(tc
->t
, j
);
225 tfc
->control
= FALSE
;
226 tfc
->tf
= ltt_trace_per_cpu_tracefile_get(tc
->t
, j
- nb_control
);
230 tfc
->e
= ltt_event_new();
231 tfc
->event
= lttv_hooks_new();
232 tfc
->event_by_id
= lttv_hooks_by_id_new();
233 tfc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
238 self
->pqueue
= g_tree_new(compare_tracefile
);
239 lttv_process_traceset_seek_time(self
, ltt_time_zero
);
240 lttv_traceset_context_compute_time_span(self
, &self
->time_span
);
245 void fini(LttvTracesetContext
*self
)
247 guint i
, j
, nb_trace
, nb_tracefile
;
249 LttvTraceContext
*tc
;
251 LttvTracefileContext
**tfc
;
253 LttvTraceset
*ts
= self
->ts
;
255 g_tree_destroy(self
->pqueue
);
256 g_object_unref(self
->a
);
257 lttv_traceset_context_position_destroy(self
->sync_position
);
259 nb_trace
= lttv_traceset_number(ts
);
261 for(i
= 0 ; i
< nb_trace
; i
++) {
262 tc
= self
->traces
[i
];
264 g_object_unref(tc
->a
);
266 nb_tracefile
= tc
->tracefiles
->len
;
268 for(j
= 0 ; j
< nb_tracefile
; j
++) {
269 tfc
= &g_array_index(tc
->tracefiles
, LttvTracefileContext
*, j
);
270 lttv_hooks_destroy((*tfc
)->event
);
271 lttv_hooks_by_id_destroy((*tfc
)->event_by_id
);
272 g_object_unref((*tfc
)->a
);
273 g_object_unref(*tfc
);
275 g_array_free(tc
->tracefiles
, TRUE
);
278 g_free(self
->traces
);
282 void lttv_traceset_context_add_hooks(LttvTracesetContext
*self
,
283 LttvHooks
*before_traceset
,
284 LttvHooks
*before_trace
,
285 LttvHooks
*before_tracefile
,
287 LttvHooksById
*event_by_id
)
289 LttvTraceset
*ts
= self
->ts
;
293 LttvTraceContext
*tc
;
295 lttv_hooks_call(before_traceset
, self
);
297 nb_trace
= lttv_traceset_number(ts
);
299 for(i
= 0 ; i
< nb_trace
; i
++) {
300 tc
= self
->traces
[i
];
301 lttv_trace_context_add_hooks(tc
,
310 void lttv_traceset_context_remove_hooks(LttvTracesetContext
*self
,
311 LttvHooks
*after_traceset
,
312 LttvHooks
*after_trace
,
313 LttvHooks
*after_tracefile
,
315 LttvHooksById
*event_by_id
)
318 LttvTraceset
*ts
= self
->ts
;
322 LttvTraceContext
*tc
;
324 nb_trace
= lttv_traceset_number(ts
);
326 for(i
= 0 ; i
< nb_trace
; i
++) {
327 tc
= self
->traces
[i
];
328 lttv_trace_context_remove_hooks(tc
,
335 lttv_hooks_call(after_traceset
, self
);
340 void lttv_trace_context_add_hooks(LttvTraceContext
*self
,
341 LttvHooks
*before_trace
,
342 LttvHooks
*before_tracefile
,
344 LttvHooksById
*event_by_id
)
346 guint i
, nb_tracefile
;
348 LttvTracefileContext
**tfc
;
350 lttv_hooks_call(before_trace
, self
);
352 nb_tracefile
= self
->tracefiles
->len
;
354 for(i
= 0 ; i
< nb_tracefile
; i
++) {
355 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
356 lttv_tracefile_context_add_hooks(*tfc
,
365 void lttv_trace_context_remove_hooks(LttvTraceContext
*self
,
366 LttvHooks
*after_trace
,
367 LttvHooks
*after_tracefile
,
369 LttvHooksById
*event_by_id
)
371 guint i
, nb_tracefile
;
373 LttvTracefileContext
**tfc
;
375 nb_tracefile
= self
->tracefiles
->len
;
377 for(i
= 0 ; i
< nb_tracefile
; i
++) {
378 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
379 lttv_tracefile_context_remove_hooks(*tfc
,
385 lttv_hooks_call(after_trace
, self
);
388 void lttv_tracefile_context_add_hooks(LttvTracefileContext
*self
,
389 LttvHooks
*before_tracefile
,
391 LttvHooksById
*event_by_id
)
397 lttv_hooks_call(before_tracefile
, self
);
398 lttv_hooks_add_list(self
->event
, event
);
399 if(event_by_id
!= NULL
) {
400 for(i
= 0; i
< event_by_id
->array
->len
; i
++) {
401 index
= g_array_index(event_by_id
->array
, guint
, i
);
402 hook
= lttv_hooks_by_id_find(self
->event_by_id
, index
);
403 lttv_hooks_add_list(hook
, lttv_hooks_by_id_get(event_by_id
, index
));
408 void lttv_tracefile_context_remove_hooks(LttvTracefileContext
*self
,
409 LttvHooks
*after_tracefile
,
411 LttvHooksById
*event_by_id
)
417 lttv_hooks_remove_list(self
->event
, event
);
418 if(event_by_id
!= NULL
) {
419 for(i
= 0; i
< event_by_id
->array
->len
; i
++) {
420 index
= g_array_index(event_by_id
->array
, guint
, i
);
421 hook
= lttv_hooks_by_id_get(self
->event_by_id
, index
);
423 lttv_hooks_remove_list(hook
, lttv_hooks_by_id_get(event_by_id
, index
));
427 lttv_hooks_call(after_tracefile
, self
);
432 void lttv_tracefile_context_add_hooks_by_id(LttvTracefileContext
*tfc
,
434 LttvHooks
*event_by_id
)
437 h
= lttv_hooks_by_id_find(tfc
->event_by_id
, i
);
438 lttv_hooks_add_list(h
, event_by_id
);
441 void lttv_tracefile_context_remove_hooks_by_id(LttvTracefileContext
*tfc
,
444 lttv_hooks_by_id_remove(tfc
->event_by_id
, i
);
447 static LttvTracesetContext
*
448 new_traceset_context(LttvTracesetContext
*self
)
450 return g_object_new(LTTV_TRACESET_CONTEXT_TYPE
, NULL
);
454 static LttvTraceContext
*
455 new_trace_context(LttvTracesetContext
*self
)
457 return g_object_new(LTTV_TRACE_CONTEXT_TYPE
, NULL
);
461 static LttvTracefileContext
*
462 new_tracefile_context(LttvTracesetContext
*self
)
464 return g_object_new(LTTV_TRACEFILE_CONTEXT_TYPE
, NULL
);
469 traceset_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
471 /* Be careful of anything which would not work well with shallow copies */
476 traceset_context_finalize (LttvTracesetContext
*self
)
478 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACESET_CONTEXT_TYPE
)))
479 ->finalize(G_OBJECT(self
));
484 traceset_context_class_init (LttvTracesetContextClass
*klass
)
486 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
488 gobject_class
->finalize
= (void (*)(GObject
*self
))traceset_context_finalize
;
491 klass
->new_traceset_context
= new_traceset_context
;
492 klass
->new_trace_context
= new_trace_context
;
493 klass
->new_tracefile_context
= new_tracefile_context
;
498 lttv_traceset_context_get_type(void)
500 static GType type
= 0;
502 static const GTypeInfo info
= {
503 sizeof (LttvTracesetContextClass
),
504 NULL
, /* base_init */
505 NULL
, /* base_finalize */
506 (GClassInitFunc
) traceset_context_class_init
, /* class_init */
507 NULL
, /* class_finalize */
508 NULL
, /* class_data */
509 sizeof (LttvTracesetContext
),
511 (GInstanceInitFunc
) traceset_context_instance_init
, /* instance_init */
512 NULL
/* Value handling */
515 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTracesetContextType",
523 trace_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
525 /* Be careful of anything which would not work well with shallow copies */
530 trace_context_finalize (LttvTraceContext
*self
)
532 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACE_CONTEXT_TYPE
)))->
533 finalize(G_OBJECT(self
));
538 trace_context_class_init (LttvTraceContextClass
*klass
)
540 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
542 gobject_class
->finalize
= (void (*)(GObject
*self
)) trace_context_finalize
;
547 lttv_trace_context_get_type(void)
549 static GType type
= 0;
551 static const GTypeInfo info
= {
552 sizeof (LttvTraceContextClass
),
553 NULL
, /* base_init */
554 NULL
, /* base_finalize */
555 (GClassInitFunc
) trace_context_class_init
, /* class_init */
556 NULL
, /* class_finalize */
557 NULL
, /* class_data */
558 sizeof (LttvTraceContext
),
560 (GInstanceInitFunc
) trace_context_instance_init
, /* instance_init */
561 NULL
/* Value handling */
564 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTraceContextType",
572 tracefile_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
574 /* Be careful of anything which would not work well with shallow copies */
579 tracefile_context_finalize (LttvTracefileContext
*self
)
581 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACEFILE_CONTEXT_TYPE
)))
582 ->finalize(G_OBJECT(self
));
587 tracefile_context_class_init (LttvTracefileContextClass
*klass
)
589 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
591 gobject_class
->finalize
= (void (*)(GObject
*self
))tracefile_context_finalize
;
596 lttv_tracefile_context_get_type(void)
598 static GType type
= 0;
600 static const GTypeInfo info
= {
601 sizeof (LttvTracefileContextClass
),
602 NULL
, /* base_init */
603 NULL
, /* base_finalize */
604 (GClassInitFunc
) tracefile_context_class_init
, /* class_init */
605 NULL
, /* class_finalize */
606 NULL
, /* class_data */
607 sizeof (LttvTracefileContext
),
609 (GInstanceInitFunc
) tracefile_context_instance_init
, /* instance_init */
610 NULL
/* Value handling */
613 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTracefileContextType",
621 static gboolean
get_first(gpointer key
, gpointer value
, gpointer user_data
) {
622 g_assert(key
== value
);
623 *((LttvTracefileContext
**)user_data
) = (LttvTracefileContext
*)value
;
628 // Test to see if pqueue is traversed in the right order.
629 static LttTime test_time
;
631 static gboolean
test_tree(gpointer key
, gpointer value
, gpointer user_data
) {
633 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)key
;
635 g_debug("Tracefile name %s, time %lu.%lu, tfi %u, ti %u",
636 g_quark_to_string(ltt_tracefile_name(tfc
->tf
)),
637 tfc
->timestamp
.tv_sec
, tfc
->timestamp
.tv_nsec
,
638 tfc
->index
, tfc
->t_context
->index
);
640 if(user_data
!= NULL
) {
641 if(((LttvTracefileContext
*)user_data
) == (LttvTracefileContext
*)value
) {
642 g_assert(compare_tracefile(user_data
, value
) == 0);
644 g_assert(compare_tracefile(user_data
, value
) != 0);
646 g_assert(ltt_time_compare(test_time
, tfc
->timestamp
) <= 0);
647 test_time
.tv_sec
= tfc
->timestamp
.tv_sec
;
648 test_time
.tv_nsec
= tfc
->timestamp
.tv_nsec
;
651 //g_assert(((LttvTracefileContext *)user_data) != (LttvTracefileContext *)value);
658 void lttv_process_traceset_begin(LttvTracesetContext
*self
,
659 LttvHooks
*before_traceset
,
660 LttvHooks
*before_trace
,
661 LttvHooks
*before_tracefile
,
663 LttvHooksById
*event_by_id
)
666 /* simply add hooks in context. _before hooks are called by add_hooks. */
667 /* It calls all before_traceset, before_trace, and before_tracefile hooks. */
668 lttv_traceset_context_add_hooks(self
,
677 enum read_state
{ LAST_NONE
, LAST_OK
, LAST_EMPTY
};
679 /* Note : a _middle must be preceded from a _seek or another middle */
680 guint
lttv_process_traceset_middle(LttvTracesetContext
*self
,
683 const LttvTracesetContextPosition
*end_position
)
685 GTree
*pqueue
= self
->pqueue
;
687 guint fac_id
, ev_id
, id
;
689 LttvTracefileContext
*tfc
;
697 enum read_state last_read_state
= LAST_NONE
;
699 gboolean last_ret
= FALSE
; /* return value of the last hook list called */
701 /* Get the next event from the pqueue, call its hooks,
702 reinsert in the pqueue the following event from the same tracefile
703 unless the tracefile is finished or the event is later than the
708 g_tree_foreach(pqueue
, get_first
, &tfc
);
709 /* End of traceset : tfc is NULL */
710 if(unlikely(tfc
== NULL
))
716 * - the maximum number of events specified?
717 * - the end position ?
719 * then the read is finished. We leave the queue in the same state and
723 if(unlikely(last_ret
== TRUE
||
724 ((count
>= nb_events
) && (nb_events
!= G_MAXULONG
)) ||
725 (end_position
!=NULL
&<tv_traceset_context_ctx_pos_compare(self
,
726 end_position
) == 0)||
727 ltt_time_compare(end
, tfc
->timestamp
) <= 0))
732 /* Get the tracefile with an event for the smallest time found. If two
733 or more tracefiles have events for the same time, hope that lookup
734 and remove are consistent. */
737 test_time
.tv_sec
= 0;
738 test_time
.tv_nsec
= 0;
739 g_debug("test tree before remove");
740 g_tree_foreach(pqueue
, test_tree
, tfc
);
742 g_tree_remove(pqueue
, tfc
);
745 test_time
.tv_sec
= 0;
746 test_time
.tv_nsec
= 0;
747 g_debug("test tree after remove");
748 g_tree_foreach(pqueue
, test_tree
, tfc
);
751 e
= ltt_tracefile_get_event(tfc
->tf
);
753 if(last_read_state
!= LAST_EMPTY
) {
754 /* Only call hooks if the last read has given an event or if we are at the
755 * first pass (not if last read returned end of tracefile) */
758 fac_id
= ltt_event_facility_id(e
);
759 ev_id
= ltt_event_eventtype_id(e
);
760 id
= GET_HOOK_ID(fac_id
, ev_id
);
761 last_ret
= lttv_hooks_call_merge(tfc
->event
, tfc
,
762 lttv_hooks_by_id_get(tfc
->event_by_id
, id
), tfc
);
765 read_ret
= ltt_tracefile_read(tfc
->tf
);
767 if(likely(!read_ret
)) {
768 //g_debug("An event is ready");
769 tfc
->timestamp
= ltt_event_time(e
);
770 g_assert(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) != 0);
771 g_tree_insert(pqueue
, tfc
, tfc
);
773 test_time
.tv_sec
= 0;
774 test_time
.tv_nsec
= 0;
775 g_debug("test tree after event ready");
776 g_tree_foreach(pqueue
, test_tree
, NULL
);
779 last_read_state
= LAST_OK
;
781 tfc
->timestamp
= ltt_time_infinite
;
783 if(read_ret
== ERANGE
) {
784 last_read_state
= LAST_EMPTY
;
785 g_debug("End of trace");
787 g_error("Error happened in lttv_process_traceset_middle");
793 void lttv_process_traceset_end(LttvTracesetContext
*self
,
794 LttvHooks
*after_traceset
,
795 LttvHooks
*after_trace
,
796 LttvHooks
*after_tracefile
,
798 LttvHooksById
*event_by_id
)
800 /* Remove hooks from context. _after hooks are called by remove_hooks. */
801 /* It calls all after_traceset, after_trace, and after_tracefile hooks. */
802 lttv_traceset_context_remove_hooks(self
,
810 /* Subtile modification :
811 * if tracefile has no event at or after the time requested, it is not put in
812 * the queue, as the next read would fail.
814 * Don't forget to empty the traceset pqueue before calling this.
816 void lttv_process_trace_seek_time(LttvTraceContext
*self
, LttTime start
)
818 guint i
, nb_tracefile
;
822 LttvTracefileContext
**tfc
;
824 nb_tracefile
= self
->tracefiles
->len
;
826 GTree
*pqueue
= self
->ts_context
->pqueue
;
828 for(i
= 0 ; i
< nb_tracefile
; i
++) {
829 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
831 //g_tree_remove(pqueue, *tfc);
833 ret
= ltt_tracefile_seek_time((*tfc
)->tf
, start
);
834 if(ret
== EPERM
) g_error("error in lttv_process_trace_seek_time seek");
836 if(ret
== 0) { /* not ERANGE especially */
837 (*tfc
)->timestamp
= ltt_event_time(ltt_tracefile_get_event((*tfc
)->tf
));
838 g_assert(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0);
839 g_tree_insert(pqueue
, (*tfc
), (*tfc
));
841 (*tfc
)->timestamp
= ltt_time_infinite
;
845 test_time
.tv_sec
= 0;
846 test_time
.tv_nsec
= 0;
847 g_debug("test tree after seek_time");
848 g_tree_foreach(pqueue
, test_tree
, NULL
);
856 void lttv_process_traceset_seek_time(LttvTracesetContext
*self
, LttTime start
)
860 LttvTraceContext
*tc
;
862 g_tree_destroy(self
->pqueue
);
863 self
->pqueue
= g_tree_new(compare_tracefile
);
865 nb_trace
= lttv_traceset_number(self
->ts
);
866 for(i
= 0 ; i
< nb_trace
; i
++) {
867 tc
= self
->traces
[i
];
868 lttv_process_trace_seek_time(tc
, start
);
873 gboolean
lttv_process_traceset_seek_position(LttvTracesetContext
*self
,
874 const LttvTracesetContextPosition
*pos
)
878 /* If a position is set, seek the traceset to this position */
879 if(ltt_time_compare(pos
->timestamp
, ltt_time_infinite
) != 0) {
880 g_tree_destroy(self
->pqueue
);
881 self
->pqueue
= g_tree_new(compare_tracefile
);
883 for(i
=0;i
<pos
->ep
->len
; i
++) {
884 LttEventPosition
**ep
= &g_array_index(pos
->ep
, LttEventPosition
*, i
);
885 LttvTracefileContext
**tfc
=
886 &g_array_index(pos
->tfc
, LttvTracefileContext
*, i
);
888 if(ltt_tracefile_seek_position((*tfc
)->tf
, *ep
) != 0)
890 (*tfc
)->timestamp
= ltt_event_time(ltt_tracefile_get_event((*tfc
)->tf
));
891 g_assert(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0);
892 g_tree_insert(self
->pqueue
, (*tfc
), (*tfc
));
894 (*tfc
)->timestamp
= ltt_time_infinite
;
899 test_time
.tv_sec
= 0;
900 test_time
.tv_nsec
= 0;
901 g_debug("test tree after seek_position");
902 g_tree_foreach(self
->pqueue
, test_tree
, NULL
);
913 find_field(LttEventType
*et
, const GQuark field
)
924 if(field
== 0) return NULL
;
926 f
= ltt_eventtype_field(et
);
927 t
= ltt_eventtype_type(et
);
928 g_assert(ltt_type_class(t
) == LTT_STRUCT
);
929 nb
= ltt_type_member_number(t
);
930 for(i
= 0 ; i
< nb
; i
++) {
931 ltt_type_member_type(t
, i
, &name
);
932 if(name
== field
) break;
935 return ltt_field_member(f
, i
);
938 LttvTraceHookByFacility
*lttv_trace_hook_get_fac(LttvTraceHook
*th
,
941 return &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, facility_id
);
944 /* Get the first facility corresponding to the name. As the types must be
945 * compatible, it is relevant to use the field name and sizes of the first
946 * facility to create data structures and assume the data will be compatible
947 * thorough the trace */
948 LttvTraceHookByFacility
*lttv_trace_hook_get_first(LttvTraceHook
*th
)
950 g_assert(th
->fac_list
->len
> 0);
951 return g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, 0);
955 /* Returns 0 on success, -1 if fails. */
957 lttv_trace_find_hook(LttTrace
*t
, GQuark facility
, GQuark event
,
958 GQuark field1
, GQuark field2
, GQuark field3
, LttvHook h
, gpointer hook_data
,
963 LttEventType
*et
, *first_et
;
967 guint i
, fac_id
, ev_id
;
969 LttvTraceHookByFacility
*thf
, *first_thf
;
971 facilities
= ltt_trace_facility_get_by_name(t
, facility
);
973 if(unlikely(facilities
== NULL
)) goto facility_error
;
975 th
->fac_index
= g_array_sized_new(FALSE
, TRUE
,
976 sizeof(LttvTraceHookByFacility
),
978 th
->fac_index
= g_array_set_size(th
->fac_index
, NUM_FACILITIES
);
980 th
->fac_list
= g_array_sized_new(FALSE
, TRUE
,
981 sizeof(LttvTraceHookByFacility
*),
983 th
->fac_list
= g_array_set_size(th
->fac_list
, facilities
->len
);
985 fac_id
= g_array_index(facilities
, guint
, 0);
986 f
= ltt_trace_get_facility_by_num(t
, fac_id
);
988 et
= ltt_facility_eventtype_get_by_name(f
, event
);
989 if(unlikely(et
== NULL
)) goto event_error
;
991 thf
= &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, fac_id
);
992 g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, 0) = thf
;
994 ev_id
= ltt_eventtype_id(et
);
997 thf
->id
= GET_HOOK_ID(fac_id
, ev_id
);
998 thf
->f1
= find_field(et
, field1
);
999 thf
->f2
= find_field(et
, field2
);
1000 thf
->f3
= find_field(et
, field3
);
1001 thf
->hook_data
= hook_data
;
1006 /* Check for type compatibility too */
1007 for(i
=1;i
<facilities
->len
;i
++) {
1008 fac_id
= g_array_index(facilities
, guint
, i
);
1009 f
= ltt_trace_get_facility_by_num(t
, fac_id
);
1011 et
= ltt_facility_eventtype_get_by_name(f
, event
);
1012 if(unlikely(et
== NULL
)) goto event_error
;
1014 thf
= &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, fac_id
);
1015 g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, i
) = thf
;
1016 ev_id
= ltt_eventtype_id(et
);
1018 thf
->id
= GET_HOOK_ID(fac_id
, ev_id
);
1019 thf
->f1
= find_field(et
, field1
);
1020 if(check_fields_compatibility(first_et
, et
,
1021 first_thf
->f1
, thf
->f1
))
1024 thf
->f2
= find_field(et
, field2
);
1025 if(check_fields_compatibility(first_et
, et
,
1026 first_thf
->f2
, thf
->f2
))
1029 thf
->f3
= find_field(et
, field3
);
1030 if(check_fields_compatibility(first_et
, et
,
1031 first_thf
->f3
, thf
->f3
))
1033 thf
->hook_data
= hook_data
;
1041 g_error("Event type does not exist for event %s",
1042 g_quark_to_string(event
));
1045 g_error("No %s facility", g_quark_to_string(facility
));
1048 g_array_free(th
->fac_index
, TRUE
);
1049 g_array_free(th
->fac_list
, TRUE
);
1050 th
->fac_index
= NULL
;
1051 th
->fac_list
= NULL
;
1055 void lttv_trace_hook_destroy(LttvTraceHook
*th
)
1057 g_array_free(th
->fac_index
, TRUE
);
1058 g_array_free(th
->fac_list
, TRUE
);
1062 LttvTracesetContextPosition
*lttv_traceset_context_position_new()
1064 LttvTracesetContextPosition
*pos
= g_new(LttvTracesetContextPosition
,1);
1065 pos
->ep
= g_array_sized_new(FALSE
, TRUE
, sizeof(LttEventPosition
*),
1067 pos
->tfc
= g_array_sized_new(FALSE
, TRUE
, sizeof(LttvTracefileContext
*),
1069 pos
->timestamp
= ltt_time_infinite
;
1073 /* Save all positions, the ones with infinite time will have NULL
1075 void lttv_traceset_context_position_save(const LttvTracesetContext
*self
,
1076 LttvTracesetContextPosition
*pos
)
1079 guint num_traces
= lttv_traceset_number(self
->ts
);
1081 for(i
=0;i
<pos
->ep
->len
;i
++){
1082 LttEventPosition
*ep
= g_array_index(pos
->ep
, LttEventPosition
*, i
);
1083 if(ep
!= NULL
) g_free(ep
);
1086 pos
->tfc
= g_array_set_size(pos
->tfc
, 0);
1087 pos
->ep
= g_array_set_size(pos
->ep
, 0);
1089 pos
->timestamp
= ltt_time_infinite
;
1091 for(i
=0; i
<num_traces
;i
++) {
1092 GArray
* tracefiles
= self
->traces
[i
]->tracefiles
;
1094 guint num_tracefiles
= tracefiles
->len
;
1096 for(j
=0;j
<num_tracefiles
;j
++) {
1097 LttvTracefileContext
**tfc
= &g_array_index(tracefiles
,
1098 LttvTracefileContext
*, j
);
1100 LttEvent
*event
= ltt_tracefile_get_event((*tfc
)->tf
);
1101 LttEventPosition
*ep
;
1103 if(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0) {
1104 ep
= ltt_event_position_new();
1105 ltt_event_position(event
, ep
);
1106 if(ltt_time_compare((*tfc
)->timestamp
, pos
->timestamp
) < 0)
1107 pos
->timestamp
= (*tfc
)->timestamp
;
1111 g_array_append_val(pos
->tfc
, *tfc
);
1112 g_array_append_val(pos
->ep
, ep
);
1118 void lttv_traceset_context_position_destroy(LttvTracesetContextPosition
*pos
)
1121 LttEventPosition
**ep
;
1123 for(i
=0;i
<pos
->ep
->len
;i
++) {
1124 ep
= &g_array_index(pos
->ep
, LttEventPosition
*, i
);
1128 g_array_free(pos
->ep
, TRUE
);
1129 g_array_free(pos
->tfc
, TRUE
);
1133 void lttv_traceset_context_position_copy(LttvTracesetContextPosition
*dest
,
1134 const LttvTracesetContextPosition
*src
)
1137 LttEventPosition
**src_ep
, **dest_ep
;
1139 dest
->ep
= g_array_set_size(dest
->ep
, src
->ep
->len
);
1140 dest
->tfc
= g_array_set_size(dest
->tfc
, src
->tfc
->len
);
1142 for(i
=0;i
<src
->ep
->len
;i
++) {
1143 src_ep
= &g_array_index(src
->ep
, LttEventPosition
*, i
);
1144 dest_ep
= &g_array_index(dest
->ep
, LttEventPosition
*, i
);
1145 if(*src_ep
!= NULL
) {
1146 if(*dest_ep
== NULL
) *dest_ep
= ltt_event_position_new();
1147 ltt_event_position_copy(
1153 for(i
=0;i
<src
->tfc
->len
;i
++) {
1154 g_array_index(dest
->tfc
, LttvTracefileContext
*, i
) =
1155 g_array_index(src
->tfc
, LttvTracefileContext
*, i
);
1157 dest
->timestamp
= src
->timestamp
;
1160 gint
lttv_traceset_context_ctx_pos_compare(const LttvTracesetContext
*self
,
1161 const LttvTracesetContextPosition
*pos
)
1166 if(pos
->ep
->len
== 0) {
1167 if(lttv_traceset_number(self
->ts
) == 0) return 0;
1170 if(lttv_traceset_number(self
->ts
) == 0)
1173 for(i
=0;i
<pos
->ep
->len
;i
++) {
1174 LttEventPosition
*ep
= g_array_index(pos
->ep
, LttEventPosition
*, i
);
1175 LttvTracefileContext
*tfc
=
1176 g_array_index(pos
->tfc
, LttvTracefileContext
*, i
);
1179 if(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) != 0) {
1183 if(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) == 0) {
1186 LttEvent
*event
= ltt_tracefile_get_event(tfc
->tf
);
1188 ret
= ltt_event_position_compare((LttEventPosition
*)event
,
1192 if(ret
!= 0) return ret
;
1199 gint
lttv_traceset_context_pos_pos_compare(
1200 const LttvTracesetContextPosition
*pos1
,
1201 const LttvTracesetContextPosition
*pos2
)
1206 if(pos1
->ep
->len
== 0) {
1207 if(pos2
->ep
->len
== 0) return 0;
1210 if(pos2
->ep
->len
== 0)
1213 for(i
=0;i
<pos1
->ep
->len
;i
++) {
1214 LttEventPosition
*ep1
= g_array_index(pos1
->ep
, LttEventPosition
*, i
);
1215 LttvTracefileContext
*tfc1
= g_array_index(pos1
->tfc
,
1216 LttvTracefileContext
*, i
);
1219 for(j
=0;j
<pos2
->ep
->len
;j
++) {
1220 LttEventPosition
*ep2
= g_array_index(pos2
->ep
, LttEventPosition
*, j
);
1221 LttvTracefileContext
*tfc2
= g_array_index(pos2
->tfc
,
1222 LttvTracefileContext
*, j
);
1225 ret
= ltt_event_position_compare(ep1
, ep2
);
1229 if(ret
!= 0) return ret
;
1233 for(j
=0;j
<pos2
->ep
->len
;j
++) {
1234 LttEventPosition
*ep2
= g_array_index(pos2
->ep
, LttEventPosition
*, j
);
1235 LttvTracefileContext
*tfc2
= g_array_index(pos2
->tfc
,
1236 LttvTracefileContext
*, j
);
1238 if(ep2
!= NULL
) ret
= 1;
1247 LttTime
lttv_traceset_context_position_get_time(
1248 const LttvTracesetContextPosition
*pos
)
1250 return pos
->timestamp
;
1254 LttvTracefileContext
*lttv_traceset_context_get_current_tfc(LttvTracesetContext
*self
)
1256 GTree
*pqueue
= self
->pqueue
;
1257 LttvTracefileContext
*tfc
= NULL
;
1259 g_tree_foreach(pqueue
, get_first
, &tfc
);
1264 /* lttv_process_traceset_synchronize_tracefiles
1266 * Use the sync_position field of the trace set context to synchronize each
1267 * tracefile with the previously saved position.
1269 * If no previous position has been saved, it simply does nothing.
1271 void lttv_process_traceset_synchronize_tracefiles(LttvTracesetContext
*tsc
)
1273 g_assert(lttv_process_traceset_seek_position(tsc
, tsc
->sync_position
) == 0);
1279 void lttv_process_traceset_get_sync_data(LttvTracesetContext
*tsc
)
1281 lttv_traceset_context_position_save(tsc
, tsc
->sync_position
);
1284 struct seek_back_data
{
1285 guint first_event
; /* Index of the first event in the array : we will always
1286 overwrite at this position : this is a circular array.
1289 guint n
; /* number of events requested */
1290 GPtrArray
*array
; /* array of LttvTracesetContextPositions pointers */
1294 static gint
seek_back_event_hook(void *hook_data
, void* call_data
)
1296 struct seek_back_data
*sd
= (struct seek_back_data
*)hook_data
;
1297 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
1298 LttvTracesetContext
*tsc
= tfc
->t_context
->ts_context
;
1299 LttvTracesetContextPosition
*pos
;
1301 if(sd
->filter
!= NULL
) {
1302 if(!lttv_filter_tree_parse(sd
->filter
->head
,
1303 ltt_tracefile_get_event(tfc
->tf
),
1310 pos
= (LttvTracesetContextPosition
*)g_ptr_array_index (sd
->array
,
1313 lttv_traceset_context_position_save(tsc
, pos
);
1315 if(sd
->first_event
>= sd
->array
->len
- 1) sd
->first_event
= 0;
1316 else sd
->first_event
++;
1318 sd
->events_found
= min(sd
->n
, sd
->events_found
+ 1);
1323 /* Seek back n events back from the current position.
1326 * @self The trace set context
1327 * @n number of events to jump over
1328 * @first_offset The initial offset value used. Hint : try about 100000ns.
1329 * never put first_offset at ltt_time_zero.
1330 * @time_seeker Function pointer of the function to use to seek time :
1331 * either lttv_process_traceset_seek_time
1332 * or lttv_state_traceset_seek_time_closest
1333 * @filter The filter to call.
1335 * Return value : the number of events found (might be lower than the number
1336 * requested if beginning of traceset is reached).
1338 * The first search will go back first_offset and try to find the last n events
1339 * matching the filter. If there are not enough, it will try to go back from the
1340 * new trace point from first_offset*2, and so on, until beginning of trace or n
1343 * Note : this function does not take in account the LttvFilter : use the
1344 * similar function found in state.c instead.
1346 * Note2 : the caller must make sure that the LttvTracesetContext does not
1347 * contain any hook, as process_traceset_middle is used in this routine.
1349 guint
lttv_process_traceset_seek_n_backward(LttvTracesetContext
*self
,
1350 guint n
, LttTime first_offset
,
1351 seek_time_fct time_seeker
,
1354 if(lttv_traceset_number(self
->ts
) == 0) return 0;
1355 g_assert(ltt_time_compare(first_offset
, ltt_time_zero
) != 0);
1358 LttvTracesetContextPosition
*next_iter_end_pos
=
1359 lttv_traceset_context_position_new();
1360 LttvTracesetContextPosition
*end_pos
= lttv_traceset_context_position_new();
1361 LttvTracesetContextPosition
*saved_pos
= lttv_traceset_context_position_new();
1363 LttTime time_offset
;
1364 struct seek_back_data sd
;
1365 LttvHooks
*hooks
= lttv_hooks_new();
1368 sd
.events_found
= 0;
1369 sd
.array
= g_ptr_array_sized_new(n
);
1372 g_ptr_array_set_size(sd
.array
, n
);
1374 g_ptr_array_index (sd
.array
, i
) = lttv_traceset_context_position_new();
1377 lttv_traceset_context_position_save(self
, next_iter_end_pos
);
1378 lttv_traceset_context_position_save(self
, saved_pos
);
1379 /* Get the current time from which we will offset */
1380 time
= lttv_traceset_context_position_get_time(next_iter_end_pos
);
1381 /* the position saved might be end of traceset... */
1382 if(ltt_time_compare(time
, self
->time_span
.end_time
) > 0) {
1383 time
= self
->time_span
.end_time
;
1385 time_offset
= first_offset
;
1387 lttv_hooks_add(hooks
, seek_back_event_hook
, &sd
, LTTV_PRIO_DEFAULT
);
1389 lttv_process_traceset_begin(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1392 /* stop criteria : - n events found
1393 * - time < beginning of trace */
1394 if(ltt_time_compare(time
, self
->time_span
.start_time
) < 0) break;
1396 lttv_traceset_context_position_copy(end_pos
, next_iter_end_pos
);
1398 /* We must seek the traceset back to time - time_offset */
1399 /* this time becomes the new reference time */
1400 time
= ltt_time_sub(time
, time_offset
);
1402 time_seeker(self
, time
);
1403 lttv_traceset_context_position_save(self
, next_iter_end_pos
);
1404 /* Resync the time in case of a seek_closest */
1405 time
= lttv_traceset_context_position_get_time(next_iter_end_pos
);
1406 if(ltt_time_compare(time
, self
->time_span
.end_time
) > 0) {
1407 time
= self
->time_span
.end_time
;
1410 /* Process the traceset, calling a hook which adds events
1411 * to the array, overwriting the tail. It changes first_event and
1412 * events_found too. */
1413 /* We would like to have a clean context here : no other hook than our's */
1415 lttv_process_traceset_middle(self
, ltt_time_infinite
,
1416 G_MAXUINT
, end_pos
);
1418 if(sd
.events_found
< n
) {
1419 if(sd
.first_event
> 0) {
1420 /* Save the first position */
1421 LttvTracesetContextPosition
*pos
=
1422 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, 0);
1423 lttv_traceset_context_position_copy(saved_pos
, pos
);
1425 g_assert(n
-sd
.events_found
<= sd
.array
->len
);
1426 /* Change array size to n - events_found */
1427 for(i
=n
-sd
.events_found
;i
<sd
.array
->len
;i
++) {
1428 LttvTracesetContextPosition
*pos
=
1429 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, i
);
1430 lttv_traceset_context_position_destroy(pos
);
1432 g_ptr_array_set_size(sd
.array
, n
-sd
.events_found
);
1435 } else break; /* Second end criterion : n events found */
1437 time_offset
= ltt_time_mul(time_offset
, BACKWARD_SEEK_MUL
);
1440 lttv_traceset_context_position_destroy(end_pos
);
1441 lttv_traceset_context_position_destroy(next_iter_end_pos
);
1443 lttv_process_traceset_end(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1445 if(sd
.events_found
>= n
) {
1446 /* Seek the traceset to the first event in the circular array */
1447 LttvTracesetContextPosition
*pos
=
1448 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
,
1450 g_assert(lttv_process_traceset_seek_position(self
, pos
) == 0);
1452 /* Will seek to the last saved position : in the worst case, it will be the
1453 * original position (if events_found is 0) */
1454 g_assert(lttv_process_traceset_seek_position(self
, saved_pos
) == 0);
1457 for(i
=0;i
<sd
.array
->len
;i
++) {
1458 LttvTracesetContextPosition
*pos
=
1459 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, i
);
1460 lttv_traceset_context_position_destroy(pos
);
1462 g_ptr_array_free(sd
.array
, TRUE
);
1464 lttv_hooks_destroy(hooks
);
1466 lttv_traceset_context_position_destroy(saved_pos
);
1468 return sd
.events_found
;
1472 struct seek_forward_data
{
1473 guint event_count
; /* event counter */
1474 guint n
; /* requested number of events to jump over */
1478 static gint
seek_forward_event_hook(void *hook_data
, void* call_data
)
1480 struct seek_forward_data
*sd
= (struct seek_forward_data
*)hook_data
;
1481 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
1483 if(sd
->filter
!= NULL
) {
1484 if(!lttv_filter_tree_parse(sd
->filter
->head
,
1485 ltt_tracefile_get_event(tfc
->tf
),
1494 if(sd
->event_count
>= sd
->n
)
1500 /* Seek back n events forward from the current position
1503 * @self the trace set context
1504 * @n number of events to jump over
1505 * @filter filter to call.
1507 * returns : the number of events jumped over (may be less than requested if end
1508 * of traceset reached) */
1509 guint
lttv_process_traceset_seek_n_forward(LttvTracesetContext
*self
,
1510 guint n
, LttvFilter
*filter
)
1512 struct seek_forward_data sd
;
1516 LttvHooks
*hooks
= lttv_hooks_new();
1518 lttv_hooks_add(hooks
, seek_forward_event_hook
, &sd
, LTTV_PRIO_DEFAULT
);
1520 lttv_process_traceset_begin(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1522 /* it will end on the end of traceset, or the fact that the
1523 * hook returns TRUE.
1525 lttv_process_traceset_middle(self
, ltt_time_infinite
,
1528 /* Here, our position is either the end of traceset, or the exact position
1529 * after n events : leave it like this. */
1531 lttv_process_traceset_end(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1533 lttv_hooks_destroy(hooks
);
1535 return sd
.event_count
;