| /* |
| <: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 */ |