devicemonitor: Use a sync bus handler for the provider to avoid accumulating all...
[platform/upstream/gstreamer.git] / subprojects / gstreamer / gst / gsttracerutils.c
1 /* GStreamer
2  * Copyright (C) 2013 Stefan Sauer <ensonic@users.sf.net>
3  *
4  * gsttracerutils.c: tracing subsystem
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 /* Tracing subsystem:
23  *
24  * The tracing subsystem provides hooks in the core library and API for modules
25  * to attach to them.
26  *
27  * The user can activate tracers by setting the environment variable GST_TRACE
28  * to a ';' separated list of tracers.
29  *
30  * Note that instantiating tracers at runtime is possible but is not thread safe
31  * and needs to be done before any pipeline state is set to PAUSED.
32  */
33
34 #include "gst_private.h"
35 #include "gsttracer.h"
36 #include "gsttracerfactory.h"
37 #include "gsttracerutils.h"
38
39 #ifndef GST_DISABLE_GST_TRACER_HOOKS
40
41 /* tracer quarks */
42
43 /* These strings must match order and number declared in the GstTracerQuarkId
44  * enum in gsttracerutils.h! */
45 static const gchar *_quark_strings[] = {
46   "pad-push-pre", "pad-push-post", "pad-push-list-pre", "pad-push-list-post",
47   "pad-pull-range-pre", "pad-pull-range-post", "pad-push-event-pre",
48   "pad-push-event-post", "pad-query-pre", "pad-query-post",
49   "element-post-message-pre",
50   "element-post-message-post", "element-query-pre", "element-query-post",
51   "element-new", "element-add-pad", "element-remove-pad",
52   "bin-add-pre", "bin-add-post", "bin-remove-pre", "bin-remove-post",
53   "pad-link-pre", "pad-link-post", "pad-unlink-pre", "pad-unlink-post",
54   "element-change-state-pre", "element-change-state-post",
55   "mini-object-created", "mini-object-destroyed", "object-created",
56   "object-destroyed", "mini-object-reffed", "mini-object-unreffed",
57   "object-reffed", "object-unreffed", "plugin-feature-loaded",
58   "pad-chain-pre", "pad-chain-post", "pad-chain-list-pre",
59   "pad-chain-list-post",
60 };
61
62 GQuark _priv_gst_tracer_quark_table[GST_TRACER_QUARK_MAX];
63
64 /* tracing helpers */
65
66 gboolean _priv_tracer_enabled = FALSE;
67 GHashTable *_priv_tracers = NULL;
68
69 /* Initialize the tracing system */
70 void
71 _priv_gst_tracing_init (void)
72 {
73   gint i = 0;
74   const gchar *env = g_getenv ("GST_TRACERS");
75
76   /* We initialize the tracer sub system even if the end
77    * user did not activate it through the env variable
78    * so that external tools can use it anyway */
79   GST_DEBUG ("Initializing GstTracer");
80   _priv_tracers = g_hash_table_new (NULL, NULL);
81
82   if (G_N_ELEMENTS (_quark_strings) != GST_TRACER_QUARK_MAX)
83     g_warning ("the quark table is not consistent! %d != %d",
84         (gint) G_N_ELEMENTS (_quark_strings), GST_TRACER_QUARK_MAX);
85
86   for (i = 0; i < GST_TRACER_QUARK_MAX; i++) {
87     _priv_gst_tracer_quark_table[i] =
88         g_quark_from_static_string (_quark_strings[i]);
89   }
90
91   if (env != NULL && *env != '\0') {
92     GstRegistry *registry = gst_registry_get ();
93     GstPluginFeature *feature;
94     GstTracerFactory *factory;
95     gchar **t = g_strsplit_set (env, ";", 0);
96     gchar *params;
97
98     GST_INFO ("enabling tracers: '%s'", env);
99     i = 0;
100     while (t[i]) {
101       // check t[i] for params
102       if ((params = strchr (t[i], '('))) {
103         gchar *end = strchr (&params[1], ')');
104         *params = '\0';
105         params++;
106         if (end)
107           *end = '\0';
108       } else {
109         params = NULL;
110       }
111
112       GST_INFO ("checking tracer: '%s'", t[i]);
113
114       if ((feature = gst_registry_lookup_feature (registry, t[i]))) {
115         factory = GST_TRACER_FACTORY (gst_plugin_feature_load (feature));
116         if (factory) {
117           GstTracer *tracer;
118
119           GST_INFO_OBJECT (factory, "creating tracer: type-id=%u",
120               (guint) factory->type);
121
122           tracer = g_object_new (factory->type, "params", params, NULL);
123
124           /* Clear floating flag */
125           gst_object_ref_sink (tracer);
126
127           /* tracers register them self to the hooks */
128           gst_object_unref (tracer);
129         } else {
130           GST_WARNING_OBJECT (feature,
131               "loading plugin containing feature %s failed!", t[i]);
132         }
133       } else {
134         GST_WARNING ("no tracer named '%s'", t[i]);
135       }
136       i++;
137     }
138     g_strfreev (t);
139   }
140 }
141
142 void
143 _priv_gst_tracing_deinit (void)
144 {
145   GList *h_list, *h_node, *t_node;
146   GstTracerHook *hook;
147
148   _priv_tracer_enabled = FALSE;
149   if (!_priv_tracers)
150     return;
151
152   /* shutdown tracers for final reports */
153   h_list = g_hash_table_get_values (_priv_tracers);
154   for (h_node = h_list; h_node; h_node = g_list_next (h_node)) {
155     for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) {
156       hook = (GstTracerHook *) t_node->data;
157       gst_object_unref (hook->tracer);
158       g_slice_free (GstTracerHook, hook);
159     }
160     g_list_free (h_node->data);
161   }
162   g_list_free (h_list);
163   g_hash_table_destroy (_priv_tracers);
164   _priv_tracers = NULL;
165 }
166
167 static void
168 gst_tracing_register_hook_id (GstTracer * tracer, GQuark detail, GCallback func)
169 {
170   gpointer key = GINT_TO_POINTER (detail);
171   GList *list = g_hash_table_lookup (_priv_tracers, key);
172   GstTracerHook *hook = g_slice_new0 (GstTracerHook);
173   hook->tracer = gst_object_ref (tracer);
174   hook->func = func;
175
176   list = g_list_prepend (list, hook);
177   g_hash_table_replace (_priv_tracers, key, list);
178   GST_DEBUG ("registering tracer for '%s', list.len=%d",
179       (detail ? g_quark_to_string (detail) : "*"), g_list_length (list));
180   _priv_tracer_enabled = TRUE;
181 }
182
183 /**
184  * gst_tracing_register_hook:
185  * @tracer: the tracer
186  * @detail: the detailed hook
187  * @func: (scope async): the callback
188  *
189  * Register @func to be called when the trace hook @detail is getting invoked.
190  * Use %NULL for @detail to register to all hooks.
191  *
192  * Since: 1.8
193  */
194 void
195 gst_tracing_register_hook (GstTracer * tracer, const gchar * detail,
196     GCallback func)
197 {
198   gst_tracing_register_hook_id (tracer, g_quark_try_string (detail), func);
199 }
200
201 /**
202  * gst_tracing_get_active_tracers:
203  *
204  * Get a list of all active tracer objects owned by the tracing framework for
205  * the entirety of the run-time of the process or till gst_deinit() is called.
206  *
207  * Returns: (transfer full) (element-type Gst.Tracer): A #GList of
208  * #GstTracer objects
209  *
210  * Since: 1.18
211  */
212 GList *
213 gst_tracing_get_active_tracers (void)
214 {
215   GList *tracers, *h_list, *h_node, *t_node;
216   GstTracerHook *hook;
217
218   if (!_priv_tracer_enabled || !_priv_tracers)
219     return NULL;
220
221   tracers = NULL;
222   h_list = g_hash_table_get_values (_priv_tracers);
223   for (h_node = h_list; h_node; h_node = g_list_next (h_node)) {
224     for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) {
225       hook = (GstTracerHook *) t_node->data;
226       /* Skip duplicate tracers from different hooks. This function is O(n), but
227        * that should be fine since the number of tracers enabled on a process
228        * should be small. */
229       if (g_list_index (tracers, hook->tracer) >= 0)
230         continue;
231       tracers = g_list_prepend (tracers, gst_object_ref (hook->tracer));
232     }
233   }
234   g_list_free (h_list);
235
236   return tracers;
237 }
238
239 #else /* !GST_DISABLE_GST_TRACER_HOOKS */
240
241 void
242 gst_tracing_register_hook (GstTracer * tracer, const gchar * detail,
243     GCallback func)
244 {
245 }
246
247 GList *
248 gst_tracing_get_active_tracers (void)
249 {
250   return NULL;
251 }
252 #endif /* GST_DISABLE_GST_TRACER_HOOKS */