tracing: add hooks when objects or miniobjects are created and destroyed
[platform/upstream/gstreamer.git] / 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 /**
23  * SECTION:gsttracerutils
24  * @short_description: Tracing subsystem
25  *
26  * The tracing subsystem provides hooks in the core library and API for modules
27  * to attach to them.
28  *
29  * The user can activate tracers by setting the environment variable GST_TRACE
30  * to a ';' separated list of tracers.
31  *
32  * Note that instanciating tracers at runtime is possible but is not thread safe
33  * and needs to be done before any pipeline state is set to PAUSED.
34  */
35
36 #define GST_USE_UNSTABLE_API
37
38 #include "gst_private.h"
39 #include "gsttracer.h"
40 #include "gsttracerfactory.h"
41 #include "gsttracerutils.h"
42
43 #ifndef GST_DISABLE_GST_TRACER_HOOKS
44
45 /* tracer quarks */
46
47 /* These strings must match order and number declared in the GstTracerQuarkId
48  * enum in gsttracerutils.h! */
49 static const gchar *_quark_strings[] = {
50   "pad-push-pre", "pad-push-post", "pad-push-list-pre", "pad-push-list-post",
51   "pad-pull-range-pre", "pad-pull-range-post", "pad-push-event-pre",
52   "pad-push-event-post", "pad-query-pre", "pad-query-post",
53   "element-post-message-pre",
54   "element-post-message-post", "element-query-pre", "element-query-post",
55   "element-new", "element-add-pad", "element-remove-pad",
56   "bin-add-pre", "bin-add-post", "bin-remove-pre", "bin-remove-post",
57   "pad-link-pre", "pad-link-post", "pad-unlink-pre", "pad-unlink-post",
58   "element-change-state-pre", "element-change-state-post",
59   "mini-object-created", "mini-object-destroyed", "object-created",
60   "object-destroyed",
61 };
62
63 GQuark _priv_gst_tracer_quark_table[GST_TRACER_QUARK_MAX];
64
65 /* tracing helpers */
66
67 gboolean _priv_tracer_enabled = FALSE;
68 GHashTable *_priv_tracers = NULL;
69
70 /* Initialize the tracing system */
71 void
72 _priv_gst_tracing_init (void)
73 {
74   gint i = 0;
75   const gchar *env = g_getenv ("GST_TRACERS");
76
77   /* We initialize the tracer sub system even if the end
78    * user did not activate it through the env variable
79    * so that external tools can use it anyway */
80   GST_DEBUG ("Initializing GstTracer");
81   _priv_tracers = g_hash_table_new (NULL, NULL);
82
83   if (G_N_ELEMENTS (_quark_strings) != GST_TRACER_QUARK_MAX)
84     g_warning ("the quark table is not consistent! %d != %d",
85         (gint) G_N_ELEMENTS (_quark_strings), GST_TRACER_QUARK_MAX);
86
87   for (i = 0; i < GST_TRACER_QUARK_MAX; i++) {
88     _priv_gst_tracer_quark_table[i] =
89         g_quark_from_static_string (_quark_strings[i]);
90   }
91
92   if (env != NULL && *env != '\0') {
93     GstRegistry *registry = gst_registry_get ();
94     GstPluginFeature *feature;
95     GstTracerFactory *factory;
96     gchar **t = g_strsplit_set (env, ";", 0);
97     gchar *params;
98
99     GST_INFO ("enabling tracers: '%s'", env);
100     i = 0;
101     while (t[i]) {
102       // check t[i] for params
103       if ((params = strchr (t[i], '('))) {
104         gchar *end = strchr (&params[1], ')');
105         *params = '\0';
106         params++;
107         if (end)
108           *end = '\0';
109       } else {
110         params = NULL;
111       }
112
113       GST_INFO ("checking tracer: '%s'", t[i]);
114
115       if ((feature = gst_registry_lookup_feature (registry, t[i]))) {
116         factory = GST_TRACER_FACTORY (gst_plugin_feature_load (feature));
117         if (factory) {
118           GST_INFO_OBJECT (factory, "creating tracer: type-id=%u",
119               (guint) factory->type);
120
121           /* tracers register them self to the hooks */
122           gst_object_unref (g_object_new (factory->type, "params", params,
123                   NULL));
124         } else {
125           GST_WARNING_OBJECT (feature,
126               "loading plugin containing feature %s failed!", t[i]);
127         }
128       } else {
129         GST_WARNING ("no tracer named '%s'", t[i]);
130       }
131       i++;
132     }
133     g_strfreev (t);
134   }
135 }
136
137 void
138 _priv_gst_tracing_deinit (void)
139 {
140   GList *h_list, *h_node, *t_node;
141   GstTracerHook *hook;
142
143   _priv_tracer_enabled = FALSE;
144   if (!_priv_tracers)
145     return;
146
147   /* shutdown tracers for final reports */
148   h_list = g_hash_table_get_values (_priv_tracers);
149   for (h_node = h_list; h_node; h_node = g_list_next (h_node)) {
150     for (t_node = h_node->data; t_node; t_node = g_list_next (t_node)) {
151       hook = (GstTracerHook *) t_node->data;
152       gst_object_unref (hook->tracer);
153       g_slice_free (GstTracerHook, hook);
154     }
155     g_list_free (h_node->data);
156   }
157   g_list_free (h_list);
158   g_hash_table_destroy (_priv_tracers);
159   _priv_tracers = NULL;
160 }
161
162 static void
163 gst_tracing_register_hook_id (GstTracer * tracer, GQuark detail, GCallback func)
164 {
165   gpointer key = GINT_TO_POINTER (detail);
166   GList *list = g_hash_table_lookup (_priv_tracers, key);
167   GstTracerHook *hook = g_slice_new0 (GstTracerHook);
168   hook->tracer = gst_object_ref (tracer);
169   hook->func = func;
170
171   list = g_list_prepend (list, hook);
172   g_hash_table_replace (_priv_tracers, key, list);
173   GST_DEBUG ("registering tracer for '%s', list.len=%d",
174       (detail ? g_quark_to_string (detail) : "*"), g_list_length (list));
175   _priv_tracer_enabled = TRUE;
176 }
177
178 /**
179  * gst_tracing_register_hook:
180  * @tracer: the tracer
181  * @detail: the detailed hook
182  * @func: (scope async): the callback
183  *
184  * Register @func to be called when the trace hook @detail is getting invoked.
185  * Use %NULL for @detail to register to all hooks.
186  */
187 void
188 gst_tracing_register_hook (GstTracer * tracer, const gchar * detail,
189     GCallback func)
190 {
191   gst_tracing_register_hook_id (tracer, g_quark_try_string (detail), func);
192 }
193
194 #endif /* GST_DISABLE_GST_TRACER_HOOKS */