tracers: leaks: fix potentially invalid memory access when trying to detect object...
[platform/upstream/gstreamer.git] / subprojects / gstreamer / plugins / tracers / gstleaks.c
1 /* GStreamer
2  * Copyright (C) 2016 Collabora Ltd. <guillaume.desmottes@collabora.co.uk>
3  * Copyright (C) 2019 Nirbheek Chauhan <nirbheek@centricular.com>
4  *
5  * gstleaks.c: tracing module detecting object leaks
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 /**
23  * SECTION:tracer-leaks
24  * @short_description: detect GstObject and GstMiniObject leaks
25  *
26  * This tracing module tracks the lifetimes of #GstObject and #GstMiniObject
27  * objects and prints a list of leaks to the debug log under `GST_TRACER:7` when
28  * gst_deinit() is called, and also prints a g_warning().
29  *
30  * Starting with GStreamer 1.18, you can also use action signals on the tracer
31  * object to fetch leak information. Use gst_tracing_get_active_tracers() to
32  * get a list of all active tracers and find the right one by name.
33  *
34  * You can activate this tracer in the usual way by adding the string 'leaks'
35  * to the environment variable `GST_TRACERS`. Such as: `GST_TRACERS=leaks`
36  *
37  * Note that the values are separated by semicolon (`;`), such as:
38  * `GST_TRACERS=leaks;latency`, and multiple instances of the same tracer can be
39  * active at the same time.
40  *
41  * Parameters can also be passed to each tracer. The leaks tracer currently
42  * accepts five params:
43  * 1. filters: (string) to filter which objects to record
44  * 2. check-refs: (boolean) whether to record every location where a leaked
45  *    object was reffed and unreffed
46  * 3. stack-traces-flags: (string) full or none; see: #GstStackTraceFlags
47  * 4. name: (string) set a name for the tracer object itself
48  * 5. log-leaks-on-deinit: (boolean) whether to report all leaks on
49  *    gst_deinit() by printing them in the debug log; "true" by default
50  *
51  * Examples:
52  * ```
53  * GST_TRACERS='leaks(filters="GstEvent,GstMessage",stack-traces-flags=none)'
54  * ```
55  * ```
56  * GST_TRACERS='leaks(filters="GstBuffer",stack-traces-flags=full,check-refs=true);leaks(name=all-leaks)'
57  * ```
58  */
59
60 #ifdef HAVE_CONFIG_H
61 #  include "config.h"
62 #endif
63
64 #include "gstleaks.h"
65
66 #ifdef G_OS_UNIX
67 #include <glib-unix.h>
68 #include <pthread.h>
69 #endif /* G_OS_UNIX */
70
71 GST_DEBUG_CATEGORY_STATIC (gst_leaks_debug);
72 #define GST_CAT_DEFAULT gst_leaks_debug
73
74 enum
75 {
76   /* actions */
77   SIGNAL_GET_LIVE_OBJECTS,
78   SIGNAL_LOG_LIVE_OBJECTS,
79   SIGNAL_ACTIVITY_START_TRACKING,
80   SIGNAL_ACTIVITY_GET_CHECKPOINT,
81   SIGNAL_ACTIVITY_LOG_CHECKPOINT,
82   SIGNAL_ACTIVITY_STOP_TRACKING,
83
84   LAST_SIGNAL
85 };
86
87 #define DEFAULT_LOG_LEAKS TRUE  /* for backwards-compat */
88
89 #define _do_init \
90     GST_DEBUG_CATEGORY_INIT (gst_leaks_debug, "leaks", 0, "leaks tracer");
91 #define gst_leaks_tracer_parent_class parent_class
92 G_DEFINE_TYPE_WITH_CODE (GstLeaksTracer, gst_leaks_tracer,
93     GST_TYPE_TRACER, _do_init);
94
95 static GstStructure *gst_leaks_tracer_get_live_objects (GstLeaksTracer * self);
96 static void gst_leaks_tracer_log_live_objects (GstLeaksTracer * self);
97 static void gst_leaks_tracer_activity_start_tracking (GstLeaksTracer * self);
98 static GstStructure *gst_leaks_tracer_activity_get_checkpoint (GstLeaksTracer *
99     self);
100 static void gst_leaks_tracer_activity_log_checkpoint (GstLeaksTracer * self);
101 static void gst_leaks_tracer_activity_stop_tracking (GstLeaksTracer * self);
102
103 #ifdef G_OS_UNIX
104 static void gst_leaks_tracer_setup_signals (GstLeaksTracer * leaks);
105 static void gst_leaks_tracer_cleanup_signals (GstLeaksTracer * leaks);
106 #endif
107
108 static GstTracerRecord *tr_alive;
109 static GstTracerRecord *tr_refings;
110 static GstTracerRecord *tr_added = NULL;
111 static GstTracerRecord *tr_removed = NULL;
112 static GQueue instances = G_QUEUE_INIT;
113 static guint gst_leaks_tracer_signals[LAST_SIGNAL] = { 0 };
114
115 G_LOCK_DEFINE_STATIC (instances);
116
117 typedef enum
118 {
119   GOBJECT,
120   MINI_OBJECT,
121 } ObjectKind;
122
123 typedef struct
124 {
125   gboolean reffed;
126   gchar *trace;
127   gint new_refcount;
128   GstClockTime ts;
129 } ObjectRefingInfo;
130
131 typedef struct
132 {
133   gchar *creation_trace;
134
135   GList *refing_infos;
136 } ObjectRefingInfos;
137
138 static void
139 object_refing_info_free (ObjectRefingInfo * refinfo)
140 {
141   g_free (refinfo->trace);
142   g_free (refinfo);
143 }
144
145 static void
146 object_refing_infos_free (ObjectRefingInfos * infos)
147 {
148   g_list_free_full (infos->refing_infos,
149       (GDestroyNotify) object_refing_info_free);
150
151   g_free (infos->creation_trace);
152   g_free (infos);
153 }
154
155 static void
156 set_print_stack_trace_from_string (GstLeaksTracer * self, const gchar * str)
157 {
158   gchar *trace;
159
160   /* Test if we can retrieve backtrace */
161   trace = gst_debug_get_stack_trace (FALSE);
162   if (!trace)
163     return;
164
165   g_free (trace);
166
167   if (g_strcmp0 (str, "full") == 0)
168     self->trace_flags = GST_STACK_TRACE_SHOW_FULL;
169   else
170     self->trace_flags = GST_STACK_TRACE_SHOW_NONE;
171 }
172
173 static void
174 set_print_stack_trace (GstLeaksTracer * self, GstStructure * params)
175 {
176   const gchar *trace_flags = g_getenv ("GST_LEAKS_TRACER_STACK_TRACE");
177
178   self->trace_flags = -1;
179   if (!trace_flags && params)
180     trace_flags = gst_structure_get_string (params, "stack-traces-flags");
181
182   if (!trace_flags)
183     return;
184
185   set_print_stack_trace_from_string (self, trace_flags);
186 }
187
188 static void
189 set_filters (GstLeaksTracer * self, const gchar * filters)
190 {
191   guint i;
192   GStrv tmp = g_strsplit (filters, ",", -1);
193
194   self->filter = g_array_sized_new (FALSE, FALSE, sizeof (GType),
195       g_strv_length (tmp));
196   for (i = 0; tmp[i]; i++) {
197     GType type;
198
199     type = g_type_from_name (tmp[i]);
200     if (type == 0) {
201       /* The type may not yet be known by the type system, typically because
202        * the plugin implementing it as not yet be loaded. Save it for now as
203        * it will have another chance to be added to the filter later in
204        * should_handle_object_type() when/if the object type is actually
205        * used. */
206       if (!self->unhandled_filter)
207         self->unhandled_filter = g_hash_table_new_full (g_str_hash, g_str_equal,
208             g_free, NULL);
209
210       g_hash_table_add (self->unhandled_filter, g_strdup (tmp[i]));
211       g_atomic_int_inc (&self->unhandled_filter_count);
212       continue;
213     }
214
215     GST_DEBUG_OBJECT (self, "add filter on %s", tmp[i]);
216
217     g_array_append_val (self->filter, type);
218   }
219
220   g_strfreev (tmp);
221 }
222
223 static void
224 set_params_from_structure (GstLeaksTracer * self, GstStructure * params)
225 {
226   const gchar *filters, *name;
227
228   filters = gst_structure_get_string (params, "filters");
229   if (filters)
230     set_filters (self, filters);
231
232   name = gst_structure_get_string (params, "name");
233   if (name)
234     gst_object_set_name (GST_OBJECT (self), name);
235
236   gst_structure_get_boolean (params, "check-refs", &self->check_refs);
237   gst_structure_get_boolean (params, "log-leaks-on-deinit", &self->log_leaks);
238 }
239
240 static void
241 set_params (GstLeaksTracer * self)
242 {
243   gchar *params, *tmp;
244   GstStructure *params_struct = NULL;
245
246   g_object_get (self, "params", &params, NULL);
247   if (!params)
248     goto set_stacktrace;
249
250   tmp = g_strdup_printf ("leaks,%s", params);
251   params_struct = gst_structure_from_string (tmp, NULL);
252   g_free (tmp);
253
254   if (params_struct)
255     set_params_from_structure (self, params_struct);
256   else
257     set_filters (self, params);
258
259   g_free (params);
260
261 set_stacktrace:
262   set_print_stack_trace (self, params_struct);
263
264   if (params_struct)
265     gst_structure_free (params_struct);
266 }
267
268 static gboolean
269 _expand_unhandled_filters (gchar * typename, gpointer unused_value,
270     GstLeaksTracer * self)
271 {
272   GType type;
273
274   type = g_type_from_name (typename);
275
276   if (type == 0)
277     return FALSE;
278
279   g_atomic_int_dec_and_test (&self->unhandled_filter_count);
280   g_array_append_val (self->filter, type);
281
282   return TRUE;
283 }
284
285 static gboolean
286 should_handle_object_type (GstLeaksTracer * self, GType object_type)
287 {
288   guint i, len;
289
290   if (!self->filter)
291     /* No filtering, handle all types */
292     return TRUE;
293
294   if (object_type == 0)
295     return FALSE;
296
297
298   if (g_atomic_int_get (&self->unhandled_filter_count)) {
299     GST_OBJECT_LOCK (self);
300     g_hash_table_foreach_remove (self->unhandled_filter,
301         (GHRFunc) _expand_unhandled_filters, self);
302     GST_OBJECT_UNLOCK (self);
303   }
304
305   len = self->filter->len;
306   for (i = 0; i < len; i++) {
307     GType type = g_array_index (self->filter, GType, i);
308
309     if (g_type_is_a (object_type, type))
310       return TRUE;
311   }
312
313   return FALSE;
314 }
315
316 /* The object may be destroyed when we log it using the checkpointing system so
317  * we have to save its type name */
318 typedef struct
319 {
320   gpointer object;
321   const gchar *type_name;
322 } ObjectLog;
323
324 static inline gboolean
325 object_is_gst_mini_object (gpointer obj)
326 {
327   return (G_TYPE_IS_DERIVED (GST_MINI_OBJECT_TYPE (obj)) &&
328       G_TYPE_FUNDAMENTAL (GST_MINI_OBJECT_TYPE (obj)) == G_TYPE_BOXED);
329 }
330
331 static ObjectLog *
332 object_log_new (gpointer obj, ObjectKind kind)
333 {
334   ObjectLog *o = g_new (ObjectLog, 1);
335
336   o->object = obj;
337
338   switch (kind) {
339     case GOBJECT:
340       o->type_name = G_OBJECT_TYPE_NAME (obj);
341       break;
342     case MINI_OBJECT:
343       o->type_name = g_type_name (GST_MINI_OBJECT_TYPE (obj));
344       break;
345     default:
346       g_assert_not_reached ();
347       break;
348   }
349
350   return o;
351 }
352
353 static void
354 object_log_free (ObjectLog * obj)
355 {
356   g_free (obj);
357 }
358
359 static void
360 handle_object_destroyed (GstLeaksTracer * self, gpointer object,
361     ObjectKind kind)
362 {
363   GST_OBJECT_LOCK (self);
364   if (self->done) {
365     g_warning
366         ("object %p destroyed while the leaks tracer was finalizing. Some threads are still running?",
367         object);
368     goto out;
369   }
370
371   g_hash_table_remove (self->objects, object);
372   if (self->removed)
373     g_hash_table_add (self->removed, object_log_new (object, kind));
374 out:
375   GST_OBJECT_UNLOCK (self);
376 }
377
378 static void
379 object_weak_cb (gpointer data, GObject * object)
380 {
381   GstLeaksTracer *self = data;
382
383   handle_object_destroyed (self, object, GOBJECT);
384 }
385
386 static void
387 mini_object_weak_cb (gpointer data, GstMiniObject * object)
388 {
389   GstLeaksTracer *self = data;
390
391   handle_object_destroyed (self, object, MINI_OBJECT);
392 }
393
394 static void
395 handle_object_created (GstLeaksTracer * self, gpointer object, GType type,
396     gboolean gobject)
397 {
398   ObjectKind kind = gobject ? GOBJECT : MINI_OBJECT;    // FIXME: change arg to pass directly
399   ObjectRefingInfos *infos;
400
401   if (!should_handle_object_type (self, type))
402     return;
403
404   infos = g_malloc0 (sizeof (ObjectRefingInfos));
405   if (gobject)
406     g_object_weak_ref ((GObject *) object, object_weak_cb, self);
407   else
408     gst_mini_object_weak_ref (GST_MINI_OBJECT_CAST (object),
409         mini_object_weak_cb, self);
410
411   GST_OBJECT_LOCK (self);
412   if ((gint) self->trace_flags != -1)
413     infos->creation_trace = gst_debug_get_stack_trace (self->trace_flags);
414
415   g_hash_table_insert (self->objects, object, infos);
416
417   if (self->added)
418     g_hash_table_add (self->added, object_log_new (object, kind));
419   GST_OBJECT_UNLOCK (self);
420 }
421
422 static void
423 mini_object_created_cb (GstTracer * tracer, GstClockTime ts,
424     GstMiniObject * object)
425 {
426   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
427
428   handle_object_created (self, object, GST_MINI_OBJECT_TYPE (object), FALSE);
429 }
430
431 static void
432 object_created_cb (GstTracer * tracer, GstClockTime ts, GstObject * object)
433 {
434   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
435   GType object_type = G_OBJECT_TYPE (object);
436
437   /* Can't track tracers as they may be disposed after the leak tracer itself */
438   if (g_type_is_a (object_type, GST_TYPE_TRACER))
439     return;
440
441   handle_object_created (self, object, object_type, TRUE);
442 }
443
444 static void
445 handle_object_reffed (GstLeaksTracer * self, gpointer object, GType type,
446     gint new_refcount, gboolean reffed, GstClockTime ts)
447 {
448   ObjectRefingInfos *infos;
449   ObjectRefingInfo *refinfo;
450
451   if (!self->check_refs)
452     return;
453
454   if (!should_handle_object_type (self, type))
455     return;
456
457   GST_OBJECT_LOCK (self);
458   infos = g_hash_table_lookup (self->objects, object);
459   if (!infos)
460     goto out;
461
462   refinfo = g_malloc0 (sizeof (ObjectRefingInfo));
463   refinfo->ts = ts;
464   refinfo->new_refcount = new_refcount;
465   refinfo->reffed = reffed;
466   if ((gint) self->trace_flags != -1)
467     refinfo->trace = gst_debug_get_stack_trace (self->trace_flags);
468
469   infos->refing_infos = g_list_prepend (infos->refing_infos, refinfo);
470
471 out:
472   GST_OBJECT_UNLOCK (self);
473 }
474
475 static void
476 object_reffed_cb (GstTracer * tracer, GstClockTime ts, GstObject * object,
477     gint new_refcount)
478 {
479   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
480
481   handle_object_reffed (self, object, G_OBJECT_TYPE (object), new_refcount,
482       TRUE, ts);
483 }
484
485 static void
486 object_unreffed_cb (GstTracer * tracer, GstClockTime ts, GstObject * object,
487     gint new_refcount)
488 {
489   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
490
491   handle_object_reffed (self, object, G_OBJECT_TYPE (object), new_refcount,
492       FALSE, ts);
493 }
494
495 static void
496 mini_object_reffed_cb (GstTracer * tracer, GstClockTime ts,
497     GstMiniObject * object, gint new_refcount)
498 {
499   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
500
501   handle_object_reffed (self, object, GST_MINI_OBJECT_TYPE (object),
502       new_refcount, TRUE, ts);
503 }
504
505 static void
506 mini_object_unreffed_cb (GstTracer * tracer, GstClockTime ts,
507     GstMiniObject * object, gint new_refcount)
508 {
509   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
510
511   handle_object_reffed (self, object, GST_MINI_OBJECT_TYPE (object),
512       new_refcount, FALSE, ts);
513 }
514
515 static void
516 gst_leaks_tracer_init (GstLeaksTracer * self)
517 {
518   self->log_leaks = DEFAULT_LOG_LEAKS;
519   self->objects = g_hash_table_new_full (NULL, NULL, NULL,
520       (GDestroyNotify) object_refing_infos_free);
521
522   if (g_getenv ("GST_LEAKS_TRACER_SIG")) {
523 #ifdef G_OS_UNIX
524     gst_leaks_tracer_setup_signals (self);
525 #else
526     g_warning ("System doesn't support POSIX signals");
527 #endif /* G_OS_UNIX */
528   }
529
530   G_LOCK (instances);
531   g_queue_push_tail (&instances, self);
532   G_UNLOCK (instances);
533 }
534
535 static void
536 gst_leaks_tracer_constructed (GObject * object)
537 {
538   GstLeaksTracer *self = GST_LEAKS_TRACER (object);
539   GstTracer *tracer = GST_TRACER (object);
540
541   set_params (self);
542
543   gst_tracing_register_hook (tracer, "mini-object-created",
544       G_CALLBACK (mini_object_created_cb));
545   gst_tracing_register_hook (tracer, "object-created",
546       G_CALLBACK (object_created_cb));
547
548   if (self->check_refs) {
549     gst_tracing_register_hook (tracer, "object-reffed",
550         G_CALLBACK (object_reffed_cb));
551     gst_tracing_register_hook (tracer, "mini-object-reffed",
552         G_CALLBACK (mini_object_reffed_cb));
553     gst_tracing_register_hook (tracer, "mini-object-unreffed",
554         G_CALLBACK (mini_object_unreffed_cb));
555     gst_tracing_register_hook (tracer, "object-unreffed",
556         G_CALLBACK (object_unreffed_cb));
557   }
558
559   /* We rely on weak pointers rather than (mini-)object-destroyed hooks so we
560    * are notified of objects being destroyed even during the shuting down of
561    * the tracing system. */
562
563   ((GObjectClass *) gst_leaks_tracer_parent_class)->constructed (object);
564 }
565
566 typedef struct
567 {
568   gpointer obj;
569   GType type;
570   guint ref_count;
571   gchar *desc;
572   ObjectRefingInfos *infos;
573 } Leak;
574
575 /* The content of the returned Leak struct is valid until the self->objects
576  * hash table has been modified. */
577 static Leak *
578 leak_new (gpointer obj, GType type, guint ref_count, ObjectRefingInfos * infos)
579 {
580   Leak *leak = g_new (Leak, 1);
581
582   leak->obj = obj;
583   leak->type = type;
584   leak->ref_count = ref_count;
585   leak->desc = gst_info_strdup_printf ("%" GST_PTR_FORMAT, obj);
586   leak->infos = infos;
587
588   return leak;
589 }
590
591 static void
592 leak_free (Leak * leak)
593 {
594   g_free (leak->desc);
595   g_free (leak);
596 }
597
598 static gint
599 sort_leaks (gconstpointer _a, gconstpointer _b)
600 {
601   const Leak *a = _a, *b = _b;
602
603   return g_strcmp0 (g_type_name (a->type), g_type_name (b->type));
604 }
605
606 static GList *
607 create_leaks_list (GstLeaksTracer * self)
608 {
609   GList *l = NULL;
610   GHashTableIter iter;
611   gpointer obj, infos;
612
613   g_hash_table_iter_init (&iter, self->objects);
614   while (g_hash_table_iter_next (&iter, &obj, &infos)) {
615     GType type;
616     guint ref_count;
617
618     if (object_is_gst_mini_object (obj)) {
619       if (GST_MINI_OBJECT_FLAG_IS_SET (obj, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED))
620         continue;
621
622       type = GST_MINI_OBJECT_TYPE (obj);
623       ref_count = ((GstMiniObject *) obj)->refcount;
624     } else {
625       if (GST_OBJECT_FLAG_IS_SET (obj, GST_OBJECT_FLAG_MAY_BE_LEAKED))
626         continue;
627
628       type = G_OBJECT_TYPE (obj);
629       ref_count = ((GObject *) obj)->ref_count;
630     }
631
632     l = g_list_prepend (l, leak_new (obj, type, ref_count, infos));
633   }
634
635   /* Sort leaks by type name so they are grouped together making the output
636    * easier to read */
637   l = g_list_sort (l, sort_leaks);
638
639   /* Reverse list to sort objects by creation time; this is needed because we
640    * prepended objects into this list earlier, and because g_list_sort() above
641    * is stable so the creation order is preserved when sorting by type name. */
642   return g_list_reverse (l);
643 }
644
645 static void
646 process_leak (Leak * leak, GValue * ret_leaks)
647 {
648   GstStructure *r, *s = NULL;
649   GList *ref;
650   GValue refings = G_VALUE_INIT;
651
652   if (!ret_leaks) {
653     /* log to the debug log */
654     gst_tracer_record_log (tr_alive, g_type_name (leak->type), leak->obj,
655         leak->desc, leak->ref_count,
656         leak->infos->creation_trace ? leak->infos->creation_trace : "");
657   } else {
658     GValue s_value = G_VALUE_INIT;
659     GValue obj_value = G_VALUE_INIT;
660     /* for leaked objects, we take ownership of the object instead of
661      * reffing ("collecting") it to avoid deadlocks */
662     g_value_init (&obj_value, leak->type);
663     if (object_is_gst_mini_object (leak->obj))
664       g_value_take_boxed (&obj_value, leak->obj);
665     else
666       g_value_take_object (&obj_value, leak->obj);
667     s = gst_structure_new_empty ("object-alive");
668     gst_structure_take_value (s, "object", &obj_value);
669     gst_structure_set (s, "ref-count", G_TYPE_UINT, leak->ref_count,
670         "trace", G_TYPE_STRING, leak->infos->creation_trace, NULL);
671     /* avoid copy of structure */
672     g_value_init (&s_value, GST_TYPE_STRUCTURE);
673     g_value_take_boxed (&s_value, s);
674     gst_value_list_append_and_take_value (ret_leaks, &s_value);
675   }
676
677   /* store refinfo if available */
678   if (leak->infos->refing_infos)
679     g_value_init (&refings, GST_TYPE_LIST);
680
681   /* iterate the list from last to first to correct the order */
682   for (ref = g_list_last (leak->infos->refing_infos); ref; ref = ref->prev) {
683     ObjectRefingInfo *refinfo = (ObjectRefingInfo *) ref->data;
684
685     if (!ret_leaks) {
686       /* log to the debug log */
687       gst_tracer_record_log (tr_refings, refinfo->ts, g_type_name (leak->type),
688           leak->obj, refinfo->reffed ? "reffed" : "unreffed",
689           refinfo->new_refcount, refinfo->trace ? refinfo->trace : "");
690     } else {
691       GValue r_value = G_VALUE_INIT;
692       r = gst_structure_new_empty ("object-refings");
693       gst_structure_set (r, "ts", GST_TYPE_CLOCK_TIME, refinfo->ts,
694           "desc", G_TYPE_STRING, refinfo->reffed ? "reffed" : "unreffed",
695           "ref-count", G_TYPE_UINT, refinfo->new_refcount,
696           "trace", G_TYPE_STRING, refinfo->trace, NULL);
697       /* avoid copy of structure */
698       g_value_init (&r_value, GST_TYPE_STRUCTURE);
699       g_value_take_boxed (&r_value, r);
700       gst_value_list_append_and_take_value (&refings, &r_value);
701     }
702   }
703
704   if (ret_leaks && leak->infos->refing_infos)
705     gst_structure_take_value (s, "ref-infos", &refings);
706 }
707
708 /* Return TRUE if at least one leaked object was found */
709 static gboolean
710 process_leaks (GstLeaksTracer * self, GValue * ret_leaks)
711 {
712   GList *leaks, *l;
713   gboolean ret = FALSE;
714   guint n = 0;
715
716   if (!ret_leaks)
717     GST_TRACE_OBJECT (self, "start listing currently alive objects");
718
719   leaks = create_leaks_list (self);
720   if (!leaks) {
721     if (!ret_leaks)
722       GST_TRACE_OBJECT (self, "No objects alive currently");
723     goto done;
724   }
725
726   for (l = leaks; l; l = l->next) {
727     process_leak (l->data, ret_leaks);
728     n++;
729   }
730
731   g_list_free_full (leaks, (GDestroyNotify) leak_free);
732
733   ret = TRUE;
734
735 done:
736   if (!ret_leaks)
737     GST_TRACE_OBJECT (self, "listed %u alive objects", n);
738
739   return ret;
740 }
741
742 static void
743 gst_leaks_tracer_finalize (GObject * object)
744 {
745   GstLeaksTracer *self = GST_LEAKS_TRACER (object);
746   gboolean leaks = FALSE;
747   GHashTableIter iter;
748   gpointer obj;
749
750   GST_DEBUG_OBJECT (self, "destroying tracer, checking for leaks");
751
752   self->done = TRUE;
753
754   /* Tracers are destroyed as part of gst_deinit() so now is a good time to
755    * report all the objects which are still alive. */
756   if (self->log_leaks)
757     leaks = process_leaks (self, NULL);
758
759   /* Remove weak references */
760   g_hash_table_iter_init (&iter, self->objects);
761   while (g_hash_table_iter_next (&iter, &obj, NULL)) {
762     if (object_is_gst_mini_object (obj))
763       gst_mini_object_weak_unref (GST_MINI_OBJECT_CAST (obj),
764           mini_object_weak_cb, self);
765     else
766       g_object_weak_unref (obj, object_weak_cb, self);
767   }
768
769   g_clear_pointer (&self->objects, g_hash_table_unref);
770   if (self->filter)
771     g_array_free (self->filter, TRUE);
772   g_clear_pointer (&self->added, g_hash_table_unref);
773   g_clear_pointer (&self->removed, g_hash_table_unref);
774   g_clear_pointer (&self->unhandled_filter, g_hash_table_unref);
775
776   G_LOCK (instances);
777   g_queue_remove (&instances, self);
778   G_UNLOCK (instances);
779
780 #ifdef G_OS_UNIX
781   gst_leaks_tracer_cleanup_signals (self);
782 #endif
783
784   if (leaks)
785     g_warning ("Leaks detected and logged under GST_DEBUG=GST_TRACER:7");
786
787   ((GObjectClass *) gst_leaks_tracer_parent_class)->finalize (object);
788 }
789
790 #define RECORD_FIELD_TYPE_TS \
791     "ts", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
792         "type", G_TYPE_GTYPE, GST_TYPE_CLOCK_TIME, \
793         NULL)
794 #define RECORD_FIELD_TYPE_NAME \
795     "type-name", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
796         "type", G_TYPE_GTYPE, G_TYPE_STRING, \
797         NULL)
798 #define RECORD_FIELD_ADDRESS \
799     "address", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
800         "type", G_TYPE_GTYPE, G_TYPE_POINTER, \
801         NULL)
802 #define RECORD_FIELD_DESC \
803     "description", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
804         "type", G_TYPE_GTYPE, G_TYPE_STRING, \
805         NULL)
806 #define RECORD_FIELD_REF_COUNT \
807     "ref-count", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
808         "type", G_TYPE_GTYPE, G_TYPE_UINT, \
809         NULL)
810 #define RECORD_FIELD_TRACE \
811     "trace", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
812         "type", G_TYPE_GTYPE, G_TYPE_STRING, \
813         NULL)
814
815 #ifdef G_OS_UNIX
816 static gboolean
817 sig_usr1_handler (gpointer data)
818 {
819   G_LOCK (instances);
820   g_queue_foreach (&instances, (GFunc) gst_leaks_tracer_log_live_objects, NULL);
821   G_UNLOCK (instances);
822
823   return G_SOURCE_CONTINUE;
824 }
825
826 static void
827 sig_usr2_handler_foreach (gpointer data, gpointer user_data)
828 {
829   GstLeaksTracer *tracer = data;
830
831   if (!tracer->added) {
832     GST_TRACE_OBJECT (tracer, "First checkpoint, start tracking objects");
833     gst_leaks_tracer_activity_start_tracking (tracer);
834   } else {
835     gst_leaks_tracer_activity_log_checkpoint (tracer);
836   }
837 }
838
839 static gboolean
840 sig_usr2_handler (gpointer data)
841 {
842   G_LOCK (instances);
843   g_queue_foreach (&instances, sig_usr2_handler_foreach, NULL);
844   G_UNLOCK (instances);
845
846   return G_SOURCE_CONTINUE;
847 }
848
849 struct signal_thread_data
850 {
851   GMutex lock;
852   GCond cond;
853   gboolean ready;
854 };
855
856 static GMainLoop *signal_loop;  /* NULL */
857 static GThread *signal_thread;  /* NULL */
858 static gint signal_thread_users;        /* 0 */
859 G_LOCK_DEFINE_STATIC (signal_thread);
860
861 static gboolean
862 unlock_mutex (gpointer data)
863 {
864   g_mutex_unlock ((GMutex *) data);
865
866   return G_SOURCE_REMOVE;
867 }
868
869 static gpointer
870 gst_leaks_tracer_signal_thread (struct signal_thread_data *data)
871 {
872   static GMainContext *signal_ctx;
873   GSource *source1, *source2, *unlock_source;
874
875   signal_ctx = g_main_context_new ();
876   signal_loop = g_main_loop_new (signal_ctx, FALSE);
877
878   unlock_source = g_idle_source_new ();
879   g_source_set_callback (unlock_source, unlock_mutex, &data->lock, NULL);
880   g_source_attach (unlock_source, signal_ctx);
881
882   source1 = g_unix_signal_source_new (SIGUSR1);
883   g_source_set_callback (source1, sig_usr1_handler, NULL, NULL);
884   g_source_attach (source1, signal_ctx);
885
886   source2 = g_unix_signal_source_new (SIGUSR2);
887   g_source_set_callback (source2, sig_usr2_handler, NULL, NULL);
888   g_source_attach (source2, signal_ctx);
889
890   g_mutex_lock (&data->lock);
891   data->ready = TRUE;
892   g_cond_broadcast (&data->cond);
893
894   g_main_loop_run (signal_loop);
895
896   g_source_destroy (source1);
897   g_source_destroy (source2);
898   g_main_loop_unref (signal_loop);
899   signal_loop = NULL;
900   g_main_context_unref (signal_ctx);
901   signal_ctx = NULL;
902
903   return NULL;
904 }
905
906 static void
907 atfork_prepare (void)
908 {
909   G_LOCK (signal_thread);
910 }
911
912 static void
913 atfork_parent (void)
914 {
915   G_UNLOCK (signal_thread);
916 }
917
918 static void
919 atfork_child (void)
920 {
921   signal_thread_users = 0;
922   signal_thread = NULL;
923   G_UNLOCK (signal_thread);
924 }
925
926 static void
927 gst_leaks_tracer_setup_signals (GstLeaksTracer * leaks)
928 {
929   struct signal_thread_data data;
930
931   G_LOCK (signal_thread);
932   signal_thread_users++;
933   if (signal_thread_users == 1) {
934     gint res;
935
936     GST_INFO_OBJECT (leaks, "Setting up signal handling");
937
938     /* If application is forked, the child process won't inherit the extra thread.
939      * As a result we need to reset the child process thread state accordingly.
940      * This is typically needed when running tests as libcheck fork the tests.
941      *
942      * See https://pubs.opengroup.org/onlinepubs/007904975/functions/pthread_atfork.html
943      * for details. */
944     res = pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
945     if (res != 0) {
946       GST_WARNING_OBJECT (leaks, "pthread_atfork() failed (%d)", res);
947     }
948
949     data.ready = FALSE;
950     g_mutex_init (&data.lock);
951     g_cond_init (&data.cond);
952     signal_thread = g_thread_new ("gstleak-signal",
953         (GThreadFunc) gst_leaks_tracer_signal_thread, &data);
954
955     g_mutex_lock (&data.lock);
956     while (!data.ready)
957       g_cond_wait (&data.cond, &data.lock);
958     g_mutex_unlock (&data.lock);
959
960     g_mutex_clear (&data.lock);
961     g_cond_clear (&data.cond);
962   }
963   G_UNLOCK (signal_thread);
964 }
965
966 static void
967 gst_leaks_tracer_cleanup_signals (GstLeaksTracer * leaks)
968 {
969   G_LOCK (signal_thread);
970   signal_thread_users--;
971   if (signal_thread_users == 0) {
972     GST_INFO_OBJECT (leaks, "Cleaning up signal handling");
973     g_main_loop_quit (signal_loop);
974     g_thread_join (signal_thread);
975     signal_thread = NULL;
976     gst_object_unref (tr_added);
977     tr_added = NULL;
978     gst_object_unref (tr_removed);
979     tr_removed = NULL;
980   }
981   G_UNLOCK (signal_thread);
982 }
983
984 #else
985 #define setup_signals() g_warning ("System doesn't support POSIX signals");
986 #endif /* G_OS_UNIX */
987
988 static GstStructure *
989 gst_leaks_tracer_get_live_objects (GstLeaksTracer * self)
990 {
991   GstStructure *info;
992   GValue live_objects = G_VALUE_INIT;
993
994   g_value_init (&live_objects, GST_TYPE_LIST);
995
996   GST_OBJECT_LOCK (self);
997   process_leaks (self, &live_objects);
998   GST_OBJECT_UNLOCK (self);
999
1000   info = gst_structure_new_empty ("live-objects-info");
1001   gst_structure_take_value (info, "live-objects-list", &live_objects);
1002
1003   return info;
1004 }
1005
1006 static void
1007 gst_leaks_tracer_log_live_objects (GstLeaksTracer * self)
1008 {
1009   GST_OBJECT_LOCK (self);
1010   process_leaks (self, NULL);
1011   GST_OBJECT_UNLOCK (self);
1012 }
1013
1014 static void
1015 gst_leaks_tracer_activity_start_tracking (GstLeaksTracer * self)
1016 {
1017   GST_OBJECT_LOCK (self);
1018   if (self->added) {
1019     GST_ERROR_OBJECT (self, "tracking is already in progress");
1020     return;
1021   }
1022
1023   self->added = g_hash_table_new_full (NULL, NULL,
1024       (GDestroyNotify) object_log_free, NULL);
1025   self->removed = g_hash_table_new_full (NULL, NULL,
1026       (GDestroyNotify) object_log_free, NULL);
1027   GST_OBJECT_UNLOCK (self);
1028 }
1029
1030 /* When @ret is %NULL, this simply logs the activities */
1031 static void
1032 process_checkpoint (GstTracerRecord * record, const gchar * record_type,
1033     GHashTable * hash, GValue * ret)
1034 {
1035   GHashTableIter iter;
1036   gpointer o;
1037
1038   g_hash_table_iter_init (&iter, hash);
1039   while (g_hash_table_iter_next (&iter, &o, NULL)) {
1040     ObjectLog *obj = o;
1041
1042     if (!ret) {
1043       /* log to the debug log */
1044       gst_tracer_record_log (record, obj->type_name, obj->object);
1045     } else {
1046       GValue s_value = G_VALUE_INIT;
1047       GValue addr_value = G_VALUE_INIT;
1048       gchar *address = g_strdup_printf ("%p", obj->object);
1049       GstStructure *s = gst_structure_new_empty (record_type);
1050       /* copy type_name because it's owned by @obj */
1051       gst_structure_set (s, "type-name", G_TYPE_STRING, obj->type_name, NULL);
1052       /* avoid copy of @address */
1053       g_value_init (&addr_value, G_TYPE_STRING);
1054       g_value_take_string (&addr_value, address);
1055       gst_structure_take_value (s, "address", &addr_value);
1056       /* avoid copy of the structure */
1057       g_value_init (&s_value, GST_TYPE_STRUCTURE);
1058       g_value_take_boxed (&s_value, s);
1059       gst_value_list_append_and_take_value (ret, &s_value);
1060     }
1061   }
1062 }
1063
1064 static GstStructure *
1065 gst_leaks_tracer_activity_get_checkpoint (GstLeaksTracer * self)
1066 {
1067   GValue added = G_VALUE_INIT;
1068   GValue removed = G_VALUE_INIT;
1069   GstStructure *s = gst_structure_new_empty ("activity-checkpoint");
1070
1071   g_value_init (&added, GST_TYPE_LIST);
1072   g_value_init (&removed, GST_TYPE_LIST);
1073
1074   GST_OBJECT_LOCK (self);
1075   process_checkpoint (tr_added, "objects-created", self->added, &added);
1076   process_checkpoint (tr_removed, "objects-removed", self->removed, &removed);
1077
1078   g_hash_table_remove_all (self->added);
1079   g_hash_table_remove_all (self->removed);
1080   GST_OBJECT_UNLOCK (self);
1081
1082   gst_structure_take_value (s, "objects-created-list", &added);
1083   gst_structure_take_value (s, "objects-removed-list", &removed);
1084
1085   return s;
1086 }
1087
1088 static void
1089 gst_leaks_tracer_activity_log_checkpoint (GstLeaksTracer * self)
1090 {
1091   GST_OBJECT_LOCK (self);
1092   GST_TRACE_OBJECT (self, "listing objects created since last checkpoint");
1093   process_checkpoint (tr_added, NULL, self->added, NULL);
1094   GST_TRACE_OBJECT (self, "listing objects removed since last checkpoint");
1095   process_checkpoint (tr_removed, NULL, self->removed, NULL);
1096   g_hash_table_remove_all (self->added);
1097   g_hash_table_remove_all (self->removed);
1098   GST_OBJECT_UNLOCK (self);
1099 }
1100
1101 static void
1102 gst_leaks_tracer_activity_stop_tracking (GstLeaksTracer * self)
1103 {
1104   GST_OBJECT_LOCK (self);
1105   g_clear_pointer (&self->added, g_hash_table_destroy);
1106   g_clear_pointer (&self->removed, g_hash_table_destroy);
1107   GST_OBJECT_UNLOCK (self);
1108 }
1109
1110 static void
1111 gst_leaks_tracer_class_init (GstLeaksTracerClass * klass)
1112 {
1113   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1114
1115   gobject_class->constructed = gst_leaks_tracer_constructed;
1116   gobject_class->finalize = gst_leaks_tracer_finalize;
1117
1118   tr_alive = gst_tracer_record_new ("object-alive.class",
1119       RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, RECORD_FIELD_DESC,
1120       RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL);
1121   GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1122
1123   tr_refings = gst_tracer_record_new ("object-refings.class",
1124       RECORD_FIELD_TYPE_TS, RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS,
1125       RECORD_FIELD_DESC, RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL);
1126   GST_OBJECT_FLAG_SET (tr_refings, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1127
1128   tr_added = gst_tracer_record_new ("object-added.class",
1129       RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
1130   GST_OBJECT_FLAG_SET (tr_added, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1131
1132   tr_removed = gst_tracer_record_new ("object-removed.class",
1133       RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
1134   GST_OBJECT_FLAG_SET (tr_removed, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1135
1136   /**
1137    * GstLeaksTracer::get-live-objects:
1138    * @leakstracer: the leaks tracer object to emit this signal on
1139    *
1140    * Returns a #GstStructure containing a #GValue of type #GST_TYPE_LIST which
1141    * is a list of #GstStructure objects containing information about the
1142    * objects that are still alive, which is useful for detecting leaks. Each
1143    * #GstStructure object has the following fields:
1144    *
1145    * `object`: containing the leaked object itself
1146    * `ref-count`: the current reference count of the object
1147    * `trace`: the allocation stack trace for the object, only available if the
1148    *          `stack-traces-flags` param is set to `full`
1149    * `ref-infos`: a #GValue of type #GST_TYPE_LIST which is a list of
1150    *             #GstStructure objects containing information about the
1151    *             ref/unref history of the object; only available if the
1152    *             `check-refs` param is set to `true`
1153    *
1154    * Each `ref-infos` #GstStructure has the following fields:
1155    *
1156    * `ts`: the timestamp for the ref/unref
1157    * `desc`: either "reffed" or "unreffed"
1158    * `ref-count`: the reference count after the ref/unref
1159    * `trace`: the stack trace for the ref/unref
1160    *
1161    * **Notes on usage**: This action signal is supposed to be called at the
1162    * end of an application before it exits, or at the end of an execution run
1163    * when all streaming has stopped and all pipelines have been freed. It is
1164    * assumed that at this point any GStreamer object that is still alive is
1165    * leaked, and there are no legitimate owners any more. As such, ownership
1166    * of the leaked objects is transferred to you then, assuming no other code
1167    * still retrains references to them.
1168    *
1169    * If that's not the case, and there is code somewhere still holding
1170    * a reference, then the application behaviour is undefined after this
1171    * function is called, since we will have stolen some other code's valid
1172    * reference and when the returned #GstStructure is freed that code will be
1173    * holding a reference to an invalid object, which will most likely crash
1174    * sooner or later.
1175    *
1176    * If you don't want to just check for leaks at the end of a program, the
1177    * activity checkpoint action signals might be a better fit for your use
1178    * case.
1179    *
1180    * Returns: (transfer full): a newly-allocated #GstStructure
1181    *
1182    * Since: 1.18
1183    */
1184   gst_leaks_tracer_signals[SIGNAL_GET_LIVE_OBJECTS] =
1185       g_signal_new ("get-live-objects", G_TYPE_FROM_CLASS (klass),
1186       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1187           get_live_objects), NULL, NULL, NULL, GST_TYPE_STRUCTURE, 0,
1188       G_TYPE_NONE);
1189
1190   /**
1191    * GstLeaksTracer::log-live-objects:
1192    * @leakstracer: the leaks tracer object to emit this signal on
1193    *
1194    * Logs all objects that are still alive to the debug log in the same format
1195    * as the logging during gst_deinit().
1196    *
1197    * Since: 1.18
1198    */
1199   gst_leaks_tracer_signals[SIGNAL_LOG_LIVE_OBJECTS] =
1200       g_signal_new ("log-live-objects", G_TYPE_FROM_CLASS (klass),
1201       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1202           log_live_objects), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE);
1203
1204   /**
1205    * GstLeaksTracer:::activity-start-tracking
1206    * @leakstracer: the leaks tracer object to emit this signal on
1207    *
1208    * Start storing information about all objects that are being created or
1209    * removed. Call `stop-tracking` to stop.
1210    *
1211    * NOTE: You do not need to call this to use the *-live-objects action
1212    * signals listed above.
1213    *
1214    * Since: 1.18
1215    */
1216   gst_leaks_tracer_signals[SIGNAL_ACTIVITY_START_TRACKING] =
1217       g_signal_new ("activity-start-tracking", G_TYPE_FROM_CLASS (klass),
1218       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1219           activity_start_tracking), NULL, NULL, NULL, G_TYPE_NONE, 0,
1220       G_TYPE_NONE);
1221
1222   /**
1223    * GstLeaksTracer:::activity-get-checkpoint
1224    * @leakstracer: the leaks tracer object to emit this signal on
1225    *
1226    * You must call this after calling `activity-start-tracking` and you should
1227    * call `activity-stop-tracking` when you are done tracking.
1228    *
1229    * Returns a #GstStructure with two fields: `"objects-created-list"` and
1230    * `"objects-removed-list"`, each of which is a #GValue of type #GST_TYPE_LIST
1231    * containing all objects that were created/removed since the last
1232    * checkpoint, or since tracking started if this is the first checkpoint.
1233    *
1234    * The list elements are in order of creation/removal. Each list element is
1235    * a #GValue containing a #GstStructure with the following fields:
1236    *
1237    * `type-name`: a string representing the type of the object
1238    * `address`: a string representing the address of the object; the object
1239    *            itself cannot be returned since we don't own it and it may be
1240    *            freed at any moment, or it may already have been freed
1241    *
1242    * Returns: (transfer full): a newly-allocated #GstStructure
1243    *
1244    * Since: 1.18
1245    */
1246   gst_leaks_tracer_signals[SIGNAL_ACTIVITY_GET_CHECKPOINT] =
1247       g_signal_new ("activity-get-checkpoint", G_TYPE_FROM_CLASS (klass),
1248       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1249           activity_get_checkpoint), NULL, NULL, NULL, GST_TYPE_STRUCTURE, 0,
1250       G_TYPE_NONE);
1251
1252   /**
1253    * GstLeaksTracer:::activity-log-checkpoint
1254    * @leakstracer: the leaks tracer object to emit this signal on
1255    *
1256    * You must call this after calling `activity-start-tracking` and you should
1257    * call `activity-stop-tracking` when you are done tracking.
1258    *
1259    * List all objects that were created or removed since the last checkpoint,
1260    * or since tracking started if this is the first checkpoint.
1261    *
1262    * This action signal is equivalent to `activity-get-checkpoint` except that
1263    * the checkpoint data will be printed to the debug log under `GST_TRACER:7`.
1264    *
1265    * Since: 1.18
1266    */
1267   gst_leaks_tracer_signals[SIGNAL_ACTIVITY_LOG_CHECKPOINT] =
1268       g_signal_new ("activity-log-checkpoint", G_TYPE_FROM_CLASS (klass),
1269       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1270           activity_log_checkpoint), NULL, NULL, NULL, G_TYPE_NONE, 0,
1271       G_TYPE_NONE);
1272
1273   /**
1274    * GstLeaksTracer:::activity-stop-tracking
1275    * @leakstracer: the leaks tracer object to emit this signal on
1276    *
1277    * Stop tracking all objects that are being created or removed, undoes the
1278    * effects of the `start-tracking` signal.
1279    *
1280    * Since: 1.18
1281    */
1282   gst_leaks_tracer_signals[SIGNAL_ACTIVITY_STOP_TRACKING] =
1283       g_signal_new ("activity-stop-tracking", G_TYPE_FROM_CLASS (klass),
1284       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1285           activity_stop_tracking), NULL, NULL, NULL, G_TYPE_NONE, 0,
1286       G_TYPE_NONE);
1287
1288   klass->get_live_objects = gst_leaks_tracer_get_live_objects;
1289   klass->log_live_objects = gst_leaks_tracer_log_live_objects;
1290   klass->activity_start_tracking = gst_leaks_tracer_activity_start_tracking;
1291   klass->activity_get_checkpoint = gst_leaks_tracer_activity_get_checkpoint;
1292   klass->activity_log_checkpoint = gst_leaks_tracer_activity_log_checkpoint;
1293   klass->activity_stop_tracking = gst_leaks_tracer_activity_stop_tracking;
1294 }