blob: 8c8cc2594343a02b4c2205d211f835b800cc359c [file] [log] [blame]
/*
<:copyright-BRCM:2016:DUAL/GPL:standard
Broadcom Proprietary and Confidential.(c) 2016 Broadcom
All Rights Reserved
Unless you and Broadcom execute a separate written software license
agreement governing use of this software, this software is licensed
to you under the terms of the GNU General Public License version 2
(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
with the following added to such license:
As a special exception, the copyright holders of this software give
you permission to link this software with independent modules, and
to copy and distribute the resulting executable under terms of your
choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module.
An independent module is a module which is not derived from this
software. The special exception does not apply to any modifications
of the software.
Not withstanding the above, under no circumstances may you combine
this software in any way with any other Broadcom software provided
under a license other than the GPL, without Broadcom's express prior
written consent.
:>
*/
#ifdef ENABLE_LOG
#include "bcm_dev_log_task.h"
#include "bcm_dev_log_task_internal.h"
#include "bcm_dev_log.h"
static bcmos_timer dev_log_timer;
/* Store timestamp per every rate-limited log ID. */
static uint64_t rate_limit_id2timestamp[DEV_LOG_RATE_LIMIT_ID__NUM_OF];
const char *dev_log_basename(const char *str)
{
const char *slash;
if ((slash = strchr(str, '/')))
{
const char *str0 = str;
str = strchr(slash+1, ' ');
/* If log format string was created by BCM_LOG macro and there is no corruption of some kind
* str above is guaranteed to be != NULL. However, making str!=NULL assumption makes dev_log
* vulnerable to bugs in the caller. In this case the following inexpensive check prevents crash
* in dev_Log and helps in identifying the real culprit.
*/
if (!str)
return str0;
while (str[0] != '/')
str--;
str++;
}
return str;
}
#ifdef TRIGGER_LOGGER_FEATURE
/* check if the message fits the level and the other criteria for a trigger
return 1 if yes, the message will be printed
and returns 0 if the message will not be printed
*/
static bcmos_bool check_trigger(dev_log_id_parm *xi_id, bcm_dev_log_level xi_log_level)
{
/* the level of the message must be exactly the trigger level */
if (xi_id->trigger_log_level != xi_log_level)
return BCMOS_FALSE; /* do not print the message - the level does not fit */
xi_id->trigger.counter++;
if (xi_id->trigger.counter >= xi_id->trigger.start_threshold)
{
if (xi_id->trigger.counter >= xi_id->trigger.stop_threshold)
{
/* trigger finished, check repeat */
if (xi_id->trigger.repeat_threshold)
{
xi_id->trigger.repeat++;
if (xi_id->trigger.repeat < xi_id->trigger.repeat_threshold)
{
/* rewind trigger conditions */
xi_id->trigger.counter = 0;
}
}
}
else /* trigger is active : counter greater than start and less than end */
return BCMOS_TRUE; /* print the message */
}
return BCMOS_FALSE;/* do not print the message - still less than start threshold */
}
/* check if the message fits the level and the criteria for throttle
return 1 if yes, the message will be printed
and returns 0 if the message will not be printed
*/
static bcmos_bool check_throttle(dev_log_id_parm *xi_id, bcm_dev_log_level xi_log_level)
{
/* check if any throttle is defined */
if (xi_id->throttle_log_level != DEV_LOG_LEVEL_NO_LOG)
return BCMOS_TRUE; /* print the message - no trigger is set */
/* the level of the message must be exactly the throttle level */
if (xi_id->throttle_log_level != xi_log_level)
return BCMOS_FALSE; /* do not print the message - the level does not fit */
xi_id->throttle.counter++;
if (xi_id->throttle.counter >= xi_id->throttle.threshold)
{
xi_id->throttle.counter = 0;
return BCMOS_TRUE;
}
return BCMOS_FALSE;/* do not print the message - still less than start threshold */
}
#endif
static bcmos_timer_rc bcm_dev_log_msg_send_timer_cb(bcmos_timer *timer, long data)
{
bcmos_msg *msg = (bcmos_msg *)data;
bcmos_msg_send(&dev_log.save_queue, msg, BCMOS_MSG_SEND_AUTO_FREE);
return BCMOS_TIMER_OK;
}
uint8_t bcm_dev_log_pool_occupancy_percent_get(void)
{
bcmos_msg_pool_info msg_pool_info;
bcmos_errno error = bcmos_msg_pool_query(&dev_log.pool, &msg_pool_info);
if (error != BCM_ERR_OK)
{
DEV_LOG_ERROR_PRINTF("bcmos_msg_pool_query() returned %s (%d)\n", bcmos_strerror(error), error);
return 0;
}
return (uint8_t)((100 * (msg_pool_info.parm.size - msg_pool_info.stat.free)) / msg_pool_info.parm.size);
}
static bcmos_bool bcm_dev_log_should_drop(
dev_log_id_parm *id,
bcm_dev_log_level log_level,
uint32_t rate_us,
dev_log_rate_limit_id rate_limit_id)
{
/* If the msg pool is sufficiently full, drop all non-error messages */
if (!bcm_dev_log_level_is_error(log_level) &&
bcm_dev_log_pool_occupancy_percent_get() >= DEV_LOG_ERROR_ONLY_THRESHOLD_PERCENT)
{
bcm_dev_log_drop_report();
return BCMOS_TRUE;
}
#ifdef TRIGGER_LOGGER_FEATURE
/* if trigger defined - ignore throttle and printing level */
if (id->trigger_log_level != DEV_LOG_LEVEL_NO_LOG)
return check_trigger(id, log_level);
#endif
/* if trigger is not fullfilled - check other conditions */
if (log_level > id->log_level_print && log_level > id->log_level_save)
return BCMOS_TRUE;
#ifdef TRIGGER_LOGGER_FEATURE
if (!check_throttle(id, log_level))
return BCMOS_TRUE;
#endif
if (rate_us && rate_limit_id != DEV_LOG_RATE_LIMIT_ID_NONE)
{
uint64_t curr_timestamp;
/* Do rate limit. */
curr_timestamp = bcmos_timestamp64();
if (curr_timestamp - rate_limit_id2timestamp[rate_limit_id] < rate_us)
return BCMOS_TRUE; /* Filter out this message. */
rate_limit_id2timestamp[rate_limit_id] = curr_timestamp;
}
return BCMOS_FALSE;
}
static void _bcm_dev_log_vlog(dev_log_id xi_id,
bcm_dev_log_level xi_log_level,
uint32_t xi_flags,
uint32_t rate_us,
dev_log_rate_limit_id rate_limit_id,
const char *fmt,
va_list args)
{
dev_log_id_parm *id = (dev_log_id_parm *)xi_id;
dev_log_queue_msg *log_queue_msg;
bcmos_errno error = BCM_ERR_OK;
bcmos_msg *msg;
if (dev_log.state != BCM_DEV_LOG_STATE_ENABLED)
{
if (dev_log.flags & BCM_DEV_LOG_FLAG_DISABLED_WITH_PRINTF)
DEV_LOG_VPRINTF(fmt, args);
return;
}
if (!xi_id || (xi_id == DEV_LOG_INVALID_ID))
{
/* If this ID was not registered - or registered incorrectly */
DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id);
DEV_LOG_VPRINTF(fmt, args); /* This will allow us to debug what line caused the bug. */
return;
}
if ((xi_log_level >= DEV_LOG_LEVEL_NUM_OF) || (xi_log_level == DEV_LOG_LEVEL_NO_LOG))
{
DEV_LOG_ERROR_PRINTF("xi_log_level (%u) is invalid\n", xi_log_level);
DEV_LOG_VPRINTF(fmt, args); /* This will allow us to debug what line caused the bug. */
return;
}
if (id->log_type == DEV_LOG_ID_TYPE_NONE)
{
return;
}
#ifdef BCM_SUBSYSTEM_HOST
/* Always use CALLER_FMT mode on the host to avoid portability issues with
* transferring va_list as an array */
xi_flags |= BCM_LOG_FLAG_CALLER_FMT;
#endif
/* Update log id counters */
id->counters[xi_log_level]++;
if (bcm_dev_log_should_drop(id, xi_log_level, rate_us, rate_limit_id))
return;
msg = bcmos_msg_pool_alloc(&dev_log.pool);
if (!msg)
{
bcm_dev_log_drop_report();
return;
}
log_queue_msg = msg->data;
/* Create log message */
log_queue_msg->log_id = id;
log_queue_msg->time_stamp = dev_log.dev_log_parm.get_time_cb();
log_queue_msg->msg_level = xi_log_level;
log_queue_msg->flags = xi_flags;
#ifndef BCM_SUBSYSTEM_HOST
/* It is not really necessary to compile out non CALLER_FMT case on the host.
* However, keeping unused code will make Coverity unhappy */
if (unlikely(xi_flags & BCM_LOG_FLAG_CALLER_FMT))
{
#endif /* #ifndef BCM_SUBSYSTEM_HOST */
vsnprintf(log_queue_msg->u.str, MAX_DEV_LOG_STRING_SIZE, (xi_flags & BCM_LOG_FLAG_FILENAME_IN_FMT) ? dev_log_basename(fmt) : fmt, args);
#ifndef BCM_SUBSYSTEM_HOST
}
else
{
uint32_t i;
uint32_t offset;
log_queue_msg->u.fmt_args.fmt = fmt;
offset = ((long)log_queue_msg->u.fmt_args.args % 8 == 0) ? 0 : 1; /* start on an 8-byte boundary */
for (i = 0; i < DEV_LOG_MAX_ARGS; i++)
{
log_queue_msg->u.fmt_args.args[i + offset] = va_arg(args, void *);
}
}
#endif /* #ifndef BCM_SUBSYSTEM_HOST */
if (bcmos_sem_post_is_allowed())
{
error = bcmos_msg_send(&dev_log.save_queue, msg, BCMOS_MSG_SEND_AUTO_FREE);
}
else
{
bcmos_timer_parm timer_params =
{
.name = "dev_log_timer",
.handler = bcm_dev_log_msg_send_timer_cb,
};
if (!(dev_log_timer.flags & BCMOS_TIMER_FLAG_VALID))
bcmos_timer_create(&dev_log_timer, &timer_params);
/* Limitation: We don't send more than 1 logger message even if _bcm_dev_log_vlog() was called multiple timers in IRQs disabled mode because we have a single timer. */
if (!bcmos_timer_is_running(&dev_log_timer))
{
bcmos_timer_handler_set(&dev_log_timer, bcm_dev_log_msg_send_timer_cb, (long)msg);
bcmos_timer_start(&dev_log_timer, 0);
}
}
if (error != BCM_ERR_OK)
{
id->lost_msg_cnt++;
}
}
void bcm_dev_log_vlog(dev_log_id xi_id,
bcm_dev_log_level xi_log_level,
uint32_t xi_flags,
const char *fmt,
va_list args)
{
_bcm_dev_log_vlog(xi_id, xi_log_level, xi_flags, 0, DEV_LOG_RATE_LIMIT_ID_NONE, fmt, args);
}
/* IMPORTANT!!!
* The function bcm_dev_log_log() must have even number of arguments before the '...' (see comments on header file).
*/
void bcm_dev_log_log(dev_log_id xi_id,
bcm_dev_log_level xi_log_level,
uint32_t xi_flags,
const char *fmt,
...)
{
va_list args;
va_start(args, fmt);
bcm_dev_log_vlog(xi_id, xi_log_level, xi_flags, fmt, args);
va_end(args);
}
void bcm_dev_log_log_ratelimit(dev_log_id xi_id,
bcm_dev_log_level xi_log_level,
uint32_t xi_flags,
uint32_t rate_us,
dev_log_rate_limit_id rate_limit_id,
const char *fmt,
...)
{
va_list args;
va_start(args, fmt);
_bcm_dev_log_vlog(xi_id, xi_log_level, xi_flags, rate_us, rate_limit_id, fmt, args);
va_end(args);
}
#ifdef BCMOS_TRACE_IN_DEV_LOG
static dev_log_id bcmos_trace_dev_log_id[] =
{
[BCMOS_TRACE_LEVEL_NONE] = DEV_LOG_INVALID_ID,
[BCMOS_TRACE_LEVEL_ERROR] = DEV_LOG_INVALID_ID,
[BCMOS_TRACE_LEVEL_INFO] = DEV_LOG_INVALID_ID,
[BCMOS_TRACE_LEVEL_VERBOSE] = DEV_LOG_INVALID_ID,
[BCMOS_TRACE_LEVEL_DEBUG] = DEV_LOG_INVALID_ID
};
static bcm_dev_log_level bcmos_trace_dev_log_level[] =
{
[BCMOS_TRACE_LEVEL_ERROR] = DEV_LOG_LEVEL_ERROR,
[BCMOS_TRACE_LEVEL_INFO] = DEV_LOG_LEVEL_INFO,
[BCMOS_TRACE_LEVEL_VERBOSE] = DEV_LOG_LEVEL_INFO,
[BCMOS_TRACE_LEVEL_DEBUG] = DEV_LOG_LEVEL_DEBUG
};
#endif
/********************************************************************************************/
/* */
/* Name: bcm_dev_log_os_trace_init */
/* */
/* Abstract: Direct bcmos_trace() output to log */
/* Arguments: NONE */
/* */
/* Return Value: */
/* bcmos_errno - Success code (BCM_ERR_OK) or Error code (see bcmos_errno.h) */
/* */
/********************************************************************************************/
bcmos_errno bcm_dev_log_os_trace_init(void)
{
#ifdef BCMOS_TRACE_IN_DEV_LOG
bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_ERROR] = bcm_dev_log_id_register("trace_error",
DEV_LOG_LEVEL_ERROR, DEV_LOG_ID_TYPE_BOTH);
bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_INFO] = bcm_dev_log_id_register("trace_info",
DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_VERBOSE] = bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_INFO];
bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_DEBUG] = bcm_dev_log_id_register("trace_debug",
DEV_LOG_LEVEL_DEBUG, DEV_LOG_ID_TYPE_BOTH);
if (bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_ERROR] == DEV_LOG_INVALID_ID ||
bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_INFO] == DEV_LOG_INVALID_ID ||
bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_DEBUG] == DEV_LOG_INVALID_ID)
{
return BCM_ERR_NORES;
}
return BCM_ERR_OK;
#else /* #ifdef BCMOS_TRACE_IN_DEV_LOG */
return BCM_ERR_NOT_SUPPORTED;
#endif
}
#ifdef BCMOS_TRACE_IN_DEV_LOG
/* Direct OS trace to dev_log */
void bcmos_trace(bcmos_trace_level level, const char *format, ...)
{
va_list args;
va_start(args, format);
if (bcmos_trace_dev_log_id[level] != DEV_LOG_INVALID_ID && dev_log.state == BCM_DEV_LOG_STATE_ENABLED)
{
if (level == BCMOS_TRACE_LEVEL_ERROR)
bcm_dev_log_vlog(bcmos_trace_dev_log_id[level], bcmos_trace_dev_log_level[level], BCM_LOG_FLAG_CALLER_FMT, format, args);
else
bcm_dev_log_vlog(bcmos_trace_dev_log_id[level], bcmos_trace_dev_log_level[level], 0, format, args);
}
va_end(args);
}
#endif
#endif /* ENABLE_LOG */