info: Add a 'flags' parametter to gst_debug_get_stack_trace
[platform/upstream/gstreamer.git] / plugins / tracers / gstleaks.c
1 /* GStreamer
2  * Copyright (C) 2016 Collabora Ltd. <guillaume.desmottes@collabora.co.uk>
3  *
4  * gstleaks.c: tracing module detecting object leaks
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 /**
22  * SECTION:gstleaks
23  * @short_description: detect GstObject and GstMiniObject leaks
24  *
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(GstEvent,GstMessage)"
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #  include "config.h"
33 #endif
34
35 #include "gstleaks.h"
36
37 #ifdef G_OS_UNIX
38 #include <signal.h>
39 #endif /* G_OS_UNIX */
40
41 GST_DEBUG_CATEGORY_STATIC (gst_leaks_debug);
42 #define GST_CAT_DEFAULT gst_leaks_debug
43
44 #define _do_init \
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);
49
50 static GstTracerRecord *tr_alive;
51 #ifdef G_OS_UNIX
52 static GstTracerRecord *tr_added = NULL;
53 static GstTracerRecord *tr_removed = NULL;
54 #endif /* G_OS_UNIX */
55 static GQueue instances = G_QUEUE_INIT;
56
57 static void
58 set_filtering (GstLeaksTracer * self)
59 {
60   gchar *params;
61   GStrv tmp;
62   guint i;
63
64   g_object_get (self, "params", &params, NULL);
65   if (!params)
66     return;
67
68   tmp = g_strsplit (params, ",", -1);
69
70   self->filter = g_array_sized_new (FALSE, FALSE, sizeof (GType),
71       g_strv_length (tmp));
72   for (i = 0; tmp[i]; i++) {
73     GType type;
74
75     type = g_type_from_name (tmp[i]);
76     if (type == 0) {
77       /* The type may not yet be known by the type system, typically because
78        * the plugin implementing it as not yet be loaded. Save it for now as
79        * it will have another chance to be added to the filter later in
80        * should_handle_object_type() when/if the object type is actually
81        * used. */
82       if (!self->unhandled_filter)
83         self->unhandled_filter = g_hash_table_new (NULL, NULL);
84
85       g_hash_table_add (self->unhandled_filter,
86           GUINT_TO_POINTER (g_quark_from_string (tmp[i])));
87       g_atomic_int_inc (&self->unhandled_filter_count);
88       continue;
89     }
90
91     GST_DEBUG_OBJECT (self, "add filter on %s", tmp[i]);
92
93     g_array_append_val (self->filter, type);
94   }
95
96   g_strfreev (tmp);
97   g_free (params);
98 }
99
100 static gboolean
101 should_handle_object_type (GstLeaksTracer * self, GType object_type)
102 {
103   guint i, len;
104
105   if (!self->filter)
106     /* No filtering, handle all types */
107     return TRUE;
108
109   if (g_atomic_int_get (&self->unhandled_filter_count)) {
110     GST_OBJECT_LOCK (self);
111     if (self->unhandled_filter) {
112       GQuark q;
113
114       q = g_type_qname (object_type);
115       if (g_hash_table_contains (self->unhandled_filter, GUINT_TO_POINTER (q))) {
116         g_array_append_val (self->filter, object_type);
117         g_hash_table_remove (self->unhandled_filter, GUINT_TO_POINTER (q));
118
119         if (g_atomic_int_dec_and_test (&self->unhandled_filter_count))
120           g_clear_pointer (&self->unhandled_filter, g_hash_table_unref);
121
122         GST_OBJECT_UNLOCK (self);
123         return TRUE;
124       }
125     }
126     GST_OBJECT_UNLOCK (self);
127   }
128
129   len = self->filter->len;
130   for (i = 0; i < len; i++) {
131     GType type = g_array_index (self->filter, GType, i);
132
133     if (g_type_is_a (object_type, type))
134       return TRUE;
135   }
136
137   return FALSE;
138 }
139
140 #ifdef G_OS_UNIX
141 /* The object may be destroyed when we log it using the checkpointing system so
142  * we have to save its type name */
143 typedef struct
144 {
145   gpointer object;
146   const gchar *type_name;
147 } ObjectLog;
148
149 static ObjectLog *
150 object_log_new (gpointer obj)
151 {
152   ObjectLog *o = g_slice_new (ObjectLog);
153
154   o->object = obj;
155
156   if (G_IS_OBJECT (obj))
157     o->type_name = G_OBJECT_TYPE_NAME (obj);
158   else
159     o->type_name = g_type_name (GST_MINI_OBJECT_TYPE (obj));
160
161   return o;
162 }
163
164 static void
165 object_log_free (ObjectLog * obj)
166 {
167   g_slice_free (ObjectLog, obj);
168 }
169 #endif /* G_OS_UNIX */
170
171 static void
172 handle_object_destroyed (GstLeaksTracer * self, gpointer object)
173 {
174   GST_OBJECT_LOCK (self);
175   if (self->done) {
176     g_warning
177         ("object %p destroyed while the leaks tracer was finalizing. Some threads are still running?",
178         object);
179     goto out;
180   }
181
182   g_hash_table_remove (self->objects, object);
183 #ifdef G_OS_UNIX
184   if (self->removed)
185     g_hash_table_add (self->removed, object_log_new (object));
186 #endif /* G_OS_UNIX */
187 out:
188   GST_OBJECT_UNLOCK (self);
189 }
190
191 static void
192 object_weak_cb (gpointer data, GObject * object)
193 {
194   GstLeaksTracer *self = data;
195
196   handle_object_destroyed (self, object);
197 }
198
199 static void
200 mini_object_weak_cb (gpointer data, GstMiniObject * object)
201 {
202   GstLeaksTracer *self = data;
203
204   handle_object_destroyed (self, object);
205 }
206
207 static void
208 handle_object_created (GstLeaksTracer * self, gpointer object, GType type,
209     gboolean gobject)
210 {
211   gchar *trace = NULL;
212
213   if (!should_handle_object_type (self, type))
214     return;
215
216   if (gobject)
217     g_object_weak_ref ((GObject *) object, object_weak_cb, self);
218   else
219     gst_mini_object_weak_ref (GST_MINI_OBJECT_CAST (object),
220         mini_object_weak_cb, self);
221
222   GST_OBJECT_LOCK (self);
223   if (self->log_stack_trace) {
224     trace = gst_debug_get_stack_trace (GST_STACK_TRACE_SHOW_FULL);
225   }
226
227   g_hash_table_insert (self->objects, object, trace);
228
229 #ifdef G_OS_UNIX
230   if (self->added)
231     g_hash_table_add (self->added, object_log_new (object));
232 #endif /* G_OS_UNIX */
233   GST_OBJECT_UNLOCK (self);
234 }
235
236 static void
237 mini_object_created_cb (GstTracer * tracer, GstClockTime ts,
238     GstMiniObject * object)
239 {
240   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
241
242   handle_object_created (self, object, GST_MINI_OBJECT_TYPE (object), FALSE);
243 }
244
245 static void
246 object_created_cb (GstTracer * tracer, GstClockTime ts, GstObject * object)
247 {
248   GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer);
249   GType object_type = G_OBJECT_TYPE (object);
250
251   /* Can't track tracers as they may be disposed after the leak tracer itself */
252   if (g_type_is_a (object_type, GST_TYPE_TRACER))
253     return;
254
255   handle_object_created (self, object, object_type, TRUE);
256 }
257
258 static void
259 gst_leaks_tracer_init (GstLeaksTracer * self)
260 {
261   self->objects = g_hash_table_new_full (NULL, NULL, NULL, g_free);
262
263   if (g_getenv ("GST_LEAKS_TRACER_STACK_TRACE")) {
264     gchar *trace;
265
266     /* Test if we can retrieve backtrace */
267     trace = gst_debug_get_stack_trace (GST_STACK_TRACE_SHOW_FULL);
268     if (trace) {
269       self->log_stack_trace = TRUE;
270       g_free (trace);
271     } else {
272       g_warning ("Can't retrieve backtrace on this system");
273     }
274   }
275
276   g_queue_push_tail (&instances, self);
277 }
278
279 static void
280 gst_leaks_tracer_constructed (GObject * object)
281 {
282   GstLeaksTracer *self = GST_LEAKS_TRACER (object);
283   GstTracer *tracer = GST_TRACER (object);
284
285   set_filtering (self);
286
287   gst_tracing_register_hook (tracer, "mini-object-created",
288       G_CALLBACK (mini_object_created_cb));
289   gst_tracing_register_hook (tracer, "object-created",
290       G_CALLBACK (object_created_cb));
291
292   /* We rely on weak pointers rather than (mini-)object-destroyed hooks so we
293    * are notified of objects being destroyed even during the shuting down of
294    * the tracing system. */
295
296   ((GObjectClass *) gst_leaks_tracer_parent_class)->constructed (object);
297 }
298
299 typedef struct
300 {
301   gpointer obj;
302   const gchar *type_name;
303   guint ref_count;
304   gchar *desc;
305   const gchar *trace;
306 } Leak;
307
308 /* The content of the returned Leak struct is valid until the self->objects
309  * hash table has been modified. */
310 static Leak *
311 leak_new (gpointer obj, GType type, guint ref_count, const gchar * trace)
312 {
313   Leak *leak = g_slice_new (Leak);
314
315   leak->obj = obj;
316   leak->type_name = g_type_name (type);
317   leak->ref_count = ref_count;
318   leak->desc = gst_info_strdup_printf ("%" GST_PTR_FORMAT, obj);
319   leak->trace = trace;
320
321   return leak;
322 }
323
324 static void
325 leak_free (Leak * leak)
326 {
327   g_free (leak->desc);
328   g_slice_free (Leak, leak);
329 }
330
331 static gint
332 sort_leaks (gconstpointer _a, gconstpointer _b)
333 {
334   const Leak *a = _a, *b = _b;
335
336   return g_strcmp0 (a->type_name, b->type_name);
337 }
338
339 static GList *
340 create_leaks_list (GstLeaksTracer * self)
341 {
342   GList *l = NULL;
343   GHashTableIter iter;
344   gpointer obj, trace;
345
346   g_hash_table_iter_init (&iter, self->objects);
347   while (g_hash_table_iter_next (&iter, &obj, &trace)) {
348     GType type;
349     guint ref_count;
350
351     if (GST_IS_OBJECT (obj)) {
352       if (GST_OBJECT_FLAG_IS_SET (obj, GST_OBJECT_FLAG_MAY_BE_LEAKED))
353         continue;
354
355       type = G_OBJECT_TYPE (obj);
356       ref_count = ((GObject *) obj)->ref_count;
357     } else {
358       if (GST_MINI_OBJECT_FLAG_IS_SET (obj, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED))
359         continue;
360
361       type = GST_MINI_OBJECT_TYPE (obj);
362       ref_count = ((GstMiniObject *) obj)->refcount;
363     }
364
365     l = g_list_prepend (l, leak_new (obj, type, ref_count, trace));
366   }
367
368   /* Sort leaks by type name so they are grouped together making the output
369    * easier to read */
370   l = g_list_sort (l, sort_leaks);
371
372   return l;
373 }
374
375 /* Return TRUE if at least one leaked object has been logged */
376 static gboolean
377 log_leaked (GstLeaksTracer * self)
378 {
379   GList *leaks, *l;
380
381   leaks = create_leaks_list (self);
382   if (!leaks)
383     return FALSE;
384
385   for (l = leaks; l != NULL; l = g_list_next (l)) {
386     Leak *leak = l->data;
387
388     gst_tracer_record_log (tr_alive, leak->type_name, leak->obj, leak->desc,
389         leak->ref_count, leak->trace ? leak->trace : "");
390   }
391
392   g_list_free_full (leaks, (GDestroyNotify) leak_free);
393
394   return TRUE;
395 }
396
397 static void
398 gst_leaks_tracer_finalize (GObject * object)
399 {
400   GstLeaksTracer *self = GST_LEAKS_TRACER (object);
401   gboolean leaks;
402   GHashTableIter iter;
403   gpointer obj;
404
405   self->done = TRUE;
406
407   /* Tracers are destroyed as part of gst_deinit() so now is a good time to
408    * report all the objects which are still alive. */
409   leaks = log_leaked (self);
410
411   /* Remove weak references */
412   g_hash_table_iter_init (&iter, self->objects);
413   while (g_hash_table_iter_next (&iter, &obj, NULL)) {
414     if (GST_IS_OBJECT (obj))
415       g_object_weak_unref (obj, object_weak_cb, self);
416     else
417       gst_mini_object_weak_unref (GST_MINI_OBJECT_CAST (obj),
418           mini_object_weak_cb, self);
419   }
420
421   g_clear_pointer (&self->objects, g_hash_table_unref);
422   if (self->filter)
423     g_array_free (self->filter, TRUE);
424   g_clear_pointer (&self->added, g_hash_table_unref);
425   g_clear_pointer (&self->removed, g_hash_table_unref);
426   g_clear_pointer (&self->unhandled_filter, g_hash_table_unref);
427
428   g_queue_remove (&instances, self);
429
430   if (leaks)
431     g_warning ("Leaks detected");
432
433   ((GObjectClass *) gst_leaks_tracer_parent_class)->finalize (object);
434 }
435
436 #define RECORD_FIELD_TYPE_NAME \
437     "type-name", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
438         "type", G_TYPE_GTYPE, G_TYPE_STRING, \
439         "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
440         NULL)
441 #define RECORD_FIELD_ADDRESS \
442     "address", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
443         "type", G_TYPE_GTYPE, G_TYPE_POINTER, \
444         "related-to", GST_TYPE_TRACER_VALUE_SCOPE, \
445         GST_TRACER_VALUE_SCOPE_PROCESS, \
446         NULL)
447 #define RECORD_FIELD_DESC \
448     "description", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
449         "type", G_TYPE_GTYPE, G_TYPE_STRING, \
450         "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
451         NULL)
452 #define RECORD_FIELD_REF_COUNT \
453     "ref-count", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
454         "type", G_TYPE_GTYPE, G_TYPE_UINT, \
455         "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
456         NULL)
457 #define RECORD_FIELD_TRACE \
458     "trace", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
459         "type", G_TYPE_GTYPE, G_TYPE_STRING, \
460         "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
461         NULL)
462
463 #ifdef G_OS_UNIX
464 static void
465 sig_usr1_handler_foreach (gpointer data, gpointer user_data)
466 {
467   GstLeaksTracer *tracer = data;
468
469   GST_OBJECT_LOCK (tracer);
470   GST_TRACE_OBJECT (tracer, "start listing currently alive objects");
471   log_leaked (tracer);
472   GST_TRACE_OBJECT (tracer, "done listing currently alive objects");
473   GST_OBJECT_UNLOCK (tracer);
474 }
475
476 static void
477 sig_usr1_handler (G_GNUC_UNUSED int signal)
478 {
479   g_queue_foreach (&instances, sig_usr1_handler_foreach, NULL);
480 }
481
482 static void
483 log_checkpoint (GHashTable * hash, GstTracerRecord * record)
484 {
485   GHashTableIter iter;
486   gpointer o;
487
488   g_hash_table_iter_init (&iter, hash);
489   while (g_hash_table_iter_next (&iter, &o, NULL)) {
490     ObjectLog *obj = o;
491
492     gst_tracer_record_log (record, obj->type_name, obj->object);
493   }
494 }
495
496 static void
497 do_checkpoint (GstLeaksTracer * self)
498 {
499   GST_TRACE_OBJECT (self, "listing objects created since last checkpoint");
500   log_checkpoint (self->added, tr_added);
501   GST_TRACE_OBJECT (self, "listing objects removed since last checkpoint");
502   log_checkpoint (self->removed, tr_removed);
503
504   g_hash_table_remove_all (self->added);
505   g_hash_table_remove_all (self->removed);
506 }
507
508 static void
509 sig_usr2_handler_foreach (gpointer data, gpointer user_data)
510 {
511   GstLeaksTracer *tracer = data;
512
513   GST_OBJECT_LOCK (tracer);
514
515   if (!tracer->added) {
516     GST_TRACE_OBJECT (tracer, "First checkpoint, start tracking objects");
517
518     tracer->added = g_hash_table_new_full (NULL, NULL,
519         (GDestroyNotify) object_log_free, NULL);
520     tracer->removed = g_hash_table_new_full (NULL, NULL,
521         (GDestroyNotify) object_log_free, NULL);
522   } else {
523     do_checkpoint (tracer);
524   }
525
526   GST_OBJECT_UNLOCK (tracer);
527 }
528
529 static void
530 sig_usr2_handler (G_GNUC_UNUSED int signal)
531 {
532   g_queue_foreach (&instances, sig_usr2_handler_foreach, NULL);
533 }
534
535 static void
536 setup_signals (void)
537 {
538   tr_added = gst_tracer_record_new ("object-added.class",
539       RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
540   GST_OBJECT_FLAG_SET (tr_added, GST_OBJECT_FLAG_MAY_BE_LEAKED);
541
542   tr_removed = gst_tracer_record_new ("object-removed.class",
543       RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, NULL);
544   GST_OBJECT_FLAG_SET (tr_removed, GST_OBJECT_FLAG_MAY_BE_LEAKED);
545
546   signal (SIGUSR1, sig_usr1_handler);
547   signal (SIGUSR2, sig_usr2_handler);
548 }
549 #endif /* G_OS_UNIX */
550
551 static void
552 gst_leaks_tracer_class_init (GstLeaksTracerClass * klass)
553 {
554   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
555
556   gobject_class->constructed = gst_leaks_tracer_constructed;
557   gobject_class->finalize = gst_leaks_tracer_finalize;
558
559   tr_alive = gst_tracer_record_new ("object-alive.class",
560       RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, RECORD_FIELD_DESC,
561       RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL);
562   GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED);
563
564   if (g_getenv ("GST_LEAKS_TRACER_SIG")) {
565 #ifdef G_OS_UNIX
566     setup_signals ();
567 #else
568     g_warning ("System doesn't support POSIX signals");
569 #endif /* G_OS_UNIX */
570   }
571 }