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 typedef struct _LttvTracefileContextPosition
{
57 LttEventPosition
*event
;
58 LttvTracefileContext
*tfc
;
59 gboolean used
; /* Tells if the tfc is at end of traceset position */
60 } LttvTracefileContextPosition
;
63 struct _LttvTracesetContextPosition
{
64 GArray
*tfcp
; /* Array of LttvTracefileContextPosition */
65 LttTime timestamp
; /* Current time at the saved position */
66 /* If ltt_time_infinite : no position is
67 * set, else, a position is set (may be end
68 * of trace, with ep->len == 0) */
71 void lttv_context_init(LttvTracesetContext
*self
, LttvTraceset
*ts
)
73 LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->init(self
, ts
);
77 void lttv_context_fini(LttvTracesetContext
*self
)
79 LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->fini(self
);
84 lttv_context_new_traceset_context(LttvTracesetContext
*self
)
86 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_traceset_context(self
);
93 lttv_context_new_trace_context(LttvTracesetContext
*self
)
95 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_trace_context(self
);
99 LttvTracefileContext
*
100 lttv_context_new_tracefile_context(LttvTracesetContext
*self
)
102 return LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_tracefile_context(self
);
105 /****************************************************************************
106 * lttv_traceset_context_compute_time_span
108 * Keep the time span is sync with on the fly addition and removal of traces
109 * in a trace set. It must be called each time a trace is added/removed from
110 * the traceset. It could be more efficient to call it only once a bunch
111 * of traces are loaded, but the calculation is not long, so it's not
114 * Author : Xang Xiu Yang
115 ***************************************************************************/
116 static void lttv_traceset_context_compute_time_span(
117 LttvTracesetContext
*self
,
118 TimeInterval
*time_span
)
120 LttvTraceset
* traceset
= self
->ts
;
121 int numTraces
= lttv_traceset_number(traceset
);
124 LttvTraceContext
*tc
;
127 time_span
->start_time
.tv_sec
= 0;
128 time_span
->start_time
.tv_nsec
= 0;
129 time_span
->end_time
.tv_sec
= 0;
130 time_span
->end_time
.tv_nsec
= 0;
132 for(i
=0; i
<numTraces
;i
++){
133 tc
= self
->traces
[i
];
136 ltt_trace_time_span_get(trace
, &s
, &e
);
137 tc
->time_span
.start_time
= s
;
138 tc
->time_span
.end_time
= e
;
141 time_span
->start_time
= s
;
142 time_span
->end_time
= e
;
144 if(s
.tv_sec
< time_span
->start_time
.tv_sec
145 || (s
.tv_sec
== time_span
->start_time
.tv_sec
146 && s
.tv_nsec
< time_span
->start_time
.tv_nsec
))
147 time_span
->start_time
= s
;
148 if(e
.tv_sec
> time_span
->end_time
.tv_sec
149 || (e
.tv_sec
== time_span
->end_time
.tv_sec
150 && e
.tv_nsec
> time_span
->end_time
.tv_nsec
))
151 time_span
->end_time
= e
;
156 static void init_tracefile_context(LttTracefile
*tracefile
,
157 LttvTraceContext
*tc
)
159 LttvTracefileContext
*tfc
;
160 LttvTracesetContext
*tsc
= tc
->ts_context
;
162 tfc
= LTTV_TRACESET_CONTEXT_GET_CLASS(tsc
)->new_tracefile_context(tsc
);
164 tfc
->index
= tc
->tracefiles
->len
;
165 tc
->tracefiles
= g_array_append_val(tc
->tracefiles
, tfc
);
170 tfc
->event
= lttv_hooks_new();
171 tfc
->event_by_id
= lttv_hooks_by_id_new();
172 tfc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
177 init(LttvTracesetContext
*self
, LttvTraceset
*ts
)
181 LttvTraceContext
*tc
;
183 GData
**tracefiles_groups
;
185 struct compute_tracefile_group_args args
;
187 nb_trace
= lttv_traceset_number(ts
);
189 self
->traces
= g_new(LttvTraceContext
*, nb_trace
);
190 self
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
191 self
->ts_a
= lttv_traceset_attribute(ts
);
192 for(i
= 0 ; i
< nb_trace
; i
++) {
193 tc
= LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_trace_context(self
);
194 self
->traces
[i
] = tc
;
196 tc
->ts_context
= self
;
198 tc
->vt
= lttv_traceset_get(ts
, i
);
199 tc
->t
= lttv_trace(tc
->vt
);
200 tc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
201 tc
->t_a
= lttv_trace_attribute(tc
->vt
);
202 tc
->tracefiles
= g_array_sized_new(FALSE
, TRUE
,
203 sizeof(LttvTracefileContext
*), 10);
205 tracefiles_groups
= ltt_trace_get_tracefiles_groups(tc
->t
);
206 if(tracefiles_groups
!= NULL
) {
207 args
.func
= (ForEachTraceFileFunc
)init_tracefile_context
;
210 g_datalist_foreach(tracefiles_groups
,
211 (GDataForeachFunc
)compute_tracefile_group
,
216 nb_control
= ltt_trace_control_tracefile_number(tc
->t
);
217 nb_per_cpu
= ltt_trace_per_cpu_tracefile_number(tc
->t
);
218 nb_tracefile
= nb_control
+ nb_per_cpu
;
219 tc
->tracefiles
= g_new(LttvTracefileContext
*, nb_tracefile
);
221 for(j
= 0 ; j
< nb_tracefile
; j
++) {
222 tfc
= LTTV_TRACESET_CONTEXT_GET_CLASS(self
)->new_tracefile_context(self
);
223 tc
->tracefiles
[j
] = tfc
;
228 tfc
->tf
= ltt_trace_control_tracefile_get(tc
->t
, j
);
231 tfc
->control
= FALSE
;
232 tfc
->tf
= ltt_trace_per_cpu_tracefile_get(tc
->t
, j
- nb_control
);
236 tfc
->e
= ltt_event_new();
237 tfc
->event
= lttv_hooks_new();
238 tfc
->event_by_id
= lttv_hooks_by_id_new();
239 tfc
->a
= g_object_new(LTTV_ATTRIBUTE_TYPE
, NULL
);
244 self
->sync_position
= lttv_traceset_context_position_new(self
);
245 self
->pqueue
= g_tree_new(compare_tracefile
);
246 lttv_process_traceset_seek_time(self
, ltt_time_zero
);
247 lttv_traceset_context_compute_time_span(self
, &self
->time_span
);
252 void fini(LttvTracesetContext
*self
)
254 guint i
, j
, nb_trace
, nb_tracefile
;
256 LttvTraceContext
*tc
;
258 LttvTracefileContext
**tfc
;
260 LttvTraceset
*ts
= self
->ts
;
262 g_tree_destroy(self
->pqueue
);
263 g_object_unref(self
->a
);
264 lttv_traceset_context_position_destroy(self
->sync_position
);
266 nb_trace
= lttv_traceset_number(ts
);
268 for(i
= 0 ; i
< nb_trace
; i
++) {
269 tc
= self
->traces
[i
];
271 g_object_unref(tc
->a
);
273 nb_tracefile
= tc
->tracefiles
->len
;
275 for(j
= 0 ; j
< nb_tracefile
; j
++) {
276 tfc
= &g_array_index(tc
->tracefiles
, LttvTracefileContext
*, j
);
277 lttv_hooks_destroy((*tfc
)->event
);
278 lttv_hooks_by_id_destroy((*tfc
)->event_by_id
);
279 g_object_unref((*tfc
)->a
);
280 g_object_unref(*tfc
);
282 g_array_free(tc
->tracefiles
, TRUE
);
285 g_free(self
->traces
);
289 void lttv_traceset_context_add_hooks(LttvTracesetContext
*self
,
290 LttvHooks
*before_traceset
,
291 LttvHooks
*before_trace
,
292 LttvHooks
*before_tracefile
,
294 LttvHooksById
*event_by_id
)
296 LttvTraceset
*ts
= self
->ts
;
300 LttvTraceContext
*tc
;
302 lttv_hooks_call(before_traceset
, self
);
304 nb_trace
= lttv_traceset_number(ts
);
306 for(i
= 0 ; i
< nb_trace
; i
++) {
307 tc
= self
->traces
[i
];
308 lttv_trace_context_add_hooks(tc
,
317 void lttv_traceset_context_remove_hooks(LttvTracesetContext
*self
,
318 LttvHooks
*after_traceset
,
319 LttvHooks
*after_trace
,
320 LttvHooks
*after_tracefile
,
322 LttvHooksById
*event_by_id
)
325 LttvTraceset
*ts
= self
->ts
;
329 LttvTraceContext
*tc
;
331 nb_trace
= lttv_traceset_number(ts
);
333 for(i
= 0 ; i
< nb_trace
; i
++) {
334 tc
= self
->traces
[i
];
335 lttv_trace_context_remove_hooks(tc
,
342 lttv_hooks_call(after_traceset
, self
);
347 void lttv_trace_context_add_hooks(LttvTraceContext
*self
,
348 LttvHooks
*before_trace
,
349 LttvHooks
*before_tracefile
,
351 LttvHooksById
*event_by_id
)
353 guint i
, nb_tracefile
;
355 LttvTracefileContext
**tfc
;
357 lttv_hooks_call(before_trace
, self
);
359 nb_tracefile
= self
->tracefiles
->len
;
361 for(i
= 0 ; i
< nb_tracefile
; i
++) {
362 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
363 lttv_tracefile_context_add_hooks(*tfc
,
372 void lttv_trace_context_remove_hooks(LttvTraceContext
*self
,
373 LttvHooks
*after_trace
,
374 LttvHooks
*after_tracefile
,
376 LttvHooksById
*event_by_id
)
378 guint i
, nb_tracefile
;
380 LttvTracefileContext
**tfc
;
382 nb_tracefile
= self
->tracefiles
->len
;
384 for(i
= 0 ; i
< nb_tracefile
; i
++) {
385 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
386 lttv_tracefile_context_remove_hooks(*tfc
,
392 lttv_hooks_call(after_trace
, self
);
395 void lttv_tracefile_context_add_hooks(LttvTracefileContext
*self
,
396 LttvHooks
*before_tracefile
,
398 LttvHooksById
*event_by_id
)
404 lttv_hooks_call(before_tracefile
, self
);
405 lttv_hooks_add_list(self
->event
, event
);
406 if(event_by_id
!= NULL
) {
407 for(i
= 0; i
< event_by_id
->array
->len
; i
++) {
408 index
= g_array_index(event_by_id
->array
, guint
, i
);
409 hook
= lttv_hooks_by_id_find(self
->event_by_id
, index
);
410 lttv_hooks_add_list(hook
, lttv_hooks_by_id_get(event_by_id
, index
));
415 void lttv_tracefile_context_remove_hooks(LttvTracefileContext
*self
,
416 LttvHooks
*after_tracefile
,
418 LttvHooksById
*event_by_id
)
424 lttv_hooks_remove_list(self
->event
, event
);
425 if(event_by_id
!= NULL
) {
426 for(i
= 0; i
< event_by_id
->array
->len
; i
++) {
427 index
= g_array_index(event_by_id
->array
, guint
, i
);
428 hook
= lttv_hooks_by_id_get(self
->event_by_id
, index
);
430 lttv_hooks_remove_list(hook
, lttv_hooks_by_id_get(event_by_id
, index
));
434 lttv_hooks_call(after_tracefile
, self
);
439 void lttv_tracefile_context_add_hooks_by_id(LttvTracefileContext
*tfc
,
441 LttvHooks
*event_by_id
)
444 h
= lttv_hooks_by_id_find(tfc
->event_by_id
, i
);
445 lttv_hooks_add_list(h
, event_by_id
);
448 void lttv_tracefile_context_remove_hooks_by_id(LttvTracefileContext
*tfc
,
451 lttv_hooks_by_id_remove(tfc
->event_by_id
, i
);
454 static LttvTracesetContext
*
455 new_traceset_context(LttvTracesetContext
*self
)
457 return g_object_new(LTTV_TRACESET_CONTEXT_TYPE
, NULL
);
461 static LttvTraceContext
*
462 new_trace_context(LttvTracesetContext
*self
)
464 return g_object_new(LTTV_TRACE_CONTEXT_TYPE
, NULL
);
468 static LttvTracefileContext
*
469 new_tracefile_context(LttvTracesetContext
*self
)
471 return g_object_new(LTTV_TRACEFILE_CONTEXT_TYPE
, NULL
);
476 traceset_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
478 /* Be careful of anything which would not work well with shallow copies */
483 traceset_context_finalize (LttvTracesetContext
*self
)
485 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACESET_CONTEXT_TYPE
)))
486 ->finalize(G_OBJECT(self
));
491 traceset_context_class_init (LttvTracesetContextClass
*klass
)
493 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
495 gobject_class
->finalize
= (void (*)(GObject
*self
))traceset_context_finalize
;
498 klass
->new_traceset_context
= new_traceset_context
;
499 klass
->new_trace_context
= new_trace_context
;
500 klass
->new_tracefile_context
= new_tracefile_context
;
505 lttv_traceset_context_get_type(void)
507 static GType type
= 0;
509 static const GTypeInfo info
= {
510 sizeof (LttvTracesetContextClass
),
511 NULL
, /* base_init */
512 NULL
, /* base_finalize */
513 (GClassInitFunc
) traceset_context_class_init
, /* class_init */
514 NULL
, /* class_finalize */
515 NULL
, /* class_data */
516 sizeof (LttvTracesetContext
),
518 (GInstanceInitFunc
) traceset_context_instance_init
, /* instance_init */
519 NULL
/* Value handling */
522 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTracesetContextType",
530 trace_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
532 /* Be careful of anything which would not work well with shallow copies */
537 trace_context_finalize (LttvTraceContext
*self
)
539 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACE_CONTEXT_TYPE
)))->
540 finalize(G_OBJECT(self
));
545 trace_context_class_init (LttvTraceContextClass
*klass
)
547 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
549 gobject_class
->finalize
= (void (*)(GObject
*self
)) trace_context_finalize
;
554 lttv_trace_context_get_type(void)
556 static GType type
= 0;
558 static const GTypeInfo info
= {
559 sizeof (LttvTraceContextClass
),
560 NULL
, /* base_init */
561 NULL
, /* base_finalize */
562 (GClassInitFunc
) trace_context_class_init
, /* class_init */
563 NULL
, /* class_finalize */
564 NULL
, /* class_data */
565 sizeof (LttvTraceContext
),
567 (GInstanceInitFunc
) trace_context_instance_init
, /* instance_init */
568 NULL
/* Value handling */
571 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTraceContextType",
579 tracefile_context_instance_init (GTypeInstance
*instance
, gpointer g_class
)
581 /* Be careful of anything which would not work well with shallow copies */
586 tracefile_context_finalize (LttvTracefileContext
*self
)
588 G_OBJECT_CLASS(g_type_class_peek(g_type_parent(LTTV_TRACEFILE_CONTEXT_TYPE
)))
589 ->finalize(G_OBJECT(self
));
594 tracefile_context_class_init (LttvTracefileContextClass
*klass
)
596 GObjectClass
*gobject_class
= G_OBJECT_CLASS(klass
);
598 gobject_class
->finalize
= (void (*)(GObject
*self
))tracefile_context_finalize
;
603 lttv_tracefile_context_get_type(void)
605 static GType type
= 0;
607 static const GTypeInfo info
= {
608 sizeof (LttvTracefileContextClass
),
609 NULL
, /* base_init */
610 NULL
, /* base_finalize */
611 (GClassInitFunc
) tracefile_context_class_init
, /* class_init */
612 NULL
, /* class_finalize */
613 NULL
, /* class_data */
614 sizeof (LttvTracefileContext
),
616 (GInstanceInitFunc
) tracefile_context_instance_init
, /* instance_init */
617 NULL
/* Value handling */
620 type
= g_type_register_static (G_TYPE_OBJECT
, "LttvTracefileContextType",
628 static gboolean
get_first(gpointer key
, gpointer value
, gpointer user_data
) {
629 g_assert(key
== value
);
630 *((LttvTracefileContext
**)user_data
) = (LttvTracefileContext
*)value
;
635 // Test to see if pqueue is traversed in the right order.
636 static LttTime test_time
;
638 static gboolean
test_tree(gpointer key
, gpointer value
, gpointer user_data
) {
640 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)key
;
642 g_debug("Tracefile name %s, time %lu.%lu, tfi %u, ti %u",
643 g_quark_to_string(ltt_tracefile_name(tfc
->tf
)),
644 tfc
->timestamp
.tv_sec
, tfc
->timestamp
.tv_nsec
,
645 tfc
->index
, tfc
->t_context
->index
);
647 if(user_data
!= NULL
) {
648 if(((LttvTracefileContext
*)user_data
) == (LttvTracefileContext
*)value
) {
649 g_assert(compare_tracefile(user_data
, value
) == 0);
651 g_assert(compare_tracefile(user_data
, value
) != 0);
653 g_assert(ltt_time_compare(test_time
, tfc
->timestamp
) <= 0);
654 test_time
.tv_sec
= tfc
->timestamp
.tv_sec
;
655 test_time
.tv_nsec
= tfc
->timestamp
.tv_nsec
;
658 //g_assert(((LttvTracefileContext *)user_data) != (LttvTracefileContext *)value);
665 void lttv_process_traceset_begin(LttvTracesetContext
*self
,
666 LttvHooks
*before_traceset
,
667 LttvHooks
*before_trace
,
668 LttvHooks
*before_tracefile
,
670 LttvHooksById
*event_by_id
)
673 /* simply add hooks in context. _before hooks are called by add_hooks. */
674 /* It calls all before_traceset, before_trace, and before_tracefile hooks. */
675 lttv_traceset_context_add_hooks(self
,
684 enum read_state
{ LAST_NONE
, LAST_OK
, LAST_EMPTY
};
686 /* Note : a _middle must be preceded from a _seek or another middle */
687 guint
lttv_process_traceset_middle(LttvTracesetContext
*self
,
690 const LttvTracesetContextPosition
*end_position
)
692 GTree
*pqueue
= self
->pqueue
;
694 guint fac_id
, ev_id
, id
;
696 LttvTracefileContext
*tfc
;
704 enum read_state last_read_state
= LAST_NONE
;
706 gboolean last_ret
= FALSE
; /* return value of the last hook list called */
708 /* Get the next event from the pqueue, call its hooks,
709 reinsert in the pqueue the following event from the same tracefile
710 unless the tracefile is finished or the event is later than the
715 g_tree_foreach(pqueue
, get_first
, &tfc
);
716 /* End of traceset : tfc is NULL */
717 if(unlikely(tfc
== NULL
))
723 * - the maximum number of events specified?
724 * - the end position ?
726 * then the read is finished. We leave the queue in the same state and
730 if(unlikely(last_ret
== TRUE
||
731 ((count
>= nb_events
) && (nb_events
!= G_MAXULONG
)) ||
732 (end_position
!=NULL
&<tv_traceset_context_ctx_pos_compare(self
,
733 end_position
) == 0)||
734 ltt_time_compare(end
, tfc
->timestamp
) <= 0))
739 /* Get the tracefile with an event for the smallest time found. If two
740 or more tracefiles have events for the same time, hope that lookup
741 and remove are consistent. */
744 test_time
.tv_sec
= 0;
745 test_time
.tv_nsec
= 0;
746 g_debug("test tree before remove");
747 g_tree_foreach(pqueue
, test_tree
, tfc
);
749 g_tree_remove(pqueue
, tfc
);
752 test_time
.tv_sec
= 0;
753 test_time
.tv_nsec
= 0;
754 g_debug("test tree after remove");
755 g_tree_foreach(pqueue
, test_tree
, tfc
);
758 e
= ltt_tracefile_get_event(tfc
->tf
);
760 if(last_read_state
!= LAST_EMPTY
) {
761 /* Only call hooks if the last read has given an event or if we are at the
762 * first pass (not if last read returned end of tracefile) */
765 fac_id
= ltt_event_facility_id(e
);
766 ev_id
= ltt_event_eventtype_id(e
);
767 id
= GET_HOOK_ID(fac_id
, ev_id
);
768 last_ret
= lttv_hooks_call_merge(tfc
->event
, tfc
,
769 lttv_hooks_by_id_get(tfc
->event_by_id
, id
), tfc
);
772 read_ret
= ltt_tracefile_read(tfc
->tf
);
774 if(likely(!read_ret
)) {
775 //g_debug("An event is ready");
776 tfc
->timestamp
= ltt_event_time(e
);
777 g_assert(ltt_time_compare(tfc
->timestamp
, ltt_time_infinite
) != 0);
778 g_tree_insert(pqueue
, tfc
, tfc
);
780 test_time
.tv_sec
= 0;
781 test_time
.tv_nsec
= 0;
782 g_debug("test tree after event ready");
783 g_tree_foreach(pqueue
, test_tree
, NULL
);
786 last_read_state
= LAST_OK
;
788 tfc
->timestamp
= ltt_time_infinite
;
790 if(read_ret
== ERANGE
) {
791 last_read_state
= LAST_EMPTY
;
792 g_debug("End of trace");
794 g_error("Error happened in lttv_process_traceset_middle");
800 void lttv_process_traceset_end(LttvTracesetContext
*self
,
801 LttvHooks
*after_traceset
,
802 LttvHooks
*after_trace
,
803 LttvHooks
*after_tracefile
,
805 LttvHooksById
*event_by_id
)
807 /* Remove hooks from context. _after hooks are called by remove_hooks. */
808 /* It calls all after_traceset, after_trace, and after_tracefile hooks. */
809 lttv_traceset_context_remove_hooks(self
,
817 /* Subtile modification :
818 * if tracefile has no event at or after the time requested, it is not put in
819 * the queue, as the next read would fail.
821 * Don't forget to empty the traceset pqueue before calling this.
823 void lttv_process_trace_seek_time(LttvTraceContext
*self
, LttTime start
)
825 guint i
, nb_tracefile
;
829 LttvTracefileContext
**tfc
;
831 nb_tracefile
= self
->tracefiles
->len
;
833 GTree
*pqueue
= self
->ts_context
->pqueue
;
835 for(i
= 0 ; i
< nb_tracefile
; i
++) {
836 tfc
= &g_array_index(self
->tracefiles
, LttvTracefileContext
*, i
);
838 //g_tree_remove(pqueue, *tfc);
840 ret
= ltt_tracefile_seek_time((*tfc
)->tf
, start
);
841 if(ret
== EPERM
) g_error("error in lttv_process_trace_seek_time seek");
843 if(ret
== 0) { /* not ERANGE especially */
844 (*tfc
)->timestamp
= ltt_event_time(ltt_tracefile_get_event((*tfc
)->tf
));
845 g_assert(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0);
846 g_tree_insert(pqueue
, (*tfc
), (*tfc
));
848 (*tfc
)->timestamp
= ltt_time_infinite
;
852 test_time
.tv_sec
= 0;
853 test_time
.tv_nsec
= 0;
854 g_debug("test tree after seek_time");
855 g_tree_foreach(pqueue
, test_tree
, NULL
);
863 void lttv_process_traceset_seek_time(LttvTracesetContext
*self
, LttTime start
)
867 LttvTraceContext
*tc
;
869 g_tree_destroy(self
->pqueue
);
870 self
->pqueue
= g_tree_new(compare_tracefile
);
872 nb_trace
= lttv_traceset_number(self
->ts
);
873 for(i
= 0 ; i
< nb_trace
; i
++) {
874 tc
= self
->traces
[i
];
875 lttv_process_trace_seek_time(tc
, start
);
880 gboolean
lttv_process_traceset_seek_position(LttvTracesetContext
*self
,
881 const LttvTracesetContextPosition
*pos
)
885 /* If a position is set, seek the traceset to this position */
886 if(ltt_time_compare(pos
->timestamp
, ltt_time_infinite
) != 0) {
887 g_tree_destroy(self
->pqueue
);
888 self
->pqueue
= g_tree_new(compare_tracefile
);
890 for(i
=0;i
<pos
->tfcp
->len
; i
++) {
891 LttvTracefileContextPosition
*tfcp
=
892 &g_array_index(pos
->tfcp
, LttvTracefileContextPosition
, i
);
895 if(tfcp
->used
== TRUE
) {
896 if(ltt_tracefile_seek_position(tfcp
->tfc
->tf
, tfcp
->event
) != 0)
898 tfcp
->tfc
->timestamp
=
899 ltt_event_time(ltt_tracefile_get_event(tfcp
->tfc
->tf
));
900 g_assert(ltt_time_compare(tfcp
->tfc
->timestamp
,
901 ltt_time_infinite
) != 0);
902 g_tree_insert(self
->pqueue
, tfcp
->tfc
, tfcp
->tfc
);
905 tfcp
->tfc
->timestamp
= ltt_time_infinite
;
910 test_time
.tv_sec
= 0;
911 test_time
.tv_nsec
= 0;
912 g_debug("test tree after seek_position");
913 g_tree_foreach(self
->pqueue
, test_tree
, NULL
);
924 find_field(LttEventType
*et
, const GQuark field
)
935 if(field
== 0) return NULL
;
937 f
= ltt_eventtype_field(et
);
938 t
= ltt_eventtype_type(et
);
939 g_assert(ltt_type_class(t
) == LTT_STRUCT
);
940 nb
= ltt_type_member_number(t
);
941 for(i
= 0 ; i
< nb
; i
++) {
942 ltt_type_member_type(t
, i
, &name
);
943 if(name
== field
) break;
946 return ltt_field_member(f
, i
);
949 LttvTraceHookByFacility
*lttv_trace_hook_get_fac(LttvTraceHook
*th
,
952 return &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, facility_id
);
955 /* Get the first facility corresponding to the name. As the types must be
956 * compatible, it is relevant to use the field name and sizes of the first
957 * facility to create data structures and assume the data will be compatible
958 * thorough the trace */
959 LttvTraceHookByFacility
*lttv_trace_hook_get_first(LttvTraceHook
*th
)
961 g_assert(th
->fac_list
->len
> 0);
962 return g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, 0);
966 /* Returns 0 on success, -1 if fails. */
968 lttv_trace_find_hook(LttTrace
*t
, GQuark facility
, GQuark event
,
969 GQuark field1
, GQuark field2
, GQuark field3
, LttvHook h
, gpointer hook_data
,
974 LttEventType
*et
, *first_et
;
978 guint i
, fac_id
, ev_id
;
980 LttvTraceHookByFacility
*thf
, *first_thf
;
982 facilities
= ltt_trace_facility_get_by_name(t
, facility
);
984 if(unlikely(facilities
== NULL
)) goto facility_error
;
986 th
->fac_index
= g_array_sized_new(FALSE
, TRUE
,
987 sizeof(LttvTraceHookByFacility
),
989 th
->fac_index
= g_array_set_size(th
->fac_index
, NUM_FACILITIES
);
991 th
->fac_list
= g_array_sized_new(FALSE
, TRUE
,
992 sizeof(LttvTraceHookByFacility
*),
994 th
->fac_list
= g_array_set_size(th
->fac_list
, facilities
->len
);
996 fac_id
= g_array_index(facilities
, guint
, 0);
997 f
= ltt_trace_get_facility_by_num(t
, fac_id
);
999 et
= ltt_facility_eventtype_get_by_name(f
, event
);
1000 if(unlikely(et
== NULL
)) goto event_error
;
1002 thf
= &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, fac_id
);
1003 g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, 0) = thf
;
1005 ev_id
= ltt_eventtype_id(et
);
1008 thf
->id
= GET_HOOK_ID(fac_id
, ev_id
);
1009 thf
->f1
= find_field(et
, field1
);
1010 thf
->f2
= find_field(et
, field2
);
1011 thf
->f3
= find_field(et
, field3
);
1012 thf
->hook_data
= hook_data
;
1017 /* Check for type compatibility too */
1018 for(i
=1;i
<facilities
->len
;i
++) {
1019 fac_id
= g_array_index(facilities
, guint
, i
);
1020 f
= ltt_trace_get_facility_by_num(t
, fac_id
);
1022 et
= ltt_facility_eventtype_get_by_name(f
, event
);
1023 if(unlikely(et
== NULL
)) goto event_error
;
1025 thf
= &g_array_index(th
->fac_index
, LttvTraceHookByFacility
, fac_id
);
1026 g_array_index(th
->fac_list
, LttvTraceHookByFacility
*, i
) = thf
;
1027 ev_id
= ltt_eventtype_id(et
);
1029 thf
->id
= GET_HOOK_ID(fac_id
, ev_id
);
1030 thf
->f1
= find_field(et
, field1
);
1031 if(check_fields_compatibility(first_et
, et
,
1032 first_thf
->f1
, thf
->f1
))
1035 thf
->f2
= find_field(et
, field2
);
1036 if(check_fields_compatibility(first_et
, et
,
1037 first_thf
->f2
, thf
->f2
))
1040 thf
->f3
= find_field(et
, field3
);
1041 if(check_fields_compatibility(first_et
, et
,
1042 first_thf
->f3
, thf
->f3
))
1044 thf
->hook_data
= hook_data
;
1052 g_error("Event type does not exist for event %s",
1053 g_quark_to_string(event
));
1056 g_error("No %s facility", g_quark_to_string(facility
));
1059 g_array_free(th
->fac_index
, TRUE
);
1060 g_array_free(th
->fac_list
, TRUE
);
1061 th
->fac_index
= NULL
;
1062 th
->fac_list
= NULL
;
1066 void lttv_trace_hook_destroy(LttvTraceHook
*th
)
1068 g_array_free(th
->fac_index
, TRUE
);
1069 g_array_free(th
->fac_list
, TRUE
);
1075 LttvTracesetContextPosition
*lttv_traceset_context_position_new(
1076 const LttvTracesetContext
*self
)
1078 guint num_traces
= lttv_traceset_number(self
->ts
);
1082 for(i
=0; i
<num_traces
;i
++) {
1083 GArray
* tracefiles
= self
->traces
[i
]->tracefiles
;
1085 guint num_tracefiles
= tracefiles
->len
;
1086 for(j
=0;j
<num_tracefiles
;j
++)
1089 LttvTracesetContextPosition
*pos
=
1090 g_new(LttvTracesetContextPosition
, 1);
1091 pos
->tfcp
= g_array_sized_new(FALSE
, TRUE
,
1092 sizeof(LttvTracefileContextPosition
),
1094 g_array_set_size(pos
->tfcp
, tf_count
);
1095 for(i
=0;i
<pos
->tfcp
->len
;i
++) {
1096 LttvTracefileContextPosition
*tfcp
=
1097 &g_array_index(pos
->tfcp
, LttvTracefileContextPosition
, i
);
1098 tfcp
->event
= ltt_event_position_new();
1101 pos
->timestamp
= ltt_time_infinite
;
1105 /* Save all positions, the ones with infinite time will have NULL
1107 /* note : a position must be destroyed when a trace is added/removed from a
1109 void lttv_traceset_context_position_save(const LttvTracesetContext
*self
,
1110 LttvTracesetContextPosition
*pos
)
1113 guint num_traces
= lttv_traceset_number(self
->ts
);
1116 pos
->timestamp
= ltt_time_infinite
;
1118 for(i
=0; i
<num_traces
;i
++) {
1119 GArray
* tracefiles
= self
->traces
[i
]->tracefiles
;
1121 guint num_tracefiles
= tracefiles
->len
;
1123 for(j
=0;j
<num_tracefiles
;j
++) {
1124 g_assert(tf_count
< pos
->tfcp
->len
);
1125 LttvTracefileContext
**tfc
= &g_array_index(tracefiles
,
1126 LttvTracefileContext
*, j
);
1127 LttvTracefileContextPosition
*tfcp
=
1128 &g_array_index(pos
->tfcp
, LttvTracefileContextPosition
, tf_count
);
1132 if(ltt_time_compare((*tfc
)->timestamp
, ltt_time_infinite
) != 0) {
1133 LttEvent
*event
= ltt_tracefile_get_event((*tfc
)->tf
);
1134 ltt_event_position(event
, tfcp
->event
);
1135 if(ltt_time_compare((*tfc
)->timestamp
, pos
->timestamp
) < 0)
1136 pos
->timestamp
= (*tfc
)->timestamp
;
1142 //g_array_append_val(pos->tfc, *tfc);
1143 //g_array_append_val(pos->ep, ep);
1150 void lttv_traceset_context_position_destroy(LttvTracesetContextPosition
*pos
)
1154 for(i
=0;i
<pos
->tfcp
->len
;i
++) {
1155 LttvTracefileContextPosition
*tfcp
=
1156 &g_array_index(pos
->tfcp
, LttvTracefileContextPosition
, i
);
1157 g_free(tfcp
->event
);
1161 g_array_free(pos
->tfcp
, TRUE
);
1165 void lttv_traceset_context_position_copy(LttvTracesetContextPosition
*dest
,
1166 const LttvTracesetContextPosition
*src
)
1169 LttvTracefileContextPosition
*src_tfcp
, *dest_tfcp
;
1171 g_assert(src
->tfcp
->len
== src
->tfcp
->len
);
1173 for(i
=0;i
<src
->tfcp
->len
;i
++) {
1175 &g_array_index(src
->tfcp
, LttvTracefileContextPosition
, i
);
1177 &g_array_index(dest
->tfcp
, LttvTracefileContextPosition
, i
);
1179 dest_tfcp
->used
= src_tfcp
->used
;
1180 dest_tfcp
->tfc
= src_tfcp
->tfc
;
1182 if(src_tfcp
->used
) {
1183 ltt_event_position_copy(
1188 dest
->timestamp
= src
->timestamp
;
1191 gint
lttv_traceset_context_ctx_pos_compare(const LttvTracesetContext
*self
,
1192 const LttvTracesetContextPosition
*pos
)
1197 if(pos
->tfcp
->len
== 0) {
1198 if(lttv_traceset_number(self
->ts
) == 0) return 0;
1201 if(lttv_traceset_number(self
->ts
) == 0)
1204 for(i
=0;i
<pos
->tfcp
->len
;i
++) {
1205 LttvTracefileContextPosition
*tfcp
=
1206 &g_array_index(pos
->tfcp
, LttvTracefileContextPosition
, i
);
1208 if(tfcp
->used
== FALSE
) {
1209 if(ltt_time_compare(tfcp
->tfc
->timestamp
, ltt_time_infinite
) < 0) {
1213 if(ltt_time_compare(tfcp
->tfc
->timestamp
, ltt_time_infinite
) == 0) {
1216 LttEvent
*event
= ltt_tracefile_get_event(tfcp
->tfc
->tf
);
1218 ret
= ltt_event_position_compare((LttEventPosition
*)event
,
1222 if(ret
!= 0) return ret
;
1229 gint
lttv_traceset_context_pos_pos_compare(
1230 const LttvTracesetContextPosition
*pos1
,
1231 const LttvTracesetContextPosition
*pos2
)
1236 if(pos1
->tfcp
->len
== 0) {
1237 if(pos2
->tfcp
->len
== 0) return 0;
1240 if(pos2
->tfcp
->len
== 0)
1243 for(i
=0;i
<pos1
->tfcp
->len
;i
++) {
1244 LttvTracefileContextPosition
*tfcp1
=
1245 &g_array_index(pos1
->tfcp
, LttvTracefileContextPosition
, i
);
1247 if(tfcp1
->used
== TRUE
) {
1248 for(j
=0;j
<pos2
->tfcp
->len
;j
++) {
1249 LttvTracefileContextPosition
*tfcp2
=
1250 &g_array_index(pos2
->tfcp
, LttvTracefileContextPosition
, j
);
1252 if(tfcp1
->tfc
== tfcp2
->tfc
) {
1253 if(tfcp2
->used
== TRUE
)
1254 ret
= ltt_event_position_compare(tfcp1
->event
, tfcp2
->event
);
1258 if(ret
!= 0) return ret
;
1263 for(j
=0;j
<pos2
->tfcp
->len
;j
++) {
1264 LttvTracefileContextPosition
*tfcp2
=
1265 &g_array_index(pos2
->tfcp
, LttvTracefileContextPosition
, j
);
1267 if(tfcp1
->tfc
== tfcp2
->tfc
)
1268 if(tfcp2
->used
== TRUE
) ret
= 1;
1269 if(ret
!= 0) return ret
;
1277 LttTime
lttv_traceset_context_position_get_time(
1278 const LttvTracesetContextPosition
*pos
)
1280 return pos
->timestamp
;
1284 LttvTracefileContext
*lttv_traceset_context_get_current_tfc(LttvTracesetContext
*self
)
1286 GTree
*pqueue
= self
->pqueue
;
1287 LttvTracefileContext
*tfc
= NULL
;
1289 g_tree_foreach(pqueue
, get_first
, &tfc
);
1294 /* lttv_process_traceset_synchronize_tracefiles
1296 * Use the sync_position field of the trace set context to synchronize each
1297 * tracefile with the previously saved position.
1299 * If no previous position has been saved, it simply does nothing.
1301 void lttv_process_traceset_synchronize_tracefiles(LttvTracesetContext
*tsc
)
1303 g_assert(lttv_process_traceset_seek_position(tsc
, tsc
->sync_position
) == 0);
1309 void lttv_process_traceset_get_sync_data(LttvTracesetContext
*tsc
)
1311 lttv_traceset_context_position_save(tsc
, tsc
->sync_position
);
1314 struct seek_back_data
{
1315 guint first_event
; /* Index of the first event in the array : we will always
1316 overwrite at this position : this is a circular array.
1319 guint n
; /* number of events requested */
1320 GPtrArray
*array
; /* array of LttvTracesetContextPositions pointers */
1324 static gint
seek_back_event_hook(void *hook_data
, void* call_data
)
1326 struct seek_back_data
*sd
= (struct seek_back_data
*)hook_data
;
1327 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
1328 LttvTracesetContext
*tsc
= tfc
->t_context
->ts_context
;
1329 LttvTracesetContextPosition
*pos
;
1331 if(sd
->filter
!= NULL
) {
1332 if(!lttv_filter_tree_parse(sd
->filter
->head
,
1333 ltt_tracefile_get_event(tfc
->tf
),
1340 pos
= (LttvTracesetContextPosition
*)g_ptr_array_index (sd
->array
,
1343 lttv_traceset_context_position_save(tsc
, pos
);
1345 if(sd
->first_event
>= sd
->array
->len
- 1) sd
->first_event
= 0;
1346 else sd
->first_event
++;
1348 sd
->events_found
= min(sd
->n
, sd
->events_found
+ 1);
1353 /* Seek back n events back from the current position.
1356 * @self The trace set context
1357 * @n number of events to jump over
1358 * @first_offset The initial offset value used.
1359 * never put first_offset at ltt_time_zero.
1360 * @time_seeker Function pointer of the function to use to seek time :
1361 * either lttv_process_traceset_seek_time
1362 * or lttv_state_traceset_seek_time_closest
1363 * @filter The filter to call.
1365 * Return value : the number of events found (might be lower than the number
1366 * requested if beginning of traceset is reached).
1368 * The first search will go back first_offset and try to find the last n events
1369 * matching the filter. If there are not enough, it will try to go back from the
1370 * new trace point from first_offset*2, and so on, until beginning of trace or n
1373 * Note : this function does not take in account the LttvFilter : use the
1374 * similar function found in state.c instead.
1376 * Note2 : the caller must make sure that the LttvTracesetContext does not
1377 * contain any hook, as process_traceset_middle is used in this routine.
1379 guint
lttv_process_traceset_seek_n_backward(LttvTracesetContext
*self
,
1380 guint n
, LttTime first_offset
,
1381 seek_time_fct time_seeker
,
1384 if(lttv_traceset_number(self
->ts
) == 0) return 0;
1385 g_assert(ltt_time_compare(first_offset
, ltt_time_zero
) != 0);
1388 LttvTracesetContextPosition
*next_iter_end_pos
=
1389 lttv_traceset_context_position_new(self
);
1390 LttvTracesetContextPosition
*end_pos
=
1391 lttv_traceset_context_position_new(self
);
1392 LttvTracesetContextPosition
*saved_pos
=
1393 lttv_traceset_context_position_new(self
);
1395 LttTime time_offset
;
1396 struct seek_back_data sd
;
1397 LttvHooks
*hooks
= lttv_hooks_new();
1400 sd
.events_found
= 0;
1401 sd
.array
= g_ptr_array_sized_new(n
);
1404 g_ptr_array_set_size(sd
.array
, n
);
1406 g_ptr_array_index (sd
.array
, i
) = lttv_traceset_context_position_new(self
);
1409 lttv_traceset_context_position_save(self
, next_iter_end_pos
);
1410 lttv_traceset_context_position_save(self
, saved_pos
);
1411 /* Get the current time from which we will offset */
1412 time
= lttv_traceset_context_position_get_time(next_iter_end_pos
);
1413 /* the position saved might be end of traceset... */
1414 if(ltt_time_compare(time
, self
->time_span
.end_time
) > 0) {
1415 time
= self
->time_span
.end_time
;
1417 time_offset
= first_offset
;
1419 lttv_hooks_add(hooks
, seek_back_event_hook
, &sd
, LTTV_PRIO_DEFAULT
);
1421 lttv_process_traceset_begin(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1424 /* stop criteria : - n events found
1425 * - time < beginning of trace */
1426 if(ltt_time_compare(time
, self
->time_span
.start_time
) < 0) break;
1428 lttv_traceset_context_position_copy(end_pos
, next_iter_end_pos
);
1430 /* We must seek the traceset back to time - time_offset */
1431 /* this time becomes the new reference time */
1432 time
= ltt_time_sub(time
, time_offset
);
1434 time_seeker(self
, time
);
1435 lttv_traceset_context_position_save(self
, next_iter_end_pos
);
1436 /* Resync the time in case of a seek_closest */
1437 time
= lttv_traceset_context_position_get_time(next_iter_end_pos
);
1438 if(ltt_time_compare(time
, self
->time_span
.end_time
) > 0) {
1439 time
= self
->time_span
.end_time
;
1442 /* Process the traceset, calling a hook which adds events
1443 * to the array, overwriting the tail. It changes first_event and
1444 * events_found too. */
1445 /* We would like to have a clean context here : no other hook than our's */
1447 lttv_process_traceset_middle(self
, ltt_time_infinite
,
1448 G_MAXUINT
, end_pos
);
1450 if(sd
.events_found
< n
) {
1451 if(sd
.first_event
> 0) {
1452 /* Save the first position */
1453 LttvTracesetContextPosition
*pos
=
1454 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, 0);
1455 lttv_traceset_context_position_copy(saved_pos
, pos
);
1457 g_assert(n
-sd
.events_found
<= sd
.array
->len
);
1458 /* Change array size to n - events_found */
1459 for(i
=n
-sd
.events_found
;i
<sd
.array
->len
;i
++) {
1460 LttvTracesetContextPosition
*pos
=
1461 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, i
);
1462 lttv_traceset_context_position_destroy(pos
);
1464 g_ptr_array_set_size(sd
.array
, n
-sd
.events_found
);
1467 } else break; /* Second end criterion : n events found */
1469 time_offset
= ltt_time_mul(time_offset
, BACKWARD_SEEK_MUL
);
1472 lttv_traceset_context_position_destroy(end_pos
);
1473 lttv_traceset_context_position_destroy(next_iter_end_pos
);
1475 lttv_process_traceset_end(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1477 if(sd
.events_found
>= n
) {
1478 /* Seek the traceset to the first event in the circular array */
1479 LttvTracesetContextPosition
*pos
=
1480 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
,
1482 g_assert(lttv_process_traceset_seek_position(self
, pos
) == 0);
1484 /* Will seek to the last saved position : in the worst case, it will be the
1485 * original position (if events_found is 0) */
1486 g_assert(lttv_process_traceset_seek_position(self
, saved_pos
) == 0);
1489 for(i
=0;i
<sd
.array
->len
;i
++) {
1490 LttvTracesetContextPosition
*pos
=
1491 (LttvTracesetContextPosition
*)g_ptr_array_index (sd
.array
, i
);
1492 lttv_traceset_context_position_destroy(pos
);
1494 g_ptr_array_free(sd
.array
, TRUE
);
1496 lttv_hooks_destroy(hooks
);
1498 lttv_traceset_context_position_destroy(saved_pos
);
1500 return sd
.events_found
;
1504 struct seek_forward_data
{
1505 guint event_count
; /* event counter */
1506 guint n
; /* requested number of events to jump over */
1510 static gint
seek_forward_event_hook(void *hook_data
, void* call_data
)
1512 struct seek_forward_data
*sd
= (struct seek_forward_data
*)hook_data
;
1513 LttvTracefileContext
*tfc
= (LttvTracefileContext
*)call_data
;
1515 if(sd
->filter
!= NULL
) {
1516 if(!lttv_filter_tree_parse(sd
->filter
->head
,
1517 ltt_tracefile_get_event(tfc
->tf
),
1526 if(sd
->event_count
>= sd
->n
)
1532 /* Seek back n events forward from the current position
1535 * @self the trace set context
1536 * @n number of events to jump over
1537 * @filter filter to call.
1539 * returns : the number of events jumped over (may be less than requested if end
1540 * of traceset reached) */
1541 guint
lttv_process_traceset_seek_n_forward(LttvTracesetContext
*self
,
1542 guint n
, LttvFilter
*filter
)
1544 struct seek_forward_data sd
;
1548 LttvHooks
*hooks
= lttv_hooks_new();
1550 lttv_hooks_add(hooks
, seek_forward_event_hook
, &sd
, LTTV_PRIO_DEFAULT
);
1552 lttv_process_traceset_begin(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1554 /* it will end on the end of traceset, or the fact that the
1555 * hook returns TRUE.
1557 lttv_process_traceset_middle(self
, ltt_time_infinite
,
1560 /* Here, our position is either the end of traceset, or the exact position
1561 * after n events : leave it like this. */
1563 lttv_process_traceset_end(self
, NULL
, NULL
, NULL
, hooks
, NULL
);
1565 lttv_hooks_destroy(hooks
);
1567 return sd
.event_count
;