Document new env-var. Add one log-line after dumpng a graph.
[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 gboolean _gst_debug_dump_dot_files_on = FALSE;
41 extern GstClockTime _gst_info_start_time;
42
43 static gchar *
44 debug_dump_make_object_name (GstObject * element)
45 {
46   return g_strcanon (g_strdup_printf ("%s_%p", GST_OBJECT_NAME (element),
47           element), G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "_", '_');
48 }
49
50 static gchar *
51 debug_dump_get_element_state (GstElement * element)
52 {
53   gchar *state_name = NULL;
54   const gchar *state_icons = "~0-=>";
55   GstState state = 0, pending = 0;
56
57   gst_element_get_state (element, &state, &pending, 0);
58   if (pending == GST_STATE_VOID_PENDING) {
59     state_name = g_strdup_printf ("\\n[%c]", state_icons[state]);
60   } else {
61     state_name = g_strdup_printf ("\\n[%c]->[%c]", state_icons[state],
62         state_icons[pending]);
63   }
64   return state_name;
65 }
66
67 static gchar *
68 debug_dump_get_element_params (GstElement * element)
69 {
70   gchar *param_name = NULL;
71   GParamSpec **properties, *property;
72   GValue value = { 0, };
73   guint i, number_of_properties;
74   gchar *tmp, *value_str;
75
76   /* get paramspecs and show non-default properties */
77   properties =
78       g_object_class_list_properties (G_OBJECT_CLASS (GST_ELEMENT_GET_CLASS
79           (element)), &number_of_properties);
80   if (properties) {
81     for (i = 0; i < number_of_properties; i++) {
82       property = properties[i];
83
84       /* ski some properties */
85       if (!(property->flags & G_PARAM_READABLE))
86         continue;
87       if (!strcmp (property->name, "name"))
88         continue;
89
90       g_value_init (&value, property->value_type);
91       g_object_get_property (G_OBJECT (element), property->name, &value);
92       if (!(g_param_value_defaults (property, &value))) {
93         tmp = g_strdup_value_contents (&value);
94         value_str = g_strescape (tmp, NULL);
95         g_free (tmp);
96         if (param_name) {
97           tmp = param_name;
98           param_name = g_strdup_printf ("%s\\n%s=%s",
99               tmp, property->name, value_str);
100           g_free (tmp);
101         } else {
102           param_name = g_strdup_printf ("\\n%s=%s", property->name, value_str);
103         }
104         g_free (value_str);
105       }
106       g_value_unset (&value);
107     }
108     g_free (properties);
109   }
110   return param_name;
111 }
112
113 /*
114  * debug_dump_element:
115  * @bin: the bin that should be analyzed
116  * @out: file to write to
117  * @indent: level of graph indentation
118  *
119  * Helper for _gst_debug_bin_to_dot_file() to recursively dump a pipeline.
120  */
121 static void
122 debug_dump_element (GstBin * bin, GstDebugGraphDetails details, FILE * out,
123     const gint indent)
124 {
125   GstIterator *element_iter, *pad_iter;
126   gboolean elements_done, pads_done;
127   GstElement *element, *peer_element, *target_element;
128   GstPad *pad, *peer_pad, *target_pad;
129   GstPadDirection dir;
130   GstCaps *caps;
131   GstStructure *structure;
132   gboolean free_caps, free_media;
133   guint src_pads, sink_pads;
134   gchar *media = NULL;
135   gchar *pad_name, *element_name;
136   gchar *peer_pad_name, *peer_element_name;
137   gchar *target_pad_name, *target_element_name;
138   gchar *color_name;
139   gchar *state_name = NULL;
140   gchar *param_name = NULL;
141   gchar spc[1 + indent * 2];
142
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 =
255                                 (GstCaps *)
256                                 gst_pad_get_pad_template_caps (pad))) {
257                           /* this should not happen */
258                           media = "?";
259                         }
260                       }
261                       if (caps) {
262                         if (details & GST_DEBUG_GRAPH_SHOW_CAPS_DETAILS) {
263                           gchar *tmp =
264                               g_strdelimit (gst_caps_to_string (caps), ",",
265                               '\n');
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 }
401
402 /*
403  * _gst_debug_bin_to_dot_file:
404  * @bin: the top-level pipeline that should be analyzed
405  * @file_name: output filename (e.g. "/tmp/metadata.dot")
406  *
407  * To aid debugging applications one can use this method to write out the whole
408  * network of gstreamer elements that form the pipeline into an dot file.
409  * This file can be processed with graphviz to get an image.
410  * <informalexample><programlisting>
411  *  dot -Tpng -oimage.png graph_lowlevel.dot
412  * </programlisting></informalexample>
413  */
414 void
415 _gst_debug_bin_to_dot_file (GstBin * bin, GstDebugGraphDetails details,
416     const gchar * file_name)
417 {
418   FILE *out;
419
420   g_return_if_fail (GST_IS_BIN (bin));
421   g_return_if_fail (file_name != NULL);
422
423   if (!_gst_debug_dump_dot_files_on)
424     return;
425
426   if ((out = fopen (file_name, "wb"))) {
427     gchar *state_name = NULL;
428     gchar *param_name = NULL;
429
430     if (details & GST_DEBUG_GRAPH_SHOW_STATES) {
431       state_name = debug_dump_get_element_state (GST_ELEMENT (bin));
432     }
433     if (details & GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS) {
434       param_name = debug_dump_get_element_params (GST_ELEMENT (bin));
435     }
436
437     /* write header */
438     fprintf (out,
439         "digraph pipeline {\n"
440         "  rankdir=LR;\n"
441         "  fontname=\"Bitstream Vera Sans\";\n"
442         "  fontsize=\"8\";\n"
443         "  labelloc=t;\n"
444         "  nodesep=.15;\n"
445         "  label=\"<%s>\\n%s%s%s\";\n"
446         "  node [style=filled, shape=box, fontsize=\"7\", fontname=\"Bitstream Vera Sans\"];\n"
447         "  edge [labelfontsize=\"7\", fontsize=\"7\", labelfontname=\"Bitstream Vera Sans\", fontname=\"Bitstream Vera Sans\"];\n"
448         "\n", G_OBJECT_TYPE_NAME (bin), GST_OBJECT_NAME (bin),
449         (state_name ? state_name : ""), (param_name ? param_name : "")
450         );
451     if (state_name)
452       g_free (state_name);
453     if (param_name)
454       g_free (param_name);
455
456     debug_dump_element (bin, details, out, 1);
457
458     /* write footer */
459     fprintf (out, "}\n");
460     fclose (out);
461   }
462   GST_INFO ("wrote bin graph to : '%s'", file_name);
463 }
464
465 /*
466  * _gst_debug_bin_to_dot_file_with_ts:
467  * @bin: the top-level pipeline that should be analyzed
468  * @file_tmpl: output filename template
469  *             (e.g. "/tmp/metadata.%" GST_TIME_FORMAT ".dot")
470  *
471  * This works like _gst_debug_bin_to_dot_file(), but fills the filename template
472  * with the timestamp, so that it can be used to take multiple snapshots.
473  */
474 void
475 _gst_debug_bin_to_dot_file_with_ts (GstBin * bin, GstDebugGraphDetails details,
476     const gchar * file_tmpl)
477 {
478   gchar *file_name = NULL;
479   const gchar *pos;
480   GTimeVal now;
481   GstClockTime elapsed;
482   guint fmt_ct = 0;
483
484   g_return_if_fail (GST_IS_BIN (bin));
485   g_return_if_fail (file_tmpl != NULL);
486
487   /* check file-name template */
488   pos = strchr (file_tmpl, '%');
489   if (pos) {
490     do {
491       pos++;
492       if (*pos != '\0') {
493         if (*pos != '%')
494           fmt_ct++;
495         pos++;
496       }
497       pos = strchr (pos, '%');
498     } while (pos);
499   }
500   if (fmt_ct == 0) {
501     GST_WARNING ("file template has no valid placeholder");
502     return;
503   } else if (fmt_ct != 4) {
504     GST_WARNING ("file template must have 4 placeholders, but has %d", fmt_ct);
505     return;
506   }
507
508   /* add timestamp */
509   g_get_current_time (&now);
510   elapsed = GST_TIMEVAL_TO_TIME (now) - _gst_info_start_time;
511   file_name = g_strdup_printf (file_tmpl, GST_TIME_ARGS (elapsed));
512
513   _gst_debug_bin_to_dot_file (bin, details, file_name);
514   g_free (file_name);
515 }
516
517 #endif /* GST_DISABLE_GST_DEBUG */