1 /**
2  * @file dbg.c  Debug printing
3  *
4  * Copyright (C) 2010 Creytiv.com
5  */
6 #include <stdio.h>
7 #ifdef HAVE_UNISTD_H
8 #include <unistd.h>
9 #endif
10 #ifdef HAVE_PTHREAD
11 #include <stdlib.h>
12 #include <pthread.h>
13 #endif
14 #include <time.h>
15 #include <re_types.h>
16 #include <re_fmt.h>
17 #include <re_list.h>
18 #include <re_tmr.h>
19 
20 
21 #define DEBUG_MODULE "dbg"
22 #define DEBUG_LEVEL 0
23 #include <re_dbg.h>
24 
25 
26 /** Debug configuration */
27 static struct {
28 	uint64_t tick;         /**< Init ticks             */
29 	int level;             /**< Current debug level    */
30 	enum dbg_flags flags;  /**< Debug flags            */
31 	dbg_print_h *ph;       /**< Optional print handler */
32 	void *arg;             /**< Handler argument       */
33 	FILE *f;               /**< Logfile                */
34 #ifdef HAVE_PTHREAD
35 	pthread_mutex_t mutex; /**< Thread locking         */
36 #endif
37 } dbg = {
38 	0,
39 	DBG_INFO,
40 	DBG_ANSI,
41 	NULL,
42 	NULL,
43 	NULL,
44 #ifdef HAVE_PTHREAD
45 	PTHREAD_MUTEX_INITIALIZER,
46 #endif
47 };
48 
49 
50 #ifdef HAVE_PTHREAD
dbg_lock(void)51 static inline void dbg_lock(void)
52 {
53 	pthread_mutex_lock(&dbg.mutex);
54 }
55 
56 
dbg_unlock(void)57 static inline void dbg_unlock(void)
58 {
59 	pthread_mutex_unlock(&dbg.mutex);
60 }
61 #else
62 #define dbg_lock()    /**< Stub */
63 #define dbg_unlock()  /**< Stub */
64 #endif
65 
66 
67 /**
68  * Initialise debug printing
69  *
70  * @param level Debug level
71  * @param flags Debug flags
72  */
dbg_init(int level,enum dbg_flags flags)73 void dbg_init(int level, enum dbg_flags flags)
74 {
75 	dbg.tick  = tmr_jiffies();
76 	dbg.level = level;
77 	dbg.flags = flags;
78 }
79 
80 
81 /**
82  * Close debugging
83  */
dbg_close(void)84 void dbg_close(void)
85 {
86 	if (dbg.f) {
87 		(void)fclose(dbg.f);
88 		dbg.f = NULL;
89 	}
90 }
91 
92 
93 /**
94  * Set debug logfile
95  *
96  * @param name Name of the logfile, NULL to close
97  *
98  * @return 0 if success, otherwise errorcode
99  */
dbg_logfile_set(const char * name)100 int dbg_logfile_set(const char *name)
101 {
102 	time_t t;
103 
104 	dbg_close();
105 
106 	if (!name)
107 		return 0;
108 
109 	dbg.f = fopen(name, "a+");
110 	if (!dbg.f)
111 		return errno;
112 
113 	(void)time(&t);
114 	(void)re_fprintf(dbg.f, "\n===== Log Started: %s", ctime(&t));
115 	(void)fflush(dbg.f);
116 
117 	return 0;
118 }
119 
120 
121 /**
122  * Set optional debug print handler
123  *
124  * @param ph  Print handler
125  * @param arg Handler argument
126  */
dbg_handler_set(dbg_print_h * ph,void * arg)127 void dbg_handler_set(dbg_print_h *ph, void *arg)
128 {
129 	dbg.ph  = ph;
130 	dbg.arg = arg;
131 }
132 
133 
134 /* NOTE: This function should not allocate memory */
dbg_vprintf(int level,const char * fmt,va_list ap)135 static void dbg_vprintf(int level, const char *fmt, va_list ap)
136 {
137 	if (level > dbg.level)
138 		return;
139 
140 	/* Print handler? */
141 	if (dbg.ph)
142 		return;
143 
144 	dbg_lock();
145 
146 	if (dbg.flags & DBG_ANSI) {
147 
148 		switch (level) {
149 
150 		case DBG_WARNING:
151 			(void)re_fprintf(stderr, "\x1b[31m"); /* Red */
152 			break;
153 
154 		case DBG_NOTICE:
155 			(void)re_fprintf(stderr, "\x1b[33m"); /* Yellow */
156 			break;
157 
158 		case DBG_INFO:
159 			(void)re_fprintf(stderr, "\x1b[32m"); /* Green */
160 			break;
161 
162 		default:
163 			break;
164 		}
165 	}
166 
167 	if (dbg.flags & DBG_TIME) {
168 		const uint64_t ticks = tmr_jiffies();
169 
170 		if (0 == dbg.tick)
171 			dbg.tick = tmr_jiffies();
172 
173 		(void)re_fprintf(stderr, "[%09llu] ", ticks - dbg.tick);
174 	}
175 
176 	(void)re_vfprintf(stderr, fmt, ap);
177 
178 	if (dbg.flags & DBG_ANSI && level < DBG_DEBUG)
179 		(void)re_fprintf(stderr, "\x1b[;m");
180 
181 	dbg_unlock();
182 }
183 
184 
185 /* Formatted output to print handler and/or logfile */
dbg_fmt_vprintf(int level,const char * fmt,va_list ap)186 static void dbg_fmt_vprintf(int level, const char *fmt, va_list ap)
187 {
188 	char buf[256];
189 	int len;
190 
191 	if (level > dbg.level)
192 		return;
193 
194 	if (!dbg.ph && !dbg.f)
195 		return;
196 
197 	dbg_lock();
198 
199 	len = re_vsnprintf(buf, sizeof(buf), fmt, ap);
200 	if (len <= 0)
201 		goto out;
202 
203 	/* Print handler? */
204 	if (dbg.ph) {
205 		dbg.ph(level, buf, len, dbg.arg);
206 	}
207 
208 	/* Output to file */
209 	if (dbg.f) {
210 		if (fwrite(buf, 1, len, dbg.f) > 0)
211 			(void)fflush(dbg.f);
212 	}
213 
214  out:
215 	dbg_unlock();
216 }
217 
218 
219 /**
220  * Print a formatted debug message
221  *
222  * @param level Debug level
223  * @param fmt   Formatted string
224  */
dbg_printf(int level,const char * fmt,...)225 void dbg_printf(int level, const char *fmt, ...)
226 {
227 	va_list ap;
228 
229 	va_start(ap, fmt);
230 	dbg_vprintf(level, fmt, ap);
231 	va_end(ap);
232 
233 	va_start(ap, fmt);
234 	dbg_fmt_vprintf(level, fmt, ap);
235 	va_end(ap);
236 }
237 
238 
239 /**
240  * Print a formatted debug message to /dev/null
241  *
242  * @param fmt   Formatted string
243  */
dbg_noprintf(const char * fmt,...)244 void dbg_noprintf(const char *fmt, ...)
245 {
246 	(void)fmt;
247 }
248 
249 
250 /**
251  * Print a formatted warning message
252  *
253  * @param fmt   Formatted string
254  */
dbg_warning(const char * fmt,...)255 void dbg_warning(const char *fmt, ...)
256 {
257 	va_list ap;
258 
259 	va_start(ap, fmt);
260 	dbg_vprintf(DBG_WARNING, fmt, ap);
261 	va_end(ap);
262 
263 	va_start(ap, fmt);
264 	dbg_fmt_vprintf(DBG_WARNING, fmt, ap);
265 	va_end(ap);
266 }
267 
268 
269 /**
270  * Print a formatted notice message
271  *
272  * @param fmt   Formatted string
273  */
dbg_notice(const char * fmt,...)274 void dbg_notice(const char *fmt, ...)
275 {
276 	va_list ap;
277 
278 	va_start(ap, fmt);
279 	dbg_vprintf(DBG_NOTICE, fmt, ap);
280 	va_end(ap);
281 
282 	va_start(ap, fmt);
283 	dbg_fmt_vprintf(DBG_NOTICE, fmt, ap);
284 	va_end(ap);
285 }
286 
287 
288 /**
289  * Print a formatted info message
290  *
291  * @param fmt   Formatted string
292  */
dbg_info(const char * fmt,...)293 void dbg_info(const char *fmt, ...)
294 {
295 	va_list ap;
296 
297 	va_start(ap, fmt);
298 	dbg_vprintf(DBG_INFO, fmt, ap);
299 	va_end(ap);
300 
301 	va_start(ap, fmt);
302 	dbg_fmt_vprintf(DBG_INFO, fmt, ap);
303 	va_end(ap);
304 }
305 
306 
307 /**
308  * Get the name of the debug level
309  *
310  * @param level Debug level
311  *
312  * @return String with debug level name
313  */
dbg_level_str(int level)314 const char *dbg_level_str(int level)
315 {
316 	switch (level) {
317 
318 	case DBG_EMERG:   return "EMERGENCY";
319 	case DBG_ALERT:   return "ALERT";
320 	case DBG_CRIT:    return "CRITICAL";
321 	case DBG_ERR:     return "ERROR";
322 	case DBG_WARNING: return "WARNING";
323 	case DBG_NOTICE:  return "NOTICE";
324 	case DBG_INFO:    return "INFO";
325 	case DBG_DEBUG:   return "DEBUG";
326 	default:          return "???";
327 	}
328 }
329