diff --git a/subprojects/gstreamer/plugins/tracers/gstleaks.c b/subprojects/gstreamer/plugins/tracers/gstleaks.c index 45e5ca9963..6efd873536 100644 --- a/subprojects/gstreamer/plugins/tracers/gstleaks.c +++ b/subprojects/gstreamer/plugins/tracers/gstleaks.c @@ -44,15 +44,9 @@ * `GST_TRACERS=leaks;latency`, and multiple instances of the same tracer can be * active at the same time. * - * Parameters can also be passed to each tracer. The leaks tracer currently - * accepts five params: - * 1. filters: (string) to filter which objects to record - * 2. check-refs: (boolean) whether to record every location where a leaked - * object was reffed and unreffed - * 3. stack-traces-flags: (string) full or none; see: #GstStackTraceFlags - * 4. name: (string) set a name for the tracer object itself - * 5. log-leaks-on-deinit: (boolean) whether to report all leaks on - * gst_deinit() by printing them in the debug log; "true" by default + * The tracer properties can also be set to each tracer by passing the object + * properties in the list of parameters to the tracer. This uses the same + * serialization format as #GstStructure (without a name). * * Examples: * ``` @@ -90,7 +84,40 @@ enum LAST_SIGNAL }; -#define DEFAULT_LOG_LEAKS TRUE /* for backwards-compat */ +#define DEFAULT_LOG_LEAKS TRUE +#define DEFAULT_CHECK_REFS FALSE + +#define GST_TYPE_LEAKS_STACK_TRACE_FLAGS (gst_leaks_stack_trace_flags_get_type()) +static GType +gst_leaks_stack_trace_flags_get_type (void) +{ + static GType type = 0; + static const GFlagsValue values[] = { + {GST_LEAKS_STACK_TRACE_DISABLED, "Disabled", "disabled"}, + {GST_LEAKS_STACK_TRACE_NONE, "None", "none"}, + {GST_LEAKS_STACK_TRACE_FULL, "Full", "full"}, + {0, NULL, NULL} + }; + + if (!type) { + type = g_flags_register_static ("GstLeaksStackTraceFlags", values); + } + return type; +} + +#define DEFAULT_STACK_TRACE_FLAGS GST_LEAKS_STACK_TRACE_DISABLED + +enum +{ + PROP_0, + PROP_FILTERS, + PROP_CHECK_REFS, + PROP_STACK_TRACES_FLAGS, + PROP_LOG_LEAKS_ON_DEINIT, + N_PROPERTIES +}; + +static GParamSpec *properties[N_PROPERTIES] = { NULL, }; #define _do_init \ GST_DEBUG_CATEGORY_INIT (gst_leaks_debug, "leaks", 0, "leaks tracer"); @@ -158,39 +185,6 @@ object_refing_infos_free (ObjectRefingInfos * infos) g_free (infos); } -static void -set_print_stack_trace_from_string (GstLeaksTracer * self, const gchar * str) -{ - gchar *trace; - - /* Test if we can retrieve backtrace */ - trace = gst_debug_get_stack_trace (FALSE); - if (!trace) - return; - - g_free (trace); - - if (g_strcmp0 (str, "full") == 0) - self->trace_flags = GST_STACK_TRACE_SHOW_FULL; - else - self->trace_flags = GST_STACK_TRACE_SHOW_NONE; -} - -static void -set_print_stack_trace (GstLeaksTracer * self, GstStructure * params) -{ - const gchar *trace_flags = g_getenv ("GST_LEAKS_TRACER_STACK_TRACE"); - - self->trace_flags = -1; - if (!trace_flags && params) - trace_flags = gst_structure_get_string (params, "stack-traces-flags"); - - if (!trace_flags) - return; - - set_print_stack_trace_from_string (self, trace_flags); -} - static void set_filters (GstLeaksTracer * self, const gchar * filters) { @@ -226,51 +220,6 @@ set_filters (GstLeaksTracer * self, const gchar * filters) g_strfreev (tmp); } -static void -set_params_from_structure (GstLeaksTracer * self, GstStructure * params) -{ - const gchar *filters, *name; - - filters = gst_structure_get_string (params, "filters"); - if (filters) - set_filters (self, filters); - - name = gst_structure_get_string (params, "name"); - if (name) - gst_object_set_name (GST_OBJECT (self), name); - - gst_structure_get_boolean (params, "check-refs", &self->check_refs); - gst_structure_get_boolean (params, "log-leaks-on-deinit", &self->log_leaks); -} - -static void -set_params (GstLeaksTracer * self) -{ - gchar *params, *tmp; - GstStructure *params_struct = NULL; - - g_object_get (self, "params", ¶ms, NULL); - if (!params) - goto set_stacktrace; - - tmp = g_strdup_printf ("leaks,%s", params); - params_struct = gst_structure_from_string (tmp, NULL); - g_free (tmp); - - if (params_struct) - set_params_from_structure (self, params_struct); - else - set_filters (self, params); - - g_free (params); - -set_stacktrace: - set_print_stack_trace (self, params_struct); - - if (params_struct) - gst_structure_free (params_struct); -} - static gboolean _expand_unhandled_filters (gchar * typename, gpointer unused_value, GstLeaksTracer * self) @@ -415,8 +364,9 @@ handle_object_created (GstLeaksTracer * self, gpointer object, GType type, } GST_OBJECT_LOCK (self); - if ((gint) self->trace_flags != -1) - infos->creation_trace = gst_debug_get_stack_trace (self->trace_flags); + if ((gint) self->trace_flags != GST_LEAKS_STACK_TRACE_DISABLED) + infos->creation_trace = + gst_debug_get_stack_trace ((GstStackTraceFlags) self->trace_flags); g_hash_table_insert (self->objects, object, infos); @@ -470,8 +420,9 @@ handle_object_reffed (GstLeaksTracer * self, gpointer object, GType type, refinfo->ts = ts; refinfo->new_refcount = new_refcount; refinfo->reffed = reffed; - if ((gint) self->trace_flags != -1) - refinfo->trace = gst_debug_get_stack_trace (self->trace_flags); + if ((gint) self->trace_flags != GST_LEAKS_STACK_TRACE_DISABLED) + refinfo->trace = + gst_debug_get_stack_trace ((GstStackTraceFlags) self->trace_flags); infos->refing_infos = g_list_prepend (infos->refing_infos, refinfo); @@ -523,6 +474,8 @@ static void gst_leaks_tracer_init (GstLeaksTracer * self) { self->log_leaks = DEFAULT_LOG_LEAKS; + self->check_refs = DEFAULT_CHECK_REFS; + self->trace_flags = DEFAULT_STACK_TRACE_FLAGS; self->objects = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) object_refing_infos_free); @@ -545,8 +498,6 @@ gst_leaks_tracer_constructed (GObject * object) GstLeaksTracer *self = GST_LEAKS_TRACER (object); GstTracer *tracer = GST_TRACER (object); - set_params (self); - gst_tracing_register_hook (tracer, "mini-object-created", G_CALLBACK (mini_object_created_cb)); gst_tracing_register_hook (tracer, "object-created", @@ -1021,6 +972,7 @@ gst_leaks_tracer_get_live_objects (GstLeaksTracer * self) g_value_init (&live_objects, GST_TYPE_LIST); + GST_TRACE_OBJECT (self, "start listing currently alive objects"); GST_OBJECT_LOCK (self); process_leaks (self, &live_objects); GST_OBJECT_UNLOCK (self); @@ -1138,13 +1090,150 @@ gst_leaks_tracer_activity_stop_tracking (GstLeaksTracer * self) GST_OBJECT_UNLOCK (self); } +static void +gst_leaks_tracer_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstLeaksTracer *self = GST_LEAKS_TRACER (object); + + GST_OBJECT_LOCK (self); + switch (prop_id) { + case PROP_CHECK_REFS: + g_value_set_boolean (value, self->check_refs); + break; + case PROP_STACK_TRACES_FLAGS: + g_value_set_flags (value, self->trace_flags); + break; + case PROP_LOG_LEAKS_ON_DEINIT: + g_value_set_boolean (value, self->log_leaks); + break; + case PROP_FILTERS: + { + GString *str = g_string_new (""); + if (self->filter) { + guint i; + for (i = 0; i < self->filter->len; i++) { + GType type = g_array_index (self->filter, GType, i); + if (i > 0) + g_string_append_c (str, ','); + g_string_append (str, g_type_name (type)); + } + } + g_value_take_string (value, g_string_free (str, FALSE)); + break; + } + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (self); +} + +static void +gst_leaks_tracer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstLeaksTracer *self = GST_LEAKS_TRACER (object); + + GST_OBJECT_LOCK (self); + switch (prop_id) { + case PROP_CHECK_REFS: + self->check_refs = g_value_get_boolean (value); + break; + case PROP_STACK_TRACES_FLAGS: + self->trace_flags = g_value_get_flags (value); + break; + case PROP_LOG_LEAKS_ON_DEINIT: + self->log_leaks = g_value_get_boolean (value); + break; + case PROP_FILTERS: + if (self->filter) { + g_array_free (self->filter, TRUE); + self->filter = NULL; + } + const gchar *filters = g_value_get_string (value); + if (filters) { + set_filters (self, g_value_get_string (value)); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (self); +} + static void gst_leaks_tracer_class_init (GstLeaksTracerClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gst_tracer_class_set_use_structure_params (GST_TRACER_CLASS (klass), TRUE); + gobject_class->constructed = gst_leaks_tracer_constructed; gobject_class->finalize = gst_leaks_tracer_finalize; + gobject_class->get_property = gst_leaks_tracer_get_property; + gobject_class->set_property = gst_leaks_tracer_set_property; + + /** + * GstLeaksTracer:filters: + * + * Comma-separated list of GObject types to track. Only objects of these types + * or their subtypes will be monitored for leaks. + * + * Example: "GstEvent,GstMessage" to only track GstEvent and GstMessage objects. + * + * Since: 1.26 + */ + properties[PROP_FILTERS] = g_param_spec_string ("filters", + "Type Filters", + "Comma-separated list of GObject types to track", NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + /** + * GstLeaksTracer:check-refs: + * + * Whether to record every location where a leaked object was reffed and unreffed. + * When enabled, the tracer will collect stack traces for every ref/unref operation + * on tracked objects. + * + * Since: 1.26 + */ + properties[PROP_CHECK_REFS] = g_param_spec_boolean ("check-refs", + "Check References", + "Whether to track ref/unref operations", DEFAULT_CHECK_REFS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + /** + * GstLeaksTracer:stack-traces-flags: + * + * Stack trace collection mode. Controls whether and how stack traces are collected + * for object allocations and ref/unref operations. + * + * Since: 1.26 + */ + properties[PROP_STACK_TRACES_FLAGS] = + g_param_spec_flags ("stack-traces-flags", "Stack Trace Flags", + "Stack trace collection mode", GST_TYPE_LEAKS_STACK_TRACE_FLAGS, + DEFAULT_STACK_TRACE_FLAGS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + /** + * GstLeaksTracer:log-leaks-on-deinit: + * + * Whether to report all leaks on gst_deinit() by printing them in the debug log. + * When enabled, any detected leaks will be logged under GST_TRACER:7 when the + * GStreamer is being shut down. + * + * Since: 1.26 + */ + properties[PROP_LOG_LEAKS_ON_DEINIT] = + g_param_spec_boolean ("log-leaks-on-deinit", "Log Leaks", + "Whether to log leaks on shutdown", DEFAULT_LOG_LEAKS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); + + + g_object_class_install_properties (gobject_class, N_PROPERTIES, properties); tr_alive = gst_tracer_record_new ("object-alive.class", RECORD_FIELD_TYPE_NAME, RECORD_FIELD_ADDRESS, RECORD_FIELD_DESC, diff --git a/subprojects/gstreamer/plugins/tracers/gstleaks.h b/subprojects/gstreamer/plugins/tracers/gstleaks.h index 6eaf850187..da26d4294e 100644 --- a/subprojects/gstreamer/plugins/tracers/gstleaks.h +++ b/subprojects/gstreamer/plugins/tracers/gstleaks.h @@ -42,6 +42,12 @@ G_BEGIN_DECLS typedef struct _GstLeaksTracer GstLeaksTracer; typedef struct _GstLeaksTracerClass GstLeaksTracerClass; +typedef enum { + GST_LEAKS_STACK_TRACE_DISABLED = -1, + GST_LEAKS_STACK_TRACE_NONE = GST_STACK_TRACE_SHOW_NONE, + GST_LEAKS_STACK_TRACE_FULL = GST_STACK_TRACE_SHOW_FULL +} GstLeaksStackTraceFlags; + /** * GstLeaksTracer: * @@ -71,7 +77,7 @@ struct _GstLeaksTracer { gboolean check_refs; gboolean log_leaks; - GstStackTraceFlags trace_flags; + GstLeaksStackTraceFlags trace_flags; }; struct _GstLeaksTracerClass {