[gst-devel] debugging system rewrite

Benjamin Otte in7y118 at public.uni-hamburg.de
Mon Jun 9 19:15:01 CEST 2003


Hi people,

I have rewritten the debugging system to solve some issues that people
(esp me ;)) were having with it, and I'd like to know if it's ok to
commit.
I haven't changed the plugins, that's a boring task I don't want to do if
this gets rejected ;)
The changes are ABI and API incompatible with current debugging stuff.
Attached are a diff for the core and the two files with the big changes if
you don't want to play around. It's documented.

What have I done?
=================
1)
Use GstDebugCategory for each category. Each category has it's own
threshold for when debugging output is printed and human readable stuff
like names and a description what it's good for.
2)
Added GST_ERROR, GST_WARNING and GST_LOG to the already existing
GST_DEBUG and GST_INFO as well as specified in what situation each of
these levels should be used.
3)
Removed unused/useless macros (GST_DEBUG_ENTER/LEAVE).
Unfortunately I also removed GST_DEBUG_ELEMENT which I found to be quite
useful while removing it. I'm in favour of adding it back.
4)
Added GST_CAT_DEBUG/INFO/WARN/LOG/ERROR. This is for when you want
to specify the category and don't want to use a default one.
5)
Made it possible to disable debugging in configure (at least added
infrastructure for that).
6)
Changed the way the debugging system is administered. No more
gst_debug_set_categories but a whole lot of other functions.
7)
Updated command line options to reflect changes.
8)
Core debugging categories are now private to the core.

How to use it?
==============
As a user:
There are the new command line options plus a new environment variable for
your pleasure:
--gst-debug-help
outputs all categories that are avilable
--gst-debug-no-color
disable colored output
--gst-debug-default=LEVEL
sets the default logging level to LEVEL. Default is to only print
something on errors.
btw: --gst-mask=-1 can now be replaced by --gst-debug-default=1
--gst-debug=LIST or env. variable GST_DEBUG=LIST
LIST is in the form CATEGORY1=LEVEL1:CATEGORY2=LEVEL2:...
You can overwrite the levels for categories with that name by this way.
So if you only want output about state changes, you would now do
--gst-debug=STATES=1. The old version was --gst-mask=0x40.

As a developer:
In the core you will mostly want to use GST_CAT_DEBUG (category, ...)
Category names are the same as before.
In the plugins you need to define your own categories now. An easy example
how this works can be seen by looking at the diff of gststaticautoplug.c
(line 6440+ in the big diff).
You have to use those macros to allow disabling the debugging system.
Ok, you do this:
You define your debugging category with
GST_DEBUG_CATEGORY(variable_name) or
GST_DEBUG_CATEGORY_STATIC(variable_name) and if other files should access
it, you use GST_DEBUG_CATEGORY_EXTERN(variable) in the header.
You have to make sure the category is initialized using
GST_DEBUG_CATEGORY_INIT. This will be done in the plugin_init function
most of the time. The category must be inited before you use it for the
first time. (I'd like to allow to define them static, but they use atomic
ints and we don't have static initializers for them.)
You may optionally do a #define GST_CAT_DEFAULT variable_name to set this
category to the default category.
Now whenever you do a GST_DEBUG in your code, it takes the default
category automatically. If you want another category, use GST_CAT_DEBUG.
So writing debugging stuff is 3 lines of category initialization and then
you can use GST_DEBUG the same way you use g_print.


What issues does this fix?
==========================
1)
Allow unlimited categories, so apps,higher level libs and plugins can use
their own categories (note the plural here ;)
2)
Allow finer grained control about what is put out when debugging
(allows for example to do export GST_DEBUG="my_plugin_category=1" so you
can easily test your own plugin).
3)
Allow disabling of colored output. (I hate these logs in vi)
4)
Make GST_DEBUG work like printf.
5)
More control about debugging stuff, handlers and so on. (See the header)


What are the issues?
=======================
1)
I made the core's categories private to the core. It's debatable if this
is needed, but I think it's reasonable.
2)
GST_DEBUG doesn't take a category anymore, this is GST_CAT_DEBUG now. This
breaks all the code we have, but I think it is unreasonable to have a
mcaro named GST_NOCATEGORY_DEBUG and leave GST_DEBUG with the category.
This isn't very problematic for applications, because they couldn't
really use our debugging system anyway (lacking own categories). It's only
an issue for plugins and this is our code, so we can do an easy
s/GST_DEBUG/GST_CAT_DEBUG/ there.
3)
It uses some preprocessor voodoo which I believe is sane, but this is only
me. (Specifically the fact that GST_CAT_DEBUG is a GstDebugCategory and
used as a macro and the fact that structs are defined by macros).



So, I hope to get some input on this, because I want this committed so I
can update some debugging stuff :)

