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