1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/*=============================================================================
29Categorized logging for VCOS - a generic implementation.
30=============================================================================*/
31
32#include "interface/vcos/vcos.h"
33#include "interface/vcos/vcos_ctype.h"
34#include "interface/vcos/vcos_string.h"
35#include "interface/vcos/vcos_inttypes.h"
36
37static VCOS_MUTEX_T lock;
38static int warned_loglevel; /* only warn about invalid log level once */
39static VCOS_VLOG_IMPL_FUNC_T vcos_vlog_impl_func = vcos_vlog_default_impl;
40
41#define VCOS_LOG_CATEGORY (&dflt_log_category)
42static VCOS_LOG_CAT_T dflt_log_category;
43VCOS_LOG_CAT_T *vcos_logging_categories = NULL;
44static int inited;
45
46#if VCOS_HAVE_CMD
47
48/*
49 * For kernel or videocore purposes, we generally want the log command. For
50 * user-space apps, they might want to provide their own log command, so we
51 * don't include the built in on.
52 *
53 * So pthreads/vcos_platform.h defines VCOS_WANT_LOG_CMD to be 0. It is
54 * undefined elsewhere.
55 */
56
57# if !defined( VCOS_WANT_LOG_CMD )
58# define VCOS_WANT_LOG_CMD 1
59# endif
60#else
61# define VCOS_WANT_LOG_CMD 0
62#endif
63
64#if VCOS_WANT_LOG_CMD
65
66/*****************************************************************************
67*
68* Does a vcos_assert(0), which is useful to test logging.
69*
70*****************************************************************************/
71
72VCOS_STATUS_T vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param )
73{
74 (void)param;
75
76#if defined( NDEBUG ) && !defined( VCOS_RELEASE_ASSERTS )
77 vcos_log_error( "vcos_asserts have been compiled out" );
78 vcos_cmd_printf( param, "vcos_asserts have been compiled out - did a vcos_log_error instead\n" );
79#else
80 vcos_assert(0);
81 vcos_cmd_printf( param, "Executed vcos_assert(0)\n" );
82#endif
83
84 return VCOS_SUCCESS;
85}
86
87/*****************************************************************************
88*
89* Sets a vcos logging level
90*
91*****************************************************************************/
92
93VCOS_STATUS_T vcos_log_set_cmd( VCOS_CMD_PARAM_T *param )
94{
95 VCOS_LOG_CAT_T *cat;
96 char *name;
97 char *levelStr;
98 VCOS_LOG_LEVEL_T level;
99 VCOS_STATUS_T status;
100
101 if ( param->argc != 3 )
102 {
103 vcos_cmd_usage( param );
104 return VCOS_EINVAL;
105 }
106
107 name = param->argv[1];
108 levelStr = param->argv[2];
109
110 if ( vcos_string_to_log_level( levelStr, &level ) != VCOS_SUCCESS )
111 {
112 vcos_cmd_printf( param, "Unrecognized logging level: '%s'\n", levelStr );
113 return VCOS_EINVAL;
114 }
115
116 vcos_mutex_lock(&lock);
117
118 status = VCOS_SUCCESS;
119 for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
120 {
121 if ( vcos_strcmp( name, cat->name ) == 0 )
122 {
123 cat->level = level;
124 vcos_cmd_printf( param, "Category %s level set to %s\n", name, levelStr );
125 break;
126 }
127 else if ( vcos_strcmp( name, "*") == 0 )
128 {
129 cat->level = level;
130 vcos_cmd_printf( param, "Category %s level set to %s\n", name, levelStr );
131 }
132 }
133 if ( cat == NULL )
134 {
135 vcos_cmd_printf( param, "Unrecognized category: '%s'\n", name );
136 status = VCOS_ENOENT;
137 }
138
139 vcos_mutex_unlock(&lock);
140
141 return status;
142}
143
144/*****************************************************************************
145*
146* Prints out the current settings for a given category (or all cvategories)
147*
148*****************************************************************************/
149
150VCOS_STATUS_T vcos_log_status_cmd( VCOS_CMD_PARAM_T *param )
151{
152 VCOS_LOG_CAT_T *cat;
153 VCOS_STATUS_T status;
154
155 vcos_mutex_lock(&lock);
156
157 if ( param->argc == 1)
158 {
159 int nw;
160 int nameWidth = 0;
161
162 /* Print information about all of the categories. */
163
164 for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
165 {
166 nw = (int)strlen( cat->name );
167
168 if ( nw > nameWidth )
169 {
170 nameWidth = nw;
171 }
172 }
173
174 for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
175 {
176 vcos_cmd_printf( param, "%-*s - %s\n", nameWidth, cat->name, vcos_log_level_to_string( cat->level ));
177 }
178 }
179 else
180 {
181 /* Print information about a particular category */
182
183 for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
184 {
185 if ( vcos_strcmp( cat->name, param->argv[1] ) == 0 )
186 {
187 vcos_cmd_printf( param, "%s - %s\n", cat->name, vcos_log_level_to_string( cat->level ));
188 break;
189 }
190 }
191 if ( cat == NULL )
192 {
193 vcos_cmd_printf( param, "Unrecognized logging category: '%s'\n", param->argv[1] );
194 status = VCOS_ENOENT;
195 goto out;
196 }
197 }
198
199 status = VCOS_SUCCESS;
200out:
201 vcos_mutex_unlock(&lock);
202
203 return status;
204}
205
206/*****************************************************************************
207*
208* Prints out the current settings for a given category (or all cvategories)
209*
210*****************************************************************************/
211
212VCOS_STATUS_T vcos_log_test_cmd( VCOS_CMD_PARAM_T *param )
213{
214 if ( param->argc == 1 )
215 {
216 static int seq_num = 100;
217
218 /* No additional arguments - generate a message with an incrementing number */
219
220 vcos_log_error( "Test message %d", seq_num );
221
222 seq_num++;
223 vcos_cmd_printf( param, "Logged 'Test message %d'\n", seq_num );
224 }
225 else
226 {
227 int arg_idx;
228
229 /* Arguments supplied - log these */
230
231 for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ )
232 {
233 vcos_log_error( "argv[%d] = '%s'", arg_idx, param->argv[arg_idx] );
234 }
235 vcos_cmd_printf( param, "Logged %d line(s) of test data\n", param->argc );
236 }
237 return VCOS_SUCCESS;
238}
239
240/*****************************************************************************
241*
242* Internal commands
243*
244*****************************************************************************/
245
246static VCOS_CMD_T log_cmd_entry[] =
247{
248 { "assert", "", vcos_log_assert_cmd, NULL, "Does a vcos_assert(0) to test logging" },
249 { "set", "category level", vcos_log_set_cmd, NULL, "Sets the vcos logging level for a category" },
250 { "status", "[category]", vcos_log_status_cmd, NULL, "Prints the vcos log status for a (or all) categories" },
251 { "test", "[arbitrary text]", vcos_log_test_cmd, NULL, "Does a vcos_log to test logging" },
252
253 { NULL, NULL, NULL, NULL, NULL }
254};
255
256static VCOS_CMD_T cmd_log =
257 { "log", "command [args]", NULL, log_cmd_entry, "Commands related to vcos logging" };
258
259#endif
260
261void vcos_logging_init(void)
262{
263 if (inited)
264 {
265 /* FIXME: should print a warning or something here */
266 return;
267 }
268 vcos_mutex_create(&lock, "vcos_log");
269
270 vcos_log_platform_init();
271
272 vcos_log_register("default", &dflt_log_category);
273
274#if VCOS_WANT_LOG_CMD
275 vcos_cmd_register( &cmd_log );
276#endif
277
278 vcos_assert(!inited);
279 inited = 1;
280}
281
282/** Read an alphanumeric token, returning True if we succeeded.
283 */
284
285static int read_tok(char *tok, size_t toklen, const char **pstr, char sep)
286{
287 const char *str = *pstr;
288 size_t n = 0;
289 char ch;
290
291 /* skip past any whitespace */
292 while (str[0] && isspace((int)(str[0])))
293 str++;
294
295 while ((ch = *str) != '\0' &&
296 ch != sep &&
297 (isalnum((int)ch) || (ch == '_') || (ch == '*')) &&
298 n != toklen-1)
299 {
300 tok[n++] = ch;
301 str++;
302 }
303
304 /* did it work out? */
305 if (ch == '\0' || ch == sep)
306 {
307 if (ch) str++; /* move to next token if not at end */
308 /* yes */
309 tok[n] = '\0';
310 *pstr = str;
311 return 1;
312 }
313 else
314 {
315 /* no */
316 return 0;
317 }
318}
319
320const char *vcos_log_level_to_string( VCOS_LOG_LEVEL_T level )
321{
322 switch (level)
323 {
324 case VCOS_LOG_UNINITIALIZED: return "uninit";
325 case VCOS_LOG_NEVER: return "never";
326 case VCOS_LOG_ERROR: return "error";
327 case VCOS_LOG_WARN: return "warn";
328 case VCOS_LOG_INFO: return "info";
329 case VCOS_LOG_TRACE: return "trace";
330 }
331 return "???";
332}
333
334VCOS_STATUS_T vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level )
335{
336 if (strcmp(str,"error") == 0)
337 *level = VCOS_LOG_ERROR;
338 else if (strcmp(str,"never") == 0)
339 *level = VCOS_LOG_NEVER;
340 else if (strcmp(str,"warn") == 0)
341 *level = VCOS_LOG_WARN;
342 else if (strcmp(str,"warning") == 0)
343 *level = VCOS_LOG_WARN;
344 else if (strcmp(str,"info") == 0)
345 *level = VCOS_LOG_INFO;
346 else if (strcmp(str,"trace") == 0)
347 *level = VCOS_LOG_TRACE;
348 else
349 return VCOS_EINVAL;
350
351 return VCOS_SUCCESS;
352}
353
354static int read_level(VCOS_LOG_LEVEL_T *level, const char **pstr, char sep)
355{
356 char buf[16];
357 int ret = 1;
358 if (read_tok(buf,sizeof(buf),pstr,sep))
359 {
360 if (vcos_string_to_log_level(buf,level) != VCOS_SUCCESS)
361 {
362 vcos_log("Invalid trace level '%s'\n", buf);
363 ret = 0;
364 }
365 }
366 else
367 {
368 ret = 0;
369 }
370 return ret;
371}
372
373void vcos_log_register(const char *name, VCOS_LOG_CAT_T *category)
374{
375 const char *env;
376 VCOS_LOG_CAT_T *i;
377
378 category->name = name;
379 if ( category->level == VCOS_LOG_UNINITIALIZED )
380 {
381 category->level = VCOS_LOG_ERROR;
382 }
383 category->flags.want_prefix = (category != &dflt_log_category );
384
385 vcos_mutex_lock(&lock);
386
387 /* is it already registered? */
388 for (i = vcos_logging_categories; i ; i = i->next )
389 {
390 if (i == category)
391 {
392 i->refcount++;
393 break;
394 }
395 }
396
397 if (!i)
398 {
399 /* not yet registered */
400 category->next = vcos_logging_categories;
401 vcos_logging_categories = category;
402 category->refcount++;
403
404 vcos_log_platform_register(category);
405 }
406
407 vcos_mutex_unlock(&lock);
408
409 /* Check to see if this log level has been enabled. Look for
410 * (<category:level>,)*
411 *
412 * VC_LOGLEVEL=ilcs:info,vchiq:warn
413 */
414
415 env = _VCOS_LOG_LEVEL();
416 if (env && env[0])
417 {
418 do
419 {
420 char env_name[64];
421 VCOS_LOG_LEVEL_T level;
422 if (read_tok(env_name, sizeof(env_name), &env, ':') &&
423 read_level(&level, &env, ','))
424 {
425 if (strcmp(env_name, name) == 0 || strcmp(env_name, "*") == 0)
426 {
427 // we could match both * and env_name, so make sure * comes
428 // first in the logging_level string
429 category->level = level;
430 }
431 }
432 else
433 {
434 if (!warned_loglevel)
435 {
436 vcos_log("VC_LOGLEVEL format invalid at %s\n", env);
437 warned_loglevel = 1;
438 }
439 return;
440 }
441 } while (env[0] != '\0');
442 }
443
444 vcos_log_info( "Registered log category '%s' with level %s",
445 category->name,
446 vcos_log_level_to_string( category->level ));
447}
448
449void vcos_log_unregister(VCOS_LOG_CAT_T *category)
450{
451 VCOS_LOG_CAT_T **pcat;
452
453 vcos_mutex_lock(&lock);
454 category->refcount--;
455 if (category->refcount == 0)
456 {
457 pcat = &vcos_logging_categories;
458 while (*pcat != category)
459 {
460 if (!*pcat)
461 break; /* possibly deregistered twice? */
462 if ((*pcat)->next == NULL)
463 {
464 vcos_assert(0); /* already removed! */
465 vcos_mutex_unlock(&lock);
466 return;
467 }
468 pcat = &(*pcat)->next;
469 }
470 if (*pcat)
471 *pcat = category->next;
472
473 vcos_log_platform_unregister(category);
474 }
475 vcos_mutex_unlock(&lock);
476}
477
478VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void)
479{
480 return &dflt_log_category;
481}
482
483void vcos_set_log_options(const char *opt)
484{
485 (void)opt;
486}
487
488void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat,
489 const char *label,
490 uint32_t addr,
491 const void *voidMem,
492 size_t numBytes )
493{
494 const uint8_t *mem = (const uint8_t *)voidMem;
495 size_t offset;
496 char lineBuf[ 100 ];
497 char *s;
498
499 while ( numBytes > 0 )
500 {
501 s = lineBuf;
502
503 for ( offset = 0; offset < 16; offset++ )
504 {
505 if ( offset < numBytes )
506 {
507 s += vcos_snprintf( s, 4, "%02x ", mem[ offset ]);
508 }
509 else
510 {
511 s += vcos_snprintf( s, 4, " " );
512 }
513 }
514
515 for ( offset = 0; offset < 16; offset++ )
516 {
517 if ( offset < numBytes )
518 {
519 uint8_t ch = mem[ offset ];
520
521 if (( ch < ' ' ) || ( ch > '~' ))
522 {
523 ch = '.';
524 }
525 *s++ = (char)ch;
526 }
527 }
528 *s++ = '\0';
529
530 if (( label != NULL ) && ( *label != '\0' ))
531 {
532 vcos_log_impl( cat, VCOS_LOG_INFO, "%s: %08" PRIx32 ": %s", label, addr, lineBuf );
533 }
534 else
535 {
536 vcos_log_impl( cat, VCOS_LOG_INFO, "%08" PRIx32 ": %s", addr, lineBuf );
537 }
538
539 addr += 16;
540 mem += 16;
541 if ( numBytes > 16 )
542 {
543 numBytes -= 16;
544 }
545 else
546 {
547 numBytes = 0;
548 }
549 }
550
551}
552
553void vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...)
554{
555 va_list ap;
556 va_start(ap,fmt);
557 vcos_vlog_impl( cat, _level, fmt, ap );
558 va_end(ap);
559}
560
561void vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args)
562{
563 vcos_vlog_impl_func( cat, _level, fmt, args );
564}
565
566void vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func )
567{
568 if ( vlog_impl_func == NULL )
569 {
570 vcos_vlog_impl_func = vcos_vlog_default_impl;
571 }
572 else
573 {
574 vcos_vlog_impl_func = vlog_impl_func;
575 }
576}
577
578