Benjamin
-------------- next part --------------
A non-text attachment was scrubbed...
Name: GST_DEBUG.diff.bz2
Type: application/octet-stream
Size: 65809 bytes
Desc: patch for core HEAD as of June 10th
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20030609/beb4cc2f/attachment.obj>
-------------- next part --------------
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega at cse.ogi.edu>
 *                    2000 Wim Taymans <wtay at chello.be>
 *                    2003 Benjamin Otte <in7y118 at public.uni-hamburg.de>
 *
 * gstinfo.c: debugging functions
 *
 * 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.
 */


#ifndef GST_DISABLE_GST_DEBUG

#include <dlfcn.h>
#include <unistd.h>
#include "gstinfo.h"
#include "gstlog.h"
#include "gst_private.h"
#include "gstelement.h"
#include "gstpad.h"
#include "gstscheduler.h"
#include "gst_private.h"

GST_DEBUG_CATEGORY_STATIC(GST_CAT_DEBUG);

#if defined __sgi__
#include <rld_interface.h>
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;

static void	gst_debug_set_threshold_for_name_func (gchar *		message,
						 GstDebugLevel		level);
static void	gst_debug_reset_threshold	(gpointer category,
						 gpointer unused);
static void	gst_debug_reset_all_thresholds	(void);

static GStaticMutex __hash_mutex = G_STATIC_MUTEX_INIT;
static GHashTable *__level_by_name = NULL;

static GStaticMutex __cat_mutex = G_STATIC_MUTEX_INIT;
static GSList *__categories = NULL;

static GStaticMutex __log_func_mutex = G_STATIC_MUTEX_INIT;
static GstLogFunction __log_func = gst_debug_log_default;
static gpointer __log_func_data = NULL;

static GstAtomicInt __default_level;
static GstAtomicInt __use_color;


GstDebugCategory *GST_CAT_DEFAULT = NULL;

GstDebugCategory *GST_CAT_GST_INIT = NULL;
GstDebugCategory *GST_CAT_COTHREADS = NULL;
GstDebugCategory *GST_CAT_COTHREAD_SWITCH = NULL;
GstDebugCategory *GST_CAT_AUTOPLUG = NULL;
GstDebugCategory *GST_CAT_AUTOPLUG_ATTEMPT = NULL;
GstDebugCategory *GST_CAT_PARENTAGE = NULL;
GstDebugCategory *GST_CAT_STATES = NULL;
GstDebugCategory *GST_CAT_PLANNING = NULL;
GstDebugCategory *GST_CAT_SCHEDULING = NULL;
GstDebugCategory *GST_CAT_DATAFLOW = NULL;
GstDebugCategory *GST_CAT_BUFFER = NULL;
GstDebugCategory *GST_CAT_CAPS = NULL;
GstDebugCategory *GST_CAT_CLOCK = NULL;
GstDebugCategory *GST_CAT_ELEMENT_PADS = NULL;
GstDebugCategory *GST_CAT_ELEMENT_FACTORY = NULL;
GstDebugCategory *GST_CAT_PADS = NULL;
GstDebugCategory *GST_CAT_PIPELINE = NULL;
GstDebugCategory *GST_CAT_PLUGIN_LOADING = NULL;
GstDebugCategory *GST_CAT_PLUGIN_ERRORS = NULL;
GstDebugCategory *GST_CAT_PLUGIN_INFO = NULL;
GstDebugCategory *GST_CAT_PROPERTIES = NULL;
GstDebugCategory *GST_CAT_THREAD = NULL;
GstDebugCategory *GST_CAT_TYPES = NULL;
GstDebugCategory *GST_CAT_XML = NULL;
GstDebugCategory *GST_CAT_NEGOTIATION = NULL;
GstDebugCategory *GST_CAT_REFCOUNTING = NULL;
GstDebugCategory *GST_CAT_EVENT = NULL;
GstDebugCategory *GST_CAT_PARAMS = NULL;
GstDebugCategory *GST_CAT_CALL_TRACE = NULL;

/**
 * _gst_debug_init:
 * Initializes the debugging system.
 * Normally you don't want to call this, because gst_init does it for you.
 */
