diff --git a/gst/gstminiobject.c b/gst/gstminiobject.c index db571ab5f7..4662f2655d 100644 --- a/gst/gstminiobject.c +++ b/gst/gstminiobject.c @@ -344,6 +344,7 @@ gst_mini_object_ref (GstMiniObject * mini_object) g_return_val_if_fail (mini_object->refcount > 0, NULL); */ + GST_TRACER_MINI_OBJECT_REFFED (mini_object, mini_object->refcount + 1); GST_CAT_TRACE (GST_CAT_REFCOUNTING, "%p ref %d->%d", mini_object, GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object), GST_MINI_OBJECT_REFCOUNT_VALUE (mini_object) + 1); @@ -433,6 +434,7 @@ gst_mini_object_unref (GstMiniObject * mini_object) if (G_UNLIKELY (g_atomic_int_dec_and_test (&mini_object->refcount))) { gboolean do_free; + GST_TRACER_MINI_OBJECT_UNREFFED (mini_object, mini_object->refcount); if (mini_object->dispose) do_free = mini_object->dispose (mini_object); else @@ -453,6 +455,8 @@ gst_mini_object_unref (GstMiniObject * mini_object) if (mini_object->free) mini_object->free (mini_object); } + } else { + GST_TRACER_MINI_OBJECT_UNREFFED (mini_object, mini_object->refcount); } } diff --git a/gst/gstobject.c b/gst/gstobject.c index 5c5c90afe0..36da2a039c 100644 --- a/gst/gstobject.c +++ b/gst/gstobject.c @@ -244,6 +244,7 @@ gst_object_ref (gpointer object) { g_return_val_if_fail (object != NULL, NULL); + GST_TRACER_OBJECT_REFFED (object, ((GObject *) object)->ref_count + 1); #ifdef DEBUG_REFCOUNT GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p ref %d->%d", object, ((GObject *) object)->ref_count, ((GObject *) object)->ref_count + 1); @@ -270,6 +271,7 @@ gst_object_unref (gpointer object) g_return_if_fail (object != NULL); g_return_if_fail (((GObject *) object)->ref_count > 0); + GST_TRACER_OBJECT_UNREFFED (object, ((GObject *) object)->ref_count - 1); #ifdef DEBUG_REFCOUNT GST_CAT_TRACE_OBJECT (GST_CAT_REFCOUNTING, object, "%p unref %d->%d", object, ((GObject *) object)->ref_count, ((GObject *) object)->ref_count - 1); @@ -1282,7 +1284,7 @@ gst_object_get_control_binding (GstObject * object, const gchar * property_name) * @binding: the binding * * Removes the corresponding #GstControlBinding. If it was the - * last ref of the binding, it will be disposed. + * last ref of the binding, it will be disposed. * * Returns: %TRUE if the binding could be removed. */ @@ -1356,7 +1358,7 @@ gst_object_get_value (GstObject * object, const gchar * property_name, * This function is useful if one wants to e.g. draw a graph of the control * curve or apply a control curve sample by sample. * - * The values are unboxed and ready to be used. The similar function + * The values are unboxed and ready to be used. The similar function * gst_object_get_g_value_array() returns the array as #GValues and is * better suites for bindings. * diff --git a/gst/gsttracerutils.c b/gst/gsttracerutils.c index d46deffe19..c7786001f4 100644 --- a/gst/gsttracerutils.c +++ b/gst/gsttracerutils.c @@ -55,7 +55,8 @@ static const gchar *_quark_strings[] = { "pad-link-pre", "pad-link-post", "pad-unlink-pre", "pad-unlink-post", "element-change-state-pre", "element-change-state-post", "mini-object-created", "mini-object-destroyed", "object-created", - "object-destroyed", + "object-destroyed", "mini-object-reffed", "mini-object-unreffed", + "object-reffed", "object-unreffed", }; GQuark _priv_gst_tracer_quark_table[GST_TRACER_QUARK_MAX]; diff --git a/gst/gsttracerutils.h b/gst/gsttracerutils.h index e61d66d414..9521b6ecf7 100644 --- a/gst/gsttracerutils.h +++ b/gst/gsttracerutils.h @@ -75,6 +75,10 @@ typedef enum /*< skip >*/ GST_TRACER_QUARK_HOOK_MINI_OBJECT_DESTROYED, GST_TRACER_QUARK_HOOK_OBJECT_CREATED, GST_TRACER_QUARK_HOOK_OBJECT_DESTROYED, + GST_TRACER_QUARK_HOOK_MINI_OBJECT_REFFED, + GST_TRACER_QUARK_HOOK_MINI_OBJECT_UNREFFED, + GST_TRACER_QUARK_HOOK_OBJECT_REFFED, + GST_TRACER_QUARK_HOOK_OBJECT_UNREFFED, GST_TRACER_QUARK_MAX } GstTracerQuarkId; @@ -591,6 +595,74 @@ typedef void (*GstTracerHookMiniObjectDestroyed) (GObject *self, GstClockTime ts GstTracerHookMiniObjectDestroyed, (GST_TRACER_ARGS, object)); \ }G_STMT_END +/** + * GstTracerHookObjectUnreffed: + * @self: the tracer instance + * @ts: the current timestamp + * @object: the object being unreffed + * @refcount: the new refcount after unrefing @object + * + * Hook called when a #GstObject is being unreffed named + * "object-unreffed" + */ +typedef void (*GstTracerHookObjectUnreffed) (GObject *self, GstClockTime ts, + GstObject *object, gint new_refcount); +#define GST_TRACER_OBJECT_UNREFFED(object, new_refcount) G_STMT_START{ \ + GST_TRACER_DISPATCH(GST_TRACER_QUARK(HOOK_OBJECT_UNREFFED), \ + GstTracerHookObjectUnreffed, (GST_TRACER_ARGS, object, new_refcount)); \ +}G_STMT_END + +/** + * GstTracerHookObjectReffed: + * @self: the tracer instance + * @ts: the current timestamp + * @object: the object being reffed + * @refcount: the new refcount after refing @object + * + * Hook called when a #GstObject is being reffed named + * "object-reffed". + */ +typedef void (*GstTracerHookObjectReffed) (GObject *self, GstClockTime ts, + GstObject *object, gint new_refcount); +#define GST_TRACER_OBJECT_REFFED(object, new_refcount) G_STMT_START{ \ + GST_TRACER_DISPATCH(GST_TRACER_QUARK(HOOK_OBJECT_REFFED), \ + GstTracerHookObjectReffed, (GST_TRACER_ARGS, object, new_refcount)); \ +}G_STMT_END + +/** + * GstTracerHookMiniObjectUnreffed: + * @self: the tracer instance + * @ts: the current timestamp + * @object: the mini object being unreffed + * @refcount: the new refcount after unrefing @object + * + * Hook called when a #GstMiniObject is being unreffed named + * "mini-object-unreffed". + */ +typedef void (*GstTracerHookMiniObjectUnreffed) (GObject *self, GstClockTime ts, + GstMiniObject *object, gint new_refcount); +#define GST_TRACER_MINI_OBJECT_UNREFFED(object, new_refcount) G_STMT_START{ \ + GST_TRACER_DISPATCH(GST_TRACER_QUARK(HOOK_MINI_OBJECT_UNREFFED), \ + GstTracerHookMiniObjectUnreffed, (GST_TRACER_ARGS, object, new_refcount)); \ +}G_STMT_END + +/** + * GstTracerHookMiniObjectReffed: + * @self: the tracer instance + * @ts: the current timestamp + * @object: the mini object being reffed + * @refcount: the new refcount after refing @object + * + * Hook called when a #GstMiniObject is being reffed named + * "mini-object-reffed". + */ +typedef void (*GstTracerHookMiniObjectReffed) (GObject *self, GstClockTime ts, + GstMiniObject *object, gint new_refcount); +#define GST_TRACER_MINI_OBJECT_REFFED(object, new_refcount) G_STMT_START{ \ + GST_TRACER_DISPATCH(GST_TRACER_QUARK(HOOK_MINI_OBJECT_REFFED), \ + GstTracerHookMiniObjectReffed, (GST_TRACER_ARGS, object, new_refcount)); \ +}G_STMT_END + /** * GstTracerHookObjectCreated: * @self: the tracer instance @@ -654,8 +726,12 @@ typedef void (*GstTracerHookObjectDestroyed) (GObject *self, GstClockTime ts, #define GST_TRACER_PAD_UNLINK_POST(srcpad, sinkpad, res) #define GST_TRACER_MINI_OBJECT_CREATED(object) #define GST_TRACER_MINI_OBJECT_DESTROYED(object) +#define GST_TRACER_MINI_OBJECT_REFFED(object, new_refcount) +#define GST_TRACER_MINI_OBJECT_UNREF(object, new_refcount) #define GST_TRACER_OBJECT_CREATED(object) #define GST_TRACER_OBJECT_DESTROYED(object) +#define GST_TRACER_OBJECT_REFFED(object, new_refcount) +#define GST_TRACER_OBJECT_UNREFFED(object, new_refcount) #endif /* GST_DISABLE_GST_TRACER_HOOKS */ diff --git a/plugins/tracers/gstleaks.c b/plugins/tracers/gstleaks.c index 08cb3f58b9..950d920691 100644 --- a/plugins/tracers/gstleaks.c +++ b/plugins/tracers/gstleaks.c @@ -48,12 +48,45 @@ G_DEFINE_TYPE_WITH_CODE (GstLeaksTracer, gst_leaks_tracer, GST_TYPE_TRACER, _do_init); static GstTracerRecord *tr_alive; +static GstTracerRecord *tr_refings; #ifdef G_OS_UNIX static GstTracerRecord *tr_added = NULL; static GstTracerRecord *tr_removed = NULL; #endif /* G_OS_UNIX */ static GQueue instances = G_QUEUE_INIT; +typedef struct +{ + gboolean reffed; + gchar *trace; + gint new_refcount; + GstClockTime ts; +} ObjectRefingInfo; + +typedef struct +{ + gchar *creation_trace; + + GList *refing_infos; +} ObjectRefingInfos; + +static void +object_refing_info_free (ObjectRefingInfo * refinfo) +{ + g_free (refinfo->trace); + g_free (refinfo); +} + +static void +object_refing_infos_free (ObjectRefingInfos * infos) +{ + g_list_free_full (infos->refing_infos, + (GDestroyNotify) object_refing_info_free); + + g_free (infos->creation_trace); + g_free (infos); +} + static void set_print_stack_trace (GstLeaksTracer * self, GstStructure * params) { @@ -120,6 +153,7 @@ set_params_from_structure (GstLeaksTracer * self, GstStructure * params) if (filters) set_filters (self, filters); + gst_structure_get_boolean (params, "check-refs", &self->check_refs); } static void @@ -261,11 +295,13 @@ static void handle_object_created (GstLeaksTracer * self, gpointer object, GType type, gboolean gobject) { - gchar *trace = NULL; + ObjectRefingInfos *infos; + if (!should_handle_object_type (self, type)) return; + infos = g_malloc0 (sizeof (ObjectRefingInfos)); if (gobject) g_object_weak_ref ((GObject *) object, object_weak_cb, self); else @@ -273,11 +309,10 @@ handle_object_created (GstLeaksTracer * self, gpointer object, GType type, mini_object_weak_cb, self); GST_OBJECT_LOCK (self); - if (self->log_stack_trace) { - trace = gst_debug_get_stack_trace (0); - } + if (self->log_stack_trace) + infos->creation_trace = gst_debug_get_stack_trace (0); - g_hash_table_insert (self->objects, object, trace); + g_hash_table_insert (self->objects, object, infos); #ifdef G_OS_UNIX if (self->added) @@ -308,10 +343,75 @@ object_created_cb (GstTracer * tracer, GstClockTime ts, GstObject * object) handle_object_created (self, object, object_type, TRUE); } +static void +handle_object_reffed (GstLeaksTracer * self, gpointer object, gint new_refcount, + gboolean reffed, GstClockTime ts) +{ + ObjectRefingInfos *infos; + ObjectRefingInfo *refinfo; + + if (!self->check_refs) + return; + + GST_OBJECT_LOCK (self); + infos = g_hash_table_lookup (self->objects, object); + if (!infos) + goto out; + + refinfo = g_malloc0 (sizeof (ObjectRefingInfo)); + refinfo->ts = ts; + refinfo->new_refcount = new_refcount; + refinfo->reffed = reffed; + if (self->log_stack_trace) + refinfo->trace = gst_debug_get_stack_trace (0); + + infos->refing_infos = g_list_prepend (infos->refing_infos, refinfo); + +out: + GST_OBJECT_UNLOCK (self); +} + +static void +object_reffed_cb (GstTracer * tracer, GstClockTime ts, GstObject * object, + gint new_refcount) +{ + GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer); + + handle_object_reffed (self, object, new_refcount, TRUE, ts); +} + +static void +object_unreffed_cb (GstTracer * tracer, GstClockTime ts, GstObject * object, + gint new_refcount) +{ + GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer); + + handle_object_reffed (self, object, new_refcount, FALSE, ts); +} + +static void +mini_object_reffed_cb (GstTracer * tracer, GstClockTime ts, + GstMiniObject * object, gint new_refcount) +{ + GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer); + + handle_object_reffed (self, object, new_refcount, TRUE, ts); +} + +static void +mini_object_unreffed_cb (GstTracer * tracer, GstClockTime ts, + GstMiniObject * object, gint new_refcount) +{ + GstLeaksTracer *self = GST_LEAKS_TRACER_CAST (tracer); + + handle_object_reffed (self, object, new_refcount, FALSE, ts); +} + static void gst_leaks_tracer_init (GstLeaksTracer * self) { - self->objects = g_hash_table_new_full (NULL, NULL, NULL, g_free); + self->objects = g_hash_table_new_full (NULL, NULL, NULL, + (GDestroyNotify) object_refing_infos_free); g_queue_push_tail (&instances, self); } @@ -329,6 +429,17 @@ gst_leaks_tracer_constructed (GObject * object) gst_tracing_register_hook (tracer, "object-created", G_CALLBACK (object_created_cb)); + if (self->check_refs) { + gst_tracing_register_hook (tracer, "object-reffed", + G_CALLBACK (object_reffed_cb)); + gst_tracing_register_hook (tracer, "mini-object-reffed", + G_CALLBACK (mini_object_reffed_cb)); + gst_tracing_register_hook (tracer, "mini-object-unreffed", + G_CALLBACK (mini_object_unreffed_cb)); + gst_tracing_register_hook (tracer, "object-unreffed", + G_CALLBACK (object_unreffed_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. */ @@ -342,13 +453,13 @@ typedef struct const gchar *type_name; guint ref_count; gchar *desc; - const gchar *trace; + ObjectRefingInfos *infos; } Leak; /* The content of the returned Leak struct is valid until the self->objects * hash table has been modified. */ static Leak * -leak_new (gpointer obj, GType type, guint ref_count, const gchar * trace) +leak_new (gpointer obj, GType type, guint ref_count, ObjectRefingInfos * infos) { Leak *leak = g_slice_new (Leak); @@ -356,7 +467,7 @@ leak_new (gpointer obj, GType type, guint ref_count, const gchar * trace) leak->type_name = g_type_name (type); leak->ref_count = ref_count; leak->desc = gst_info_strdup_printf ("%" GST_PTR_FORMAT, obj); - leak->trace = trace; + leak->infos = infos; return leak; } @@ -381,10 +492,10 @@ create_leaks_list (GstLeaksTracer * self) { GList *l = NULL; GHashTableIter iter; - gpointer obj, trace; + gpointer obj, infos; g_hash_table_iter_init (&iter, self->objects); - while (g_hash_table_iter_next (&iter, &obj, &trace)) { + while (g_hash_table_iter_next (&iter, &obj, &infos)) { GType type; guint ref_count; @@ -402,7 +513,7 @@ create_leaks_list (GstLeaksTracer * self) ref_count = ((GstMiniObject *) obj)->refcount; } - l = g_list_prepend (l, leak_new (obj, type, ref_count, trace)); + l = g_list_prepend (l, leak_new (obj, type, ref_count, infos)); } /* Sort leaks by type name so they are grouped together making the output @@ -416,7 +527,7 @@ create_leaks_list (GstLeaksTracer * self) static gboolean log_leaked (GstLeaksTracer * self) { - GList *leaks, *l; + GList *ref, *leaks, *l; leaks = create_leaks_list (self); if (!leaks) @@ -426,7 +537,17 @@ log_leaked (GstLeaksTracer * self) Leak *leak = l->data; gst_tracer_record_log (tr_alive, leak->type_name, leak->obj, leak->desc, - leak->ref_count, leak->trace ? leak->trace : ""); + leak->ref_count, + leak->infos->creation_trace ? leak->infos->creation_trace : ""); + + leak->infos->refing_infos = g_list_reverse (leak->infos->refing_infos); + for (ref = leak->infos->refing_infos; ref; ref = ref->next) { + ObjectRefingInfo *refinfo = (ObjectRefingInfo *) ref->data; + + gst_tracer_record_log (tr_refings, refinfo->ts, leak->type_name, + leak->obj, refinfo->reffed ? "reffed" : "unreffed", + refinfo->new_refcount, refinfo->trace ? refinfo->trace : ""); + } } g_list_free_full (leaks, (GDestroyNotify) leak_free); @@ -473,6 +594,11 @@ gst_leaks_tracer_finalize (GObject * object) ((GObjectClass *) gst_leaks_tracer_parent_class)->finalize (object); } +#define RECORD_FIELD_TYPE_TS \ + "ts", GST_TYPE_STRUCTURE, gst_structure_new ("value", \ + "type", G_TYPE_GTYPE, GST_TYPE_CLOCK_TIME, \ + "related-to", GST_TYPE_TRACER_VALUE_SCOPE, GST_TRACER_VALUE_SCOPE_PROCESS, \ + NULL) #define RECORD_FIELD_TYPE_NAME \ "type-name", GST_TYPE_STRUCTURE, gst_structure_new ("value", \ "type", G_TYPE_GTYPE, G_TYPE_STRING, \ @@ -601,6 +727,11 @@ gst_leaks_tracer_class_init (GstLeaksTracerClass * klass) RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL); GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED); + tr_refings = gst_tracer_record_new ("object-refings.class", + RECORD_FIELD_TYPE_TS, RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, + RECORD_FIELD_DESC, RECORD_FIELD_REF_COUNT, RECORD_FIELD_TRACE, NULL); + GST_OBJECT_FLAG_SET (tr_alive, GST_OBJECT_FLAG_MAY_BE_LEAKED); + if (g_getenv ("GST_LEAKS_TRACER_SIG")) { #ifdef G_OS_UNIX setup_signals (); diff --git a/plugins/tracers/gstleaks.h b/plugins/tracers/gstleaks.h index 6dd3def932..7539b26b48 100644 --- a/plugins/tracers/gstleaks.h +++ b/plugins/tracers/gstleaks.h @@ -68,6 +68,8 @@ struct _GstLeaksTracer { gint unhandled_filter_count; gboolean done; + gboolean check_refs; + gboolean log_stack_trace; };