gst/gst.c: Make _gst_disable_segtrap static, it's only used in gstplugin.c and we...
[platform/upstream/gstreamer.git] / gst / gstdebugutils.c
1 /* GStreamer
2  * Copyright (C) 2007 Stefan Kost <ensonic@users.sf.net>
3  *
4  * gstdebugutils.c: debugging and analysis utillities
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #ifndef GST_DISABLE_GST_DEBUG
23
24 #include "gst_private.h"
25
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "gstinfo.h"
31 #include "gstdebugutils.h"
32 #include "gstbin.h"
33 #include "gstobject.h"
34 #include "gstghostpad.h"
35 #include "gstpad.h"
36 #include "gstutils.h"
37
38 /*** PIPELINE GRAPHS **********************************************************/
39
40 extern GstClockTime _priv_gst_info_start_time;
41
42 static gchar *
43 debug_dump_make_object_name (GstObject * element)
44 {
45   return g_strcanon (g_strdup_printf ("%s_%p", GST_OBJECT_NAME (element),
46           element), G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_", '_');
47 }
48
49 static gchar *
50 debug_dump_get_element_state (GstElement * element)
51 {
52   gchar *state_name = NULL;
53   const gchar *state_icons = "~0-=>";
54   GstState state = 0, pending = 0;
55
56   gst_element_get_state (element, &state, &pending, 0);
57   if (pending == GST_STATE_VOID_PENDING) {
58     state_name = g_strdup_printf ("\\n[%c]", state_icons[state]);
59   } else {
60     state_name = g_strdup_printf ("\\n[%c]->[%c]", state_icons[state],
61         state_icons[pending]);
62   }
63   return state_name;
64 }
65
66 static gchar *
67 debug_dump_get_element_params (GstElement * element)
68 {
69   gchar *param_name = NULL;
70   GParamSpec **properties, *property;
71   GValue value = { 0, };
72   guint i, number_of_properties;
73   gchar *tmp, *value_str;
74
75   /* get paramspecs and show non-default properties */
76   properties =
77       g_object_class_list_properties (G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS
78           (element)), &number_of_properties);
79   if (properties) {
80     for (i = 0; i < number_of_properties; i++) {
81       property = properties[i];
82
83       /* ski some properties */
84       if (!(property->flags & G_PARAM_READABLE))
85         continue;
86       if (!strcmp (property->name, "name"))
87         continue;
88
89       g_value_init (&value, property->value_type);
90       g_object_get_property (G_OBJECT (element), property->name, &value);
91       if (!(g_param_value_defaults (property, &value))) {
92         tmp = g_strdup_value_contents (&value);
93         value_str = g_strescape (tmp, NULL);
94         g_free (tmp);
95         if (param_name) {
96           tmp = param_name;
97           param_name = g_strdup_printf ("%s\\n%s=%s",
98               tmp, property->name, value_str);
99           g_free (tmp);
100         } else {
101           param_name = g_strdup_printf ("\\n%s=%s", property->name, value_str);
102         }
103         g_free (value_str);
104       }
105       g_value_unset (&value);
106     }
107     g_free (properties);
108   }
109   return param_name;
110 }
111
112 /*
113  * debug_dump_element:
114  * @bin: the bin that should be analyzed
115  * @out: file to write to
116  * @indent: level of graph indentation
117  *
118  * Helper for _gst_debug_bin_to_dot_file() to recursively dump a pipeline.
119  */
120 static void
121 debug_dump_element (GstBin * bin, GstDebugGraphDetails details, FILE * out,
122     const gint indent)
123 {
124   GstIterator *element_iter, *pad_iter;
125   gboolean elements_done, pads_done;
126   GstElement *element, *peer_element, *target_element;
127   GstPad *pad, *peer_pad, *target_pad;
128   GstPadDirection dir;
129   GstCaps *caps;
130   GstStructure *structure;
131   gboolean free_caps, free_media;
132   guint src_pads, sink_pads;
133   gchar *media = NULL;
134   gchar *pad_name, *element_name;
135   gchar *peer_pad_name, *peer_element_name;
136   gchar *target_pad_name, *target_element_name;
137   gchar *color_name;
138   gchar *state_name = NULL;
139   gchar *param_name = NULL;
140   gchar *spc = NULL;
141
142   spc = g_malloc (1 + indent * 2);
143   memset (spc, 32, indent * 2);
144   spc[indent * 2] = '\0';
145
146   element_iter = gst_bin_iterate_elements (bin);
147   elements_done = FALSE;
148   while (!elements_done) {
149     switch (gst_iterator_next (element_iter, (gpointer) & element)) {
150       case GST_ITERATOR_OK:
151         element_name = debug_dump_make_object_name (GST_OBJECT (element));
152
153         if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
154           state_name = debug_dump_get_element_state (GST_ELEMENT (element));
155         }
156         if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
157           param_name = debug_dump_get_element_params (GST_ELEMENT (element));
158         }
159         /* elements */
160         fprintf (out, "%ssubgraph cluster_%s {\n", spc, element_name);
161         fprintf (out, "%s  fontname=\"Bitstream Vera Sans\";\n", spc);
162         fprintf (out, "%s  fontsize=\"8\";\n", spc);
163         fprintf (out, "%s  style=filled;\n", spc);
164         fprintf (out, "%s  color=black;\n\n", spc);
165         fprintf (out, "%s  label=\"<%s>\\n%s%s%s\";\n", spc,
166             G_OBJECT_TYPE_NAME (element), GST_OBJECT_NAME (element),
167             (state_name ? state_name : ""), (param_name ? param_name : "")
168             );
169         if (state_name) {
170           g_free (state_name);
171           state_name = NULL;
172         }
173         if (param_name) {
174           g_free (param_name);
175           param_name = NULL;
176         }
177         g_free (element_name);
178
179         src_pads = sink_pads = 0;
180         if ((pad_iter = gst_element_iterate_pads (element))) {
181           pads_done = FALSE;
182           while (!pads_done) {
183             switch (gst_iterator_next (pad_iter, (gpointer) & pad)) {
184               case GST_ITERATOR_OK:
185                 dir = gst_pad_get_direction (pad);
186                 pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
187                 element_name =
188                     debug_dump_make_object_name (GST_OBJECT (element));
189                 if (GST_IS_GHOST_PAD (pad)) {
190                   color_name =
191                       (dir == GST_PAD_SRC) ? "#ffdddd" : ((dir ==
192                           GST_PAD_SINK) ? "#ddddff" : "#ffffff");
193                 } else {
194                   color_name =
195                       (dir == GST_PAD_SRC) ? "#ffaaaa" : ((dir ==
196                           GST_PAD_SINK) ? "#aaaaff" : "#cccccc");
197                 }
198                 /* pads */
199                 fprintf (out,
200                     "%s  %s_%s [color=black, fillcolor=\"%s\", label=\"%s\"];\n",
201                     spc, element_name, pad_name, color_name,
202                     GST_OBJECT_NAME (pad));
203
204                 if (dir == GST_PAD_SRC)
205                   src_pads++;
206                 else if (dir == GST_PAD_SINK)
207                   sink_pads++;
208                 g_free (pad_name);
209                 g_free (element_name);
210                 gst_object_unref (pad);
211                 break;
212               case GST_ITERATOR_RESYNC:
213                 gst_iterator_resync (pad_iter);
214                 break;
215               case GST_ITERATOR_ERROR:
216               case GST_ITERATOR_DONE:
217                 pads_done = TRUE;
218                 break;
219             }
220           }
221           gst_iterator_free (pad_iter);
222         }
223         if (GST_IS_BIN (element)) {
224           fprintf (out, "%s  fillcolor=\"#ffffff\";\n", spc);
225           /* recurse */
226           debug_dump_element (GST_BIN (element), details, out, indent + 1);
227         } else {
228           if (src_pads && !sink_pads)
229             fprintf (out, "%s  fillcolor=\"#ffaaaa\";\n", spc);
230           else if (!src_pads && sink_pads)
231             fprintf (out, "%s  fillcolor=\"#aaaaff\";\n", spc);
232           else if (src_pads && sink_pads)
233             fprintf (out, "%s  fillcolor=\"#aaffaa\";\n", spc);
234           else
235             fprintf (out, "%s  fillcolor=\"#ffffff\";\n", spc);
236         }
237         fprintf (out, "%s}\n\n", spc);
238         if ((pad_iter = gst_element_iterate_pads (element))) {
239           pads_done = FALSE;
240           while (!pads_done) {
241             switch (gst_iterator_next (pad_iter, (gpointer) & pad)) {
242               case GST_ITERATOR_OK:
243                 if (gst_pad_is_linked (pad)
244                     && gst_pad_get_direction (pad) == GST_PAD_SRC) {
245                   if ((peer_pad = gst_pad_get_peer (pad))) {
246                     free_media = FALSE;
247                     if ((details & GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE) ||
248                         (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS)
249                         ) {
250                       if ((caps = gst_pad_get_negotiated_caps (pad))) {
251                         free_caps = TRUE;
252                       } else {
253                         free_caps = FALSE;
254                         if (!(caps = (GstCaps *)
255                                 gst_pad_get_pad_template_caps (pad))) {
256                           /* this should not happen */
257                           media = "?";
258                         }
259                       }
260                       if (caps) {
261                         if (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS) {
262                           gchar *tmp =
263                               g_strdelimit (gst_caps_to_string (caps), ",",
264                               '\n');
265
266                           media = g_strescape (tmp, NULL);
267                           free_media = TRUE;
268                           g_free (tmp);
269                         } else {
270                           if (GST_CAPS_IS_SIMPLE (caps)) {
271                             structure = gst_caps_get_structure (caps, 0);
272                             media =
273                                 (gchar *) gst_structure_get_name (structure);
274                           } else
275                             media = "*";
276                         }
277                         if (free_caps) {
278                           gst_caps_unref (caps);
279                         }
280                       }
281                     }
282
283                     pad_name = debug_dump_make_object_name (GST_OBJECT (pad));
284                     element_name =
285                         debug_dump_make_object_name (GST_OBJECT (element));
286                     peer_pad_name =
287                         debug_dump_make_object_name (GST_OBJECT (peer_pad));
288                     if ((peer_element = gst_pad_get_parent_element (peer_pad))) {
289                       peer_element_name =
290                           debug_dump_make_object_name (GST_OBJECT
291                           (peer_element));
292                     } else {
293                       peer_element_name = "";
294                     }
295                     /* pad link */
296                     if (media) {
297                       fprintf (out, "%s%s_%s -> %s_%s [label=\"%s\"]\n", spc,
298                           element_name, pad_name, peer_element_name,
299                           peer_pad_name, media);
300                       if (free_media) {
301                         g_free (media);
302                       }
303                     } else {
304                       fprintf (out, "%s%s_%s -> %s_%s\n", spc,
305                           element_name, pad_name, peer_element_name,
306                           peer_pad_name);
307                     }
308
309                     if (GST_IS_GHOST_PAD (pad)) {
310                       if ((target_pad =
311                               gst_ghost_pad_get_target (GST_GHOST_PAD (pad)))) {
312                         target_pad_name =
313                             debug_dump_make_object_name (GST_OBJECT
314                             (target_pad));
315                         if ((target_element =
316                                 gst_pad_get_parent_element (target_pad))) {
317                           target_element_name =
318                               debug_dump_make_object_name (GST_OBJECT
319                               (target_element));
320                         } else {
321                           target_element_name = "";
322                         }
323                         /* src ghostpad relationship */
324                         fprintf (out, "%s%s_%s -> %s_%s [style=dashed]\n", spc,
325                             target_element_name, target_pad_name, element_name,
326                             pad_name);
327
328                         g_free (target_pad_name);
329                         if (target_element) {
330                           g_free (target_element_name);
331                           gst_object_unref (target_element);
332                         }
333                         gst_object_unref (target_pad);
334                       }
335                     }
336                     if (GST_IS_GHOST_PAD (peer_pad)) {
337                       if ((target_pad =
338                               gst_ghost_pad_get_target (GST_GHOST_PAD
339                                   (peer_pad)))) {
340                         target_pad_name =
341                             debug_dump_make_object_name (GST_OBJECT
342                             (target_pad));
343                         if ((target_element =
344                                 gst_pad_get_parent_element (target_pad))) {
345                           target_element_name =
346                               debug_dump_make_object_name (GST_OBJECT
347                               (target_element));
348                         } else {
349                           target_element_name = "";
350                         }
351                         /* sink ghostpad relationship */
352                         fprintf (out, "%s%s_%s -> %s_%s [style=dashed]\n", spc,
353                             peer_element_name, peer_pad_name,
354                             target_element_name, target_pad_name);
355
356                         g_free (target_pad_name);
357                         if (target_element) {
358                           g_free (target_element_name);
359                           gst_object_unref (target_element);
360                         }
361                         gst_object_unref (target_pad);
362                       }
363                     }
364
365                     g_free (pad_name);
366                     g_free (element_name);
367                     g_free (peer_pad_name);
368                     if (peer_element) {
369                       g_free (peer_element_name);
370                       gst_object_unref (peer_element);
371                     }
372                     gst_object_unref (peer_pad);
373                   }
374                 }
375                 gst_object_unref (pad);
376                 break;
377               case GST_ITERATOR_RESYNC:
378                 gst_iterator_resync (pad_iter);
379                 break;
380               case GST_ITERATOR_ERROR:
381               case GST_ITERATOR_DONE:
382                 pads_done = TRUE;
383                 break;
384             }
385           }
386           gst_iterator_free (pad_iter);
387         }
388         gst_object_unref (element);
389         break;
390       case GST_ITERATOR_RESYNC:
391         gst_iterator_resync (element_iter);
392         break;
393       case GST_ITERATOR_ERROR:
394       case GST_ITERATOR_DONE:
395         elements_done = TRUE;
396         break;
397     }
398   }
399   gst_iterator_free (element_iter);
400   g_free (spc);
401 }
402
403 /*
404  * _gst_debug_bin_to_dot_file:
405  * @bin: the top-level pipeline that should be analyzed
406  * @file_name: output base filename (e.g. "myplayer")
407  *
408  * To aid debugging applications one can use this method to write out the whole
409  * network of gstreamer elements that form the pipeline into an dot file.
410  * This file can be processed with graphviz to get an image.
411  * <informalexample><programlisting>
412  *  dot -Tpng -oimage.png graph_lowlevel.dot
413  * </programlisting></informalexample>
414  */
415 void
416 _gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
417     const gchar * file_name)
418 {
419   const gchar *dump_dot_dir;
420   gchar *full_file_name = NULL;
421   FILE *out;
422
423   g_return_if_fail (GST_IS_BIN (bin));
424
425   dump_dot_dir = g_getenv ("GST_DEBUG_DUMP_DOT_DIR");
426   if (!dump_dot_dir)
427     return;
428
429   if (!file_name) {
430     file_name = g_get_application_name ();
431     if (!file_name)
432       file_name = "unnamed";
433   }
434
435   full_file_name = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s.dot",
436       dump_dot_dir, file_name);
437
438   if ((out = fopen (full_file_name, "wb"))) {
439     gchar *state_name = NULL;
440     gchar *param_name = NULL;
441
442     if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
443       state_name = debug_dump_get_element_state (GST_ELEMENT (bin));
444     }
445     if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
446       param_name = debug_dump_get_element_params (GST_ELEMENT (bin));
447     }
448
449     /* write header */
450     fprintf (out,
451         "digraph pipeline {\n"
452         "  rankdir=LR;\n"
453         "  fontname=\"Bitstream Vera Sans\";\n"
454         "  fontsize=\"8\";\n"
455         "  labelloc=t;\n"
456         "  nodesep=.15;\n"
457         "  label=\"<%s>\\n%s%s%s\";\n"
458         "  node [style=filled, shape=box, fontsize=\"7\", fontname=\"Bitstream Vera Sans\"];\n"
459         "  edge [labelfontsize=\"7\", fontsize=\"7\", labelfontname=\"Bitstream Vera Sans\", fontname=\"Bitstream Vera Sans\"];\n"
460         "\n", G_OBJECT_TYPE_NAME (bin), GST_OBJECT_NAME (bin),
461         (state_name ? state_name : ""), (param_name ? param_name : "")
462         );
463     if (state_name)
464       g_free (state_name);
465     if (param_name)
466       g_free (param_name);
467
468     debug_dump_element (bin, details, out, 1);
469
470     /* write footer */
471     fprintf (out, "}\n");
472     fclose (out);
473   }
474   GST_INFO ("wrote bin graph to : '%s'", full_file_name);
475   g_free (full_file_name);
476 }
477
478 /*
479  * _gst_debug_bin_to_dot_file_with_ts:
480  * @bin: the top-level pipeline that should be analyzed
481  * @file_name: output base filename (e.g. "myplayer")
482  *
483  * This works like _gst_debug_bin_to_dot_file(), but adds the current timestamp
484  * to the filename, so that it can be used to take multiple snapshots.
485  */
486 void
487 _gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
488     const gchar * file_name)
489 {
490   gchar *ts_file_name = NULL;
491   GTimeVal now;
492   GstClockTime elapsed;
493
494   g_return_if_fail (GST_IS_BIN (bin));
495
496   if (!file_name) {
497     file_name = g_get_application_name ();
498     if (!file_name)
499       file_name = "unnamed";
500   }
501
502   /* add timestamp */
503   g_get_current_time (&now);
504   elapsed = GST_TIMEVAL_TO_TIME (now) - _priv_gst_info_start_time;
505   ts_file_name =
506       g_strdup_printf ("%" GST_TIME_FORMAT "-%s", GST_TIME_ARGS (elapsed),
507       file_name);
508
509   _gst_debug_bin_to_dot_file (bin, details, ts_file_name);
510   g_free (ts_file_name);
511 }
512
513 #endif /* GST_DISABLE_GST_DEBUG */