void _gst_debug_init (void)
{
  gst_atomic_int_init (&__default_level, GST_DEBUG_LEVEL_DEFAULT);
  gst_atomic_int_init (&__use_color, 1);

  __level_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

  GST_CAT_DEFAULT	= _gst_debug_category_new ("default", NULL, NULL);
  GST_CAT_DEBUG		= _gst_debug_category_new ("DEBUG", NULL, "debugging subsystem");

  /* FIXME: add descriptions here */
  GST_CAT_GST_INIT	= _gst_debug_category_new ("GST_INIT", "01;07;37", NULL);
  GST_CAT_COTHREADS	= _gst_debug_category_new ("COTHREADS", "01;00;32", NULL);
  GST_CAT_COTHREAD_SWITCH = _gst_debug_category_new ("COTHREAD_SWITCH", "01;00;37;42", NULL);
  GST_CAT_AUTOPLUG	= _gst_debug_category_new ("AUTOPLUG", "01;00;34", NULL);
  GST_CAT_AUTOPLUG_ATTEMPT = _gst_debug_category_new ("AUTOPLUG_ATTEMPT", "01;00;36;44", NULL);
  GST_CAT_PARENTAGE	= _gst_debug_category_new ("PARENTAGE", "01;01;37;41", NULL);
  GST_CAT_STATES	= _gst_debug_category_new ("STATES", "01;00;31", NULL);
  GST_CAT_PLANNING	= _gst_debug_category_new ("PLANNING", "01;07;35", NULL);
  GST_CAT_SCHEDULING	= _gst_debug_category_new ("SCHEDULING", "01;00;35", NULL);
  GST_CAT_DATAFLOW	= _gst_debug_category_new ("DATAFLOW", "01;00;32", NULL);
  GST_CAT_BUFFER	= _gst_debug_category_new ("BUFFER", "01;00;32", NULL);
  GST_CAT_CAPS		= _gst_debug_category_new ("CAPS", "01;04;34", NULL);
  GST_CAT_CLOCK		= _gst_debug_category_new ("CLOCK", "01;00;33", NULL);
  GST_CAT_ELEMENT_PADS	= _gst_debug_category_new ("ELEMENT_PADS", "01;01;37;41", NULL);
  GST_CAT_ELEMENT_FACTORY = _gst_debug_category_new ("ELEMENT_FACTORY", "01;01;37;41", NULL);
  GST_CAT_PADS		= _gst_debug_category_new ("PADS", "01;01;37;41", NULL);
  GST_CAT_PIPELINE	= _gst_debug_category_new ("PIPELINE", "01;01;37;41", NULL);
  GST_CAT_PLUGIN_LOADING = _gst_debug_category_new ("PLUGIN_LOADING", "01;00;36", NULL);
  GST_CAT_PLUGIN_ERRORS	= _gst_debug_category_new ("PLUGIN_ERRORS", "01;05;31", NULL);
  GST_CAT_PLUGIN_INFO	= _gst_debug_category_new ("PLUGIN_INFO", "01;00;36", NULL);
  GST_CAT_PROPERTIES	= _gst_debug_category_new ("PROPERTIES", "01;00;37;44", NULL);
  GST_CAT_THREAD	= _gst_debug_category_new ("THREAD", "01;00;31", NULL);
  GST_CAT_TYPES		= _gst_debug_category_new ("TYPES", "01;01;37;41", NULL);
  GST_CAT_XML		= _gst_debug_category_new ("XML", "01;01;37;41", NULL);
  GST_CAT_NEGOTIATION	= _gst_debug_category_new ("NEGOTIATION", "01;07;34", NULL);
  GST_CAT_REFCOUNTING	= _gst_debug_category_new ("REFCOUNTING", "01;00;34;42", NULL);
  GST_CAT_EVENT		= _gst_debug_category_new ("EVENT", "01;01;37;41", NULL);
  GST_CAT_PARAMS	= _gst_debug_category_new ("PARAMS", "01;00;30;43", NULL);
  GST_CAT_CALL_TRACE	= _gst_debug_category_new ("CALL_TRACE", NULL, NULL);
}

/* we can't do this further above, because we initialize the GST_CAT_DEFAULT struct */
#define GST_CAT_DEFAULT GST_CAT_DEBUG

/**
 * gst_debug_log:
 * @category: category to log
 * @level: level of the message is in
 * @file: the file that emitted the message, usually the __FILE__ identifier
 * @function: the function that emitted the message
 * @line: the line from that the message was emitted, usually __LINE__
 * @format: a printf style format string
 * @...: optional arguments for the format
 * Logs the given message using the current debugging handler.
 */
void gst_debug_log (GstDebugCategory *category, GstDebugLevel level,
		    const gchar *file, const gchar *function, gint line,
		    gchar *format, ...)
{
  va_list var_args;
  
  va_start (var_args, format);
  gst_debug_logv (category, level, file, function, line, format, var_args);
  va_end (var_args);
}
/**
 * gst_debug_logv:
 * @category: category to log
 * @level: level of the message is in
 * @file: the file that emitted the message, usually the __FILE__ identifier
 * @function: the function that emitted the message
 * @line: the line from that the message was emitted, usually __LINE__
 * @format: a printf style format string
 * @args: optional arguments for the format
 * Logs the given message using the current debugging handler.
 */
