/* GStreamer * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * * gstinfo.c: INFO, ERROR, and DEBUG systems * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "gst_private.h" #include "gstelement.h" #include "gstpad.h" #include "gstscheduler.h" #if defined __sgi__ #include typedef struct DL_INFO { const char * dli_fname; void * dli_fbase; const char * dli_sname; void * dli_saddr; int dli_version; int dli_reserved1; long dli_reserved[4]; } Dl_info; #define _RLD_DLADDR 14 int dladdr(void *address, Dl_info *dl); int dladdr(void *address, Dl_info *dl) { void *v; v = _rld_new_interface(_RLD_DLADDR,address,dl); return (int)v; } #endif extern gchar *_gst_progname; /***** Categories and colorization *****/ static gchar *_gst_info_category_strings[] = { "GST_INIT", "COTHREADS", "COTHREAD_SWITCH", "AUTOPLUG", "AUTOPLUG_ATTEMPT", "PARENTAGE", "STATES", "PLANNING", "SCHEDULING", "DATAFLOW", "BUFFER", "CAPS", "CLOCK", "ELEMENT_PADS", "ELEMENTFACTORY", "PADS", "PIPELINE", "PLUGIN_LOADING", "PLUGIN_ERRORS", "PLUGIN_INFO", "PROPERTIES", "THREAD", "TYPES", "XML", "NEGOTIATION", "REFCOUNTING", "EVENT", "PARAMS", [30] = "CALL_TRACE", }; /** * gst_get_category_name: * @category: the category to return the name of * * Return a string containing the name of the category * * Returns: string containing the name of the category */ const gchar * gst_get_category_name (gint category) { if ((category >= 0) && (category < GST_CAT_MAX_CATEGORY)) return _gst_info_category_strings[category]; else return NULL; } /* * Attribute codes: * 00=none 01=bold 04=underscore 05=blink 07=reverse 08=concealed * Text color codes: * 30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white * Background color codes: * 40=black 41=red 42=green 43=yellow 44=blue 45=magenta 46=cyan 47=white */ const gchar *_gst_category_colors[32] = { [GST_CAT_GST_INIT] = "07;37", [GST_CAT_COTHREADS] = "00;32", [GST_CAT_COTHREAD_SWITCH] = "00;37;42", [GST_CAT_AUTOPLUG] = "00;34", [GST_CAT_AUTOPLUG_ATTEMPT] = "00;36;44", [GST_CAT_PARENTAGE] = "01;37;41", /* !! */ [GST_CAT_STATES] = "00;31", [GST_CAT_PLANNING] = "07;35", [GST_CAT_SCHEDULING] = "00;35", [GST_CAT_DATAFLOW] = "00;32", [GST_CAT_BUFFER] = "00;32", [GST_CAT_CAPS] = "04;34", [GST_CAT_CLOCK] = "00;33", /* !! */ [GST_CAT_ELEMENT_PADS] = "01;37;41", /* !! */ [GST_CAT_ELEMENT_FACTORY] = "01;37;41", /* !! */ [GST_CAT_PADS] = "01;37;41", /* !! */ [GST_CAT_PIPELINE] = "01;37;41", /* !! */ [GST_CAT_PLUGIN_LOADING] = "00;36", [GST_CAT_PLUGIN_ERRORS] = "05;31", [GST_CAT_PLUGIN_INFO] = "00;36", [GST_CAT_PROPERTIES] = "00;37;44", /* !! */ [GST_CAT_THREAD] = "00;31", [GST_CAT_TYPES] = "01;37;41", /* !! */ [GST_CAT_XML] = "01;37;41", /* !! */ [GST_CAT_NEGOTIATION] = "07;34", [GST_CAT_REFCOUNTING] = "00;34:42", [GST_CAT_EVENT] = "01;37;41", /* !! */ [GST_CAT_PARAMS] = "00;30;43", /* !! */ [GST_CAT_CALL_TRACE] = "", [31] = "05;31", }; /* colorization hash - DEPRACATED in favor of above */ inline gint _gst_debug_stringhash_color(gchar *file) { int filecolor = 0; while (file[0]) filecolor += *(char *)(file++); filecolor = (filecolor % 6) + 31; return filecolor; } /***** DEBUG system *****/ GstDebugHandler _gst_debug_handler = gst_default_debug_handler; guint32 _gst_debug_categories = 0x00000000; /** * gst_default_debug_handler: * @category: category of the DEBUG message * @incore: if the debug handler is for core code. * @file: the file the DEBUG occurs in * @function: the function the DEBUG occurs in * @line: the line number in the file * @debug_string: the current debug_string in the function, if any * @element: pointer to the #GstElement in question * @string: the actual DEBUG string * * Prints out the DEBUG mesage in a variant of the following form: * * DEBUG(pid:cid):gst_function:542(args): [elementname] something neat happened */ void gst_default_debug_handler (gint category, gboolean incore, const gchar *file, const gchar *function, gint line, const gchar *debug_string, void *element, gchar *string) __attribute__ ((no_instrument_function)); void gst_default_debug_handler (gint category, gboolean incore, const gchar *file, const gchar *function, gint line, const gchar *debug_string, void *element, gchar *string) { gchar *empty = ""; gchar *elementname = empty,*location = empty; int pid = getpid(); int cothread_id = 0; /*FIXME*/ #ifdef GST_DEBUG_COLOR int pid_color = pid%6 + 31; int cothread_color = (cothread_id < 0) ? 37 : (cothread_id%6 + 31); #endif if (debug_string == NULL) debug_string = ""; /* if (category != GST_CAT_GST_INIT) */ location = g_strdup_printf("%s:%d%s:",function,line,debug_string); if (element && GST_IS_ELEMENT (element)) #ifdef GST_DEBUG_COLOR elementname = g_strdup_printf (" \033[04m[%s]\033[00m", GST_OBJECT_NAME (element)); #else elementname = g_strdup_printf (" [%s]", GST_OBJECT_NAME (element)); #endif #ifdef GST_DEBUG_COLOR fprintf(stderr,"DEBUG(\033[00;%dm%5d\033[00m:\033[00;%dm%2d\033[00m)\033[" "%s;%sm%s%s\033[00m %s\n", pid_color,pid,cothread_color,cothread_id,incore?"00":"01", _gst_category_colors[category],location,elementname,string); #else fprintf(stderr,"DEBUG(%5d:%2d)%s%s %s\n", pid,cothread_id,location,elementname,string); #endif /* GST_DEBUG_COLOR */ if (location != empty) g_free(location); if (elementname != empty) g_free(elementname); g_free(string); } /** * gst_debug_set_categories: * @categories: bitmask of DEBUG categories to enable * * Enable the output of DEBUG categories based on the given bitmask. * The bit for any given category is (1 << GST_CAT_...). */ void gst_debug_set_categories (guint32 categories) { _gst_debug_categories = categories; } /** * gst_debug_get_categories: * * Return the current bitmask of enabled DEBUG categories * * Returns: the current bitmask of enabled DEBUG categories * The bit for any given category is (1 << GST_CAT_...). */ guint32 gst_debug_get_categories () { return _gst_debug_categories; } /** * gst_debug_enable_category: * @category: the category to enable * * Enables the given GST_CAT_... DEBUG category. */ void gst_debug_enable_category (gint category) { _gst_debug_categories |= (1 << category); } /** * gst_debug_disable_category: * @category: the category to disable * * Disables the given GST_CAT_... DEBUG category. */ void gst_debug_disable_category (gint category) { _gst_debug_categories &= ~ (1 << category); } /***** INFO system *****/ GstInfoHandler _gst_info_handler = gst_default_info_handler; guint32 _gst_info_categories = 0x00000001; /** * gst_default_info_handler: * @category: category of the INFO message * @incore: if the info handler is for core code. * @file: the file the INFO occurs in * @function: the function the INFO occurs in * @line: the line number in the file * @debug_string: the current debug_string in the function, if any * @element: pointer to the #GstElement in question * @string: the actual INFO string * * Prints out the INFO mesage in a variant of the following form: * * INFO:gst_function:542(args): [elementname] something neat happened */ void gst_default_info_handler (gint category, gboolean incore, const gchar *file, const gchar *function, gint line, const gchar *debug_string, void *element, gchar *string) { gchar *empty = ""; gchar *elementname = empty,*location = empty; int pid = getpid(); int cothread_id = 0; /*FIXME*/ #ifdef GST_DEBUG_COLOR int pid_color = pid%6 + 31; int cothread_color = (cothread_id < 0) ? 37 : (cothread_id%6 + 31); #endif if (debug_string == NULL) debug_string = ""; if (category != GST_CAT_GST_INIT) location = g_strdup_printf("%s:%d%s:",function,line,debug_string); if (element && GST_IS_ELEMENT (element)) elementname = g_strdup_printf (" \033[04m[%s]\033[00m", GST_OBJECT_NAME (element)); /* #ifdef GST_DEBUG_ENABLED */ #ifdef GST_DEBUG_COLOR fprintf(stderr,"\033[01mINFO\033[00m (\033[00;%dm%5d\033[00m:\033[00;%dm%2d\033[00m)\033[" GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n", pid_color,pid,cothread_color,cothread_id, _gst_category_colors[category],location,elementname,string); #else fprintf(stderr,"INFO (%5d:%2d)%s%s %s\n", pid,cothread_id,location,elementname,string); #endif /* GST_DEBUG_COLOR */ /* #else #ifdef GST_DEBUG_COLOR fprintf(stderr,"\033[01mINFO\033[00m:\033[" GST_DEBUG_CHAR_MODE ";%sm%s%s\033[00m %s\n", location,elementname,_gst_category_colors[category],string); #else fprintf(stderr,"INFO:%s%s %s\n", location,elementname,string); #endif * GST_DEBUG_COLOR * #endif */ if (location != empty) g_free(location); if (elementname != empty) g_free(elementname); g_free(string); } /** * gst_info_set_categories: * @categories: bitmask of INFO categories to enable * * Enable the output of INFO categories based on the given bitmask. * The bit for any given category is (1 << GST_CAT_...). */ void gst_info_set_categories (guint32 categories) { _gst_info_categories = categories; } /** * gst_info_get_categories: * * Return the current bitmask of enabled INFO categories * The bit for any given category is (1 << GST_CAT_...). * * Returns: the current bitmask of enabled INFO categories * The bit for any given category is (1 << GST_CAT_...). */ guint32 gst_info_get_categories () { return _gst_info_categories; } /** * gst_info_enable_category: * @category: the category to enable * * Enables the given GST_CAT_... INFO category. */ void gst_info_enable_category (gint category) { _gst_info_categories |= (1 << category); } /** * gst_info_disable_category: * @category: the category to disable * * Disables the given GST_CAT_... INFO category. */ void gst_info_disable_category (gint category) { _gst_info_categories &= ~ (1 << category); } /***** ERROR system *****/ GstErrorHandler _gst_error_handler = gst_default_error_handler; /** * gst_default_error_handler: * @file: the file the ERROR occurs in * @function: the function the INFO occurs in * @line: the line number in the file * @debug_string: the current debug_string in the function, if any * @element: pointer to the #GstElement in question * @object: pointer to a related object * @string: the actual ERROR string * * Prints out the given ERROR string in a variant of the following format: * * ***** GStreamer ERROR ***** in file gstsomething.c at gst_function:399(arg) * Element: /pipeline/thread/element.src * Error: peer is null! * ***** attempting to stack trace.... ***** * * At the end, it attempts to print the stack trace via GDB. */ void gst_default_error_handler (gchar *file, gchar *function, gint line, gchar *debug_string, void *element, void *object, gchar *string) { int chars = 0; gchar *path; int i; /* if there are NULL pointers, point them to null strings to clean up output */ if (!debug_string) debug_string = ""; if (!string) string = ""; /* print out a preamble */ fprintf(stderr,"***** GStreamer ERROR ***** in file %s at %s:%d%s\n", file,function,line,debug_string); /* if there's an element, print out the pertinent information */ if (element) { if (GST_IS_OBJECT(element)) { path = gst_object_get_path_string(element); fprintf(stderr,"Element: %s",path); chars = 9 + strlen(path); g_free(path); } else { fprintf(stderr,"Element ptr: %p",element); chars = 15 + sizeof(void*)*2; } } /* if there's an object, print it out as well */ if (object) { /* attempt to pad the line, or create a new one */ if (chars < 40) for (i=0;i<(40-chars)/8+1;i++) fprintf(stderr,"\t"); else fprintf(stderr,"\n"); if (GST_IS_OBJECT(object)) { path = gst_object_get_path_string(object); fprintf(stderr,"Object: %s",path); g_free(path); } else { fprintf(stderr,"Object ptr: %p",object); } } fprintf(stderr,"\n"); fprintf(stderr,"Error: %s\n",string); g_free(string); fprintf(stderr,"***** attempting to stack trace.... *****\n"); g_on_error_stack_trace (_gst_progname); exit(1); } /***** DEBUG system *****/ GHashTable *__gst_function_pointers = NULL; /* FIXME make this thread specific */ /* static GSList *stack_trace = NULL; */ gchar *_gst_debug_nameof_funcptr (void *ptr) __attribute__ ((no_instrument_function)); gchar * _gst_debug_nameof_funcptr (void *ptr) { gchar *ptrname; Dl_info dlinfo; if (__gst_function_pointers && (ptrname = g_hash_table_lookup(__gst_function_pointers,ptr))) { return g_strdup(ptrname); } else if (dladdr(ptr,&dlinfo) && dlinfo.dli_sname) { return g_strdup(dlinfo.dli_sname); } else { return g_strdup_printf("%p",ptr); } return NULL; } #ifdef GST_ENABLE_FUNC_INSTRUMENTATION void __cyg_profile_func_enter(void *this_fn,void *call_site) __attribute__ ((no_instrument_function)); void __cyg_profile_func_enter(void *this_fn,void *call_site) { gchar *name = _gst_debug_nameof_funcptr (this_fn); gchar *site = _gst_debug_nameof_funcptr (call_site); GST_DEBUG(GST_CAT_CALL_TRACE, "entering function %s from %s", name, site); stack_trace = g_slist_prepend (stack_trace, g_strdup_printf ("%8p in %s from %p (%s)", this_fn, name, call_site, site)); g_free (name); g_free (site); } void __cyg_profile_func_exit(void *this_fn,void *call_site) __attribute__ ((no_instrument_function)); void __cyg_profile_func_exit(void *this_fn,void *call_site) { gchar *name = _gst_debug_nameof_funcptr (this_fn); GST_DEBUG(GST_CAT_CALL_TRACE, "leaving function %s", name); g_free (stack_trace->data); stack_trace = g_slist_delete_link (stack_trace, stack_trace); g_free (name); } void gst_debug_print_stack_trace (void) { GSList *walk = stack_trace; gint count = 0; if (walk) walk = g_slist_next (walk); while (walk) { gchar *name = (gchar *) walk->data; g_print ("#%-2d %s\n", count++, name); walk = g_slist_next (walk); } } #else void gst_debug_print_stack_trace (void) { /* nothing because it's compiled out */ } #endif /* GST_ENABLE_FUNC_INTSTRUMENTATION */