print available memory in log_level 3
[openocd.git] / 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 #include "time_support.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
32 #include <malloc.h>
33
34 int debug_level = -1;
35
36 static FILE* log_output;
37 static log_callback_t *log_callbacks = NULL;
38
39 static long long start;
40
41 static char *log_strings[5] =
42 {
43 "User: ",
44 "Error: ",
45 "Warning:",
46 "Info: ",
47 "Debug: "
48 };
49
50 static int count = 0;
51
52 /* The log_puts() serves to somewhat different goals:
53 *
54 * - logging
55 * - feeding low-level info to the user in GDB or Telnet
56 *
57 * The latter dictates that strings without newline are not logged, lest there
58 * will be *MANY log lines when sending one char at the time(e.g.
59 * target_request.c).
60 *
61 */
62 static void log_puts(enum log_levels level, const char *file, int line, const char *function, const char *string)
63 {
64 char *f;
65 if (level == LOG_LVL_OUTPUT)
66 {
67 /* do not prepend any headers, just print out what we were given and return */
68 fputs(string, log_output);
69 fflush(log_output);
70 return;
71 }
72
73 f = strrchr(file, '/');
74 if (f != NULL)
75 file = f + 1;
76
77 if (strchr(string, '\n')!=NULL)
78 {
79 if (debug_level >= LOG_LVL_DEBUG)
80 {
81 /* print with count and time information */
82 int t=(int)(timeval_ms()-start);
83
84 struct mallinfo info;
85 info = mallinfo();
86
87 fprintf(log_output, "%s %d %d %s:%d %s() %d: %s", log_strings[level+1], count, t, file, line, function, info.fordblks, string);
88 }
89 else
90 {
91 /* do not print count and time */
92 fprintf(log_output, "%s %s:%d %s(): %s", log_strings[level+1], file, line, function, string);
93 }
94 } else
95 {
96 /* only entire lines are logged. Otherwise it's
97 * single chars intended for the log callbacks. */
98 }
99
100 fflush(log_output);
101
102 /* Never forward LOG_LVL_DEBUG, too verbose and they can be found in the log if need be */
103 if (level <= LOG_LVL_INFO)
104 {
105 log_callback_t *cb, *next;
106 cb = log_callbacks;
107 /* DANGER!!!! the log callback can remove itself!!!! */
108 while (cb)
109 {
110 next=cb->next;
111 cb->fn(cb->priv, file, line, function, string);
112 cb=next;
113 }
114 }
115 }
116
117 void log_printf(enum log_levels level, const char *file, int line, const char *function, const char *format, ...)
118 {
119 char *string;
120 va_list ap;
121
122 count++;
123 if (level > debug_level)
124 return;
125
126 va_start(ap, format);
127
128 string = alloc_vprintf(format, ap);
129 if (string != NULL)
130 {
131 log_puts(level, file, line, function, string);
132 free(string);
133 }
134
135 va_end(ap);
136 }
137
138 void log_printf_lf(enum log_levels level, const char *file, int line, const char *function, const char *format, ...)
139 {
140 char *string;
141 va_list ap;
142
143 count++;
144 if (level > debug_level)
145 return;
146
147 va_start(ap, format);
148
149 string = alloc_vprintf(format, ap);
150 if (string != NULL)
151 {
152 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
153 log_puts(level, file, line, function, string);
154 free(string);
155 }
156
157 va_end(ap);
158 }
159
160 /* change the current debug level on the fly
161 * 0: only ERRORS
162 * 1: + WARNINGS
163 * 2: + INFORMATIONAL MSGS
164 * 3: + DEBUG MSGS
165 */
166 int handle_debug_level_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
167 {
168 if (argc == 0)
169 command_print(cmd_ctx, "debug_level: %i", debug_level);
170
171 if (argc > 0)
172 debug_level = strtoul(args[0], NULL, 0);
173
174 if (debug_level < 0)
175 debug_level = 0;
176
177 if (debug_level > 3)
178 debug_level = 3;
179
180 return ERROR_OK;
181 }
182
183 int handle_log_output_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
184 {
185 if (argc == 1)
186 {
187 FILE* file = fopen(args[0], "w");
188
189 if (file)
190 {
191 log_output = file;
192 }
193 }
194
195 return ERROR_OK;
196 }
197
198 int log_register_commands(struct command_context_s *cmd_ctx)
199 {
200 start = timeval_ms();
201 register_command(cmd_ctx, NULL, "log_output", handle_log_output_command,
202 COMMAND_ANY, "redirect logging to <file> (default: stderr)");
203 register_command(cmd_ctx, NULL, "debug_level", handle_debug_level_command,
204 COMMAND_ANY, "adjust debug level <0-3>");
205
206 return ERROR_OK;
207 }
208
209 int log_init(struct command_context_s *cmd_ctx)
210 {
211 /* set defaults for daemon configuration, if not set by cmdline or cfgfile */
212 if (debug_level == -1)
213 debug_level = LOG_LVL_INFO;
214
215 if (log_output == NULL)
216 {
217 log_output = stderr;
218 }
219
220 return ERROR_OK;
221 }
222
223 int set_log_output(struct command_context_s *cmd_ctx, FILE *output)
224 {
225 log_output = output;
226 return ERROR_OK;
227 }
228
229 /* add/remove log callback handler */
230 int log_add_callback(log_callback_fn fn, void *priv)
231 {
232 log_callback_t *cb;
233
234 /* prevent the same callback to be registered more than once, just for sure */
235 for (cb = log_callbacks; cb; cb = cb->next)
236 {
237 if (cb->fn == fn && cb->priv == priv)
238 return ERROR_INVALID_ARGUMENTS;
239 }
240
241 /* alloc memory, it is safe just to return in case of an error, no need for the caller to check this */
242 if ((cb = malloc(sizeof(log_callback_t))) == NULL)
243 return ERROR_BUF_TOO_SMALL;
244
245 /* add item to the beginning of the linked list */
246 cb->fn = fn;
247 cb->priv = priv;
248 cb->next = log_callbacks;
249 log_callbacks = cb;
250
251 return ERROR_OK;
252 }
253
254 int log_remove_callback(log_callback_fn fn, void *priv)
255 {
256 log_callback_t *cb, **p;
257
258 for (p = &log_callbacks; (cb = *p); p = &(*p)->next)
259 {
260 if (cb->fn == fn && cb->priv == priv)
261 {
262 *p = cb->next;
263 free(cb);
264 return ERROR_OK;
265 }
266 }
267
268 /* no such item */
269 return ERROR_INVALID_ARGUMENTS;
270 }
271
272 /* return allocated string w/printf() result */
273 char *alloc_vprintf(const char *fmt, va_list ap)
274 {
275 /* no buffer at the beginning, force realloc to do the job */
276 char *string = NULL;
277
278 /* start with buffer size suitable for typical messages */
279 int size = 128;
280
281 for (;;)
282 {
283 char *t = string;
284 va_list ap_copy;
285 int ret;
286 string = realloc(string, size);
287 if (string == NULL)
288 {
289 if (t != NULL)
290 free(t);
291 return NULL;
292 }
293
294 va_copy(ap_copy, ap);
295
296 ret = vsnprintf(string, size, fmt, ap_copy);
297 /* NB! The result of the vsnprintf() might be an *EMPTY* string! */
298 if ((ret >= 0) && ((ret + 1) < size))
299 break;
300
301 /* there was just enough or not enough space, allocate more in the next round */
302 size *= 2; /* double the buffer size */
303 }
304
305 /* the returned buffer is by principle guaranteed to be at least one character longer */
306 return string;
307 }
308
309 char *alloc_printf(const char *format, ...)
310 {
311 char *string;
312 va_list ap;
313 va_start(ap, format);
314 string = alloc_vprintf(format, ap);
315 va_end(ap);
316 return string;
317 }

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)