void gst_debug_logv (GstDebugCategory *category, GstDebugLevel level,
		     const gchar *file, const gchar *function, gint line,
		     gchar *format, va_list args)
{
  gchar *message;

  g_return_if_fail (category != NULL);
  g_return_if_fail (file != NULL);
  g_return_if_fail (function != NULL);
  g_return_if_fail (format != NULL);

  message = g_strdup_vprintf (format, args);
  __log_func (category, level, file, function, line, message, __log_func_data);
  g_free (message);
}
/**
 * gst_debug_log_default:
 * @category: category to log
 * @level: level of the message
 * @file: the file that emitted the message, usually the __FILE__ identifier
 * @function: the function that emitted the message
 * @line: the line from that the message was emitted, usually __LINE__
 * @message: the actual message
 * @unused: an unused variable, reserved for some user_data.
 * The default logging function used by GStreamer. You can set a different 
 * function by using #gst_debug_set_log_function. This function outputs the
 * message and additional info using the glib error handler.
 * Logging functions get called whenever a macro like GST_DEBUG or similar is 
 * used. 
 */
void
gst_debug_log_default (GstDebugCategory *category, GstDebugLevel level,
		       const gchar *file, const gchar *function, gint line,
		       gchar *message, gpointer unused)
{
  gint pid = getpid();
  gint pid_color = pid % 6 + 31;

  if (gst_debug_is_colored ()) {
    g_printerr ("%s \033[%sm%s\033[00m(\033[00;%dm%5d\033[00m) \033[%sm%s(%d):%s\033[00m: %s\n", 
  		gst_debug_level_get_name (level), gst_debug_category_get_color (category), 
  		gst_debug_category_get_name (category), pid_color, pid,
  		gst_debug_category_get_color (category), file, line, function, 
  		message);
  } else {
    g_printerr ("%s %s(%5d) %s(%d):%s: %s\n", 
  		gst_debug_level_get_name (level), gst_debug_category_get_name (category), 
  		pid, file, line, function, message);
  }
}
/**
 * gst_debug_level_get_name:
 * @level: the level to get the name for
 * Get the string trepresentation of a debugging level
 * Returns: the name
 */
const gchar *
gst_debug_level_get_name (GstDebugLevel level)
{
  switch (level) {
    case GST_DEBUG_NONE:	return "";
    case GST_DEBUG_ERROR:	return "ERROR";
    case GST_DEBUG_WARNING:	return "WARN ";
    case GST_DEBUG_INFO:	return "INFO ";
    case GST_DEBUG_DEBUG:	return "DEBUG";
    case GST_DEBUG_LOG:		return "LOG  ";
    default:
      g_warning ("invalid level specified for gst_debug_level_get_name");
      return "";
  }
}
/**
 * gst_debug_set_log_function:
 * @func: the function to use
 * @data: user data
 * Sets the logging function to the given function.
 * Be sure to use G_GNUC_NO_INSTRUMENT on that function, it is needed.
 */
void
gst_debug_set_log_function (GstLogFunction func, gpointer data)
{
  g_static_mutex_lock (&__log_func_mutex);
  __log_func = func;
  __log_func_data = data;
  g_static_mutex_unlock (&__log_func_mutex);
}
/**
 * gst_debug_reset_log_function:
 * Resets the logging function to the default.
 */
void
gst_debug_reset_log_function (void)
{
   gst_debug_set_log_function (gst_debug_log_default, NULL);
}
/**
 * gst_debug_set_colored:
 * @colored: Whether to use colored output or not
 * Sets or unsets the use of coloured debugging output.
 */
void
gst_debug_set_colored (gboolean colored)
{
  gst_atomic_int_set (&__use_color, colored ? 1 : 0);
}
/**
 * gst_debug_is_colored:
 * Checks if the debugging output should be colored.
 * Returns: TRUE, if the debug output should be colored.
 */
gboolean
gst_debug_is_colored (void)
{
  return gst_atomic_int_read (&__use_color) == 0 ? FALSE : TRUE;
}
/**
 * gst_debug_set_default_threshold:
 * @level: level to set
 * Sets the default threshold to the given level and updates all categories to
 * use this threshold.
 */
void
gst_debug_set_default_threshold (GstDebugLevel level)
{
  gst_atomic_int_set (&__default_level, level);
  gst_debug_reset_all_thresholds ();
}
/**
 * gst_debug_get_default_threshold:
 * Returns the default threshold that is used for new categories.
 * Returns: the default threshold level
 */
