Fixed GDB timeout crash - regression introduced back when log_add/remove_callback...
[fw/openocd] / src / helper / log.c
1 /***************************************************************************
2  *   Copyright (C) 2005 by Dominic Rath                                    *
3  *   Dominic.Rath@gmx.de                                                   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "log.h"
25 #include "configuration.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdarg.h>
31 #include <time.h>
32
33 int debug_level = -1;
34
35 static FILE* log_output;
36 static log_callback_t *log_callbacks = NULL;
37
38 static time_t start;
39
40 static char *log_strings[5] =
41 {
42         "User:   ",
43         "Error:  ",
44         "Warning:",
45         "Info:   ",
46         "Debug:  "
47 };
48
49 static int count = 0;
50
51 /* The log_puts() serves to somewhat different goals:
52  * 
53  * - logging
54  * - feeding low-level info to the user in GDB or Telnet
55  * 
56  * The latter dictates that strings without newline are not logged, lest there
57  * will be *MANY log lines when sending one char at the time(e.g. 
58  * target_request.c).
59  * 
60  */
61 static void log_puts(enum log_levels level, const char *file, int line, const char *function, const char *string)
62 {
63         if (level == LOG_OUTPUT)
64         {
65                 /* do not prepend any headers, just print out what we were given and return */
66                 fputs(string, log_output);
67                 fflush(log_output);
68                 return;
69         }
70
71         char *f = strrchr(file, '/');
72         if (f != NULL)
73                 file = f + 1;
74
75         if (strchr(string, '\n')!=NULL)
76         {
77                 if (debug_level >= LOG_DEBUG)
78                 {
79                         /* print with count and time information */
80                         int t=(int)(time(NULL)-start);
81                         fprintf(log_output, "%s %d %d %s:%d %s(): %s", log_strings[level+1], count, t, file, line, function, string);
82                 }
83                 else
84                 {
85                         /* do not print count and time */
86                         fprintf(log_output, "%s %s:%d %s(): %s", log_strings[level+1], file, line, function, string);
87                 }
88         } else
89         {
90                 /* only entire lines are logged. Otherwise it's 
91                  * single chars intended for the log callbacks. */
92         }
93
94         fflush(log_output);
95         
96         /* Never forward LOG_DEBUG, too verbose and they can be found in the log if need be */
97         if (level <= LOG_INFO)
98         {
99                 log_callback_t *cb, *next;
100                 cb = log_callbacks;
101                 /* DANGER!!!! the log callback can remove itself!!!! */
102                 while (cb)
103                 {
104                         next=cb->next;
105                         cb->fn(cb->priv, file, line, function, string);
106                         cb=next;
107                 }
108         }
109 }
110
111 void log_printf(enum log_levels level, const char *file, int line, const char *function, const char *format, ...)
112 {
113         char *string;
114
115         count++;
116         if (level > debug_level)
117                 return;
118
119         va_list ap;
120         va_start(ap, format);
121
122         string = alloc_printf(format, ap);
123         if (string != NULL)
124         {
125                 log_puts(level, file, line, function, string);
126                 free(string);
127         }
128         
129         va_end(ap);
130 }
131
132 void log_printf_lf(enum log_levels level, const char *file, int line, const char *function, const char *format, ...)
133 {
134         char *string;
135
136         count++;
137         if (level > debug_level)
138                 return;
139         
140         va_list ap;
141         va_start(ap, format);
142         
143         string = alloc_printf(format, ap);
144         if (string != NULL)
145         {
146                 strcat(string, "\n"); /* alloc_printf guaranteed the buffer to be at least one char longer */
147                 log_puts(level, file, line, function, string);
148                 free(string);
149         }
150         
151         va_end(ap);
152 }
153
154 /* change the current debug level on the fly
155  * 0: only ERRORS
156  * 1: + WARNINGS
157  * 2: + INFORMATIONAL MSGS
158  * 3: + DEBUG MSGS
159  */
160 int handle_debug_level_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
161 {
162         if (argc == 0)
163                 command_print(cmd_ctx, "debug_level: %i", debug_level);
164
165         if (argc > 0)
166                 debug_level = strtoul(args[0], NULL, 0);
167
168         if (debug_level < 0)
169                 debug_level = 0;
170
171         if (debug_level > 3)
172                 debug_level = 3;
173
174         return ERROR_OK;
175 }
176
177 int handle_log_output_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
178 {
179         if (argc == 1)
180         {
181                 FILE* file = fopen(args[0], "w");
182                 
183                 if (file)
184                 {
185                         log_output = file;
186                 }
187         }
188
189         return ERROR_OK;
190 }
191
192 int log_register_commands(struct command_context_s *cmd_ctx)
193 {
194         start = time(NULL);
195         register_command(cmd_ctx, NULL, "log_output", handle_log_output_command,
196                 COMMAND_ANY, "redirect logging to <file> (default: stderr)");
197         register_command(cmd_ctx, NULL, "debug_level", handle_debug_level_command,
198                 COMMAND_ANY, "adjust debug level <0-3>");
199
200         return ERROR_OK;
201 }
202
203 int log_init(struct command_context_s *cmd_ctx)
204 {
205         /* set defaults for daemon configuration, if not set by cmdline or cfgfile */
206         if (debug_level == -1)
207                 debug_level = LOG_INFO;
208         
209         if (log_output == NULL)
210         {
211                 log_output = stderr;
212         }
213         
214         return ERROR_OK;
215 }
216         
217 int set_log_output(struct command_context_s *cmd_ctx, FILE *output)
218 {
219         log_output = output;
220         return ERROR_OK;
221 }
222
223 /* add/remove log callback handler */
224 int log_add_callback(log_callback_fn fn, void *priv)
225 {
226         log_callback_t *cb;
227
228         /* prevent the same callback to be registered more than once, just for sure */
229         for (cb = log_callbacks; cb; cb = cb->next)
230         {
231                 if (cb->fn == fn && cb->priv == priv)
232                         return ERROR_INVALID_ARGUMENTS;
233         }
234
235         /* alloc memory, it is safe just to return in case of an error, no need for the caller to check this */
236         if ((cb = malloc(sizeof(log_callback_t))) == NULL)
237                 return ERROR_BUF_TOO_SMALL;
238
239         /* add item to the beginning of the linked list */
240         cb->fn = fn;
241         cb->priv = priv;
242         cb->next = log_callbacks;
243         log_callbacks = cb;
244
245         return ERROR_OK;
246 }
247
248 int log_remove_callback(log_callback_fn fn, void *priv)
249 {
250         log_callback_t *cb, **p;
251
252         for (p = &log_callbacks; (cb = *p); p = &(*p)->next)
253         {
254                 if (cb->fn == fn && cb->priv == priv)
255                 {
256                         *p = cb->next;
257                         free(cb);
258                         return ERROR_OK;
259                 }
260         }
261
262         /* no such item */
263         return ERROR_INVALID_ARGUMENTS;
264 }
265
266 /* return allocated string w/printf() result */
267 char *alloc_printf(const char *fmt, va_list ap)
268 {
269         /* no buffer at the beginning, force realloc to do the job */
270         char *string = NULL;
271         
272         /* start with minimal length to exercise all the code paths */
273         int size = 1;
274
275         for (;;)
276         {
277                 size *= 2; /* double the buffer size */
278
279                         char *t = string;
280                         string = realloc(string, size);
281                         if (string == NULL)
282                         {
283                                 if (t != NULL)
284                                         free(t);
285                                 return NULL;
286                         }
287         
288                 int ret;
289                 ret = vsnprintf(string, size, fmt, ap);
290                 /* NB! The result of the vsnprintf() might be an *EMPTY* string! */
291                 if ((ret >= 0) && ((ret + 1) < size))
292                         break;
293
294                 /* there was just enough or not enough space, allocate more in the next round */
295         }
296         
297         /* the returned buffer is by principle guaranteed to be at least one character longer */
298         return string;
299 }