1 /*
2 Copyright (c) 2012, Broadcom Europe Ltd
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, 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 
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON 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
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 
28 /*=============================================================================
29 Categorized 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 
37 static VCOS_MUTEX_T lock;
38 static int warned_loglevel;             /* only warn about invalid log level once */
39 static VCOS_VLOG_IMPL_FUNC_T vcos_vlog_impl_func = vcos_vlog_default_impl;
40 
41 #define  VCOS_LOG_CATEGORY (&dflt_log_category)
42 static VCOS_LOG_CAT_T dflt_log_category;
43 VCOS_LOG_CAT_T *vcos_logging_categories = NULL;
44 static 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 
vcos_log_assert_cmd(VCOS_CMD_PARAM_T * param)72 VCOS_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 
vcos_log_set_cmd(VCOS_CMD_PARAM_T * param)93 VCOS_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 
vcos_log_status_cmd(VCOS_CMD_PARAM_T * param)150 VCOS_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;
200 out:
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 
vcos_log_test_cmd(VCOS_CMD_PARAM_T * param)212 VCOS_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 
246 static 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 
256 static VCOS_CMD_T cmd_log =
257     { "log",        "command [args]",  NULL,    log_cmd_entry, "Commands related to vcos logging" };
258 
259 #endif
260 
vcos_logging_init(void)261 void 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 
read_tok(char * tok,size_t toklen,const char ** pstr,char sep)285 static 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 
vcos_log_level_to_string(VCOS_LOG_LEVEL_T level)320 const 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 
vcos_string_to_log_level(const char * str,VCOS_LOG_LEVEL_T * level)334 VCOS_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 
read_level(VCOS_LOG_LEVEL_T * level,const char ** pstr,char sep)354 static 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 
vcos_log_register(const char * name,VCOS_LOG_CAT_T * category)373 void 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 
vcos_log_unregister(VCOS_LOG_CAT_T * category)449 void 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 
vcos_log_get_default_category(void)478 VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void)
479 {
480    return &dflt_log_category;
481 }
482 
vcos_set_log_options(const char * opt)483 void vcos_set_log_options(const char *opt)
484 {
485    (void)opt;
486 }
487 
vcos_log_dump_mem_impl(const VCOS_LOG_CAT_T * cat,const char * label,uint32_t addr,const void * voidMem,size_t numBytes)488 void 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 
vcos_log_impl(const VCOS_LOG_CAT_T * cat,VCOS_LOG_LEVEL_T _level,const char * fmt,...)553 void 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 
vcos_vlog_impl(const VCOS_LOG_CAT_T * cat,VCOS_LOG_LEVEL_T _level,const char * fmt,va_list args)561 void 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 
vcos_set_vlog_impl(VCOS_VLOG_IMPL_FUNC_T vlog_impl_func)566 void 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