GstDebugLevel
gst_debug_get_default_threshold (void)
{
  return (GstDebugLevel) gst_atomic_int_read (&__default_level);
}
static void
gst_debug_reset_threshold (gpointer category, gpointer unused)
{
  GstDebugCategory *cat = (GstDebugCategory *) category;
  gint level;
  
  g_static_mutex_lock (&__hash_mutex);
  level = GPOINTER_TO_INT (g_hash_table_lookup (__level_by_name, cat->name));
  g_static_mutex_unlock (&__hash_mutex);
  if (level == 0) {
    level = gst_debug_get_default_threshold ();
  }
  gst_debug_category_set_threshold (cat, level);
}
static void
gst_debug_reset_all_thresholds (void)
{
  g_static_mutex_lock (&__cat_mutex);
  g_slist_foreach (__categories, gst_debug_reset_threshold, NULL);
  g_static_mutex_unlock (&__cat_mutex);
}
typedef struct {
  gchar *	name;
  GstDebugLevel	level;
} LevelAndName;
static void
for_each_threshold_by_name (gpointer data, gpointer user_data)
{
  GstDebugCategory *cat = (GstDebugCategory *) data;
  LevelAndName *lan = (LevelAndName *) user_data;

  if (strcmp (cat->name, lan->name) == 0) {
    GST_LOG ("category %s matches name %s - gets set to level %d",
             cat->name, lan->name, lan->level);
    gst_debug_category_set_threshold (cat, lan->level);
  }
}
static void
gst_debug_set_threshold_for_name_func (gchar *name, GstDebugLevel level)
{
  LevelAndName lan;
  lan.name = name;
  lan.level = level;
  g_static_mutex_lock (&__cat_mutex);
  g_slist_foreach (__categories, for_each_threshold_by_name, &lan);
  g_static_mutex_unlock (&__cat_mutex);
}
/**
 * gst_debug_set_threshold_for_name:
 * @name: name of the categories to set
 * @level: level to set them to
 * Sets all categories with the given name to the given level.
 */
void
gst_debug_set_threshold_for_name (gchar *name, GstDebugLevel level)
{
  g_return_if_fail (name != NULL);

  g_static_mutex_lock (&__hash_mutex);
  g_hash_table_insert (__level_by_name, g_strdup (name), GINT_TO_POINTER (level));
  g_static_mutex_unlock (&__hash_mutex);
  gst_debug_set_threshold_for_name_func (name, level);
}
/**
 * gst_debug_unset_threshold_for_name:
 * @name: name of the categories to set
 * Resets all categories with the given name back to the default level.
 */
void
gst_debug_unset_threshold_for_name (gchar *name)
{
  g_return_if_fail (name != NULL);

  g_static_mutex_lock (&__hash_mutex);
  g_hash_table_remove (__level_by_name, name);
  g_static_mutex_unlock (&__hash_mutex);
  gst_debug_set_threshold_for_name_func (name, gst_debug_get_default_threshold());
}

GstDebugCategory *
_gst_debug_category_new	(gchar *name, gchar *color, gchar *description)
{
  GstDebugCategory *cat;
  
  g_return_val_if_fail (name != NULL, NULL);

  cat = g_new (GstDebugCategory, 1);
  cat->name = g_strdup (name);
  if (color != NULL) {
    cat->color = g_strdup (color);
  } else {
    cat->color = g_strdup ("00");
  }
  if (description != NULL) {
    cat->description = g_strdup (description);
  } else {
    cat->description = g_strdup ("no description");
  }
  cat->threshold = g_new (GstAtomicInt, 1);
  gst_atomic_int_init (cat->threshold, 0);
  gst_debug_reset_threshold (cat, NULL);

  /* add to category list */
  g_static_mutex_lock (&__cat_mutex);
  __categories = g_slist_prepend (__categories, cat);
  g_static_mutex_unlock (&__cat_mutex);

  return cat;
}
/**
 * gst_debug_category_free:
 * @category: category to remove
 * Removes and frees the category and all associated ressources.
 */
void
gst_debug_category_free (GstDebugCategory *category)
{
  if (category == NULL) return;

  /* remove from category list */
  g_static_mutex_lock (&__cat_mutex);
  __categories = g_slist_remove (__categories, category);
  g_static_mutex_unlock (&__cat_mutex);

  g_free ((gpointer) category->name); 
  g_free ((gpointer) category->color); 
  g_free ((gpointer) category->description); 
  gst_atomic_int_destroy (category->threshold);
  g_free (category->threshold);
  g_free (category);
}
/**
 * gst_debug_category_set_threshold:
 * @category: category to set threshold for
 * @level: the threshold to set
 * Sets the threshold of the category to the given level. Debug information will
 * only be output if the threshold is lower or equal to the level of the 
 * debugging message.
 * Warning: It is suggested not to rely on this function in production code,
 * because other functions may change the threshold of categories as side 
 * effect. It is however a good function to use when debugging (even from gdb).
 */
void
gst_debug_category_set_threshold (GstDebugCategory *category, GstDebugLevel level)
{
  g_return_if_fail (category != NULL);

  gst_atomic_int_set (category->threshold, level);
}
/**
 * gst_debug_category_reset_threshold:
 * @category: category to set threshold for
 * Resets the threshold of the category to the default level. Debug information 
 * will only be output if the threshold is lower or equal to the level of the 
 * debugging message.
 * Use this function to set the threshold back to where it was after using 
 * #gst_debug_category_set_threshold.
 */
void
gst_debug_category_reset_threshold (GstDebugCategory *category)
{
  gst_debug_reset_threshold (category, NULL);
}
/**
 * gst_debug_category_get_threshold:
 * @category: category to get threshold for
 * Returns the threshold of a #GstCategory.
 * Returns: the level that is used as threshold
 */
