summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Desmottes <guillaume.desmottes@collabora.co.uk>2016-05-10 09:29:12 +0200
committerTim-Philipp Müller <tim@centricular.com>2016-06-03 00:36:46 +0100
commit0bb7fa985528bc94feddd9ebbece482078797c6e (patch)
tree974a218fe8690225afd196ebe425f64f5b9961f7
parent4a41468ce7aa8abbf354c19c7d1bf5e8d1327048 (diff)
tracers: add leaks tracer
https://bugzilla.gnome.org/show_bug.cgi?id=765052
-rw-r--r--docs/design/part-tracing.txt10
-rw-r--r--plugins/tracers/Makefile.am4
-rw-r--r--plugins/tracers/gstleaks.c346
-rw-r--r--plugins/tracers/gstleaks.h68
-rw-r--r--plugins/tracers/gsttracers.c3
5 files changed, 430 insertions, 1 deletions
diff --git a/docs/design/part-tracing.txt b/docs/design/part-tracing.txt
index 242b17e9f3..5f2c1c16e2 100644
--- a/docs/design/part-tracing.txt
+++ b/docs/design/part-tracing.txt
@@ -260,6 +260,10 @@ memory (not yet implemented)
- use an atexit handler to dump leaked instance
https://bugzilla.gnome.org/show_bug.cgi?id=756760#c6
+leaks
+-----
+- track creation/destruction of GstObject and GstMiniObject
+- log those which are still alive when app is exiting and raise an error if any
User interfaces
===============
@@ -349,6 +353,12 @@ eog trace.log.*.png
GST_DEBUG="GST_TRACER:7" GST_TRACERS=latency gst-launch-1.0 audiotestsrc num-buffers=10 ! audioconvert ! volume volume=0.7 ! autoaudiosink
- print processing latencies
+GST_TRACERS="leaks" gst-launch-1.0 videotestsrc num-buffers=10 ! fakesink
+- Raise a warning if a leak is detected
+
+GST_DEBUG="GST_TRACER:7" GST_TRACERS="leaks(GstEvent,GstMessage)" gst-launch-1.0 videotestsrc num-buffers=10 ! fakesink
+- check if any GstEvent or GstMessage is leaked and raise a warning
+
Performance
===========
run ./tests/benchmarks/tracing.sh <tracer(s)> <media>
diff --git a/plugins/tracers/Makefile.am b/plugins/tracers/Makefile.am
index f6efee64de..d52bb28ba7 100644
--- a/plugins/tracers/Makefile.am
+++ b/plugins/tracers/Makefile.am
@@ -18,10 +18,11 @@ endif
libgstcoretracers_la_DEPENDENCIES = $(top_builddir)/gst/libgstreamer-@GST_API_VERSION@.la
libgstcoretracers_la_SOURCES = \
gstlatency.c \
+ gstleaks.c \
$(LOG_SOURCES) \
$(RUSAGE_SOURCES) \
gststats.c \
- gsttracers.c
+ gsttracers.c
libgstcoretracers_la_CFLAGS = $(GST_OBJ_CFLAGS) \
-DGST_USE_UNSTABLE_API
@@ -33,6 +34,7 @@ libgstcoretracers_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
noinst_HEADERS = \
gstlatency.h \
+ gstleaks.h \
gstlog.h \
gstrusage.h \
gststats.h
diff --git a/plugins/tracers/gstleaks.c b/plugins/tracers/gstleaks.c
new file mode 100644
index 0000000000..7ab199e6ff
--- /dev/null
+++ b/plugins/tracers/gstleaks.c
@@ -0,0 +1,346 @@
+/* GStreamer
+ * Copyright (C) 2016 Collabora Ltd. <guillaume.desmottes@collabora.co.uk>
+ *
+ * gstleaks.c: tracing module detecting object leaks
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+/**
+ * SECTION:gstleaks
+ * @short_description: detect GstObject and GstMiniObject leaks
+ *
+ * A tracing module tracking the lifetime of objects by logging those still
+ * alive when program is exiting and raising a warning.
+ * The type of objects tracked can be filtered using the parameters of the
+ * tracer, for example: GST_TRACERS="leaks(GstEvent,GstMessage)"
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstleaks.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_leaks_debug);
+#define GST_CAT_DEFAULT gst_leaks_debug
+
+#define _do_init \
+ GST_DEBUG_CATEGORY_INIT (gst_leaks_debug, "leaks", 0, "leaks tracer");
+#define gst_leaks_tracer_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstLeaksTracer, gst_leaks_tracer,
+ GST_TYPE_TRACER, _do_init);
+
+static GstTracerRecord *tr_alive;
+
+static void
+set_filtering (GstLeaksTracer * self)
+{
+ gchar *params;
+ GStrv tmp;
+ guint i;
+
+ g_object_get (self, "params", &params, NULL);
+ if (!params)
+ return;
+
+ tmp = g_strsplit (params, ",", -1);
+
+ self->filter = g_array_sized_new (FALSE, FALSE, sizeof (GType),
+ g_strv_length (tmp));
+ for (i = 0; tmp[i]; i++) {
+ GType type;
+
+ type = g_type_from_name (tmp[i]);
+ if (type == 0) {
+ GST_WARNING_OBJECT (self, "unknown type %s", tmp[i]);
+ continue;
+ }
+
+ GST_DEBUG_OBJECT (self, "add filter on %s", tmp[i]);
+
+ g_array_append_val (self->filter, type);
+ }
+
+ g_strfreev (tmp);
+ g_free (params);
+}
+
+static gboolean
+should_handle_object (GstLeaksTracer * self, gpointer object, GType object_type)
+{
+ guint i;
+
+ if (GST_IS_TRACER (object))
+ /* We can't track tracers as they may be disposed after the leak tracer
+ * itself */
+ return FALSE;
+
+ if (!self->filter)
+ /* No filtering, handle all types */
+ return TRUE;
+
+ for (i = 0; i < self->filter->len; i++) {
+ GType type = g_array_index (self->filter, GType, i);
+
+ if (g_type_is_a (object_type, type))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+object_weak_cb (gpointer data, GObject * object)
+{
+ GstLeaksTracer *self = data;
+
+ GST_OBJECT_LOCK (self);
+ g_hash_table_remove (self->objects, object);
+ GST_OBJECT_UNLOCK (self);
+}
+
+static void
+mini_object_weak_cb (gpointer data, GstMiniObject * object)
+{
+ GstLeaksTracer *self = data;
+
+ GST_OBJECT_LOCK (self);
+ g_hash_table_remove (self->objects, object);
+ GST_OBJECT_UNLOCK (self);
+}
+
+static void
+handle_object_created (GstLeaksTracer * self, gpointer object, GType type,
+ gboolean gobject)
+{
+ if (!should_handle_object (self, object, type))
+ return;
+
+ if (gobject)
+ g_object_weak_ref ((GObject *) object, object_weak_cb, self);
+ else
+ gst_mini_object_weak_ref (GST_MINI_OBJECT_CAST (object),
+ mini_object_weak_cb, self);
+
+ GST_OBJECT_LOCK (self);
+ g_hash_table_add (self->objects, object);
+ GST_OBJECT_UNLOCK (self);
+}
+
+static void
+mini_object_created_cb (GstTracer * tracer, GstClockTime ts,
+ GstMiniObject * object)
+{
+ GstLeaksTracer *self = GST_LEAKS_TRACER (tracer);
+
+ handle_object_created (self, object, GST_MINI_OBJECT_TYPE (object), FALSE);
+}
+
+static void
+object_created_cb (GstTracer * tracer, GstClockTime ts, GstObject * object)
+{
+ GstLeaksTracer *self = GST_LEAKS_TRACER (tracer);
+
+ handle_object_created (self, object, G_OBJECT_TYPE (object), TRUE);
+}
+
+static void
+gst_leaks_tracer_init (GstLeaksTracer * self)
+{
+ self->objects = g_hash_table_new (NULL, NULL);
+}
+
+static void
+gst_leaks_tracer_constructed (GObject * object)
+{
+ GstLeaksTracer *self = GST_LEAKS_TRACER (object);
+ GstTracer *tracer = GST_TRACER (object);
+
+ set_filtering (self);
+
+ gst_tracing_register_hook (tracer, "mini-object-created",
+ G_CALLBACK (mini_object_created_cb));
+ gst_tracing_register_hook (tracer, "object-created",
+ G_CALLBACK (object_created_cb));
+
+ /* We rely on weak pointers rather than (mini-)object-destroyed hooks so we
+ * are notified of objects being destroyed even during the shuting down of
+ * the tracing system. */
+
+ ((GObjectClass *) gst_leaks_tracer_parent_class)->constructed (object);
+}
+
+typedef struct
+{
+ gpointer obj;
+ const gchar *type_name;
+ guint ref_count;
+ gchar *desc;
+} Leak;
+
+static Leak *
+leak_new (gpointer obj, GType type, guint ref_count)
+{
+ Leak *leak = g_slice_new (Leak);
+
+ leak->obj = obj;
+ leak->type_name = g_type_name (type);
+ leak->ref_count = ref_count;
+ leak->desc = gst_info_strdup_printf ("%" GST_PTR_FORMAT, obj);
+
+ return leak;
+}
+
+static void
+leak_free (Leak * leak)
+{
+ g_free (leak->desc);
+ g_slice_free (Leak, leak);
+}
+
+static gint
+sort_leaks (gconstpointer _a, gconstpointer _b)
+{
+ const Leak *a = _a, *b = _b;
+
+ return g_strcmp0 (a->type_name, b->type_name);
+}
+
+static GList *
+create_leaks_list (GstLeaksTracer * self)
+{
+ GList *l = NULL;
+ GHashTableIter iter;
+ gpointer obj;
+
+ g_hash_table_iter_init (&iter, self->objects);
+ while (g_hash_table_iter_next (&iter, &obj, NULL)) {
+ GType type;
+ guint ref_count;
+
+ if (GST_IS_OBJECT (obj)) {
+ if (GST_OBJECT_FLAG_IS_SET (obj, GST_OBJECT_FLAG_MAY_BE_LEAKED))
+ continue;
+
+ type = G_OBJECT_TYPE (obj);
+ ref_count = ((GObject *) obj)->ref_count;
+ } else {
+ if (GST_MINI_OBJECT_FLAG_IS_SET (obj, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED))
+ continue;
+
+ type = GST_MINI_OBJECT_TYPE (obj);
+ ref_count = ((GstMiniObject *) obj)->refcount;
+ }
+
+ l = g_list_prepend (l, leak_new (obj, type, ref_count));
+ }
+
+ /* Sort leaks by type name so they are grouped together making the output
+ * easier to read */
+ l = g_list_sort (l, sort_leaks);
+
+ return l;
+}
+
+/* Return TRUE if at least one leaked object has been logged */
+static gboolean
+log_leaked (GstLeaksTracer * self)
+{
+ GList *leaks, *l;
+
+ leaks = create_leaks_list (self);
+ if (!leaks)
+ return FALSE;
+
+ for (l = leaks; l != NULL; l = g_list_next (l)) {
+ Leak *leak = l->data;
+
+ gst_tracer_record_log (tr_alive, leak->type_name, leak->obj, leak->desc,
+ leak->ref_count);
+ }
+
+ g_list_free_full (leaks, (GDestroyNotify) leak_free);
+
+ return TRUE;
+}
+
+static void
+gst_leaks_tracer_finalize (GObject * object)
+{
+ GstLeaksTracer *self = GST_LEAKS_TRACER (object);
+ gboolean leaks;
+ GHashTableIter iter;
+ gpointer obj;
+
+ /* Tracers are destroyed as part of gst_deinit() so now is a good time to
+ * report all the objects which are still alive. */
+ leaks = log_leaked (self);
+
+ /* Remove weak references */
+ g_hash_table_iter_init (&iter, self->objects);
+ while (g_hash_table_iter_next (&iter, &obj, NULL)) {
+ if (GST_IS_OBJECT (obj))
+ g_object_weak_unref (obj, object_weak_cb, self);
+ else
+ gst_mini_object_weak_unref (GST_MINI_OBJECT_CAST (obj),
+ mini_object_weak_cb, self);
+ }
+
+ g_clear_pointer (&self->objects, g_hash_table_unref);
+ if (self->filter)
+ g_array_free (self->filter, TRUE);
+
+ if (leaks)
+ g_warning ("Leaks detected");
+
+ ((GObjectClass *) gst_leaks_tracer_parent_class)->finalize (object);
+}
+
+#define RECORD_FIELD_TYPE_NAME \
+ "type-name", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
+ "type", G_TYPE_GTYPE, G_TYPE_STRING, \
+ "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
+ NULL)
+#define RECORD_FIELD_ADDRESS \
+ "address", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
+ "type", G_TYPE_GTYPE, G_TYPE_POINTER, \
+ "related-to", GST_TYPE_TRACER_VALUE_SCOPE, \
+ GST_TRACER_VALUE_SCOPE_PROCESS, \
+ NULL)
+#define RECORD_FIELD_DESC \
+ "description", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
+ "type", G_TYPE_GTYPE, G_TYPE_STRING, \
+ "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
+ NULL)
+#define RECORD_FIELD_REF_COUNT \
+ "ref-count", GST_TYPE_STRUCTURE, gst_structure_new ("value", \
+ "type", G_TYPE_GTYPE, G_TYPE_UINT, \
+ "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \
+ NULL)
+
+static void
+gst_leaks_tracer_class_init (GstLeaksTracerClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->constructed = gst_leaks_tracer_constructed;
+ gobject_class->finalize = gst_leaks_tracer_finalize;
+
+ tr_alive = gst_tracer_record_new ("object-alive.class",
+ RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, RECORD_FIELD_DESC,
+ RECORD_FIELD_REF_COUNT, NULL);
+ GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED);
+}
diff --git a/plugins/tracers/gstleaks.h b/plugins/tracers/gstleaks.h
new file mode 100644
index 0000000000..9f2a2b9c7d
--- /dev/null
+++ b/plugins/tracers/gstleaks.h
@@ -0,0 +1,68 @@
+/* GStreamer
+ * Copyright (C) 2016 Collabora Ltd. <guillaume.desmottes@collabora.co.uk>
+ *
+ * gstleaks.c: tracing module detecting object leaks
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_LEAKS_TRACER_H__
+#define __GST_LEAKS_TRACER_H__
+
+#include <gst/gst.h>
+#include <gst/gsttracer.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_LEAKS_TRACER \
+ (gst_leaks_tracer_get_type())
+#define GST_LEAKS_TRACER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_LEAKS_TRACER,GstLeaksTracer))
+#define GST_LEAKS_TRACER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_LEAKS_TRACER,GstLeaksTracerClass))
+#define GST_IS_LEAKS_TRACER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_LEAKS_TRACER))
+#define GST_IS_LEAKS_TRACER_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_LEAKS_TRACER))
+#define GST_LEAKS_TRACER_CAST(obj) ((GstLeaksTracer *)(obj))
+
+typedef struct _GstLeaksTracer GstLeaksTracer;
+typedef struct _GstLeaksTracerClass GstLeaksTracerClass;
+
+/**
+ * GstLeaksTracer:
+ *
+ * Opaque #GstLeaksTracer data structure
+ */
+struct _GstLeaksTracer {
+ GstTracer parent;
+
+ /*< private >*/
+ /* Set of objects currently alive. Protected by object lock */
+ GHashTable *objects;
+ /* array of GType used as filtering */
+ GArray *filter;
+};
+
+struct _GstLeaksTracerClass {
+ GstTracerClass parent_class;
+};
+
+G_GNUC_INTERNAL GType gst_leaks_tracer_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_LEAKS_TRACER_H__ */
diff --git a/plugins/tracers/gsttracers.c b/plugins/tracers/gsttracers.c
index 010c185aaa..b03e25aeec 100644
--- a/plugins/tracers/gsttracers.c
+++ b/plugins/tracers/gsttracers.c
@@ -28,6 +28,7 @@
#include "gstlog.h"
#include "gstrusage.h"
#include "gststats.h"
+#include "gstleaks.h"
static gboolean
plugin_init (GstPlugin * plugin)
@@ -44,6 +45,8 @@ plugin_init (GstPlugin * plugin)
#endif
if (!gst_tracer_register (plugin, "stats", gst_stats_tracer_get_type ()))
return FALSE;
+ if (!gst_tracer_register (plugin, "leaks", gst_leaks_tracer_get_type ()))
+ return FALSE;
return TRUE;
}