2 * Copyright (C) 2016 Collabora Ltd. <guillaume.desmottes@collabora.co.uk>
4 * gstleaks.c: tracing module detecting object leaks
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 * @short_description: detect GstObject and GstMiniObject leaks
25 * A tracing module tracking the lifetime of objects by logging those still
26 * alive when program is exiting and raising a warning.
27 * The type of objects tracked can be filtered using the parameters of the
28 * tracer, for example: GST_TRACERS=leaks(filters="GstEvent,GstMessage",print-traces=true)
39 #endif /* G_OS_UNIX */
41 GST_DEBUG_CATEGORY_STATIC (gst_leaks_debug);
42 #define GST_CAT_DEFAULT gst_leaks_debug
45 GST_DEBUG_CATEGORY_INIT (gst_leaks_debug, "leaks", 0, "leaks tracer");
46 #define gst_leaks_tracer_parent_class parent_class
47 G_DEFINE_TYPE_WITH_CODE (GstLeaksTracer, gst_leaks_tracer,
48 GST_TYPE_TRACER, _do_init);
50 static GstTracerRecord *tr_alive;
52 static GstTracerRecord *tr_added = NULL;
53 static GstTracerRecord *tr_removed = NULL;
54 #endif /* G_OS_UNIX */
55 static GQueue instances = G_QUEUE_INIT;
58 set_print_stack_trace (GstLeaksTracer * self, GstStructure * params)
61 gboolean check_stack_trace =
62 g_getenv ("GST_LEAKS_TRACER_STACK_TRACE") != NULL;
64 if (!check_stack_trace && params)
65 gst_structure_get_boolean (params, "print-traces", &check_stack_trace);
67 if (!check_stack_trace)
71 /* Test if we can retrieve backtrace */
72 trace = gst_debug_get_stack_trace (0);
74 self->log_stack_trace = TRUE;
77 g_warning ("Can't retrieve backtrace on this system");
82 set_filters (GstLeaksTracer * self, const gchar * filters)
85 GStrv tmp = g_strsplit (filters, ",", -1);
87 self->filter = g_array_sized_new (FALSE, FALSE, sizeof (GType),
89 for (i = 0; tmp[i]; i++) {
92 type = g_type_from_name (tmp[i]);
94 /* The type may not yet be known by the type system, typically because
95 * the plugin implementing it as not yet be loaded. Save it for now as
96 * it will have another chance to be added to the filter later in
97 * should_handle_object_type() when/if the object type is actually
99 if (!self->unhandled_filter)
100 self->unhandled_filter = g_hash_table_new (NULL, NULL);
102 g_hash_table_add (self->unhandled_filter,
103 GUINT_TO_POINTER (g_quark_from_string (tmp[i])));
104 g_atomic_int_inc (&self->unhandled_filter_count);
108 GST_DEBUG_OBJECT (self, "add filter on %s", tmp[i]);
110 g_array_append_val (self->filter, type);
117 set_params_from_structure (GstLeaksTracer * self, GstStructure * params)
119 const gchar *filters = gst_structure_get_string (params, "filters");
122 set_filters (self, filters);
126 set_params (GstLeaksTracer * self)
129 GstStructure *params_struct = NULL;
131 g_object_get (self, "params", ¶ms, NULL);
135 tmp = g_strdup_printf ("leaks,%s", params);
136 params_struct = gst_structure_from_string (tmp, NULL);
140 set_params_from_structure (self, params_struct);
142 set_filters (self, params);
147 set_print_stack_trace (self, params_struct);
150 gst_structure_free (params_struct);
154 should_handle_object_type (GstLeaksTracer * self, GType object_type)
159 /* No filtering, handle all types */
162 if (g_atomic_int_get (&self->unhandled_filter_count)) {
163 GST_OBJECT_LOCK (self);
164 if (self->unhandled_filter) {
167 q = g_type_qname (object_type);
168 if (g_hash_table_contains (self->unhandled_filter, GUINT_TO_POINTER (q))) {
169 g_array_append_val (self->filter, object_type);
170 g_hash_table_remove (self->unhandled_filter, GUINT_TO_POINTER (q));
172 if (g_atomic_int_dec_and_test (&self->unhandled_filter_count))
173 g_clear_pointer (&self->unhandled_filter, g_hash_table_unref);
175 GST_OBJECT_UNLOCK (self);
179 GST_OBJECT_UNLOCK (self);
182 len = self->filter->len;
183 for (i = 0; i < len; i++) {
184 GType type = g_array_index (self->filter, GType, i);
186 if (g_type_is_a (object_type, type))
194 /* The object may be destroyed when we log it using the checkpointing system so
195 * we have to save its type name */
199 const gchar *type_name;
203 object_log_new (gpointer obj)
205 ObjectLog *o = g_slice_new (ObjectLog);
209 if (G_IS_OBJECT (obj))
210 o->type_name = G_OBJECT_TYPE_NAME (obj);
212 o->type_name = g_type_name (GST_MINI_OBJECT_TYPE (obj));
218 object_log_free (ObjectLog * obj)
220 g_slice_free (ObjectLog, obj);
222 #endif /* G_OS_UNIX */
225 handle_object_destroyed (GstLeaksTracer * self, gpointer object)
227 GST_OBJECT_LOCK (self);
230 ("object %p destroyed while the leaks tracer was finalizing. Some threads are still running?",
235 g_hash_table_remove (self->objects, object);
238 g_hash_table_add (self->removed, object_log_new (object));
239 #endif /* G_OS_UNIX */
241 GST_OBJECT_UNLOCK (self);
245 object_weak_cb (gpointer data, GObject * object)
247 GstLeaksTracer *self = data;
249 handle_object_destroyed (self, object);
253 mini_object_weak_cb (gpointer data, GstMiniObject * object)
255 GstLeaksTracer *self = data;
257 handle_object_destroyed (self, object);
261 handle_object_created (GstLeaksTracer * self, gpointer object, GType type,
266 if (!should_handle_object_type (self, type))
270 g_object_weak_ref ((GObject *) object, object_weak_cb, self);
272 gst_mini_object_weak_ref (GST_MINI_OBJECT_CAST (object),
273 mini_object_weak_cb, self);
275 GST_OBJECT_LOCK (self);
276 if (self->log_stack_trace) {
277 trace = gst_debug_get_stack_trace (0);
280 g_hash_table_insert (self->objects, object, trace);
284 g_hash_table_add (self->added, object_log_new (object));
285 #endif /* G_OS_UNIX */
286 GST_OBJECT_UNLOCK (self);
290 mini_object_created_cb (GstTracer * tracer, GstClockTime ts,
291 GstMiniObject * object)
293 GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
295 handle_object_created (self, object, GST_MINI_OBJECT_TYPE (object), FALSE);
299 object_created_cb (GstTracer * tracer, GstClockTime ts, GstObject * object)
301 GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
302 GType object_type = G_OBJECT_TYPE (object);
304 /* Can't track tracers as they may be disposed after the leak tracer itself */
305 if (g_type_is_a (object_type, GST_TYPE_TRACER))
308 handle_object_created (self, object, object_type, TRUE);
312 gst_leaks_tracer_init (GstLeaksTracer * self)
314 self->objects = g_hash_table_new_full (NULL, NULL, NULL, g_free);
316 g_queue_push_tail (&instances, self);
320 gst_leaks_tracer_constructed (GObject * object)
322 GstLeaksTracer *self = GST_LEAKS_TRACER (object);
323 GstTracer *tracer = GST_TRACER (object);
327 gst_tracing_register_hook (tracer, "mini-object-created",
328 G_CALLBACK (mini_object_created_cb));
329 gst_tracing_register_hook (tracer, "object-created",
330 G_CALLBACK (object_created_cb));
332 /* We rely on weak pointers rather than (mini-)object-destroyed hooks so we
333 * are notified of objects being destroyed even during the shuting down of
334 * the tracing system. */
336 ((GObjectClass *) gst_leaks_tracer_parent_class)->constructed (object);
342 const gchar *type_name;
348 /* The content of the returned Leak struct is valid until the self->objects
349 * hash table has been modified. */
351 leak_new (gpointer obj, GType type, guint ref_count, const gchar * trace)
353 Leak *leak = g_slice_new (Leak);
356 leak->type_name = g_type_name (type);
357 leak->ref_count = ref_count;
358 leak->desc = gst_info_strdup_printf ("%" GST_PTR_FORMAT, obj);
365 leak_free (Leak * leak)
368 g_slice_free (Leak, leak);
372 sort_leaks (gconstpointer _a, gconstpointer _b)
374 const Leak *a = _a, *b = _b;
376 return g_strcmp0 (a->type_name, b->type_name);
380 create_leaks_list (GstLeaksTracer * self)
386 g_hash_table_iter_init (&iter, self->objects);
387 while (g_hash_table_iter_next (&iter, &obj, &trace)) {
391 if (GST_IS_OBJECT (obj)) {
392 if (GST_OBJECT_FLAG_IS_SET (obj, GST_OBJECT_FLAG_MAY_BE_LEAKED))
395 type = G_OBJECT_TYPE (obj);
396 ref_count = ((GObject *) obj)->ref_count;
398 if (GST_MINI_OBJECT_FLAG_IS_SET (obj, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED))
401 type = GST_MINI_OBJECT_TYPE (obj);
402 ref_count = ((GstMiniObject *) obj)->refcount;
405 l = g_list_prepend (l, leak_new (obj, type, ref_count, trace));
408 /* Sort leaks by type name so they are grouped together making the output
410 l = g_list_sort (l, sort_leaks);
415 /* Return TRUE if at least one leaked object has been logged */
417 log_leaked (GstLeaksTracer * self)
421 leaks = create_leaks_list (self);
425 for (l = leaks; l != NULL; l = g_list_next (l)) {
426 Leak *leak = l->data;
428 gst_tracer_record_log (tr_alive, leak->type_name, leak->obj, leak->desc,
429 leak->ref_count, leak->trace ? leak->trace : "");
432 g_list_free_full (leaks, (GDestroyNotify) leak_free);
438 gst_leaks_tracer_finalize (GObject * object)
440 GstLeaksTracer *self = GST_LEAKS_TRACER (object);
447 /* Tracers are destroyed as part of gst_deinit() so now is a good time to
448 * report all the objects which are still alive. */
449 leaks = log_leaked (self);
451 /* Remove weak references */
452 g_hash_table_iter_init (&iter, self->objects);
453 while (g_hash_table_iter_next (&iter, &obj, NULL)) {
454 if (GST_IS_OBJECT (obj))
455 g_object_weak_unref (obj, object_weak_cb, self);
457 gst_mini_object_weak_unref (GST_MINI_OBJECT_CAST (obj),
458 mini_object_weak_cb, self);
461 g_clear_pointer (&self->objects, g_hash_table_unref);
463 g_array_free (self->filter, TRUE);
464 g_clear_pointer (&self->added, g_hash_table_unref);
465 g_clear_pointer (&self->removed, g_hash_table_unref);
466 g_clear_pointer (&self->unhandled_filter, g_hash_table_unref);
468 g_queue_remove (&instances, self);
471 g_warning ("Leaks detected");
473 ((GObjectClass *) gst_leaks_tracer_parent_class)->finalize (object);
476 #define RECORD_FIELD_TYPE_NAME \
477 "type-name", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
478 "type", G_TYPE_GTYPE, G_TYPE_STRING, \
479 "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
481 #define RECORD_FIELD_ADDRESS \
482 "address", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
483 "type", G_TYPE_GTYPE, G_TYPE_POINTER, \
484 "related-to", GST_TYPE_TRACER_VALUE_SCOPE, \
485 GST_TRACER_VALUE_SCOPE_PROCESS, \
487 #define RECORD_FIELD_DESC \
488 "description", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
489 "type", G_TYPE_GTYPE, G_TYPE_STRING, \
490 "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
492 #define RECORD_FIELD_REF_COUNT \
493 "ref-count", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
494 "type", G_TYPE_GTYPE, G_TYPE_UINT, \
495 "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
497 #define RECORD_FIELD_TRACE \
498 "trace", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
499 "type", G_TYPE_GTYPE, G_TYPE_STRING, \
500 "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
505 sig_usr1_handler_foreach (gpointer data, gpointer user_data)
507 GstLeaksTracer *tracer = data;
509 GST_OBJECT_LOCK (tracer);
510 GST_TRACE_OBJECT (tracer, "start listing currently alive objects");
512 GST_TRACE_OBJECT (tracer, "done listing currently alive objects");
513 GST_OBJECT_UNLOCK (tracer);
517 sig_usr1_handler (G_GNUC_UNUSED int signal)
519 g_queue_foreach (&instances, sig_usr1_handler_foreach, NULL);
523 log_checkpoint (GHashTable * hash, GstTracerRecord * record)
528 g_hash_table_iter_init (&iter, hash);
529 while (g_hash_table_iter_next (&iter, &o, NULL)) {
532 gst_tracer_record_log (record, obj->type_name, obj->object);
537 do_checkpoint (GstLeaksTracer * self)
539 GST_TRACE_OBJECT (self, "listing objects created since last checkpoint");
540 log_checkpoint (self->added, tr_added);
541 GST_TRACE_OBJECT (self, "listing objects removed since last checkpoint");
542 log_checkpoint (self->removed, tr_removed);
544 g_hash_table_remove_all (self->added);
545 g_hash_table_remove_all (self->removed);
549 sig_usr2_handler_foreach (gpointer data, gpointer user_data)
551 GstLeaksTracer *tracer = data;
553 GST_OBJECT_LOCK (tracer);
555 if (!tracer->added) {
556 GST_TRACE_OBJECT (tracer, "First checkpoint, start tracking objects");
558 tracer->added = g_hash_table_new_full (NULL, NULL,
559 (GDestroyNotify) object_log_free, NULL);
560 tracer->removed = g_hash_table_new_full (NULL, NULL,
561 (GDestroyNotify) object_log_free, NULL);
563 do_checkpoint (tracer);
566 GST_OBJECT_UNLOCK (tracer);
570 sig_usr2_handler (G_GNUC_UNUSED int signal)
572 g_queue_foreach (&instances, sig_usr2_handler_foreach, NULL);
578 tr_added = gst_tracer_record_new ("object-added.class",
579 RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
580 GST_OBJECT_FLAG_SET (tr_added, GST_OBJECT_FLAG_MAY_BE_LEAKED);
582 tr_removed = gst_tracer_record_new ("object-removed.class",
583 RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
584 GST_OBJECT_FLAG_SET (tr_removed, GST_OBJECT_FLAG_MAY_BE_LEAKED);
586 signal (SIGUSR1, sig_usr1_handler);
587 signal (SIGUSR2, sig_usr2_handler);
589 #endif /* G_OS_UNIX */
592 gst_leaks_tracer_class_init (GstLeaksTracerClass * klass)
594 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
596 gobject_class->constructed = gst_leaks_tracer_constructed;
597 gobject_class->finalize = gst_leaks_tracer_finalize;
599 tr_alive = gst_tracer_record_new ("object-alive.class",
600 RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, RECORD_FIELD_DESC,
601 RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL);
602 GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED);
604 if (g_getenv ("GST_LEAKS_TRACER_SIG")) {
608 g_warning ("System doesn't support POSIX signals");
609 #endif /* G_OS_UNIX */