GstDebugLevel
gst_debug_category_get_threshold (GstDebugCategory *category)
{
  return gst_atomic_int_read (category->threshold);
}
/**
 * gst_debug_category_get_name:
 * @category: category to get name for
 * Returns the name of a #GstCategory.
 * Returns: the name of the category
 */
const gchar *
gst_debug_category_get_name (GstDebugCategory *category)
{
  return category->name;
}
/**
 * gst_debug_category_get_color:
 * @category: category to get color for
 * Returns the color string of a #GstCategory to use when outputting this to a
 * terminal.
 * Returns: the color of the category
 */
const gchar *
gst_debug_category_get_color (GstDebugCategory *category)
{
  return category->color;
}
/**
 * gst_debug_category_get_description:
 * @description: category to get description for
 * Returns the description of a #GstCategory
 * Returns: the description of the category
 */
const gchar *
gst_debug_category_get_description (GstDebugCategory *category)
{
  return category->description;
}
/**
 * gst_debug_get_all_categories:
 * Returns a snapshot of a all categories that are currently in use . This list
 * may change anytime.
 * The caller has to free the list after use.
 * Returns: the list of categories
 */
GSList *
gst_debug_get_all_categories (void)
{
  GSList *ret;

  g_static_mutex_lock (&__cat_mutex);
  ret = g_slist_copy (__categories);
  g_static_mutex_unlock (&__cat_mutex);

  return ret; 
}

/*** FUNCTION POINTERS ********************************************************/

GHashTable *__gst_function_pointers = NULL;
gchar *_gst_debug_nameof_funcptr (void *ptr) G_GNUC_NO_INSTRUMENT;

/* This function MUST NOT return NULL */
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);
  }
}
void *
_gst_debug_register_funcptr (void *ptr, gchar *ptrname)
{
  if (!__gst_function_pointers)
    __gst_function_pointers = g_hash_table_new (g_direct_hash, g_direct_equal);
  if (!g_hash_table_lookup (__gst_function_pointers, ptr))
    g_hash_table_insert (__gst_function_pointers, ptr, ptrname);

  return ptr;
}

#endif /* GST_DISABLE_GST_DEBUG */

#ifdef GST_ENABLE_FUNC_INSTRUMENTATION
/* FIXME make this thread specific */
static GSList *stack_trace = NULL;

