588 lines
15 KiB
C
588 lines
15 KiB
C
/*
|
|
Copyright (c) 2012, Broadcom Europe Ltd
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the copyright holder nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*=============================================================================
|
|
Categorized logging for VCOS - a generic implementation.
|
|
=============================================================================*/
|
|
|
|
#include "interface/vcos/vcos.h"
|
|
#include "interface/vcos/vcos_ctype.h"
|
|
#include "interface/vcos/vcos_string.h"
|
|
#include "interface/vcos/vcos_inttypes.h"
|
|
|
|
static VCOS_MUTEX_T lock;
|
|
static int warned_loglevel; /* only warn about invalid log level once */
|
|
static VCOS_VLOG_IMPL_FUNC_T vcos_vlog_impl_func = vcos_vlog_default_impl;
|
|
|
|
#define VCOS_LOG_CATEGORY (&dflt_log_category)
|
|
static VCOS_LOG_CAT_T dflt_log_category;
|
|
VCOS_LOG_CAT_T *vcos_logging_categories = NULL;
|
|
static int inited;
|
|
|
|
#if VCOS_HAVE_CMD
|
|
|
|
/*
|
|
* For kernel or videocore purposes, we generally want the log command. For
|
|
* user-space apps, they might want to provide their own log command, so we
|
|
* don't include the built in on.
|
|
*
|
|
* So pthreads/vcos_platform.h defines VCOS_WANT_LOG_CMD to be 0. It is
|
|
* undefined elsewhere.
|
|
*/
|
|
|
|
# if !defined( VCOS_WANT_LOG_CMD )
|
|
# define VCOS_WANT_LOG_CMD 1
|
|
# endif
|
|
#else
|
|
# define VCOS_WANT_LOG_CMD 0
|
|
#endif
|
|
|
|
/* For now, do not link logging categories into linked lists
|
|
* as it breaks when people unload shared libraries without
|
|
* getting the counts right.
|
|
*/
|
|
#ifdef __VIDEOCORE__
|
|
#define REGISTER_CATEGORIES 1
|
|
#else
|
|
#define REGISTER_CATEGORIES 0
|
|
#endif
|
|
|
|
#if VCOS_WANT_LOG_CMD
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Does a vcos_assert(0), which is useful to test logging.
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VCOS_STATUS_T vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param )
|
|
{
|
|
(void)param;
|
|
|
|
#if defined( NDEBUG ) && !defined( VCOS_RELEASE_ASSERTS )
|
|
vcos_log_error( "vcos_asserts have been compiled out" );
|
|
vcos_cmd_printf( param, "vcos_asserts have been compiled out - did a vcos_log_error instead\n" );
|
|
#else
|
|
vcos_assert(0);
|
|
vcos_cmd_printf( param, "Executed vcos_assert(0)\n" );
|
|
#endif
|
|
|
|
return VCOS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Sets a vcos logging level
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VCOS_STATUS_T vcos_log_set_cmd( VCOS_CMD_PARAM_T *param )
|
|
{
|
|
VCOS_LOG_CAT_T *cat;
|
|
char *name;
|
|
char *levelStr;
|
|
VCOS_LOG_LEVEL_T level;
|
|
VCOS_STATUS_T status;
|
|
|
|
if ( param->argc != 3 )
|
|
{
|
|
vcos_cmd_usage( param );
|
|
return VCOS_EINVAL;
|
|
}
|
|
|
|
name = param->argv[1];
|
|
levelStr = param->argv[2];
|
|
|
|
if ( vcos_string_to_log_level( levelStr, &level ) != VCOS_SUCCESS )
|
|
{
|
|
vcos_cmd_printf( param, "Unrecognized logging level: '%s'\n", levelStr );
|
|
return VCOS_EINVAL;
|
|
}
|
|
|
|
vcos_mutex_lock(&lock);
|
|
|
|
status = VCOS_SUCCESS;
|
|
for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
{
|
|
if ( vcos_strcmp( name, cat->name ) == 0 )
|
|
{
|
|
cat->level = level;
|
|
vcos_cmd_printf( param, "Category %s level set to %s\n", name, levelStr );
|
|
break;
|
|
}
|
|
}
|
|
if ( cat == NULL )
|
|
{
|
|
vcos_cmd_printf( param, "Unrecognized category: '%s'\n", name );
|
|
status = VCOS_ENOENT;
|
|
}
|
|
|
|
vcos_mutex_unlock(&lock);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Prints out the current settings for a given category (or all cvategories)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VCOS_STATUS_T vcos_log_status_cmd( VCOS_CMD_PARAM_T *param )
|
|
{
|
|
VCOS_LOG_CAT_T *cat;
|
|
VCOS_STATUS_T status;
|
|
|
|
vcos_mutex_lock(&lock);
|
|
|
|
if ( param->argc == 1)
|
|
{
|
|
int nw;
|
|
int nameWidth = 0;
|
|
|
|
/* Print information about all of the categories. */
|
|
|
|
for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
{
|
|
nw = (int)strlen( cat->name );
|
|
|
|
if ( nw > nameWidth )
|
|
{
|
|
nameWidth = nw;
|
|
}
|
|
}
|
|
|
|
for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
{
|
|
vcos_cmd_printf( param, "%-*s - %s\n", nameWidth, cat->name, vcos_log_level_to_string( cat->level ));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Print information about a particular category */
|
|
|
|
for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next )
|
|
{
|
|
if ( vcos_strcmp( cat->name, param->argv[1] ) == 0 )
|
|
{
|
|
vcos_cmd_printf( param, "%s - %s\n", cat->name, vcos_log_level_to_string( cat->level ));
|
|
break;
|
|
}
|
|
}
|
|
if ( cat == NULL )
|
|
{
|
|
vcos_cmd_printf( param, "Unrecognized logging category: '%s'\n", param->argv[1] );
|
|
status = VCOS_ENOENT;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
status = VCOS_SUCCESS;
|
|
out:
|
|
vcos_mutex_unlock(&lock);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Prints out the current settings for a given category (or all cvategories)
|
|
*
|
|
*****************************************************************************/
|
|
|
|
VCOS_STATUS_T vcos_log_test_cmd( VCOS_CMD_PARAM_T *param )
|
|
{
|
|
if ( param->argc == 1 )
|
|
{
|
|
static int seq_num = 100;
|
|
|
|
/* No additional arguments - generate a message with an incrementing number */
|
|
|
|
vcos_log_error( "Test message %d", seq_num );
|
|
|
|
seq_num++;
|
|
vcos_cmd_printf( param, "Logged 'Test message %d'\n", seq_num );
|
|
}
|
|
else
|
|
{
|
|
int arg_idx;
|
|
|
|
/* Arguments supplied - log these */
|
|
|
|
for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ )
|
|
{
|
|
vcos_log_error( "argv[%d] = '%s'", arg_idx, param->argv[arg_idx] );
|
|
}
|
|
vcos_cmd_printf( param, "Logged %d line(s) of test data\n", param->argc );
|
|
}
|
|
return VCOS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
*
|
|
* Internal commands
|
|
*
|
|
*****************************************************************************/
|
|
|
|
static VCOS_CMD_T log_cmd_entry[] =
|
|
{
|
|
{ "assert", "", vcos_log_assert_cmd, NULL, "Does a vcos_assert(0) to test logging" },
|
|
{ "set", "category level", vcos_log_set_cmd, NULL, "Sets the vcos logging level for a category" },
|
|
{ "status", "[category]", vcos_log_status_cmd, NULL, "Prints the vcos log status for a (or all) categories" },
|
|
{ "test", "[arbitrary text]", vcos_log_test_cmd, NULL, "Does a vcos_log to test logging" },
|
|
|
|
{ NULL, NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
static VCOS_CMD_T cmd_log =
|
|
{ "log", "command [args]", NULL, log_cmd_entry, "Commands related to vcos logging" };
|
|
|
|
#endif
|
|
|
|
void vcos_logging_init(void)
|
|
{
|
|
if (inited)
|
|
{
|
|
/* FIXME: should print a warning or something here */
|
|
return;
|
|
}
|
|
vcos_mutex_create(&lock, "vcos_log");
|
|
|
|
vcos_log_platform_init();
|
|
|
|
vcos_log_register("default", &dflt_log_category);
|
|
|
|
#if VCOS_WANT_LOG_CMD
|
|
vcos_cmd_register( &cmd_log );
|
|
#endif
|
|
|
|
vcos_assert(!inited);
|
|
inited = 1;
|
|
}
|
|
|
|
/** Read an alphanumeric token, returning True if we succeeded.
|
|
*/
|
|
|
|
static int read_tok(char *tok, size_t toklen, const char **pstr, char sep)
|
|
{
|
|
const char *str = *pstr;
|
|
size_t n = 0;
|
|
char ch;
|
|
|
|
/* skip past any whitespace */
|
|
while (str[0] && isspace((int)(str[0])))
|
|
str++;
|
|
|
|
while ((ch = *str) != '\0' &&
|
|
ch != sep &&
|
|
(isalnum((int)ch) || (ch == '_')) &&
|
|
n != toklen-1)
|
|
{
|
|
tok[n++] = ch;
|
|
str++;
|
|
}
|
|
|
|
/* did it work out? */
|
|
if (ch == '\0' || ch == sep)
|
|
{
|
|
if (ch) str++; /* move to next token if not at end */
|
|
/* yes */
|
|
tok[n] = '\0';
|
|
*pstr = str;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* no */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
const char *vcos_log_level_to_string( VCOS_LOG_LEVEL_T level )
|
|
{
|
|
switch (level)
|
|
{
|
|
case VCOS_LOG_UNINITIALIZED: return "uninit";
|
|
case VCOS_LOG_NEVER: return "never";
|
|
case VCOS_LOG_ERROR: return "error";
|
|
case VCOS_LOG_WARN: return "warn";
|
|
case VCOS_LOG_INFO: return "info";
|
|
case VCOS_LOG_TRACE: return "trace";
|
|
}
|
|
return "???";
|
|
}
|
|
|
|
VCOS_STATUS_T vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level )
|
|
{
|
|
if (strcmp(str,"error") == 0)
|
|
*level = VCOS_LOG_ERROR;
|
|
else if (strcmp(str,"never") == 0)
|
|
*level = VCOS_LOG_NEVER;
|
|
else if (strcmp(str,"warn") == 0)
|
|
*level = VCOS_LOG_WARN;
|
|
else if (strcmp(str,"warning") == 0)
|
|
*level = VCOS_LOG_WARN;
|
|
else if (strcmp(str,"info") == 0)
|
|
*level = VCOS_LOG_INFO;
|
|
else if (strcmp(str,"trace") == 0)
|
|
*level = VCOS_LOG_TRACE;
|
|
else
|
|
return VCOS_EINVAL;
|
|
|
|
return VCOS_SUCCESS;
|
|
}
|
|
|
|
static int read_level(VCOS_LOG_LEVEL_T *level, const char **pstr, char sep)
|
|
{
|
|
char buf[16];
|
|
int ret = 1;
|
|
if (read_tok(buf,sizeof(buf),pstr,sep))
|
|
{
|
|
if (vcos_string_to_log_level(buf,level) != VCOS_SUCCESS)
|
|
{
|
|
vcos_log("Invalid trace level '%s'\n", buf);
|
|
ret = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void vcos_log_register(const char *name, VCOS_LOG_CAT_T *category)
|
|
{
|
|
const char *env;
|
|
VCOS_LOG_CAT_T *i;
|
|
|
|
category->name = name;
|
|
if ( category->level == VCOS_LOG_UNINITIALIZED )
|
|
{
|
|
category->level = VCOS_LOG_ERROR;
|
|
}
|
|
category->flags.want_prefix = (category != &dflt_log_category );
|
|
|
|
if (!REGISTER_CATEGORIES)
|
|
return;
|
|
|
|
vcos_mutex_lock(&lock);
|
|
|
|
/* is it already registered? */
|
|
for (i = vcos_logging_categories; i ; i = i->next )
|
|
{
|
|
if (i == category)
|
|
{
|
|
i->refcount++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!i)
|
|
{
|
|
/* not yet registered */
|
|
category->next = vcos_logging_categories;
|
|
vcos_logging_categories = category;
|
|
category->refcount++;
|
|
|
|
vcos_log_platform_register(category);
|
|
}
|
|
|
|
vcos_mutex_unlock(&lock);
|
|
|
|
/* Check to see if this log level has been enabled. Look for
|
|
* (<category:level>,)*
|
|
*
|
|
* VC_LOGLEVEL=ilcs:info,vchiq:warn
|
|
*/
|
|
|
|
env = _VCOS_LOG_LEVEL();
|
|
if (env)
|
|
{
|
|
do
|
|
{
|
|
char env_name[64];
|
|
VCOS_LOG_LEVEL_T level;
|
|
if (read_tok(env_name, sizeof(env_name), &env, ':') &&
|
|
read_level(&level, &env, ','))
|
|
{
|
|
if (strcmp(env_name, name) == 0)
|
|
{
|
|
category->level = level;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!warned_loglevel)
|
|
{
|
|
vcos_log("VC_LOGLEVEL format invalid at %s\n", env);
|
|
warned_loglevel = 1;
|
|
}
|
|
return;
|
|
}
|
|
} while (env[0] != '\0');
|
|
}
|
|
|
|
vcos_log_info( "Registered log category '%s' with level %s",
|
|
category->name,
|
|
vcos_log_level_to_string( category->level ));
|
|
}
|
|
|
|
void vcos_log_unregister(VCOS_LOG_CAT_T *category)
|
|
{
|
|
VCOS_LOG_CAT_T **pcat;
|
|
|
|
if (!REGISTER_CATEGORIES)
|
|
return;
|
|
|
|
vcos_mutex_lock(&lock);
|
|
category->refcount--;
|
|
if (category->refcount == 0)
|
|
{
|
|
pcat = &vcos_logging_categories;
|
|
while (*pcat != category)
|
|
{
|
|
if (!*pcat)
|
|
break; /* possibly deregistered twice? */
|
|
if ((*pcat)->next == NULL)
|
|
{
|
|
vcos_assert(0); /* already removed! */
|
|
vcos_mutex_unlock(&lock);
|
|
return;
|
|
}
|
|
pcat = &(*pcat)->next;
|
|
}
|
|
if (*pcat)
|
|
*pcat = category->next;
|
|
|
|
vcos_log_platform_unregister(category);
|
|
}
|
|
vcos_mutex_unlock(&lock);
|
|
}
|
|
|
|
VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void)
|
|
{
|
|
return &dflt_log_category;
|
|
}
|
|
|
|
void vcos_set_log_options(const char *opt)
|
|
{
|
|
(void)opt;
|
|
}
|
|
|
|
void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat,
|
|
const char *label,
|
|
uint32_t addr,
|
|
const void *voidMem,
|
|
size_t numBytes )
|
|
{
|
|
const uint8_t *mem = (const uint8_t *)voidMem;
|
|
size_t offset;
|
|
char lineBuf[ 100 ];
|
|
char *s;
|
|
|
|
while ( numBytes > 0 )
|
|
{
|
|
s = lineBuf;
|
|
|
|
for ( offset = 0; offset < 16; offset++ )
|
|
{
|
|
if ( offset < numBytes )
|
|
{
|
|
s += vcos_snprintf( s, 4, "%02x ", mem[ offset ]);
|
|
}
|
|
else
|
|
{
|
|
s += vcos_snprintf( s, 4, " " );
|
|
}
|
|
}
|
|
|
|
for ( offset = 0; offset < 16; offset++ )
|
|
{
|
|
if ( offset < numBytes )
|
|
{
|
|
uint8_t ch = mem[ offset ];
|
|
|
|
if (( ch < ' ' ) || ( ch > '~' ))
|
|
{
|
|
ch = '.';
|
|
}
|
|
*s++ = (char)ch;
|
|
}
|
|
}
|
|
*s++ = '\0';
|
|
|
|
if (( label != NULL ) && ( *label != '\0' ))
|
|
{
|
|
vcos_log_impl( cat, VCOS_LOG_INFO, "%s: %08" PRIx32 ": %s", label, addr, lineBuf );
|
|
}
|
|
else
|
|
{
|
|
vcos_log_impl( cat, VCOS_LOG_INFO, "%08" PRIx32 ": %s", addr, lineBuf );
|
|
}
|
|
|
|
addr += 16;
|
|
mem += 16;
|
|
if ( numBytes > 16 )
|
|
{
|
|
numBytes -= 16;
|
|
}
|
|
else
|
|
{
|
|
numBytes = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap,fmt);
|
|
vcos_vlog_impl( cat, _level, fmt, ap );
|
|
va_end(ap);
|
|
}
|
|
|
|
void vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args)
|
|
{
|
|
vcos_vlog_impl_func( cat, _level, fmt, args );
|
|
}
|
|
|
|
void vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func )
|
|
{
|
|
if ( vlog_impl_func == NULL )
|
|
{
|
|
vcos_vlog_impl_func = vcos_vlog_default_impl;
|
|
}
|
|
else
|
|
{
|
|
vcos_vlog_impl_func = vlog_impl_func;
|
|
}
|
|
}
|
|
|