summaryrefslogtreecommitdiff
path: root/libyelp/yelp-debug.c
blob: d787409dfeb341f9ac2c53a5e67e0ab98ab32516 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
 * Copyright (C) 2006 Brent Smith <gnome@nextreality.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include <config.h>
#include <glib/gi18n.h>
#include <glib.h>
#include <glib/gprintf.h>
#include <unistd.h>
#include <time.h>

#include "yelp-debug.h"

/*
 * @file:     you should pass the __FILE__ constant as this parameter
 * @line:     you should pass the __LINE__ constant as this parameter
 * @function: you should pass the __FUNCTION__ constant as this parameter
 * @flags:    should be one of #YelpDebugEnums. Arguments can be bitwise OR'd together.
 * @format:   the printf style format
 *
 * This function is not meant to be called directly, instead you should use the
 * debug_print() macro which will pass the __FILE__, __LINE__, and __FUNCTION__ 
 * constants for you.
 * The flags passed to the function identify the type of debug statement.  Some action
 * (usually a print to the console) will take place depending on if the corresponding
 * flag is set in the YELP_DEBUG environment variable.  YELP_DEBUG is a colon separated 
 * list of zero or more the following values: 
 * "log"              - debug_print calls with DB_LOG, DB_INFO, DB_DEBUG, DB_WARN, and
 *                      DB_ERROR will be printed to stdout
 * "info"             - debug_print calls with DB_INFO, DB_DEBUG, DB_WARN and DB_ERROR 
 *                      will be printed to stdout
 * "debug"            - debug_print calls with DB_DEBUG, DB_WARN, and DB_ERROR will be 
 *                      printed to stdout
 * "warn"             - debug_print calls with DB_WARN and DB_ERROR will be printed to 
 *                      stdout
 * "error"            - debug_print calls with DB_ERROR will be printed to stdout
 * "function-calls"   - debug_print (DB_FUNCTION, ...) calls will be printed to stdout
 *                      along with the function name
 * "function-args"    - debug_print (DB_ARG, ...) calls will be printed to stdout
 * "enable-profiling" - debug_print (DB_PROFILE, ...) calls will make an access() call
 *                      with a "MARK:" at the beginning: this is for profiling with 
 *                      Federico's plot-timeline.py python script: 
 *                      see http://primates.ximian.com/~federico/docs/login-profile/plot-timeline.py
 * "all"              - Turns on all options.
 * 
 */
void yelp_debug (const gchar *file,     guint line, 
                 const gchar *function, guint flags, const gchar *format, ...)
{
	static gboolean  first_call = TRUE;
	static guint     debug_flags = 0;
	static gchar   **debug_files = NULL;
	va_list          args;
	const gchar     *debugenv = NULL;
	gchar           *formatted = NULL;
	gchar           *str = NULL;
	gint             i;

	const GDebugKey debug_keys[] = {
	    { "function-calls",   DB_FUNCTION },
	    { "function-args",    DB_ARG }, 
	    { "enable-profiling", DB_PROFILE },
	    { "log",              DB_LOG },
	    { "info",             DB_INFO },
	    { "debug",            DB_DEBUG },
	    { "warn",             DB_WARN },
	    { "error",            DB_ERROR },
	    { "all",              DB_ALL }
	};

	/* figure out which debug flags were set in the environment on the first
	 * call to this function */
	if (first_call) {
		debugenv = g_getenv ("YELP_DEBUG");

		if (debugenv != NULL)
			debug_flags = g_parse_debug_string (debugenv, debug_keys, 
		                                            G_N_ELEMENTS (debug_keys));
		else
			debug_flags = 0;

		/* turn on all flags if "all" option was specified */
		if (debug_flags & DB_ALL)
			debug_flags |= (DB_LOG | DB_INFO | DB_DEBUG | DB_WARN | DB_ERROR | 
			                DB_FUNCTION | DB_ARG | DB_PROFILE);

		/* turn on all higher severity logging levels; for instance if DB_LOG is set
		 * in debug_flags, then we turn on INFO, DEBUG, WARN and ERROR */
		else if (debug_flags & DB_LOG)
			debug_flags |= (DB_INFO | DB_DEBUG | DB_WARN | DB_ERROR);
		else if (debug_flags & DB_INFO)
			debug_flags |= (DB_DEBUG | DB_WARN | DB_ERROR);
		else if (debug_flags & DB_DEBUG)
			debug_flags |= (DB_WARN | DB_ERROR);
		else if (debug_flags & DB_WARN)
			debug_flags |= (DB_ERROR);

		debugenv = g_getenv ("YELP_DEBUG_FILTER");

		if (debugenv != NULL && *debugenv != '\0')
			debug_files = g_strsplit (debugenv, ":", -1);
		else
			debug_files = NULL;
	
		first_call = FALSE;
	}

	/* if none of the flags are set either by the calling function
	 * or in the YELP_DEBUG environment variable, then just return */
	if ((flags & debug_flags) == 0)
		return;

	/* if the YELP_DEBUG_FILTER environment variable was specified with
	 * a colon delimited list of source files, then debug_files will not be
	 * NULL and will contain a list of files for which we print out debug
	 * statements.  Check if this function was called from one of the files
	 * in the list.  If not, return. */
	if (debug_files != NULL) {
		for (i=0; debug_files[i] != NULL; i++) {
			if (*(debug_files[i]) == '\0')
				continue;
			if (g_strrstr (file, debug_files[i]) != NULL)
				break;
		}

		/* if we are on the NULL element, then a match was not found so
		 * we should return now */
		if (debug_files[i] == NULL)
			return;
	}
	
	va_start (args, format);

	if (flags & DB_FUNCTION) {
		g_fprintf  (stdout, "%s:%u: %s: ", file, line, function);
		g_vfprintf (stdout, format, args);
	} else if ((flags & DB_LOG)   ||
	           (flags & DB_INFO)  ||
	           (flags & DB_DEBUG) ||
	           (flags & DB_WARN)  ||
	           (flags & DB_ERROR) ||
	           (flags & DB_ARG)) {
		g_fprintf  (stdout, "%s:%u: ", file, line);
		g_vfprintf (stdout, format, args);
	}

	if (flags & DB_PROFILE) {
		time_t t;
		struct tm *tmp;
		gchar timestamp[20];

		t = time (NULL);
		tmp = localtime(&t);

		strftime (timestamp, 20, "%H:%M:%S", tmp);
		formatted = g_strdup_vprintf (format, args);

		g_fprintf (stdout, "PROFILE [%s]: %s\n", timestamp, formatted);
		str = g_strdup_printf ("MARK: %s: %s", g_get_prgname(), formatted);
		access (str, F_OK);
		g_free (formatted);
		g_free (str);
	}

	va_end (args);
}