void __cyg_profile_func_enter(void *this_fn,void *call_site) G_GNUC_NO_INSTRUMENT;
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_CAT_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) G_GNUC_NO_INSTRUMENT;
void __cyg_profile_func_exit(void *this_fn,void *call_site) 
{
  gchar *name = _gst_debug_nameof_funcptr (this_fn);

  GST_CAT_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 */
-------------- next part --------------
/* GStreamer
 * Copyright (C) 1999,2000 Erik Walthinsen <omega at cse.ogi.edu>
 *                    2000 Wim Taymans <wtay at chello.be>
 *                    2003 Benjamin Otte <in7y118 at public.uni-hamburg.de>
 *
 * gstinfo.h: debugging functions
 *
 * 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.
 */

#ifndef __GSTINFO_H__
#define __GSTINFO_H__

#include <glib.h>
#include <gst/gstatomic.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/**
 * GStreamer's debugging subsystem is an easy way to get information about what
 * the application is doing.
 * It is not meant for programming errors. Use GLibs methods (g_warning and so
 * on for that.
 */

/* log levels */
typedef enum {
  GST_DEBUG_NONE = 0,
  /**
   * Log messages are stuiff that is very common but might be useful to know.
   * Examples for this are refcounting or cothread switches.
   */
  GST_DEBUG_LOG,
  /**
   * Debugging messages should be used when something common happens that is
   * interesting.
   * An example would be a udp reader detecting a packet loss.
   */
  GST_DEBUG_DEBUG,
  /**
   * Information messages should be used to keep the user updated about what is
   * happening. (FIXME: be more specific)
   * Examples where this should be used are when a typefind function has 
   * successfully determined the type of the stream or when an mp3 plugin
   * detects the format to be used. "This is mono"
   */
  GST_DEBUG_INFO,
  /**
   * Warning messages are to inform about abnormal behaviour that could lead to
   * problems later on.
   * An example of this would be clocking issues ("your computer is pretty 
   * slow").
   */
  GST_DEBUG_WARNING,
  /**
   * Error messages are to be used only when an error occured that stops the 
   * application from working correctly.
   * An examples is gst_element_error.
   * It does not mean that the application is terminating as with g_error.
   */
  GST_DEBUG_ERROR,
  
  /* add more */
  GST_DEBUG_LEVELS
} GstDebugLevel;

#ifndef GST_DEBUG_LEVEL_DEFAULT
#define GST_DEBUG_LEVEL_DEFAULT GST_DEBUG_ERROR /* might want to change that to none */
#endif

typedef struct _GstDebugCategory GstDebugCategory;
struct _GstDebugCategory {
  GstAtomicInt *	threshold;
  const gchar *		name;
  const gchar *		color;
  const gchar *		description;
};

/********** some convenience macros for debugging **********/
#define GST_DEBUG_PAD_NAME(pad) \
  (GST_OBJECT_PARENT(pad) != NULL) ? \
  GST_STR_NULL (GST_OBJECT_NAME (GST_OBJECT_PARENT(pad))) : \
  "''", GST_OBJECT_NAME (pad)

/* This is needed in printf's if a char* might be NULL. Solaris crashes then */
#define GST_STR_NULL(str) ((str) ? (str) : "(NULL)")

/* FIXME: convert to using G_STRLOC all the way if we can ! */

#ifndef FUNCTION
#ifdef G_GNUC_PRETTY_FUNCTION
#define FUNCTION G_GNUC_PRETTY_FUNCTION
#elif HAVE_FUNC
#define FUNCTION __func__
#elif HAVE_PRETTY_FUNCTION
#define FUNCTION __PRETTY_FUNCTION__
#elif HAVE_FUNCTION
#define FUNCTION __FUNCTION__
#else
#define FUNCTION ""
#endif
#endif /* ifndef FUNCTION */


typedef void (*GstLogFunction)	(GstDebugCategory *	category,
				 GstDebugLevel		level,
				 const gchar *		file,
				 const gchar *		function,
				 gint			line,
				 gchar *		message,
				 gpointer		data);


#ifdef GST_DISABLE_GST_DEBUG

#pragma GCC poison gst_debug_log
#pragma GCC poison gst_debug_logv

#define gst_debug_set_log_function(func,data)		/* NOP */
#define gst_debug_reset_log_function(void)		/* NOP */
#define gst_debug_set_default_threshold(level)		/* NOP */
#define gst_debug_get_default_threshold()		(GST_DEBUG_NONE)
#define gst_debug_category_set_threshold_for_name(name, level) /* NOP */
#define gst_debug_category_unset_threshold_for_name(name) /* NOP */

#define GST_DEBUG_CATEGORY(var)				/* NOP */
#define GST_DEBUG_CATEGORY_STATIC(var)			/* NOP */
#define GST_DEBUG_CATEGORY_INIT(var,name,color,desc)	/* NOP */
#pragma GCC poison _gst_debug_category_new
#define gst_debug_category_free(category)		/* NOP */
#define gst_debug_category_set_threshold(category,level) /* NOP */
#define gst_debug_category_reset_threshold(category)	/* NOP */

#define GST_CAT_LEVEL_LOG(cat,level,...)		/* NOP */

#define GST_CAT_ERROR(cat,...)				/* NOP */
#define GST_CAT_WARNING(cat,...)			/* NOP */
#define GST_CAT_INFO(cat,...)				/* NOP */
#define GST_CAT_DEBUG(cat,...)				/* NOP */
#define GST_CAT_LOG(cat,...)				/* NOP */

#define GST_ERROR(...)					/* NOP */
#define GST_WARNING(...)				/* NOP */
#define GST_INFO(...)					/* NOP */
#define GST_DEBUG(...)					/* NOP */
#define GST_LOG(...)					/* NOP */

#define GST_DEBUG_FUNCPTR(ptr) (ptr)
#define GST_DEBUG_FUNCPTR_NAME(ptr) ""

#else /* !GST_DISABLE_GST_DEBUG */

void		_gst_debug_init			(void);

void		gst_debug_log			(GstDebugCategory *	category,
						 GstDebugLevel		level,
						 const gchar *		file,
						 const gchar *		function,
						 gint			line,
						 gchar *		format,
						 ...)  G_GNUC_PRINTF (6, 7) G_GNUC_NO_INSTRUMENT;
void		gst_debug_logv			(GstDebugCategory *	category,
						 GstDebugLevel		level,
						 const gchar *		file,
						 const gchar *		function,
						 gint			line,
						 gchar *		format,
						 va_list		args) G_GNUC_NO_INSTRUMENT;

void		gst_debug_log_default		(GstDebugCategory *	category,
						 GstDebugLevel		level,
						 const gchar *		file,
						 const gchar *		function,
						 gint			line,
						 gchar *		message,
						 gpointer		unused) G_GNUC_NO_INSTRUMENT;

const gchar *	gst_debug_level_get_name	(GstDebugLevel		level);

void		gst_debug_set_log_function	(GstLogFunction		func,
						 gpointer data);
void		gst_debug_reset_log_function	(void);

void		gst_debug_set_colored		(gboolean colored);
gboolean	gst_debug_is_colored		(void);

void		gst_debug_set_default_threshold	(GstDebugLevel		level);
GstDebugLevel	gst_debug_get_default_threshold	(void);
void		gst_debug_set_threshold_for_name (gchar *		name,
						 GstDebugLevel		level);
void		gst_debug_unset_threshold_for_name (gchar *		name);

/**
 * GST_DEBUG_CATEGORY:
 * @cat: the category
 * Defines a GstCategory variable.
 * This macro expands to nothing if debugging is disabled.
 */
#define GST_DEBUG_CATEGORY(cat) GstDebugCategory *cat = NULL
/**
 * GST_DEBUG_CATEGORY_EXTERN:
 * @cat: the category
 * Defines an extern GstCategory variable. Use in header files.
 * This macro expands to nothing if debugging is disabled.
 */
#define GST_DEBUG_CATEGORY_EXTERN(cat) extern GstDebugCategory *cat
/**
 * GST_DEBUG_CATEGORY_STATIC:
 * @cat: the category
 * Defines a static GstCategory variable.
 * This macro expands to nothing if debugging is disabled.
 */
#define GST_DEBUG_CATEGORY_STATIC(cat) static GstDebugCategory *cat = NULL
/* do not use this function, use the macros below */
GstDebugCategory *_gst_debug_category_new	(gchar *		name,
						 gchar *		color,
						 gchar *		description);
/**
 * GST_DEBUG_CATEGORY_INIT:
 * @cat: the category to initialize
 * @name: the name of the category
 * @color: the string to use for a color representation in terminals or NULL for
 *         no color
 * @description: optional description of the category
 * Creates a new #GstDebugCategory cat with the given properties and sets it to
 * the default threshold.
 * This macro expands to nothing if debugging is disabled.
 */
#define GST_DEBUG_CATEGORY_INIT(cat,name,color,description) G_STMT_START{	\
  if (cat == NULL)								\
    cat = _gst_debug_category_new (name,color,description);			\
}G_STMT_END
void		gst_debug_category_free		(GstDebugCategory *	category);
void		gst_debug_category_set_threshold (GstDebugCategory *	category,
						 GstDebugLevel		level);
void		gst_debug_category_reset_threshold (GstDebugCategory *	category);
GstDebugLevel	gst_debug_category_get_threshold (GstDebugCategory *	category);
const gchar *	gst_debug_category_get_name	(GstDebugCategory *	category);
const gchar *	gst_debug_category_get_color	(GstDebugCategory *	category);
const gchar *	gst_debug_category_get_description (GstDebugCategory *	category);
GSList *	gst_debug_get_all_categories	(void);


extern GstDebugCategory *	GST_CAT_DEFAULT;

#define GST_CAT_LEVEL_LOG(cat,level,...) G_STMT_START{				\
  if (gst_atomic_int_read (cat->threshold) <= level && 				\
      gst_atomic_int_read (cat->threshold) > 0) {				\
    gst_debug_log (cat, level, __FILE__, FUNCTION, __LINE__, __VA_ARGS__);	\
  }										\
}G_STMT_END

