FIX: NOBUG_LOG env parsing error
[nobug.git] / src / nobug.c
blob8bf28a4d1e2b9a44c241a848d19a0bc71fcd223a
1 /*
2 This file is part of the NoBug debugging library.
4 Copyright (C)
5 2007, 2008, 2009, 2010, Christian Thaeter <ct@pipapo.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program 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
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, contact Christian Thaeter <ct@pipapo.org>.
21 #if NOBUG_USE_PTHREAD
22 #include <pthread.h>
23 #endif
25 #define NOBUG_LIBNOBUG_C
26 #include "nobug.h"
28 struct nobug_ringbuffer nobug_default_ringbuffer;
29 FILE* nobug_default_file = NULL;
30 unsigned long long nobug_counter = 0;
31 nobug_logging_cb nobug_logging_callback = NULL;
32 nobug_logging_cb nobug_postlogging_callback = NULL;
33 nobug_abort_cb nobug_abort_callback = NULL;
34 void* nobug_callback_data = NULL;
38 //predefflags HEAD~ Predefined Flags;;
39 //predefflags
40 //predefflags There are some debugging flags which are predefined by NoBug.
41 //predefflags
42 //predefflags PARA NOBUG_ON; NOBUG_ON; log flag which is always enabled
43 //predefflags
44 //predefflags The flag `NOBUG_ON` is always enabled at LOG_DEBUG level. This is
45 //predefflags static and can not be changed.
46 //predefflags
48 struct nobug_flag nobug_flag_NOBUG_ON =
49 {NULL, NULL, 0, {LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, LOG_DEBUG}, NULL, NULL, NULL};
52 //predefflags PARA NOBUG_ANN; NOBUG_ANN; log flag for annotations
53 //predefflags
54 //predefflags The flag `NOBUG_ANN` is used for the source annotations. This is
55 //predefflags static and can not be changed. It differs from `NOBUG_ON` as in
56 //predefflags never logging to syslog and only define a LOG_WARNING limit for the
57 //predefflags application callback.
58 //predefflags
60 struct nobug_flag nobug_flag_NOBUG_ANN =
61 {NULL, NULL, 0, {LOG_DEBUG, LOG_DEBUG, LOG_DEBUG, -1, LOG_WARNING}, NULL, NULL, NULL};
64 //predefflags PARA nobug; nobugflag; log flag used to show nobug actions
65 //predefflags
66 //predefflags Actions on NoBug itself will be logged under the `nobug` flag itself.
67 //predefflags To see whats going on you can enable it with `NOBUG_LOG=nobug:TRACE`.
68 //predefflags This is particulary useful to check if `NOBUG_INIT_FLAG()` is called on all flags.
69 //predefflags
71 struct nobug_flag nobug_flag_nobug =
72 {"nobug", NULL, 0, {LOG_DEBUG, LOG_WARNING, LOG_ERR, LOG_ERR, -1}, NULL, NULL, NULL};
75 #if NOBUG_USE_PTHREAD
76 static void
77 nobug_tls_free (void * tls)
79 free((void*)((struct nobug_tls_data*)tls)->thread_id);
80 free(tls);
82 #endif
85 default initialization
87 static int nobug_initialized = 0;
89 void
90 nobug_init (const struct nobug_context context)
92 if (nobug_initialized)
93 return;
94 nobug_initialized = 1;
96 #if NOBUG_USE_PTHREAD
97 pthread_key_create (&nobug_tls_key, nobug_tls_free);
98 #endif
100 /* we initialize a minimal ringbuffer, if not already done */
101 if (!nobug_default_ringbuffer.start)
102 nobug_ringbuffer_init (&nobug_default_ringbuffer, 4096, 4096, NULL, NOBUG_RINGBUFFER_DEFAULT);
104 /* initialize the always-on flag*/
105 nobug_flag_NOBUG_ON.ringbuffer_target = &nobug_default_ringbuffer;
106 nobug_flag_NOBUG_ON.console_target = stderr;
107 nobug_flag_NOBUG_ON.file_target = nobug_default_file;
108 nobug_flag_NOBUG_ON.initialized = 1;
110 nobug_flag_NOBUG_ANN.ringbuffer_target = &nobug_default_ringbuffer;
111 nobug_flag_NOBUG_ANN.console_target = stderr;
112 nobug_flag_NOBUG_ANN.file_target = nobug_default_file;
113 nobug_flag_NOBUG_ANN.initialized = 1;
115 nobug_resource_init ();
117 nobug_env_init_flag (&nobug_flag_nobug, NOBUG_TARGET_CONSOLE, LOG_WARNING, context);
119 nobug_log (&nobug_flag_nobug, LOG_NOTICE, "NOBUG", context, " INITIALIZED");
121 nobug_coverage_init (context);
125 void
126 nobug_destroy (const struct nobug_context context)
128 if (nobug_initialized)
130 nobug_log (&nobug_flag_nobug, LOG_NOTICE, "NOBUG" , context, " DESTROYING");
132 nobug_resource_destroy ();
134 if (nobug_default_ringbuffer.start)
135 nobug_ringbuffer_destroy (&nobug_default_ringbuffer);
137 #if NOBUG_USE_PTHREAD
138 nobug_tls_free (nobug_thread_get ());
139 pthread_key_delete (nobug_tls_key);
140 #endif
142 nobug_initialized = 0;
146 const char*
147 nobug_basename (const char* const file)
149 return strrchr(file, '/') ? strrchr(file, '/') + 1 : file;
153 void
154 nobug_log_targets (const char* start, struct nobug_flag* flag, int lvl)
156 if (NOBUG_EXPECT_FALSE(lvl <= flag->limits[NOBUG_TARGET_CONSOLE]))
158 #if HAVE_VALGRIND
159 if (NOBUG_EXPECT_FALSE (NOBUG_ACTIVE_DBG == NOBUG_DBG_VALGRIND))
161 VALGRIND_PRINTF ("%s"VALGRIND_NEWLINE, start);
163 else
164 #endif
165 fprintf (flag->console_target?flag->console_target:stderr, "%s\n", start);
168 if (NOBUG_EXPECT_FALSE(lvl <= flag->limits[NOBUG_TARGET_FILE] && flag->file_target))
170 fprintf (flag->file_target, "%s\n", start);
173 if (NOBUG_EXPECT_FALSE(lvl <= flag->limits[NOBUG_TARGET_SYSLOG]))
175 syslog (lvl, "%s\n", start);
178 if (nobug_logging_callback)
179 nobug_logging_callback (flag, lvl, start, nobug_callback_data);
183 void
184 nobug_log_va_ (char* start, char* header, struct nobug_flag* flag, int lvl, const char* fmt, va_list ap)
186 nobug_ringbuffer_printf (flag->ringbuffer_target, "%s", header);
187 nobug_ringbuffer_append (flag->ringbuffer_target);
189 nobug_ringbuffer_vprintf (flag->ringbuffer_target, fmt, ap);
191 char* lines_in[NOBUG_MAX_LOG_LINES];
192 char* lines_out[NOBUG_MAX_LOG_LINES];
193 lines_in[0] = lines_out[0] = start-1;
194 int n=1;
196 while (n<NOBUG_MAX_LOG_LINES-1 &&(lines_in[n] = strchr(lines_in[n-1]+1, '\n')))
197 ++n;
199 if (n>1)
201 lines_in[n] = lines_in[n-1] + strlen(lines_in[n-1]+1) + 1;
203 size_t newlen = lines_in[n]-lines_in[0] - 1 + (strlen (header)+1) * (n-1);
205 if (nobug_ringbuffer_extend (flag->ringbuffer_target, newlen, 0))
207 header[10] = '!';
209 size_t headerlen = strlen(header);
211 for (int i = n-1; i>0; --i)
213 size_t len = lines_in[i+1] - lines_in[i] - 1;
214 char* dst = start+newlen-len;
216 memmove (dst, lines_in[i]+1, len);
217 dst[-1] = ' ';
219 dst -= (headerlen+1);
221 memmove (dst, header, headerlen);
222 dst[-1] ='\0';
224 lines_out[i] = dst-1;
225 newlen -= (len+headerlen+2);
230 for (int i = 0; i < n; ++i)
231 nobug_log_targets (lines_out[i]+1, flag, lvl);
235 char*
236 nobug_log_begin (char* header, struct nobug_flag* flag, const char* what, const struct nobug_context ctx)
238 #if NOBUG_USE_PTHREAD
239 pthread_mutex_lock (&nobug_logging_mutex);
240 #endif
242 snprintf (header, NOBUG_MAX_LOG_HEADER_SIZE, "%.10llu: %s: %s:%d: "
243 #if NOBUG_USE_PTHREAD
244 "%s: "
245 #else
246 "-: " /*no thread id*/
247 #endif
248 "%s:",
249 ++nobug_counter,
250 what?what:"NOBUG",
251 nobug_basename(ctx.file),
252 ctx.line,
253 #if NOBUG_USE_PTHREAD
254 nobug_thread_id_get(),
255 #endif
256 ctx.func);
258 return nobug_ringbuffer_pos (flag->ringbuffer_target);
262 void
263 nobug_log_end (struct nobug_flag* flag, int lvl)
265 #if NOBUG_USE_PTHREAD
266 /* the resource tracker bails out with the resource lock held for logging it, we can release it here now */
267 if (nobug_resource_error)
269 nobug_resource_error = NULL;
270 pthread_mutex_unlock (&nobug_resource_mutex);
273 pthread_mutex_unlock (&nobug_logging_mutex);
274 #endif
276 if (nobug_postlogging_callback)
277 nobug_postlogging_callback (flag, lvl, NULL, nobug_callback_data);
280 /* must be called inbetween log_begin and log_end */
281 void
282 nobug_log_line (char** start, char* header, struct nobug_flag* flag, int lvl, const char* fmt, ...)
284 va_list ap;
285 va_start (ap, fmt);
286 nobug_log_va_ (*start, header, flag, lvl, fmt, ap);
287 va_end (ap);
289 *start = nobug_ringbuffer_pos (flag->ringbuffer_target);
293 void
294 nobug_log (struct nobug_flag* flag, int lvl, const char* what, const struct nobug_context ctx, const char* fmt, ...)
296 char header[NOBUG_MAX_LOG_HEADER_SIZE];
298 char* start = nobug_log_begin (header, flag, what, ctx);
300 va_list ap;
301 va_start (ap, fmt);
302 nobug_log_va_ (start, header, flag, lvl, fmt, ap);
303 va_end (ap);
305 nobug_log_end (flag, lvl);
309 void
310 nobug_backtrace_glibc (const struct nobug_context context)
312 #ifdef HAVE_EXECINFO_H
313 void* nobug_backtrace_buffer[NOBUG_BACKTRACE_DEPTH];
315 int depth = backtrace (nobug_backtrace_buffer, NOBUG_BACKTRACE_DEPTH);
317 char** symbols = backtrace_symbols (nobug_backtrace_buffer, depth);
319 if (symbols)
321 /* start at [1] to suppress nobug_backtrace_glibc itself */
322 int i;
323 for (i = 1; i<depth; ++i)
324 nobug_log (&nobug_flag_NOBUG_ON, LOG_NOTICE, "BACKTRACE", context, " %s", symbols[i]);
326 free (symbols);
328 #endif
331 void
332 nobug_backtrace_valgrind (const struct nobug_context context)
334 #if NOBUG_USE_VALGRIND
335 #if NOBUG_USE_PTHREAD
336 pthread_mutex_lock (&nobug_logging_mutex);
337 #endif
338 VALGRIND_PRINTF_BACKTRACE("----------: BACKTRACE: %s@%d %s",
339 nobug_basename(context.file),
340 context.line,
341 context.func);
342 #if NOBUG_USE_PTHREAD
343 pthread_mutex_unlock (&nobug_logging_mutex);
344 #endif
345 #else
346 (void) context;
347 #endif