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
object_refing_info_free(ObjectRefingInfo * refinfo)133 object_refing_info_free (ObjectRefingInfo * refinfo)
134 {
135 g_free (refinfo->trace);
136 g_free (refinfo);
137 }
138
139 static void
object_refing_infos_free(ObjectRefingInfos * infos)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
set_print_stack_trace_from_string(GstLeaksTracer * self,const gchar * str)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
set_print_stack_trace(GstLeaksTracer * self,GstStructure * params)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
set_filters(GstLeaksTracer * self,const gchar * filters)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
set_params_from_structure(GstLeaksTracer * self,GstStructure * params)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
set_params(GstLeaksTracer * self)235 set_params (GstLeaksTracer * self)
236 {
237 gchar *params, *tmp;
238 GstStructure *params_struct = NULL;
239
240 g_object_get (self, "params", ¶ms, 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
_expand_unhandled_filters(gchar * typename,gpointer unused_value,GstLeaksTracer * self)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
should_handle_object_type(GstLeaksTracer * self,GType object_type)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
object_is_gst_mini_object(gpointer obj)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 *
object_log_new(gpointer obj)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
object_log_free(ObjectLog * obj)341 object_log_free (ObjectLog * obj)
342 {
343 g_free (obj);
344 }
345
346 static void
handle_object_destroyed(GstLeaksTracer * self,gpointer object)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
object_weak_cb(gpointer data,GObject * object)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
mini_object_weak_cb(gpointer data,GstMiniObject * object)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
handle_object_created(GstLeaksTracer * self,gpointer object,GType type,gboolean gobject)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
mini_object_created_cb(GstTracer * tracer,GstClockTime ts,GstMiniObject * object)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
object_created_cb(GstTracer * tracer,GstClockTime ts,GstObject * object)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
handle_object_reffed(GstLeaksTracer * self,gpointer object,GType type,gint new_refcount,gboolean reffed,GstClockTime ts)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
object_reffed_cb(GstTracer * tracer,GstClockTime ts,GstObject * object,gint new_refcount)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
object_unreffed_cb(GstTracer * tracer,GstClockTime ts,GstObject * object,gint new_refcount)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
mini_object_reffed_cb(GstTracer * tracer,GstClockTime ts,GstMiniObject * object,gint new_refcount)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
mini_object_unreffed_cb(GstTracer * tracer,GstClockTime ts,GstMiniObject * object,gint new_refcount)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
gst_leaks_tracer_init(GstLeaksTracer * self)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
gst_leaks_tracer_constructed(GObject * object)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 *
leak_new(gpointer obj,GType type,guint ref_count,ObjectRefingInfos * infos)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
leak_free(Leak * leak)578 leak_free (Leak * leak)
579 {
580 g_free (leak->desc);
581 g_free (leak);
582 }
583
584 static gint
sort_leaks(gconstpointer _a,gconstpointer _b)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 *
create_leaks_list(GstLeaksTracer * self)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
process_leak(Leak * leak,GValue * ret_leaks)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
process_leaks(GstLeaksTracer * self,GValue * ret_leaks)696 process_leaks (GstLeaksTracer * self, GValue * ret_leaks)
697 {
698 GList *leaks, *l;
699 gboolean ret = FALSE;
700
701 if (!ret_leaks)
702 GST_TRACE_OBJECT (self, "start listing currently alive objects");
703
704 leaks = create_leaks_list (self);
705 if (!leaks) {
706 if (!ret_leaks)
707 GST_TRACE_OBJECT (self, "No objects alive currently");
708 goto done;
709 }
710
711 for (l = leaks; l; l = l->next)
712 process_leak (l->data, ret_leaks);
713
714 g_list_free_full (leaks, (GDestroyNotify) leak_free);
715
716 ret = TRUE;
717
718 done:
719 if (!ret_leaks)
720 GST_TRACE_OBJECT (self, "done listing currently alive objects");
721
722 return ret;
723 }
724
725 static void
gst_leaks_tracer_finalize(GObject * object)726 gst_leaks_tracer_finalize (GObject * object)
727 {
728 GstLeaksTracer *self = GST_LEAKS_TRACER (object);
729 gboolean leaks = FALSE;
730 GHashTableIter iter;
731 gpointer obj;
732
733 GST_DEBUG_OBJECT (self, "destroying tracer, checking for leaks");
734
735 self->done = TRUE;
736
737 /* Tracers are destroyed as part of gst_deinit() so now is a good time to
738 * report all the objects which are still alive. */
739 if (self->log_leaks)
740 leaks = process_leaks (self, NULL);
741
742 /* Remove weak references */
743 g_hash_table_iter_init (&iter, self->objects);
744 while (g_hash_table_iter_next (&iter, &obj, NULL)) {
745 if (object_is_gst_mini_object (obj))
746 gst_mini_object_weak_unref (GST_MINI_OBJECT_CAST (obj),
747 mini_object_weak_cb, self);
748 else
749 g_object_weak_unref (obj, object_weak_cb, self);
750 }
751
752 g_clear_pointer (&self->objects, g_hash_table_unref);
753 if (self->filter)
754 g_array_free (self->filter, TRUE);
755 g_clear_pointer (&self->added, g_hash_table_unref);
756 g_clear_pointer (&self->removed, g_hash_table_unref);
757 g_clear_pointer (&self->unhandled_filter, g_hash_table_unref);
758
759 G_LOCK (instances);
760 g_queue_remove (&instances, self);
761 G_UNLOCK (instances);
762
763 #ifdef G_OS_UNIX
764 gst_leaks_tracer_cleanup_signals (self);
765 #endif
766
767 if (leaks)
768 g_warning ("Leaks detected and logged under GST_DEBUG=GST_TRACER:7");
769
770 ((GObjectClass *) gst_leaks_tracer_parent_class)->finalize (object);
771 }
772
773 #define RECORD_FIELD_TYPE_TS \
774 "ts", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
775 "type", G_TYPE_GTYPE, GST_TYPE_CLOCK_TIME, \
776 NULL)
777 #define RECORD_FIELD_TYPE_NAME \
778 "type-name", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
779 "type", G_TYPE_GTYPE, G_TYPE_STRING, \
780 NULL)
781 #define RECORD_FIELD_ADDRESS \
782 "address", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
783 "type", G_TYPE_GTYPE, G_TYPE_POINTER, \
784 NULL)
785 #define RECORD_FIELD_DESC \
786 "description", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
787 "type", G_TYPE_GTYPE, G_TYPE_STRING, \
788 NULL)
789 #define RECORD_FIELD_REF_COUNT \
790 "ref-count", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
791 "type", G_TYPE_GTYPE, G_TYPE_UINT, \
792 NULL)
793 #define RECORD_FIELD_TRACE \
794 "trace", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
795 "type", G_TYPE_GTYPE, G_TYPE_STRING, \
796 NULL)
797
798 #ifdef G_OS_UNIX
799 static gboolean
sig_usr1_handler(gpointer data)800 sig_usr1_handler (gpointer data)
801 {
802 G_LOCK (instances);
803 g_queue_foreach (&instances, (GFunc) gst_leaks_tracer_log_live_objects, NULL);
804 G_UNLOCK (instances);
805
806 return G_SOURCE_CONTINUE;
807 }
808
809 static void
sig_usr2_handler_foreach(gpointer data,gpointer user_data)810 sig_usr2_handler_foreach (gpointer data, gpointer user_data)
811 {
812 GstLeaksTracer *tracer = data;
813
814 if (!tracer->added) {
815 GST_TRACE_OBJECT (tracer, "First checkpoint, start tracking objects");
816 gst_leaks_tracer_activity_start_tracking (tracer);
817 } else {
818 gst_leaks_tracer_activity_log_checkpoint (tracer);
819 }
820 }
821
822 static gboolean
sig_usr2_handler(gpointer data)823 sig_usr2_handler (gpointer data)
824 {
825 G_LOCK (instances);
826 g_queue_foreach (&instances, sig_usr2_handler_foreach, NULL);
827 G_UNLOCK (instances);
828
829 return G_SOURCE_CONTINUE;
830 }
831
832 struct signal_thread_data
833 {
834 GMutex lock;
835 GCond cond;
836 gboolean ready;
837 };
838
839 static GMainLoop *signal_loop; /* NULL */
840 static GThread *signal_thread; /* NULL */
841 static gint signal_thread_users; /* 0 */
842 G_LOCK_DEFINE_STATIC (signal_thread);
843
844 static gboolean
unlock_mutex(gpointer data)845 unlock_mutex (gpointer data)
846 {
847 g_mutex_unlock ((GMutex *) data);
848
849 return G_SOURCE_REMOVE;
850 }
851
852 static gpointer
gst_leaks_tracer_signal_thread(struct signal_thread_data * data)853 gst_leaks_tracer_signal_thread (struct signal_thread_data *data)
854 {
855 static GMainContext *signal_ctx;
856 GSource *source1, *source2, *unlock_source;
857
858 signal_ctx = g_main_context_new ();
859 signal_loop = g_main_loop_new (signal_ctx, FALSE);
860
861 unlock_source = g_idle_source_new ();
862 g_source_set_callback (unlock_source, unlock_mutex, &data->lock, NULL);
863 g_source_attach (unlock_source, signal_ctx);
864
865 source1 = g_unix_signal_source_new (SIGUSR1);
866 g_source_set_callback (source1, sig_usr1_handler, NULL, NULL);
867 g_source_attach (source1, signal_ctx);
868
869 source2 = g_unix_signal_source_new (SIGUSR2);
870 g_source_set_callback (source2, sig_usr2_handler, NULL, NULL);
871 g_source_attach (source2, signal_ctx);
872
873 g_mutex_lock (&data->lock);
874 data->ready = TRUE;
875 g_cond_broadcast (&data->cond);
876
877 g_main_loop_run (signal_loop);
878
879 g_source_destroy (source1);
880 g_source_destroy (source2);
881 g_main_loop_unref (signal_loop);
882 signal_loop = NULL;
883 g_main_context_unref (signal_ctx);
884 signal_ctx = NULL;
885
886 return NULL;
887 }
888
889 static void
atfork_prepare(void)890 atfork_prepare (void)
891 {
892 G_LOCK (signal_thread);
893 }
894
895 static void
atfork_parent(void)896 atfork_parent (void)
897 {
898 G_UNLOCK (signal_thread);
899 }
900
901 static void
atfork_child(void)902 atfork_child (void)
903 {
904 signal_thread_users = 0;
905 signal_thread = NULL;
906 G_UNLOCK (signal_thread);
907 }
908
909 static void
gst_leaks_tracer_setup_signals(GstLeaksTracer * leaks)910 gst_leaks_tracer_setup_signals (GstLeaksTracer * leaks)
911 {
912 struct signal_thread_data data;
913
914 G_LOCK (signal_thread);
915 signal_thread_users++;
916 if (signal_thread_users == 1) {
917 gint res;
918
919 GST_INFO_OBJECT (leaks, "Setting up signal handling");
920
921 /* If application is forked, the child process won't inherit the extra thread.
922 * As a result we need to reset the child process thread state accordingly.
923 * This is typically needed when running tests as libcheck fork the tests.
924 *
925 * See https://pubs.opengroup.org/onlinepubs/007904975/functions/pthread_atfork.html
926 * for details. */
927 res = pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
928 if (res != 0) {
929 GST_WARNING_OBJECT (leaks, "pthread_atfork() failed (%d)", res);
930 }
931
932 data.ready = FALSE;
933 g_mutex_init (&data.lock);
934 g_cond_init (&data.cond);
935 signal_thread = g_thread_new ("gstleak-signal",
936 (GThreadFunc) gst_leaks_tracer_signal_thread, &data);
937
938 g_mutex_lock (&data.lock);
939 while (!data.ready)
940 g_cond_wait (&data.cond, &data.lock);
941 g_mutex_unlock (&data.lock);
942
943 g_mutex_clear (&data.lock);
944 g_cond_clear (&data.cond);
945 }
946 G_UNLOCK (signal_thread);
947 }
948
949 static void
gst_leaks_tracer_cleanup_signals(GstLeaksTracer * leaks)950 gst_leaks_tracer_cleanup_signals (GstLeaksTracer * leaks)
951 {
952 G_LOCK (signal_thread);
953 signal_thread_users--;
954 if (signal_thread_users == 0) {
955 GST_INFO_OBJECT (leaks, "Cleaning up signal handling");
956 g_main_loop_quit (signal_loop);
957 g_thread_join (signal_thread);
958 signal_thread = NULL;
959 gst_object_unref (tr_added);
960 tr_added = NULL;
961 gst_object_unref (tr_removed);
962 tr_removed = NULL;
963 }
964 G_UNLOCK (signal_thread);
965 }
966
967 #else
968 #define setup_signals() g_warning ("System doesn't support POSIX signals");
969 #endif /* G_OS_UNIX */
970
971 static GstStructure *
gst_leaks_tracer_get_live_objects(GstLeaksTracer * self)972 gst_leaks_tracer_get_live_objects (GstLeaksTracer * self)
973 {
974 GstStructure *info;
975 GValue live_objects = G_VALUE_INIT;
976
977 g_value_init (&live_objects, GST_TYPE_LIST);
978
979 GST_OBJECT_LOCK (self);
980 process_leaks (self, &live_objects);
981 GST_OBJECT_UNLOCK (self);
982
983 info = gst_structure_new_empty ("live-objects-info");
984 gst_structure_take_value (info, "live-objects-list", &live_objects);
985
986 return info;
987 }
988
989 static void
gst_leaks_tracer_log_live_objects(GstLeaksTracer * self)990 gst_leaks_tracer_log_live_objects (GstLeaksTracer * self)
991 {
992 GST_OBJECT_LOCK (self);
993 process_leaks (self, NULL);
994 GST_OBJECT_UNLOCK (self);
995 }
996
997 static void
gst_leaks_tracer_activity_start_tracking(GstLeaksTracer * self)998 gst_leaks_tracer_activity_start_tracking (GstLeaksTracer * self)
999 {
1000 GST_OBJECT_LOCK (self);
1001 if (self->added) {
1002 GST_ERROR_OBJECT (self, "tracking is already in progress");
1003 return;
1004 }
1005
1006 self->added = g_hash_table_new_full (NULL, NULL,
1007 (GDestroyNotify) object_log_free, NULL);
1008 self->removed = g_hash_table_new_full (NULL, NULL,
1009 (GDestroyNotify) object_log_free, NULL);
1010 GST_OBJECT_UNLOCK (self);
1011 }
1012
1013 /* When @ret is %NULL, this simply logs the activities */
1014 static void
process_checkpoint(GstTracerRecord * record,const gchar * record_type,GHashTable * hash,GValue * ret)1015 process_checkpoint (GstTracerRecord * record, const gchar * record_type,
1016 GHashTable * hash, GValue * ret)
1017 {
1018 GHashTableIter iter;
1019 gpointer o;
1020
1021 g_hash_table_iter_init (&iter, hash);
1022 while (g_hash_table_iter_next (&iter, &o, NULL)) {
1023 ObjectLog *obj = o;
1024
1025 if (!ret) {
1026 /* log to the debug log */
1027 gst_tracer_record_log (record, obj->type_name, obj->object);
1028 } else {
1029 GValue s_value = G_VALUE_INIT;
1030 GValue addr_value = G_VALUE_INIT;
1031 gchar *address = g_strdup_printf ("%p", obj->object);
1032 GstStructure *s = gst_structure_new_empty (record_type);
1033 /* copy type_name because it's owned by @obj */
1034 gst_structure_set (s, "type-name", G_TYPE_STRING, obj->type_name, NULL);
1035 /* avoid copy of @address */
1036 g_value_init (&addr_value, G_TYPE_STRING);
1037 g_value_take_string (&addr_value, address);
1038 gst_structure_take_value (s, "address", &addr_value);
1039 /* avoid copy of the structure */
1040 g_value_init (&s_value, GST_TYPE_STRUCTURE);
1041 g_value_take_boxed (&s_value, s);
1042 gst_value_list_append_and_take_value (ret, &s_value);
1043 }
1044 }
1045 }
1046
1047 static GstStructure *
gst_leaks_tracer_activity_get_checkpoint(GstLeaksTracer * self)1048 gst_leaks_tracer_activity_get_checkpoint (GstLeaksTracer * self)
1049 {
1050 GValue added = G_VALUE_INIT;
1051 GValue removed = G_VALUE_INIT;
1052 GstStructure *s = gst_structure_new_empty ("activity-checkpoint");
1053
1054 g_value_init (&added, GST_TYPE_LIST);
1055 g_value_init (&removed, GST_TYPE_LIST);
1056
1057 GST_OBJECT_LOCK (self);
1058 process_checkpoint (tr_added, "objects-created", self->added, &added);
1059 process_checkpoint (tr_removed, "objects-removed", self->removed, &removed);
1060
1061 g_hash_table_remove_all (self->added);
1062 g_hash_table_remove_all (self->removed);
1063 GST_OBJECT_UNLOCK (self);
1064
1065 gst_structure_take_value (s, "objects-created-list", &added);
1066 gst_structure_take_value (s, "objects-removed-list", &removed);
1067
1068 return s;
1069 }
1070
1071 static void
gst_leaks_tracer_activity_log_checkpoint(GstLeaksTracer * self)1072 gst_leaks_tracer_activity_log_checkpoint (GstLeaksTracer * self)
1073 {
1074 GST_OBJECT_LOCK (self);
1075 GST_TRACE_OBJECT (self, "listing objects created since last checkpoint");
1076 process_checkpoint (tr_added, NULL, self->added, NULL);
1077 GST_TRACE_OBJECT (self, "listing objects removed since last checkpoint");
1078 process_checkpoint (tr_removed, NULL, self->removed, NULL);
1079 g_hash_table_remove_all (self->added);
1080 g_hash_table_remove_all (self->removed);
1081 GST_OBJECT_UNLOCK (self);
1082 }
1083
1084 static void
gst_leaks_tracer_activity_stop_tracking(GstLeaksTracer * self)1085 gst_leaks_tracer_activity_stop_tracking (GstLeaksTracer * self)
1086 {
1087 GST_OBJECT_LOCK (self);
1088 g_clear_pointer (&self->added, g_hash_table_destroy);
1089 g_clear_pointer (&self->removed, g_hash_table_destroy);
1090 GST_OBJECT_UNLOCK (self);
1091 }
1092
1093 static void
gst_leaks_tracer_class_init(GstLeaksTracerClass * klass)1094 gst_leaks_tracer_class_init (GstLeaksTracerClass * klass)
1095 {
1096 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1097
1098 gobject_class->constructed = gst_leaks_tracer_constructed;
1099 gobject_class->finalize = gst_leaks_tracer_finalize;
1100
1101 tr_alive = gst_tracer_record_new ("object-alive.class",
1102 RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, RECORD_FIELD_DESC,
1103 RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL);
1104 GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1105
1106 tr_refings = gst_tracer_record_new ("object-refings.class",
1107 RECORD_FIELD_TYPE_TS, RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS,
1108 RECORD_FIELD_DESC, RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL);
1109 GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1110
1111 tr_added = gst_tracer_record_new ("object-added.class",
1112 RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
1113 GST_OBJECT_FLAG_SET (tr_added, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1114
1115 tr_removed = gst_tracer_record_new ("object-removed.class",
1116 RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
1117 GST_OBJECT_FLAG_SET (tr_removed, GST_OBJECT_FLAG_MAY_BE_LEAKED);
1118
1119 /**
1120 * GstLeaksTracer::get-live-objects:
1121 * @leakstracer: the leaks tracer object to emit this signal on
1122 *
1123 * Returns a #GstStructure containing a #GValue of type #GST_TYPE_LIST which
1124 * is a list of #GstStructure objects containing information about the
1125 * objects that are still alive, which is useful for detecting leaks. Each
1126 * #GstStructure object has the following fields:
1127 *
1128 * `object`: containing the leaked object itself
1129 * `ref-count`: the current reference count of the object
1130 * `trace`: the allocation stack trace for the object, only available if the
1131 * `stack-traces-flags` param is set to `full`
1132 * `ref-infos`: a #GValue of type #GST_TYPE_LIST which is a list of
1133 * #GstStructure objects containing information about the
1134 * ref/unref history of the object; only available if the
1135 * `check-refs` param is set to `true`
1136 *
1137 * Each `ref-infos` #GstStructure has the following fields:
1138 *
1139 * `ts`: the timestamp for the ref/unref
1140 * `desc`: either "reffed" or "unreffed"
1141 * `ref-count`: the reference count after the ref/unref
1142 * `trace`: the stack trace for the ref/unref
1143 *
1144 * **Notes on usage**: This action signal is supposed to be called at the
1145 * end of an application before it exits, or at the end of an execution run
1146 * when all streaming has stopped and all pipelines have been freed. It is
1147 * assumed that at this point any GStreamer object that is still alive is
1148 * leaked, and there are no legitimate owners any more. As such, ownership
1149 * of the leaked objects is transferred to you then, assuming no other code
1150 * still retrains references to them.
1151 *
1152 * If that's not the case, and there is code somewhere still holding
1153 * a reference, then the application behaviour is undefined after this
1154 * function is called, since we will have stolen some other code's valid
1155 * reference and when the returned #GstStructure is freed that code will be
1156 * holding a reference to an invalid object, which will most likely crash
1157 * sooner or later.
1158 *
1159 * If you don't want to just check for leaks at the end of a program, the
1160 * activity checkpoint action signals might be a better fit for your use
1161 * case.
1162 *
1163 * Returns: (transfer full): a newly-allocated #GstStructure
1164 *
1165 * Since: 1.18
1166 */
1167 gst_leaks_tracer_signals[SIGNAL_GET_LIVE_OBJECTS] =
1168 g_signal_new ("get-live-objects", G_TYPE_FROM_CLASS (klass),
1169 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1170 get_live_objects), NULL, NULL, NULL, GST_TYPE_STRUCTURE, 0,
1171 G_TYPE_NONE);
1172
1173 /**
1174 * GstLeaksTracer::log-live-objects:
1175 * @leakstracer: the leaks tracer object to emit this signal on
1176 *
1177 * Logs all objects that are still alive to the debug log in the same format
1178 * as the logging during gst_deinit().
1179 *
1180 * Since: 1.18
1181 */
1182 gst_leaks_tracer_signals[SIGNAL_LOG_LIVE_OBJECTS] =
1183 g_signal_new ("log-live-objects", G_TYPE_FROM_CLASS (klass),
1184 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1185 log_live_objects), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE);
1186
1187 /**
1188 * GstLeaksTracer:::activity-start-tracking
1189 * @leakstracer: the leaks tracer object to emit this signal on
1190 *
1191 * Start storing information about all objects that are being created or
1192 * removed. Call `stop-tracking` to stop.
1193 *
1194 * NOTE: You do not need to call this to use the *-live-objects action
1195 * signals listed above.
1196 *
1197 * Since: 1.18
1198 */
1199 gst_leaks_tracer_signals[SIGNAL_ACTIVITY_START_TRACKING] =
1200 g_signal_new ("activity-start-tracking", G_TYPE_FROM_CLASS (klass),
1201 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1202 activity_start_tracking), NULL, NULL, NULL, G_TYPE_NONE, 0,
1203 G_TYPE_NONE);
1204
1205 /**
1206 * GstLeaksTracer:::activity-get-checkpoint
1207 * @leakstracer: the leaks tracer object to emit this signal on
1208 *
1209 * You must call this after calling `activity-start-tracking` and you should
1210 * call `activity-stop-tracking` when you are done tracking.
1211 *
1212 * Returns a #GstStructure with two fields: `"objects-created-list"` and
1213 * `"objects-removed-list"`, each of which is a #GValue of type #GST_TYPE_LIST
1214 * containing all objects that were created/removed since the last
1215 * checkpoint, or since tracking started if this is the first checkpoint.
1216 *
1217 * The list elements are in order of creation/removal. Each list element is
1218 * a #GValue containing a #GstStructure with the following fields:
1219 *
1220 * `type-name`: a string representing the type of the object
1221 * `address`: a string representing the address of the object; the object
1222 * itself cannot be returned since we don't own it and it may be
1223 * freed at any moment, or it may already have been freed
1224 *
1225 * Returns: (transfer full): a newly-allocated #GstStructure
1226 *
1227 * Since: 1.18
1228 */
1229 gst_leaks_tracer_signals[SIGNAL_ACTIVITY_GET_CHECKPOINT] =
1230 g_signal_new ("activity-get-checkpoint", G_TYPE_FROM_CLASS (klass),
1231 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1232 activity_get_checkpoint), NULL, NULL, NULL, GST_TYPE_STRUCTURE, 0,
1233 G_TYPE_NONE);
1234
1235 /**
1236 * GstLeaksTracer:::activity-log-checkpoint
1237 * @leakstracer: the leaks tracer object to emit this signal on
1238 *
1239 * You must call this after calling `activity-start-tracking` and you should
1240 * call `activity-stop-tracking` when you are done tracking.
1241 *
1242 * List all objects that were created or removed since the last checkpoint,
1243 * or since tracking started if this is the first checkpoint.
1244 *
1245 * This action signal is equivalent to `activity-get-checkpoint` except that
1246 * the checkpoint data will be printed to the debug log under `GST_TRACER:7`.
1247 *
1248 * Since: 1.18
1249 */
1250 gst_leaks_tracer_signals[SIGNAL_ACTIVITY_LOG_CHECKPOINT] =
1251 g_signal_new ("activity-log-checkpoint", G_TYPE_FROM_CLASS (klass),
1252 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1253 activity_log_checkpoint), NULL, NULL, NULL, G_TYPE_NONE, 0,
1254 G_TYPE_NONE);
1255
1256 /**
1257 * GstLeaksTracer:::activity-stop-tracking
1258 * @leakstracer: the leaks tracer object to emit this signal on
1259 *
1260 * Stop tracking all objects that are being created or removed, undoes the
1261 * effects of the `start-tracking` signal.
1262 *
1263 * Since: 1.18
1264 */
1265 gst_leaks_tracer_signals[SIGNAL_ACTIVITY_STOP_TRACKING] =
1266 g_signal_new ("activity-stop-tracking", G_TYPE_FROM_CLASS (klass),
1267 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstLeaksTracerClass,
1268 activity_stop_tracking), NULL, NULL, NULL, G_TYPE_NONE, 0,
1269 G_TYPE_NONE);
1270
1271 klass->get_live_objects = gst_leaks_tracer_get_live_objects;
1272 klass->log_live_objects = gst_leaks_tracer_log_live_objects;
1273 klass->activity_start_tracking = gst_leaks_tracer_activity_start_tracking;
1274 klass->activity_get_checkpoint = gst_leaks_tracer_activity_get_checkpoint;
1275 klass->activity_log_checkpoint = gst_leaks_tracer_activity_log_checkpoint;
1276 klass->activity_stop_tracking = gst_leaks_tracer_activity_stop_tracking;
1277 }
1278