#define GST_CAT_ERROR(cat,...)		GST_CAT_LEVEL_LOG (cat, GST_DEBUG_ERROR, __VA_ARGS__)
#define GST_CAT_WARNING(cat,...)	GST_CAT_LEVEL_LOG (cat, GST_DEBUG_WARNING, __VA_ARGS__)
#define GST_CAT_INFO(cat,...)		GST_CAT_LEVEL_LOG (cat, GST_DEBUG_INFO, __VA_ARGS__)
#define GST_CAT_DEBUG(cat,...)		GST_CAT_LEVEL_LOG (cat, GST_DEBUG_DEBUG, __VA_ARGS__)
#define GST_CAT_LOG(cat,...)		GST_CAT_LEVEL_LOG (cat, GST_DEBUG_LOG, __VA_ARGS__)

#define GST_ERROR(...)			GST_CAT_ERROR (GST_CAT_DEFAULT, __VA_ARGS__)
#define GST_WARNING(...)		GST_CAT_WARNING (GST_CAT_DEFAULT, __VA_ARGS__)
#define GST_INFO(...)			GST_CAT_INFO (GST_CAT_DEFAULT, __VA_ARGS__)
#define GST_DEBUG(...)			GST_CAT_DEBUG (GST_CAT_DEFAULT, __VA_ARGS__)
#define GST_LOG(...)			GST_CAT_LOG (GST_CAT_DEFAULT, __VA_ARGS__)

/********** function pointer stuff **********/
void* 		_gst_debug_register_funcptr	(void *			ptr,
						 gchar *		ptrname);
gchar*		_gst_debug_nameof_funcptr 	(void *			ptr);

#define GST_DEBUG_FUNCPTR(ptr) (_gst_debug_register_funcptr((void *)(ptr), #ptr) , ptr)
#define GST_DEBUG_FUNCPTR_NAME(ptr) _gst_debug_nameof_funcptr((void *)ptr)

#endif /* GST_DISABLE_GST_DEBUG */
void gst_debug_print_stack_trace (void);

#endif /* __GSTINFO_H__ */


More information about the gstreamer-devel mailing list