| /* GStreamer |
| * Copyright (C) 2013 Stefan Sauer <ensonic@users.sf.net> |
| * |
| * gsttracerutils.c: tracing subsystem |
| * |
| * 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. |
| */ |
| |
| /* Tracing subsystem: |
| * |
| * The tracing subsystem provides hooks in the core library and API for modules |
| * to attach to them. |
| * |
| * The user can activate tracers by setting the environment variable GST_TRACE |
| * to a ';' separated list of tracers. |
| * |
| * Note that instanciating tracers at runtime is possible but is not thread safe |
| * and needs to be done before any pipeline state is set to PAUSED. |
| */ |
| |
| #define GST_USE_UNSTABLE_API |
| |
| #include "gst_private.h" |
| #include "gsttracer.h" |
| #include "gsttracerfactory.h" |
| #include "gsttracerutils.h" |
| |
| #ifndef GST_DISABLE_GST_TRACER_HOOKS |
| |
| /* tracer quarks */ |
| |
| /* These strings must match order and number declared in the GstTracerQuarkId |
| * enum in gsttracerutils.h! */ |
| static const gchar *_quark_strings[] = { |
| "pad-push-pre", "pad-push-post", "pad-push-list-pre", "pad-push-list-post", |
| "pad-pull-range-pre", "pad-pull-range-post", "pad-push-event-pre", |
| "pad-push-event-post", "pad-query-pre", "pad-query-post", |
| "element-post-message-pre", |
| "element-post-message-post", "element-query-pre", "element-query-post", |
| "element-new", "element-add-pad", "element-remove-pad", |
| "bin-add-pre", "bin-add-post", "bin-remove-pre", "bin-remove-post", |
| "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", "mini-object-reffed", "mini-object-unreffed", |
| "object-reffed", "object-unreffed", |
| }; |
| |
| GQuark _priv_gst_tracer_quark_table[GST_TRACER_QUARK_MAX]; |
| |
| /* tracing helpers */ |
| |
| gboolean _priv_tracer_enabled = FALSE; |
| GHashTable *_priv_tracers = NULL; |
| |
| /* Initialize the tracing system */ |
| void |
| _priv_gst_tracing_init (void) |
| { |
| gint i = 0; |
| const gchar *env = g_getenv ("GST_TRACERS"); |
| |
| /* We initialize the tracer sub system even if the end |
| * user did not activate it through the env variable |
| * so that external tools can use it anyway */ |
| GST_DEBUG ("Initializing GstTracer"); |
| _priv_tracers = g_hash_table_new (NULL, NULL); |
| |
| if (G_N_ELEMENTS (_quark_strings) != GST_TRACER_QUARK_MAX) |
| g_warning ("the quark table is not consistent! %d != %d", |
| (gint) G_N_ELEMENTS (_quark_strings), GST_TRACER_QUARK_MAX); |
| |
| for (i = 0; i < GST_TRACER_QUARK_MAX; i++) { |
| _priv_gst_tracer_quark_table[i] = |
| g_quark_from_static_string (_quark_strings[i]); |
| } |
| |
| if (env != NULL && *env != '\0') { |
| GstRegistry *registry = gst_registry_get (); |
| GstPluginFeature *feature; |
| GstTracerFactory *factory; |
| gchar **t = g_strsplit_set (env, ";", 0); |
| gchar *params; |
| |
| GST_INFO ("enabling tracers: '%s'", env); |
| i = 0; |
| while (t[i]) { |
| // check t[i] for params |
| if ((params = strchr (t[i], '('))) { |
| gchar *end = strchr (¶ms[1], ')'); |
| *params = '\0'; |
| params++; |
| if (end) |
| *end = '\0'; |
| } else { |
| params = NULL; |
| } |
| |
| GST_INFO ("checking tracer: '%s'", t[i]); |
| |
| if ((feature = gst_registry_lookup_feature (registry, t[i]))) { |
| factory = GST_TRACER_FACTORY (gst_plugin_feature_load (feature)); |
| if (factory) { |
| GstTracer *tracer; |
| |
| GST_INFO_OBJECT (factory, "creating tracer: type-id=%u", |
| (guint) factory->type); |
| |
| tracer = g_object_new (factory->type, "params", params, NULL); |
| |
| /* Clear floating flag */ |
| gst_object_ref_sink (tracer); |
| |
| /* tracers register them self to the hooks */ |
| gst_object_unref (tracer); |
| } else { |
| GST_WARNING_OBJECT (feature, |
| "loading plugin containing feature %s failed!", t[i]); |
| } |
| } else { |
| GST_WARNING ("no tracer named '%s'", t[i]); |
| } |
| i++; |
| } |
| g_strfreev (t); |
| } |
| } |
| |
| void |
| _priv_gst_tracing_deinit (void) |
| { |
| GList *h_list, *h_node, *t_node; |
| GstTracerHook *hook; |
| |
| _priv_tracer_enabled = FALSE; |
| if (!_priv_tracers) |
| return; |
| |
| /* shutdown tracers for final reports */ |
| h_list = g_hash_table_get_values (_priv_tracers); |
| for (h_node = h_list; h_node; h_node = g_list_next (h_node)) { |
| for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) { |
| hook = (GstTracerHook *) t_node->data; |
| gst_object_unref (hook->tracer); |
| g_slice_free (GstTracerHook, hook); |
| } |
| g_list_free (h_node->data); |
| } |
| g_list_free (h_list); |
| g_hash_table_destroy (_priv_tracers); |
| _priv_tracers = NULL; |
| } |
| |
| static void |
| gst_tracing_register_hook_id (GstTracer * tracer, GQuark detail, GCallback func) |
| { |
| gpointer key = GINT_TO_POINTER (detail); |
| GList *list = g_hash_table_lookup (_priv_tracers, key); |
| GstTracerHook *hook = g_slice_new0 (GstTracerHook); |
| hook->tracer = gst_object_ref (tracer); |
| hook->func = func; |
| |
| list = g_list_prepend (list, hook); |
| g_hash_table_replace (_priv_tracers, key, list); |
| GST_DEBUG ("registering tracer for '%s', list.len=%d", |
| (detail ? g_quark_to_string (detail) : "*"), g_list_length (list)); |
| _priv_tracer_enabled = TRUE; |
| } |
| |
| /** |
| * gst_tracing_register_hook: |
| * @tracer: the tracer |
| * @detail: the detailed hook |
| * @func: (scope async): the callback |
| * |
| * Register @func to be called when the trace hook @detail is getting invoked. |
| * Use %NULL for @detail to register to all hooks. |
| */ |
| void |
| gst_tracing_register_hook (GstTracer * tracer, const gchar * detail, |
| GCallback func) |
| { |
| gst_tracing_register_hook_id (tracer, g_quark_try_string (detail), func); |
| } |
| |
| #else /* !GST_DISABLE_GST_TRACER_HOOKS */ |
| |
| void |
| gst_tracing_register_hook (GstTracer * tracer, const gchar * detail, |
| GCallback func) |
| { |
| } |
| |
| #endif /* GST_DISABLE_GST_TRACER_HOOKS */ |