tracer: drop the HookId hid from the invoke method
[platform/upstream/gstreamer.git] / plugins / tracers / gststats.c
1 /* GStreamer
2  * Copyright (C) 2013 Stefan Sauer <ensonic@users.sf.net>
3  *
4  * gststats.c: tracing module that logs events
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 /**
22  * SECTION:gststats
23  * @short_description: log event stats
24  *
25  * A tracing module that builds usage statistic for elements and pads. 
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #  include "config.h"
30 #endif
31
32 #include "gststats.h"
33
34 #include <stdio.h>
35
36 GST_DEBUG_CATEGORY_STATIC (gst_stats_debug);
37 #define GST_CAT_DEFAULT gst_stats_debug
38
39 static GQuark data_quark;
40 G_LOCK_DEFINE (_stats);
41
42 #define _do_init \
43     GST_DEBUG_CATEGORY_INIT (gst_stats_debug, "stats", 0, "stats tracer"); \
44     data_quark = g_quark_from_static_string ("gststats:data");
45 #define gst_stats_tracer_parent_class parent_class
46 G_DEFINE_TYPE_WITH_CODE (GstStatsTracer, gst_stats_tracer, GST_TYPE_TRACER,
47     _do_init);
48
49 typedef struct
50 {
51   /* we can't rely on the address to be unique over time */
52   guint index;
53   /* for pre + post */
54   GstClockTime last_ts;
55   /* hierarchy */
56   guint parent_ix;
57 } GstPadStats;
58
59 typedef struct
60 {
61   /* we can't rely on the address to be unique over time */
62   guint index;
63   /* for pre + post */
64   GstClockTime last_ts;
65   /* time spend in this element */
66   GstClockTime treal;
67   /* hierarchy */
68   guint parent_ix;
69 } GstElementStats;
70
71 /* data helper */
72
73 static GstElementStats no_elem_stats = { 0, };
74
75 static GstElementStats *
76 fill_element_stats (GstStatsTracer * self, GstElement * element)
77 {
78   GstElementStats *stats = g_slice_new0 (GstElementStats);
79
80   stats->index = self->num_elements++;
81   stats->parent_ix = G_MAXUINT;
82   return stats;
83 }
84
85 static void
86 log_new_element_stats (GstElementStats * stats, GstElement * element)
87 {
88   gst_tracer_log_trace (gst_structure_new ("new-element",
89           "ix", G_TYPE_UINT, stats->index,
90           "parent-ix", G_TYPE_UINT, stats->parent_ix,
91           "name", G_TYPE_STRING, GST_OBJECT_NAME (element),
92           "type", G_TYPE_STRING, G_OBJECT_TYPE_NAME (element),
93           "is-bin", G_TYPE_BOOLEAN, GST_IS_BIN (element), NULL));
94 }
95
96 static inline GstElementStats *
97 get_element_stats (GstStatsTracer * self, GstElement * element)
98 {
99   GstElementStats *stats;
100   gboolean is_new = FALSE;
101
102   if (!element) {
103     no_elem_stats.index = G_MAXUINT;
104     return &no_elem_stats;
105   }
106
107   G_LOCK (_stats);
108   if (!(stats = g_object_get_qdata ((GObject *) element, data_quark))) {
109     stats = fill_element_stats (self, element);
110     g_object_set_qdata ((GObject *) element, data_quark, stats);
111     if (self->elements->len <= stats->index)
112       g_ptr_array_set_size (self->elements, stats->index + 1);
113     g_ptr_array_index (self->elements, stats->index) = stats;
114     is_new = TRUE;
115   }
116   G_UNLOCK (_stats);
117   if (G_UNLIKELY (stats->parent_ix == G_MAXUINT)) {
118     GstElement *parent = GST_ELEMENT_PARENT (element);
119     if (parent) {
120       GstElementStats *parent_stats = get_element_stats (self, parent);
121       stats->parent_ix = parent_stats->index;
122     }
123   }
124   if (G_UNLIKELY (is_new)) {
125     log_new_element_stats (stats, element);
126   }
127   return stats;
128 }
129
130 static void
131 free_element_stats (gpointer data)
132 {
133   g_slice_free (GstElementStats, data);
134 }
135
136 /*
137  * Get the element/bin owning the pad. 
138  *
139  * in: a normal pad
140  * out: the element
141  *
142  * in: a proxy pad
143  * out: the element that contains the peer of the proxy
144  *
145  * in: a ghost pad
146  * out: the bin owning the ghostpad
147  */
148 /* TODO(ensonic): gst_pad_get_parent_element() would not work here, should we
149  * add this as new api, e.g. gst_pad_find_parent_element();
150  */
151 static GstElement *
152 get_real_pad_parent (GstPad * pad)
153 {
154   GstObject *parent;
155
156   if (!pad)
157     return NULL;
158
159   parent = GST_OBJECT_PARENT (pad);
160
161   /* if parent of pad is a ghost-pad, then pad is a proxy_pad */
162   if (parent && GST_IS_GHOST_PAD (parent)) {
163     pad = GST_PAD_CAST (parent);
164     parent = GST_OBJECT_PARENT (pad);
165   }
166   return GST_ELEMENT_CAST (parent);
167 }
168
169 static GstPadStats no_pad_stats = { 0, };
170
171 static GstPadStats *
172 fill_pad_stats (GstStatsTracer * self, GstPad * pad)
173 {
174   GstPadStats *stats = g_slice_new0 (GstPadStats);
175
176   stats->index = self->num_pads++;
177   stats->parent_ix = G_MAXUINT;
178
179   return stats;
180 }
181
182 static void
183 log_new_pad_stats (GstPadStats * stats, GstPad * pad)
184 {
185   gst_tracer_log_trace (gst_structure_new ("new-pad",
186           "ix", G_TYPE_UINT, stats->index,
187           "parent-ix", G_TYPE_UINT, stats->parent_ix,
188           "name", G_TYPE_STRING, GST_OBJECT_NAME (pad),
189           "type", G_TYPE_STRING, G_OBJECT_TYPE_NAME (pad),
190           "is-ghostpad", G_TYPE_BOOLEAN, GST_IS_GHOST_PAD (pad),
191           "pad-direction", GST_TYPE_PAD_DIRECTION, GST_PAD_DIRECTION (pad),
192           "thread-id", G_TYPE_UINT, GPOINTER_TO_UINT (g_thread_self ()), NULL));
193 }
194
195 static GstPadStats *
196 get_pad_stats (GstStatsTracer * self, GstPad * pad)
197 {
198   GstPadStats *stats;
199   gboolean is_new = FALSE;
200
201   if (!pad) {
202     no_pad_stats.index = G_MAXUINT;
203     return &no_pad_stats;
204   }
205
206   G_LOCK (_stats);
207   if (!(stats = g_object_get_qdata ((GObject *) pad, data_quark))) {
208     stats = fill_pad_stats (self, pad);
209     g_object_set_qdata ((GObject *) pad, data_quark, stats);
210     if (self->pads->len <= stats->index)
211       g_ptr_array_set_size (self->pads, stats->index + 1);
212     g_ptr_array_index (self->pads, stats->index) = stats;
213     is_new = TRUE;
214   }
215   G_UNLOCK (_stats);
216   if (G_UNLIKELY (stats->parent_ix == G_MAXUINT)) {
217     GstElement *elem = get_real_pad_parent (pad);
218     if (elem) {
219       GstElementStats *elem_stats = get_element_stats (self, elem);
220
221       stats->parent_ix = elem_stats->index;
222     }
223   }
224   if (G_UNLIKELY (is_new)) {
225     log_new_pad_stats (stats, pad);
226   }
227   return stats;
228 }
229
230 static void
231 free_pad_stats (gpointer data)
232 {
233   g_slice_free (GstPadStats, data);
234 }
235
236 static void
237 do_buffer_stats (GstStatsTracer * self, GstPad * this_pad,
238     GstPadStats * this_pad_stats, GstPad * that_pad,
239     GstPadStats * that_pad_stats, GstBuffer * buf, GstClockTime elapsed)
240 {
241   GstElement *this_elem = get_real_pad_parent (this_pad);
242   GstElementStats *this_elem_stats = get_element_stats (self, this_elem);
243   GstElement *that_elem = get_real_pad_parent (that_pad);
244   GstElementStats *that_elem_stats = get_element_stats (self, that_elem);
245
246   /* TODO(ensonic): need a quark-table (shared with the tracer-front-ends?) */
247   gst_tracer_log_trace (gst_structure_new ("buffer",
248           "ts", G_TYPE_UINT64, elapsed,
249           "pad-ix", G_TYPE_UINT, this_pad_stats->index,
250           "elem-ix", G_TYPE_UINT, this_elem_stats->index,
251           "peer-pad-ix", G_TYPE_UINT, that_pad_stats->index,
252           "peer-elem-ix", G_TYPE_UINT, that_elem_stats->index,
253           "buffer-size", G_TYPE_UINT, gst_buffer_get_size (buf),
254           /* TODO(ensonic): do PTS and DTS */
255           "buffer-ts", G_TYPE_UINT64, GST_BUFFER_TIMESTAMP (buf),
256           "buffer-duration", G_TYPE_UINT64, GST_BUFFER_DURATION (buf),
257           "buffer-flags", GST_TYPE_BUFFER_FLAGS, GST_BUFFER_FLAGS (buf),
258           /*
259              scheduling-jitter: for this we need the last_ts on the pad
260            */
261           NULL));
262 }
263
264 static void
265 do_element_stats (GstStatsTracer * self, GstPad * pad, GstClockTime elapsed1,
266     GstClockTime elapsed2)
267 {
268   GstClockTimeDiff elapsed = GST_CLOCK_DIFF (elapsed1, elapsed2);
269   GstObject *parent = GST_OBJECT_PARENT (pad);
270   GstElement *this =
271       GST_ELEMENT_CAST (GST_IS_PAD (parent) ? GST_OBJECT_PARENT (parent) :
272       parent);
273   GstElementStats *this_stats = get_element_stats (self, this);
274   GstPad *peer_pad = GST_PAD_PEER (pad);
275   GstElementStats *peer_stats;
276
277   if (!peer_pad)
278     return;
279
280   /* walk the ghost pad chain downstream to get the real pad */
281   /* if parent of peer_pad is a ghost-pad, then peer_pad is a proxy_pad */
282   parent = GST_OBJECT_PARENT (peer_pad);
283   if (parent && GST_IS_GHOST_PAD (parent)) {
284     peer_pad = GST_PAD_CAST (parent);
285     /* if this is now the ghost pad, get the peer of this */
286     get_pad_stats (self, peer_pad);
287     if ((parent = GST_OBJECT_PARENT (peer_pad))) {
288       get_element_stats (self, GST_ELEMENT_CAST (parent));
289     }
290     peer_pad = GST_PAD_PEER (GST_GHOST_PAD_CAST (peer_pad));
291     parent = peer_pad ? GST_OBJECT_PARENT (peer_pad) : NULL;
292   }
293   /* walk the ghost pad chain upstream to get the real pad */
294   /* if peer_pad is a ghost-pad, then parent is a bin and it is the parent of
295    * a proxy_pad */
296   while (peer_pad && GST_IS_GHOST_PAD (peer_pad)) {
297     get_pad_stats (self, peer_pad);
298     get_element_stats (self, GST_ELEMENT_CAST (parent));
299     peer_pad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (peer_pad));
300     parent = peer_pad ? GST_OBJECT_PARENT (peer_pad) : NULL;
301   }
302
303   if (!parent) {
304     printf ("%" GST_TIME_FORMAT
305         " transmission on unparented target pad %s_%s -> %s_%s\n",
306         GST_TIME_ARGS (elapsed), GST_DEBUG_PAD_NAME (pad),
307         GST_DEBUG_PAD_NAME (peer_pad));
308     return;
309   }
310   peer_stats = get_element_stats (self, GST_ELEMENT_CAST (parent));
311
312   /* we'd like to gather time spend in each element, but this does not make too
313    * much sense yet
314    * pure push/pull-based:
315    *   - the time spend in the push/pull_range is accounted for the peer and
316    *     removed from the current element
317    *   - this works for chains
318    *   - drawback is sink elements that block to sync have a high time usage
319    *     - we could rerun the ests with sync=false
320    * both:
321    *   - a.g. demuxers both push and pull. thus we subtract time for the pull
322    *     and the push operations, but never add anything.
323    *   - can we start a counter after push/pull in such elements and add then
324    *     time to the element upon next pad activity?
325    */
326 #if 1
327   /* this does not make sense for demuxers */
328   this_stats->treal -= elapsed;
329   peer_stats->treal += elapsed;
330 #else
331   /* this creates several >100% figures */
332   this_stats->treal += GST_CLOCK_DIFF (this_stats->last_ts, elapsed2) - elapsed;
333   peer_stats->treal += elapsed;
334   this_stats->last_ts = elapsed2;
335   peer_stats->last_ts = elapsed2;
336 #endif
337 }
338
339 /* tracer class */
340
341 static void gst_stats_tracer_finalize (GObject * obj);
342 static void gst_stats_tracer_invoke (GstTracer * obj, GstTracerMessageId mid,
343     va_list var_args);
344
345 static void
346 gst_stats_tracer_class_init (GstStatsTracerClass * klass)
347 {
348   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
349   GstTracerClass *gst_tracer_class = GST_TRACER_CLASS (klass);
350
351   gobject_class->finalize = gst_stats_tracer_finalize;
352
353   gst_tracer_class->invoke = gst_stats_tracer_invoke;
354 }
355
356 static void
357 gst_stats_tracer_init (GstStatsTracer * self)
358 {
359   g_object_set (self, "mask",
360       GST_TRACER_HOOK_BUFFERS | GST_TRACER_HOOK_EVENTS |
361       GST_TRACER_HOOK_MESSAGES | GST_TRACER_HOOK_QUERIES, NULL);
362   self->elements = g_ptr_array_new_with_free_func (free_element_stats);
363   self->pads = g_ptr_array_new_with_free_func (free_pad_stats);
364
365   /* announce trace formats */
366   /* *INDENT-OFF* */
367   gst_tracer_log_trace (gst_structure_new ("buffer.class",
368       "pad-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
369           "related-to", G_TYPE_STRING, "pad",  /* TODO: use genum */
370           NULL),
371       "element-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
372           "related-to", G_TYPE_STRING, "element",  /* TODO: use genum */
373           NULL),
374       "peer-pad-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
375           "related-to", G_TYPE_STRING, "pad",  /* TODO: use genum */
376           NULL),
377       "peer-element-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
378           "related-to", G_TYPE_STRING, "element",  /* TODO: use genum */
379           NULL),
380       "buffer-size", GST_TYPE_STRUCTURE, gst_structure_new ("value",
381           "type", G_TYPE_GTYPE, G_TYPE_UINT,
382           "description", G_TYPE_STRING, "size of buffer in bytes",
383           "flags", G_TYPE_STRING, "",  /* TODO: use gflags */ 
384           "min", G_TYPE_UINT, 0, 
385           "max", G_TYPE_UINT, G_MAXUINT,
386           NULL),
387       "buffer-ts", GST_TYPE_STRUCTURE, gst_structure_new ("value",
388           "type", G_TYPE_GTYPE, G_TYPE_UINT64,
389           "description", G_TYPE_STRING, "timestamp of the buffer in ns",
390           "flags", G_TYPE_STRING, "",  /* TODO: use gflags */ 
391           "min", G_TYPE_UINT64, G_GUINT64_CONSTANT (0),
392           "max", G_TYPE_UINT64, G_MAXUINT64,
393           NULL),
394       "buffer-duration", GST_TYPE_STRUCTURE, gst_structure_new ("value",
395           "type", G_TYPE_GTYPE, G_TYPE_UINT64,
396           "description", G_TYPE_STRING, "duration of the buffer in ns",
397           "flags", G_TYPE_STRING, "",  /* TODO: use gflags */ 
398           "min", G_TYPE_UINT64, G_GUINT64_CONSTANT (0),
399           "max", G_TYPE_UINT64, G_MAXUINT64,
400           NULL),
401       /* TODO(ensonic): "buffer-flags" */
402       NULL));
403   gst_tracer_log_trace (gst_structure_new ("event.class",
404       "pad-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
405           "related-to", G_TYPE_STRING, "pad",  /* TODO: use genum */
406           NULL),
407       "element-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
408           "related-to", G_TYPE_STRING, "element",  /* TODO: use genum */
409           NULL),
410       "name", GST_TYPE_STRUCTURE, gst_structure_new ("value",
411           "type", G_TYPE_GTYPE, G_TYPE_STRING,
412           "description", G_TYPE_STRING, "name of the event",
413           "flags", G_TYPE_STRING, "",  /* TODO: use gflags */ 
414           NULL),
415       NULL));
416   gst_tracer_log_trace (gst_structure_new ("message.class",
417       "element-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
418           "related-to", G_TYPE_STRING, "element",  /* TODO: use genum */
419           NULL),
420       "name", GST_TYPE_STRUCTURE, gst_structure_new ("value",
421           "type", G_TYPE_GTYPE, G_TYPE_STRING,
422           "description", G_TYPE_STRING, "name of the message",
423           "flags", G_TYPE_STRING, "",  /* TODO: use gflags */ 
424           NULL),
425       NULL));
426   gst_tracer_log_trace (gst_structure_new ("query.class",
427       "element-ix", GST_TYPE_STRUCTURE, gst_structure_new ("scope",
428           "related-to", G_TYPE_STRING, "element",  /* TODO: use genum */
429           NULL),
430       "name", GST_TYPE_STRUCTURE, gst_structure_new ("value",
431           "type", G_TYPE_GTYPE, G_TYPE_STRING,
432           "description", G_TYPE_STRING, "name of the query",
433           "flags", G_TYPE_STRING, "",  /* TODO: use gflags */ 
434           NULL),
435       NULL));
436   /* *INDENT-ON* */
437 }
438
439 /* hooks */
440
441 static void
442 do_push_buffer_pre (GstStatsTracer * self, va_list var_args)
443 {
444   guint64 ts = va_arg (var_args, guint64);
445   GstPad *this_pad = va_arg (var_args, GstPad *);
446   GstBuffer *buffer = va_arg (var_args, GstBuffer *);
447   GstPadStats *this_pad_stats = get_pad_stats (self, this_pad);
448   GstPad *that_pad = GST_PAD_PEER (this_pad);
449   GstPadStats *that_pad_stats = get_pad_stats (self, that_pad);
450
451   do_buffer_stats (self, this_pad, this_pad_stats, that_pad, that_pad_stats,
452       buffer, ts);
453 }
454
455 static void
456 do_push_buffer_post (GstStatsTracer * self, va_list var_args)
457 {
458   guint64 ts = va_arg (var_args, guint64);
459   GstPad *pad = va_arg (var_args, GstPad *);
460   GstPadStats *stats = get_pad_stats (self, pad);
461
462   do_element_stats (self, pad, stats->last_ts, ts);
463 }
464
465 typedef struct
466 {
467   GstStatsTracer *self;
468   GstPad *this_pad;
469   GstPadStats *this_pad_stats;
470   GstPad *that_pad;
471   GstPadStats *that_pad_stats;
472   guint64 ts;
473 } DoPushBufferListArgs;
474
475 static gboolean
476 do_push_buffer_list_item (GstBuffer ** buffer, guint idx, gpointer user_data)
477 {
478   DoPushBufferListArgs *args = (DoPushBufferListArgs *) user_data;
479
480   do_buffer_stats (args->self, args->this_pad, args->this_pad_stats,
481       args->that_pad, args->that_pad_stats, *buffer, args->ts);
482   return TRUE;
483 }
484
485 static void
486 do_push_buffer_list_pre (GstStatsTracer * self, va_list var_args)
487 {
488   guint64 ts = va_arg (var_args, guint64);
489   GstPad *this_pad = va_arg (var_args, GstPad *);
490   GstBufferList *list = va_arg (var_args, GstBufferList *);
491   GstPadStats *this_pad_stats = get_pad_stats (self, this_pad);
492   GstPad *that_pad = GST_PAD_PEER (this_pad);
493   GstPadStats *that_pad_stats = get_pad_stats (self, that_pad);
494   DoPushBufferListArgs args = { self, this_pad, this_pad_stats, that_pad,
495     that_pad_stats, ts
496   };
497
498   gst_buffer_list_foreach (list, do_push_buffer_list_item, &args);
499 }
500
501 static void
502 do_push_buffer_list_post (GstStatsTracer * self, va_list var_args)
503 {
504   guint64 ts = va_arg (var_args, guint64);
505   GstPad *pad = va_arg (var_args, GstPad *);
506   GstPadStats *stats = get_pad_stats (self, pad);
507
508   do_element_stats (self, pad, stats->last_ts, ts);
509 }
510
511 static void
512 do_pull_range_pre (GstStatsTracer * self, va_list var_args)
513 {
514   guint64 ts = va_arg (var_args, guint64);
515   GstPad *pad = va_arg (var_args, GstPad *);
516   GstPadStats *stats = get_pad_stats (self, pad);
517   stats->last_ts = ts;
518 }
519
520 static void
521 do_pull_range_post (GstStatsTracer * self, va_list var_args)
522 {
523   guint64 ts = va_arg (var_args, guint64);
524   GstPad *this_pad = va_arg (var_args, GstPad *);
525   GstBuffer *buffer = va_arg (var_args, GstBuffer *);
526   GstPadStats *this_pad_stats = get_pad_stats (self, this_pad);
527   guint64 last_ts = this_pad_stats->last_ts;
528   GstPad *that_pad = GST_PAD_PEER (this_pad);
529   GstPadStats *that_pad_stats = get_pad_stats (self, that_pad);
530
531   if (buffer != NULL) {
532     do_buffer_stats (self, this_pad, this_pad_stats, that_pad, that_pad_stats,
533         buffer, ts);
534   }
535   do_element_stats (self, this_pad, last_ts, ts);
536 }
537
538 static void
539 do_push_event_pre (GstStatsTracer * self, va_list var_args)
540 {
541   guint64 ts = va_arg (var_args, guint64);
542   GstPad *pad = va_arg (var_args, GstPad *);
543   GstEvent *ev = va_arg (var_args, GstEvent *);
544   GstElement *elem = get_real_pad_parent (pad);
545   GstElementStats *elem_stats = get_element_stats (self, elem);
546   GstPadStats *pad_stats = get_pad_stats (self, pad);
547
548   elem_stats->last_ts = ts;
549   gst_tracer_log_trace (gst_structure_new ("event",
550           "ts", G_TYPE_UINT64, ts,
551           "pad-ix", G_TYPE_UINT, pad_stats->index,
552           "elem-ix", G_TYPE_UINT, elem_stats->index,
553           "name", G_TYPE_STRING, GST_EVENT_TYPE_NAME (ev), NULL));
554 }
555
556 static void
557 do_push_event_post (GstStatsTracer * self, va_list var_args)
558 {
559 #if 0
560   guint64 ts = va_arg (var_args, guint64);
561   GstPad *pad = va_arg (var_args, GstPad *);
562 #endif
563 }
564
565 static void
566 do_post_message_pre (GstStatsTracer * self, va_list var_args)
567 {
568   guint64 ts = va_arg (var_args, guint64);
569   GstElement *elem = va_arg (var_args, GstElement *);
570   GstMessage *msg = va_arg (var_args, GstMessage *);
571   GstElementStats *stats = get_element_stats (self, elem);
572
573   stats->last_ts = ts;
574   gst_tracer_log_trace (gst_structure_new ("message",
575           "ts", G_TYPE_UINT64, ts,
576           "elem-ix", G_TYPE_UINT, stats->index,
577           "name", G_TYPE_STRING, GST_MESSAGE_TYPE_NAME (msg), NULL));
578 }
579
580 static void
581 do_post_message_post (GstStatsTracer * self, va_list var_args)
582 {
583 #if 0
584   guint64 ts = va_arg (var_args, guint64);
585   GstElement *elem = va_arg (var_args, GstElement *);
586 #endif
587 }
588
589 static void
590 do_query_pre (GstStatsTracer * self, va_list var_args)
591 {
592   guint64 ts = va_arg (var_args, guint64);
593   GstElement *elem = va_arg (var_args, GstElement *);
594   GstQuery *qry = va_arg (var_args, GstQuery *);
595   GstElementStats *stats = get_element_stats (self, elem);
596
597   stats->last_ts = ts;
598   gst_tracer_log_trace (gst_structure_new ("query",
599           "ts", G_TYPE_UINT64, ts,
600           "elem-ix", G_TYPE_UINT, stats->index,
601           "name", G_TYPE_STRING, GST_QUERY_TYPE_NAME (qry), NULL));
602 }
603
604 static void
605 do_query_post (GstStatsTracer * self, va_list var_args)
606 {
607 #if 0
608   guint64 ts = va_arg (var_args, guint64);
609   GstElement *elem = va_arg (var_args, GstElement *);
610 #endif
611 }
612
613 static void
614 gst_stats_tracer_invoke (GstTracer * obj, GstTracerMessageId mid,
615     va_list var_args)
616 {
617   GstStatsTracer *self = GST_STATS_TRACER_CAST (obj);
618
619   switch (mid) {
620     case GST_TRACER_MESSAGE_ID_PAD_PUSH_PRE:
621       do_push_buffer_pre (self, var_args);
622       break;
623     case GST_TRACER_MESSAGE_ID_PAD_PUSH_POST:
624       do_push_buffer_post (self, var_args);
625       break;
626     case GST_TRACER_MESSAGE_ID_PAD_PUSH_LIST_PRE:
627       do_push_buffer_list_pre (self, var_args);
628       break;
629     case GST_TRACER_MESSAGE_ID_PAD_PUSH_LIST_POST:
630       do_push_buffer_list_post (self, var_args);
631       break;
632     case GST_TRACER_MESSAGE_ID_PAD_PULL_RANGE_PRE:
633       do_pull_range_pre (self, var_args);
634       break;
635     case GST_TRACER_MESSAGE_ID_PAD_PULL_RANGE_POST:
636       do_pull_range_post (self, var_args);
637       break;
638     case GST_TRACER_MESSAGE_ID_PAD_PUSH_EVENT_PRE:
639       do_push_event_pre (self, var_args);
640       break;
641     case GST_TRACER_MESSAGE_ID_PAD_PUSH_EVENT_POST:
642       do_push_event_post (self, var_args);
643       break;
644     case GST_TRACER_MESSAGE_ID_ELEMENT_POST_MESSAGE_PRE:
645       do_post_message_pre (self, var_args);
646       break;
647     case GST_TRACER_MESSAGE_ID_ELEMENT_POST_MESSAGE_POST:
648       do_post_message_post (self, var_args);
649       break;
650     case GST_TRACER_MESSAGE_ID_ELEMENT_QUERY_PRE:
651       do_query_pre (self, var_args);
652       break;
653     case GST_TRACER_MESSAGE_ID_ELEMENT_QUERY_POST:
654       do_query_post (self, var_args);
655       break;
656     default:
657       break;
658   }
659 }
660
661 static void
662 gst_stats_tracer_finalize (GObject * obj)
663 {
664   G_OBJECT_CLASS (parent_class)->finalize (obj);
665 }