added empty string for category 31, lest the coloration break
[platform/upstream/gstreamer.git] / gst / gstinfo.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstinfo.c: INFO, ERROR, and DEBUG systems
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include <dlfcn.h>
24 #include "gst_private.h"
25 #include "gstelement.h"
26 #include "gstpad.h"
27
28 extern gchar *_gst_progname;
29
30
31 /***** Categories and colorization *****/
32 static gchar *_gst_info_category_strings[] = {
33   "GST_INIT",
34   "COTHREADS",
35   "COTHREAD_SWITCH",
36   "AUTOPLUG",
37   "AUTOPLUG_ATTEMPT",
38   "PARENTAGE",
39   "STATES",
40   "PLANNING",
41   "SCHEDULING",
42   "DATAFLOW",
43   "BUFFER",
44   "CAPS",
45   "CLOCK",
46   "ELEMENT_PADS",
47   "ELEMENTFACTORY",
48   "PADS",
49   "PIPELINE",
50   "PLUGIN_LOADING",
51   "PLUGIN_ERRORS",
52   "PLUGIN_INFO",
53   "PROPERTIES",
54   "THREAD",
55   "TYPES",
56   "XML",
57   "NEGOTIATION",
58   "REFCOUNTING",
59   "EVENT",
60   "PARAMS",
61
62   [30] = "CALL_TRACE",
63 };
64
65 /**
66  * gst_get_category_name:
67  * @category: the category to return the name of
68  *
69  * Returns: string containing the name of the category
70  */
71 const gchar *
72 gst_get_category_name (gint category) {
73   if ((category >= 0) && (category < GST_CAT_MAX_CATEGORY))
74     return _gst_info_category_strings[category];
75   else
76     return NULL;
77 }
78
79
80 /*
81  * Attribute codes:
82  * 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed
83  * Text color codes:
84  * 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
85  * Background color codes:
86  * 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white
87  */
88 const gchar *_gst_category_colors[32] = {
89   [GST_CAT_GST_INIT]            = "07;37",
90   [GST_CAT_COTHREADS]           = "00;32",
91   [GST_CAT_COTHREAD_SWITCH]     = "00;37;42",
92   [GST_CAT_AUTOPLUG]            = "00;34",
93   [GST_CAT_AUTOPLUG_ATTEMPT]    = "00;36;44",
94   [GST_CAT_PARENTAGE]           = "01;37;41",           // !!
95   [GST_CAT_STATES]              = "00;31",
96   [GST_CAT_PLANNING]            = "07;35",
97   [GST_CAT_SCHEDULING]          = "00;35",
98   [GST_CAT_DATAFLOW]            = "00;32",
99   [GST_CAT_BUFFER]              = "00;32",
100   [GST_CAT_CAPS]                = "04;34",
101   [GST_CAT_CLOCK]               = "00;33",              // !!
102   [GST_CAT_ELEMENT_PADS]        = "01;37;41",           // !!
103   [GST_CAT_ELEMENTFACTORY]      = "01;37;41",           // !!
104   [GST_CAT_PADS]                = "01;37;41",           // !!
105   [GST_CAT_PIPELINE]            = "01;37;41",           // !!
106   [GST_CAT_PLUGIN_LOADING]      = "00;36",
107   [GST_CAT_PLUGIN_ERRORS]       = "05;31",
108   [GST_CAT_PLUGIN_INFO]         = "00;36",
109   [GST_CAT_PROPERTIES]          = "00;37;44",           // !!
110   [GST_CAT_THREAD]              = "00;31",
111   [GST_CAT_TYPES]               = "01;37;41",           // !!
112   [GST_CAT_XML]                 = "01;37;41",           // !!
113   [GST_CAT_NEGOTIATION]         = "07;34",
114   [GST_CAT_REFCOUNTING]         = "00;34:42",
115   [GST_CAT_EVENT]               = "01;37;41",           // !!
116   [GST_CAT_PARAMS]              = "00;30;43",           // !!
117
118   [GST_CAT_CALL_TRACE]          = "",
119   [31]                          = "",
120 };
121
122 /* colorization hash - DEPRACATED in favor of above */
123 inline gint _gst_debug_stringhash_color(gchar *file) {
124   int filecolor = 0;
125   while (file[0]) filecolor += *(char *)(file++);
126   filecolor = (filecolor % 6) + 31;
127   return filecolor;
128 }
129
130
131
132 /***** DEBUG system *****/
133 GstDebugHandler _gst_debug_handler = gst_default_debug_handler;
134 guint32 _gst_debug_categories = 0x00000000;
135
136 /**
137  * gst_default_debug_handler:
138  * @category: category of the DEBUG message
139  * @incore: if the debug handler is for core code.
140  * @file: the file the DEBUG occurs in
141  * @function: the function the DEBUG occurs in
142  * @line: the line number in the file
143  * @debug_string: the current debug_string in the function, if any
144  * @element: pointer to the #GstElement in question
145  * @string: the actual DEBUG string
146  *
147  * Prints out the DEBUG mesage in a variant of the following form:
148  *
149  *   DEBUG(pid:cid):gst_function:542(args): [elementname] something neat happened
150  */
151 void
152 gst_default_debug_handler (gint category, gboolean incore,
153                            const gchar *file, const gchar *function,
154                            gint line, const gchar *debug_string,
155                            void *element, gchar *string)
156   __attribute__ ((no_instrument_function));
157
158 void
159 gst_default_debug_handler (gint category, gboolean incore,
160                            const gchar *file, const gchar *function,
161                            gint line, const gchar *debug_string,
162                            void *element, gchar *string)
163 {
164   gchar *empty = "";
165   gchar *elementname = empty,*location = empty;
166   int pthread_id = getpid();
167   int cothread_id = cothread_getcurrent();
168 #ifdef GST_DEBUG_COLOR
169   int pthread_color = pthread_id%6 + 31;
170   int cothread_color = (cothread_id < 0) ? 37 : (cothread_id%6 + 31);
171 #endif
172
173   if (debug_string == NULL) debug_string = "";
174 //  if (category != GST_CAT_GST_INIT)
175     location = g_strdup_printf("%s:%d%s:",function,line,debug_string);
176   if (element && GST_IS_ELEMENT (element))
177 #ifdef GST_DEBUG_COLOR
178     elementname = g_strdup_printf (" \033[04m[%s]\033[00m", GST_OBJECT_NAME (element));
179 #else
180     elementname = g_strdup_printf (" [%s]", GST_OBJECT_NAME (element));
181 #endif
182
183 #ifdef GST_DEBUG_COLOR
184   fprintf(stderr,"DEBUG(\033[00;%dm%5d\033[00m:\033[00;%dm%2d\033[00m)\033["
185           "%s;%sm%s%s\033[00m %s",
186           pthread_color,pthread_id,cothread_color,cothread_id,incore?"00":"01",
187           _gst_category_colors[category],location,elementname,string);
188 #else
189   fprintf(stderr,"DEBUG(%5d:%2d)%s%s %s",
190           pthread_id,cothread_id,location,elementname,string);
191 #endif /* GST_DEBUG_COLOR */
192
193   if (location != empty) g_free(location);
194   if (elementname != empty) g_free(elementname);
195
196   g_free(string);
197 }
198
199
200 /**
201  * gst_debug_set_categories:
202  * @categories: bitmask of DEBUG categories to enable
203  *
204  * Enable the output of DEBUG categories based on the given bitmask.
205  * The bit for any given category is (1 << GST_CAT_...).
206  */
207 void
208 gst_debug_set_categories (guint32 categories) {
209   _gst_debug_categories = categories;
210   if (categories)
211     GST_INFO (0, "setting DEBUG categories to 0x%08X",categories);
212 }
213
214 /**
215  * gst_debug_get_categories:
216  *
217  * Returns: the current bitmask of enabled DEBUG categories
218  * The bit for any given category is (1 << GST_CAT_...).
219  */
220 guint32
221 gst_debug_get_categories () {
222   return _gst_debug_categories;
223 }
224
225 /**
226  * gst_debug_enable_category:
227  * @category: the category to enable
228  *
229  * Enables the given GST_CAT_... DEBUG category.
230  */
231 void
232 gst_debug_enable_category (gint category) {
233   _gst_debug_categories |= (1 << category);
234   if (_gst_debug_categories)
235     GST_INFO (0, "setting DEBUG categories to 0x%08X",_gst_debug_categories);
236 }
237
238 /**
239  * gst_debug_disable_category:
240  * @category: the category to disable
241  *
242  * Disables the given GST_CAT_... DEBUG category.
243  */
244 void
245 gst_debug_disable_category (gint category) {
246   _gst_debug_categories &= ~ (1 << category);
247   if (_gst_debug_categories)
248     GST_INFO (0, "setting DEBUG categories to 0x%08X",_gst_debug_categories);
249 }
250
251
252
253
254 /***** INFO system *****/
255 GstInfoHandler _gst_info_handler = gst_default_info_handler;
256 guint32 _gst_info_categories = 0x00000001;
257
258
259 /**
260  * gst_default_info_handler:
261  * @category: category of the INFO message
262  * @incore: if the info handler is for core code.
263  * @file: the file the INFO occurs in
264  * @function: the function the INFO occurs in
265  * @line: the line number in the file
266  * @debug_string: the current debug_string in the function, if any
267  * @element: pointer to the #GstElement in question
268  * @string: the actual INFO string
269  *
270  * Prints out the INFO mesage in a variant of the following form:
271  *
272  *   INFO:gst_function:542(args): [elementname] something neat happened
273  */
274 void
275 gst_default_info_handler (gint category, gboolean incore,
276                           const gchar *file, const gchar *function,
277                           gint line, const gchar *debug_string,
278                           void *element, gchar *string)
279 {
280   gchar *empty = "";
281   gchar *elementname = empty,*location = empty;
282   int pthread_id = getpid();
283   int cothread_id = cothread_getcurrent();
284 #ifdef GST_DEBUG_COLOR
285   int pthread_color = pthread_id%6 + 31;
286   int cothread_color = (cothread_id < 0) ? 37 : (cothread_id%6 + 31);
287 #endif
288
289   if (debug_string == NULL) debug_string = "";
290   if (category != GST_CAT_GST_INIT)
291     location = g_strdup_printf("%s:%d%s:",function,line,debug_string);
292   if (element && GST_IS_ELEMENT (element))
293     elementname = g_strdup_printf (" \033[04m[%s]\033[00m", GST_OBJECT_NAME (element));
294
295 /*
296 #ifdef GST_DEBUG_ENABLED
297 */
298   #ifdef GST_DEBUG_COLOR
299     fprintf(stderr,"\033[01mINFO\033[00m (\033[00;%dm%5d\033[00m:\033[00;%dm%2d\033[00m)\033["
300             GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n",
301             pthread_color,pthread_id,cothread_color,cothread_id,
302             _gst_category_colors[category],location,elementname,string);
303   #else
304     fprintf(stderr,"INFO (%5d:%2d)%s%s %s\n",
305             pthread_id,cothread_id,location,elementname,string);
306   #endif // GST_DEBUG_COLOR
307 /*
308 #else
309   #ifdef GST_DEBUG_COLOR
310     fprintf(stderr,"\033[01mINFO\033[00m:\033[" GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n",
311             location,elementname,_gst_category_colors[category],string);
312   #else
313     fprintf(stderr,"INFO:%s%s %s\n",
314             location,elementname,string);
315   #endif // GST_DEBUG_COLOR
316 #endif
317 */
318
319   if (location != empty) g_free(location);
320   if (elementname != empty) g_free(elementname);
321
322   g_free(string);
323 }
324
325 /**
326  * gst_info_set_categories:
327  * @categories: bitmask of INFO categories to enable
328  *
329  * Enable the output of INFO categories based on the given bitmask.
330  * The bit for any given category is (1 << GST_CAT_...).
331  */
332 void
333 gst_info_set_categories (guint32 categories) {
334   _gst_info_categories = categories;
335   if (categories)
336     GST_INFO (0, "setting INFO categories to 0x%08X",categories);
337 }
338
339 /**
340  * gst_info_get_categories:
341  *
342  * Returns: the current bitmask of enabled INFO categories
343  * The bit for any given category is (1 << GST_CAT_...).
344  */
345 guint32
346 gst_info_get_categories () {
347   return _gst_info_categories;
348 }
349
350 /**
351  * gst_info_enable_category:
352  * @category: the category to enable
353  *
354  * Enables the given GST_CAT_... INFO category.
355  */
356 void
357 gst_info_enable_category (gint category) {
358   _gst_info_categories |= (1 << category);
359   if (_gst_info_categories)
360     GST_INFO (0, "setting INFO categories to 0x%08X",_gst_info_categories);
361 }
362
363 /**
364  * gst_info_disable_category:
365  * @category: the category to disable
366  *
367  * Disables the given GST_CAT_... INFO category.
368  */
369 void
370 gst_info_disable_category (gint category) {
371   _gst_info_categories &= ~ (1 << category);
372   if (_gst_info_categories)
373     GST_INFO (0, "setting INFO categories to 0x%08X",_gst_info_categories);
374 }
375
376
377
378 /***** ERROR system *****/
379 GstErrorHandler _gst_error_handler = gst_default_error_handler;
380
381 /**
382  * gst_default_error_handler:
383  * @file: the file the ERROR occurs in
384  * @function: the function the INFO occurs in
385  * @line: the line number in the file
386  * @debug_string: the current debug_string in the function, if any
387  * @element: pointer to the #GstElement in question
388  * @object: pointer to a related object
389  * @string: the actual ERROR string
390  *
391  * Prints out the given ERROR string in a variant of the following format:
392  *
393  * ***** GStreamer ERROR ***** in file gstsomething.c at gst_function:399(arg)
394  * Element: /pipeline/thread/element.src
395  * Error: peer is null!
396  * ***** attempting to stack trace.... *****
397  *
398  * At the end, it attempts to print the stack trace via GDB.
399  */
400 void
401 gst_default_error_handler (gchar *file, gchar *function,
402                            gint line, gchar *debug_string,
403                            void *element, void *object, gchar *string)
404 {
405   int chars = 0;
406   gchar *path;
407   int i;
408
409   // if there are NULL pointers, point them to null strings to clean up output
410   if (!debug_string) debug_string = "";
411   if (!string) string = "";
412
413   // print out a preamble
414   fprintf(stderr,"***** GStreamer ERROR ***** in file %s at %s:%d%s\n",
415           file,function,line,debug_string);
416
417   // if there's an element, print out the pertinent information
418   if (element) {
419     if (GST_IS_OBJECT(element)) {
420       path = gst_object_get_path_string(element);
421       fprintf(stderr,"Element: %s",path);
422       chars = 9 + strlen(path);
423       g_free(path);
424     } else {
425       fprintf(stderr,"Element ptr: %p",element);
426       chars = 15 + sizeof(void*)*2;
427     }
428   }
429
430   // if there's an object, print it out as well
431   if (object) {
432     // attempt to pad the line, or create a new one
433     if (chars < 40)
434       for (i=0;i<(40-chars)/8+1;i++) fprintf(stderr,"\t");
435     else
436       fprintf(stderr,"\n");
437
438     if (GST_IS_OBJECT(object)) {
439       path = gst_object_get_path_string(object);
440       fprintf(stderr,"Object: %s",path);
441       g_free(path);
442     } else {
443       fprintf(stderr,"Object ptr: %p",object);
444     }
445   }
446
447   fprintf(stderr,"\n");
448   fprintf(stderr,"Error: %s\n",string);
449
450   g_free(string);
451
452   fprintf(stderr,"***** attempting to stack trace.... *****\n");
453
454   g_on_error_stack_trace (_gst_progname);
455
456   exit(1);
457 }
458
459
460
461 /***** DEBUG system *****/
462 GHashTable *__gst_function_pointers = NULL;
463 // FIXME make this thread specific
464 static GSList* stack_trace = NULL;
465
466 gchar *_gst_debug_nameof_funcptr (void *ptr) __attribute__ ((no_instrument_function));
467
468 gchar *
469 _gst_debug_nameof_funcptr (void *ptr)
470 {
471   gchar *ptrname;
472   Dl_info dlinfo;
473   if (__gst_function_pointers && (ptrname = g_hash_table_lookup(__gst_function_pointers,ptr))) {
474     return g_strdup(ptrname);
475   } else if (dladdr(ptr,&dlinfo) && dlinfo.dli_sname) {
476     return g_strdup(dlinfo.dli_sname);
477   } else {
478     return g_strdup_printf("%p",ptr);
479   }
480   return NULL;
481 }
482
483
484 #ifdef GST_ENABLE_FUNC_INSTRUMENTATION
485 void __cyg_profile_func_enter(void *this_fn,void *call_site) __attribute__ ((no_instrument_function));
486 void __cyg_profile_func_enter(void *this_fn,void *call_site) 
487 {
488   gchar *name = _gst_debug_nameof_funcptr (this_fn);
489   gchar *site = _gst_debug_nameof_funcptr (call_site);
490         
491   GST_DEBUG(GST_CAT_CALL_TRACE, "entering function %s from %s\n", name, site);
492   stack_trace = g_slist_prepend (stack_trace, g_strdup_printf ("%8p in %s from %p (%s)", this_fn, name, call_site, site));
493
494   g_free (name);
495   g_free (site);
496 }
497
498 void __cyg_profile_func_exit(void *this_fn,void *call_site) __attribute__ ((no_instrument_function));
499 void __cyg_profile_func_exit(void *this_fn,void *call_site) 
500 {
501   gchar *name = _gst_debug_nameof_funcptr (this_fn);
502
503   GST_DEBUG(GST_CAT_CALL_TRACE, "leaving function %s\n", name);
504   g_free (stack_trace->data);
505   stack_trace = g_slist_delete_link (stack_trace, stack_trace);
506
507   g_free (name);
508 }
509
510 void 
511 gst_debug_print_stack_trace (void)
512 {
513   GSList *walk = stack_trace;
514   gint count = 0;
515
516   if (walk)
517     walk = g_slist_next (walk);
518
519   while (walk) {
520     gchar *name = (gchar *) walk->data;
521
522     g_print ("#%-2d %s\n", count++, name);
523
524     walk = g_slist_next (walk);
525   }
526 }
527 #else
528 void 
529 gst_debug_print_stack_trace (void)
530 {
531 }
532
533 #endif /* GST_ENABLE_FUNC_INTSTRUMENTATION */