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 };
59
60 GQuark _priv_gst_tracer_quark_table[GST_TRACER_QUARK_MAX];
61
62 /* tracing helpers */
63
64 gboolean _priv_tracer_enabled = FALSE;
65 GHashTable *_priv_tracers = NULL;
66
67 /* Initialize the tracing system */
68 void
_priv_gst_tracing_init(void)69 _priv_gst_tracing_init (void)
70 {
71 gint i = 0;
72 const gchar *env = g_getenv ("GST_TRACERS");
73
74 /* We initialize the tracer sub system even if the end
75 * user did not activate it through the env variable
76 * so that external tools can use it anyway */
77 GST_DEBUG ("Initializing GstTracer");
78 _priv_tracers = g_hash_table_new (NULL, NULL);
79
80 if (G_N_ELEMENTS (_quark_strings) != GST_TRACER_QUARK_MAX)
81 g_warning ("the quark table is not consistent! %d != %d",
82 (gint) G_N_ELEMENTS (_quark_strings), GST_TRACER_QUARK_MAX);
83
84 for (i = 0; i < GST_TRACER_QUARK_MAX; i++) {
85 _priv_gst_tracer_quark_table[i] =
86 g_quark_from_static_string (_quark_strings[i]);
87 }
88
89 if (env != NULL && *env != '\0') {
90 GstRegistry *registry = gst_registry_get ();
91 GstPluginFeature *feature;
92 GstTracerFactory *factory;
93 gchar **t = g_strsplit_set (env, ";", 0);
94 gchar *params;
95
96 GST_INFO ("enabling tracers: '%s'", env);
97 i = 0;
98 while (t[i]) {
99 // check t[i] for params
100 if ((params = strchr (t[i], '('))) {
101 gchar *end = strchr (¶ms[1], ')');
102 *params = '\0';
103 params++;
104 if (end)
105 *end = '\0';
106 } else {
107 params = NULL;
108 }
109
110 GST_INFO ("checking tracer: '%s'", t[i]);
111
112 if ((feature = gst_registry_lookup_feature (registry, t[i]))) {
113 factory = GST_TRACER_FACTORY (gst_plugin_feature_load (feature));
114 if (factory) {
115 GstTracer *tracer;
116
117 GST_INFO_OBJECT (factory, "creating tracer: type-id=%u",
118 (guint) factory->type);
119
120 tracer = g_object_new (factory->type, "params", params, NULL);
121
122 /* Clear floating flag */
123 gst_object_ref_sink (tracer);
124
125 /* tracers register them self to the hooks */
126 gst_object_unref (tracer);
127 } else {
128 GST_WARNING_OBJECT (feature,
129 "loading plugin containing feature %s failed!", t[i]);
130 }
131 } else {
132 GST_WARNING ("no tracer named '%s'", t[i]);
133 }
134 i++;
135 }
136 g_strfreev (t);
137 }
138 }
139
140 void
_priv_gst_tracing_deinit(void)141 _priv_gst_tracing_deinit (void)
142 {
143 GList *h_list, *h_node, *t_node;
144 GstTracerHook *hook;
145
146 _priv_tracer_enabled = FALSE;
147 if (!_priv_tracers)
148 return;
149
150 /* shutdown tracers for final reports */
151 h_list = g_hash_table_get_values (_priv_tracers);
152 for (h_node = h_list; h_node; h_node = g_list_next (h_node)) {
153 for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) {
154 hook = (GstTracerHook *) t_node->data;
155 gst_object_unref (hook->tracer);
156 g_slice_free (GstTracerHook, hook);
157 }
158 g_list_free (h_node->data);
159 }
160 g_list_free (h_list);
161 g_hash_table_destroy (_priv_tracers);
162 _priv_tracers = NULL;
163 }
164
165 static void
gst_tracing_register_hook_id(GstTracer * tracer,GQuark detail,GCallback func)166 gst_tracing_register_hook_id (GstTracer * tracer, GQuark detail, GCallback func)
167 {
168 gpointer key = GINT_TO_POINTER (detail);
169 GList *list = g_hash_table_lookup (_priv_tracers, key);
170 GstTracerHook *hook = g_slice_new0 (GstTracerHook);
171 hook->tracer = gst_object_ref (tracer);
172 hook->func = func;
173
174 list = g_list_prepend (list, hook);
175 g_hash_table_replace (_priv_tracers, key, list);
176 GST_DEBUG ("registering tracer for '%s', list.len=%d",
177 (detail ? g_quark_to_string (detail) : "*"), g_list_length (list));
178 _priv_tracer_enabled = TRUE;
179 }
180
181 /**
182 * gst_tracing_register_hook:
183 * @tracer: the tracer
184 * @detail: the detailed hook
185 * @func: (scope async): the callback
186 *
187 * Register @func to be called when the trace hook @detail is getting invoked.
188 * Use %NULL for @detail to register to all hooks.
189 *
190 * Since: 1.8
191 */
192 void
gst_tracing_register_hook(GstTracer * tracer,const gchar * detail,GCallback func)193 gst_tracing_register_hook (GstTracer * tracer, const gchar * detail,
194 GCallback func)
195 {
196 gst_tracing_register_hook_id (tracer, g_quark_try_string (detail), func);
197 }
198
199 /**
200 * gst_tracing_get_active_tracers:
201 *
202 * Get a list of all active tracer objects owned by the tracing framework for
203 * the entirety of the run-time of the process or till gst_deinit() is called.
204 *
205 * Returns: (transfer full) (element-type Gst.Tracer): A #GList of
206 * #GstTracer objects
207 *
208 * Since: 1.18
209 */
210 GList *
gst_tracing_get_active_tracers(void)211 gst_tracing_get_active_tracers (void)
212 {
213 GList *tracers, *h_list, *h_node, *t_node;
214 GstTracerHook *hook;
215
216 if (!_priv_tracer_enabled || !_priv_tracers)
217 return NULL;
218
219 tracers = NULL;
220 h_list = g_hash_table_get_values (_priv_tracers);
221 for (h_node = h_list; h_node; h_node = g_list_next (h_node)) {
222 for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) {
223 hook = (GstTracerHook *) t_node->data;
224 /* Skip duplicate tracers from different hooks. This function is O(n), but
225 * that should be fine since the number of tracers enabled on a process
226 * should be small. */
227 if (g_list_index (tracers, hook->tracer) >= 0)
228 continue;
229 tracers = g_list_prepend (tracers, gst_object_ref (hook->tracer));
230 }
231 }
232 g_list_free (h_list);
233
234 return tracers;
235 }
236
237 #else /* !GST_DISABLE_GST_TRACER_HOOKS */
238
239 void
gst_tracing_register_hook(GstTracer * tracer,const gchar * detail,GCallback func)240 gst_tracing_register_hook (GstTracer * tracer, const gchar * detail,
241 GCallback func)
242 {
243 }
244
245 GList *
gst_tracing_get_active_tracers(void)246 gst_tracing_get_active_tracers (void)
247 {
248 return NULL;
249 }
250 #endif /* GST_DISABLE_GST_TRACER_HOOKS */
251