Shad Ansari | 2f7f9be | 2017-06-07 13:34:53 -0700 | [diff] [blame] | 1 | /* |
| 2 | <:copyright-BRCM:2016:DUAL/GPL:standard |
| 3 | |
| 4 | Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| 5 | All Rights Reserved |
| 6 | |
| 7 | Unless you and Broadcom execute a separate written software license |
| 8 | agreement governing use of this software, this software is licensed |
| 9 | to you under the terms of the GNU General Public License version 2 |
| 10 | (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| 11 | with the following added to such license: |
| 12 | |
| 13 | As a special exception, the copyright holders of this software give |
| 14 | you permission to link this software with independent modules, and |
| 15 | to copy and distribute the resulting executable under terms of your |
| 16 | choice, provided that you also meet, for each linked independent |
| 17 | module, the terms and conditions of the license of that module. |
| 18 | An independent module is a module which is not derived from this |
| 19 | software. The special exception does not apply to any modifications |
| 20 | of the software. |
| 21 | |
| 22 | Not withstanding the above, under no circumstances may you combine |
| 23 | this software in any way with any other Broadcom software provided |
| 24 | under a license other than the GPL, without Broadcom's express prior |
| 25 | written consent. |
| 26 | |
| 27 | :> |
| 28 | */ |
| 29 | |
| 30 | #ifdef ENABLE_LOG |
| 31 | |
| 32 | #include "bcm_dev_log_task.h" |
| 33 | #include "bcm_dev_log_task_internal.h" |
| 34 | #include "bcm_dev_log.h" |
| 35 | |
| 36 | static bcmos_timer dev_log_timer; |
| 37 | |
| 38 | /* Store timestamp per every rate-limited log ID. */ |
| 39 | static uint64_t rate_limit_id2timestamp[DEV_LOG_RATE_LIMIT_ID__NUM_OF]; |
| 40 | |
| 41 | const char *dev_log_basename(const char *str) |
| 42 | { |
| 43 | const char *slash; |
| 44 | if ((slash = strchr(str, '/'))) |
| 45 | { |
| 46 | const char *str0 = str; |
| 47 | str = strchr(slash+1, ' '); |
| 48 | /* If log format string was created by BCM_LOG macro and there is no corruption of some kind |
| 49 | * str above is guaranteed to be != NULL. However, making str!=NULL assumption makes dev_log |
| 50 | * vulnerable to bugs in the caller. In this case the following inexpensive check prevents crash |
| 51 | * in dev_Log and helps in identifying the real culprit. |
| 52 | */ |
| 53 | if (!str) |
| 54 | return str0; |
| 55 | while (str[0] != '/') |
| 56 | str--; |
| 57 | str++; |
| 58 | } |
| 59 | return str; |
| 60 | } |
| 61 | |
| 62 | #ifdef TRIGGER_LOGGER_FEATURE |
| 63 | |
| 64 | /* check if the message fits the level and the other criteria for a trigger |
| 65 | return 1 if yes, the message will be printed |
| 66 | and returns 0 if the message will not be printed |
| 67 | */ |
| 68 | static bcmos_bool check_trigger(dev_log_id_parm *xi_id, bcm_dev_log_level xi_log_level) |
| 69 | { |
| 70 | /* the level of the message must be exactly the trigger level */ |
| 71 | if (xi_id->trigger_log_level != xi_log_level) |
| 72 | return BCMOS_FALSE; /* do not print the message - the level does not fit */ |
| 73 | |
| 74 | xi_id->trigger.counter++; |
| 75 | if (xi_id->trigger.counter >= xi_id->trigger.start_threshold) |
| 76 | { |
| 77 | if (xi_id->trigger.counter >= xi_id->trigger.stop_threshold) |
| 78 | { |
| 79 | /* trigger finished, check repeat */ |
| 80 | if (xi_id->trigger.repeat_threshold) |
| 81 | { |
| 82 | xi_id->trigger.repeat++; |
| 83 | if (xi_id->trigger.repeat < xi_id->trigger.repeat_threshold) |
| 84 | { |
| 85 | /* rewind trigger conditions */ |
| 86 | xi_id->trigger.counter = 0; |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | else /* trigger is active : counter greater than start and less than end */ |
| 91 | return BCMOS_TRUE; /* print the message */ |
| 92 | } |
| 93 | return BCMOS_FALSE;/* do not print the message - still less than start threshold */ |
| 94 | } |
| 95 | |
| 96 | /* check if the message fits the level and the criteria for throttle |
| 97 | return 1 if yes, the message will be printed |
| 98 | and returns 0 if the message will not be printed |
| 99 | */ |
| 100 | static bcmos_bool check_throttle(dev_log_id_parm *xi_id, bcm_dev_log_level xi_log_level) |
| 101 | { |
| 102 | /* check if any throttle is defined */ |
| 103 | if (xi_id->throttle_log_level != DEV_LOG_LEVEL_NO_LOG) |
| 104 | return BCMOS_TRUE; /* print the message - no trigger is set */ |
| 105 | |
| 106 | /* the level of the message must be exactly the throttle level */ |
| 107 | if (xi_id->throttle_log_level != xi_log_level) |
| 108 | return BCMOS_FALSE; /* do not print the message - the level does not fit */ |
| 109 | |
| 110 | xi_id->throttle.counter++; |
| 111 | if (xi_id->throttle.counter >= xi_id->throttle.threshold) |
| 112 | { |
| 113 | xi_id->throttle.counter = 0; |
| 114 | return BCMOS_TRUE; |
| 115 | } |
| 116 | return BCMOS_FALSE;/* do not print the message - still less than start threshold */ |
| 117 | } |
| 118 | |
| 119 | #endif |
| 120 | |
| 121 | static bcmos_timer_rc bcm_dev_log_msg_send_timer_cb(bcmos_timer *timer, long data) |
| 122 | { |
| 123 | bcmos_msg *msg = (bcmos_msg *)data; |
| 124 | bcmos_msg_send(&dev_log.save_queue, msg, BCMOS_MSG_SEND_AUTO_FREE); |
| 125 | return BCMOS_TIMER_OK; |
| 126 | } |
| 127 | |
| 128 | uint8_t bcm_dev_log_pool_occupancy_percent_get(void) |
| 129 | { |
| 130 | bcmos_msg_pool_info msg_pool_info; |
| 131 | bcmos_errno error = bcmos_msg_pool_query(&dev_log.pool, &msg_pool_info); |
| 132 | if (error != BCM_ERR_OK) |
| 133 | { |
| 134 | DEV_LOG_ERROR_PRINTF("bcmos_msg_pool_query() returned %s (%d)\n", bcmos_strerror(error), error); |
| 135 | return 0; |
| 136 | } |
| 137 | return (uint8_t)((100 * (msg_pool_info.parm.size - msg_pool_info.stat.free)) / msg_pool_info.parm.size); |
| 138 | } |
| 139 | |
| 140 | static bcmos_bool bcm_dev_log_should_drop( |
| 141 | dev_log_id_parm *id, |
| 142 | bcm_dev_log_level log_level, |
| 143 | uint32_t rate_us, |
| 144 | dev_log_rate_limit_id rate_limit_id) |
| 145 | { |
| 146 | /* If the msg pool is sufficiently full, drop all non-error messages */ |
| 147 | if (!bcm_dev_log_level_is_error(log_level) && |
| 148 | bcm_dev_log_pool_occupancy_percent_get() >= DEV_LOG_ERROR_ONLY_THRESHOLD_PERCENT) |
| 149 | { |
| 150 | bcm_dev_log_drop_report(); |
| 151 | return BCMOS_TRUE; |
| 152 | } |
| 153 | |
| 154 | #ifdef TRIGGER_LOGGER_FEATURE |
| 155 | /* if trigger defined - ignore throttle and printing level */ |
| 156 | if (id->trigger_log_level != DEV_LOG_LEVEL_NO_LOG) |
| 157 | return check_trigger(id, log_level); |
| 158 | #endif |
| 159 | |
| 160 | /* if trigger is not fullfilled - check other conditions */ |
| 161 | if (log_level > id->log_level_print && log_level > id->log_level_save) |
| 162 | return BCMOS_TRUE; |
| 163 | |
| 164 | #ifdef TRIGGER_LOGGER_FEATURE |
| 165 | if (!check_throttle(id, log_level)) |
| 166 | return BCMOS_TRUE; |
| 167 | #endif |
| 168 | |
| 169 | if (rate_us && rate_limit_id != DEV_LOG_RATE_LIMIT_ID_NONE) |
| 170 | { |
| 171 | uint64_t curr_timestamp; |
| 172 | |
| 173 | /* Do rate limit. */ |
| 174 | curr_timestamp = bcmos_timestamp64(); |
| 175 | if (curr_timestamp - rate_limit_id2timestamp[rate_limit_id] < rate_us) |
| 176 | return BCMOS_TRUE; /* Filter out this message. */ |
| 177 | rate_limit_id2timestamp[rate_limit_id] = curr_timestamp; |
| 178 | } |
| 179 | |
| 180 | return BCMOS_FALSE; |
| 181 | } |
| 182 | |
| 183 | static void _bcm_dev_log_vlog(dev_log_id xi_id, |
| 184 | bcm_dev_log_level xi_log_level, |
| 185 | uint32_t xi_flags, |
| 186 | uint32_t rate_us, |
| 187 | dev_log_rate_limit_id rate_limit_id, |
| 188 | const char *fmt, |
| 189 | va_list args) |
| 190 | { |
| 191 | dev_log_id_parm *id = (dev_log_id_parm *)xi_id; |
| 192 | dev_log_queue_msg *log_queue_msg; |
| 193 | bcmos_errno error = BCM_ERR_OK; |
| 194 | bcmos_msg *msg; |
| 195 | |
| 196 | if (dev_log.state != BCM_DEV_LOG_STATE_ENABLED) |
| 197 | { |
| 198 | if (dev_log.flags & BCM_DEV_LOG_FLAG_DISABLED_WITH_PRINTF) |
| 199 | DEV_LOG_VPRINTF(fmt, args); |
| 200 | return; |
| 201 | } |
| 202 | if (!xi_id || (xi_id == DEV_LOG_INVALID_ID)) |
| 203 | { |
| 204 | /* If this ID was not registered - or registered incorrectly */ |
| 205 | DEV_LOG_ERROR_PRINTF("Error: xi_id not valid (0x%x)\n", (unsigned int)xi_id); |
| 206 | DEV_LOG_VPRINTF(fmt, args); /* This will allow us to debug what line caused the bug. */ |
| 207 | return; |
| 208 | } |
| 209 | if ((xi_log_level >= DEV_LOG_LEVEL_NUM_OF) || (xi_log_level == DEV_LOG_LEVEL_NO_LOG)) |
| 210 | { |
| 211 | DEV_LOG_ERROR_PRINTF("xi_log_level (%u) is invalid\n", xi_log_level); |
| 212 | DEV_LOG_VPRINTF(fmt, args); /* This will allow us to debug what line caused the bug. */ |
| 213 | return; |
| 214 | } |
| 215 | if (id->log_type == DEV_LOG_ID_TYPE_NONE) |
| 216 | { |
| 217 | return; |
| 218 | } |
| 219 | #ifdef BCM_SUBSYSTEM_HOST |
| 220 | /* Always use CALLER_FMT mode on the host to avoid portability issues with |
| 221 | * transferring va_list as an array */ |
| 222 | xi_flags |= BCM_LOG_FLAG_CALLER_FMT; |
| 223 | #endif |
| 224 | |
| 225 | /* Update log id counters */ |
| 226 | id->counters[xi_log_level]++; |
| 227 | |
| 228 | if (bcm_dev_log_should_drop(id, xi_log_level, rate_us, rate_limit_id)) |
| 229 | return; |
| 230 | |
| 231 | msg = bcmos_msg_pool_alloc(&dev_log.pool); |
| 232 | if (!msg) |
| 233 | { |
| 234 | bcm_dev_log_drop_report(); |
| 235 | return; |
| 236 | } |
| 237 | log_queue_msg = msg->data; |
| 238 | /* Create log message */ |
| 239 | log_queue_msg->log_id = id; |
| 240 | log_queue_msg->time_stamp = dev_log.dev_log_parm.get_time_cb(); |
| 241 | log_queue_msg->msg_level = xi_log_level; |
| 242 | log_queue_msg->flags = xi_flags; |
| 243 | #ifndef BCM_SUBSYSTEM_HOST |
| 244 | /* It is not really necessary to compile out non CALLER_FMT case on the host. |
| 245 | * However, keeping unused code will make Coverity unhappy */ |
| 246 | if (unlikely(xi_flags & BCM_LOG_FLAG_CALLER_FMT)) |
| 247 | { |
| 248 | #endif /* #ifndef BCM_SUBSYSTEM_HOST */ |
| 249 | 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); |
| 250 | #ifndef BCM_SUBSYSTEM_HOST |
| 251 | } |
| 252 | else |
| 253 | { |
| 254 | uint32_t i; |
| 255 | uint32_t offset; |
| 256 | log_queue_msg->u.fmt_args.fmt = fmt; |
| 257 | |
| 258 | offset = ((long)log_queue_msg->u.fmt_args.args % 8 == 0) ? 0 : 1; /* start on an 8-byte boundary */ |
| 259 | for (i = 0; i < DEV_LOG_MAX_ARGS; i++) |
| 260 | { |
| 261 | log_queue_msg->u.fmt_args.args[i + offset] = va_arg(args, void *); |
| 262 | } |
| 263 | } |
| 264 | #endif /* #ifndef BCM_SUBSYSTEM_HOST */ |
| 265 | |
| 266 | if (bcmos_sem_post_is_allowed()) |
| 267 | { |
| 268 | error = bcmos_msg_send(&dev_log.save_queue, msg, BCMOS_MSG_SEND_AUTO_FREE); |
| 269 | } |
| 270 | else |
| 271 | { |
| 272 | bcmos_timer_parm timer_params = |
| 273 | { |
| 274 | .name = "dev_log_timer", |
| 275 | .handler = bcm_dev_log_msg_send_timer_cb, |
| 276 | }; |
| 277 | |
| 278 | if (!(dev_log_timer.flags & BCMOS_TIMER_FLAG_VALID)) |
| 279 | bcmos_timer_create(&dev_log_timer, &timer_params); |
| 280 | /* 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. */ |
| 281 | if (!bcmos_timer_is_running(&dev_log_timer)) |
| 282 | { |
| 283 | bcmos_timer_handler_set(&dev_log_timer, bcm_dev_log_msg_send_timer_cb, (long)msg); |
| 284 | bcmos_timer_start(&dev_log_timer, 0); |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | if (error != BCM_ERR_OK) |
| 289 | { |
| 290 | id->lost_msg_cnt++; |
| 291 | } |
| 292 | } |
| 293 | |
| 294 | void bcm_dev_log_vlog(dev_log_id xi_id, |
| 295 | bcm_dev_log_level xi_log_level, |
| 296 | uint32_t xi_flags, |
| 297 | const char *fmt, |
| 298 | va_list args) |
| 299 | { |
| 300 | _bcm_dev_log_vlog(xi_id, xi_log_level, xi_flags, 0, DEV_LOG_RATE_LIMIT_ID_NONE, fmt, args); |
| 301 | } |
| 302 | |
| 303 | /* IMPORTANT!!! |
| 304 | * The function bcm_dev_log_log() must have even number of arguments before the '...' (see comments on header file). |
| 305 | */ |
| 306 | void bcm_dev_log_log(dev_log_id xi_id, |
| 307 | bcm_dev_log_level xi_log_level, |
| 308 | uint32_t xi_flags, |
| 309 | const char *fmt, |
| 310 | ...) |
| 311 | { |
| 312 | va_list args; |
| 313 | |
| 314 | va_start(args, fmt); |
| 315 | bcm_dev_log_vlog(xi_id, xi_log_level, xi_flags, fmt, args); |
| 316 | va_end(args); |
| 317 | } |
| 318 | |
| 319 | void bcm_dev_log_log_ratelimit(dev_log_id xi_id, |
| 320 | bcm_dev_log_level xi_log_level, |
| 321 | uint32_t xi_flags, |
| 322 | uint32_t rate_us, |
| 323 | dev_log_rate_limit_id rate_limit_id, |
| 324 | const char *fmt, |
| 325 | ...) |
| 326 | { |
| 327 | va_list args; |
| 328 | |
| 329 | va_start(args, fmt); |
| 330 | _bcm_dev_log_vlog(xi_id, xi_log_level, xi_flags, rate_us, rate_limit_id, fmt, args); |
| 331 | va_end(args); |
| 332 | } |
| 333 | |
| 334 | #ifdef BCMOS_TRACE_IN_DEV_LOG |
| 335 | |
| 336 | static dev_log_id bcmos_trace_dev_log_id[] = |
| 337 | { |
| 338 | [BCMOS_TRACE_LEVEL_NONE] = DEV_LOG_INVALID_ID, |
| 339 | [BCMOS_TRACE_LEVEL_ERROR] = DEV_LOG_INVALID_ID, |
| 340 | [BCMOS_TRACE_LEVEL_INFO] = DEV_LOG_INVALID_ID, |
| 341 | [BCMOS_TRACE_LEVEL_VERBOSE] = DEV_LOG_INVALID_ID, |
| 342 | [BCMOS_TRACE_LEVEL_DEBUG] = DEV_LOG_INVALID_ID |
| 343 | }; |
| 344 | |
| 345 | static bcm_dev_log_level bcmos_trace_dev_log_level[] = |
| 346 | { |
| 347 | [BCMOS_TRACE_LEVEL_ERROR] = DEV_LOG_LEVEL_ERROR, |
| 348 | [BCMOS_TRACE_LEVEL_INFO] = DEV_LOG_LEVEL_INFO, |
| 349 | [BCMOS_TRACE_LEVEL_VERBOSE] = DEV_LOG_LEVEL_INFO, |
| 350 | [BCMOS_TRACE_LEVEL_DEBUG] = DEV_LOG_LEVEL_DEBUG |
| 351 | }; |
| 352 | |
| 353 | #endif |
| 354 | |
| 355 | /********************************************************************************************/ |
| 356 | /* */ |
| 357 | /* Name: bcm_dev_log_os_trace_init */ |
| 358 | /* */ |
| 359 | /* Abstract: Direct bcmos_trace() output to log */ |
| 360 | /* Arguments: NONE */ |
| 361 | /* */ |
| 362 | /* Return Value: */ |
| 363 | /* bcmos_errno - Success code (BCM_ERR_OK) or Error code (see bcmos_errno.h) */ |
| 364 | /* */ |
| 365 | /********************************************************************************************/ |
| 366 | bcmos_errno bcm_dev_log_os_trace_init(void) |
| 367 | { |
| 368 | #ifdef BCMOS_TRACE_IN_DEV_LOG |
| 369 | bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_ERROR] = bcm_dev_log_id_register("trace_error", |
| 370 | DEV_LOG_LEVEL_ERROR, DEV_LOG_ID_TYPE_BOTH); |
| 371 | bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_INFO] = bcm_dev_log_id_register("trace_info", |
| 372 | DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH); |
| 373 | bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_VERBOSE] = bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_INFO]; |
| 374 | bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_DEBUG] = bcm_dev_log_id_register("trace_debug", |
| 375 | DEV_LOG_LEVEL_DEBUG, DEV_LOG_ID_TYPE_BOTH); |
| 376 | if (bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_ERROR] == DEV_LOG_INVALID_ID || |
| 377 | bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_INFO] == DEV_LOG_INVALID_ID || |
| 378 | bcmos_trace_dev_log_id[BCMOS_TRACE_LEVEL_DEBUG] == DEV_LOG_INVALID_ID) |
| 379 | { |
| 380 | return BCM_ERR_NORES; |
| 381 | } |
| 382 | return BCM_ERR_OK; |
| 383 | #else /* #ifdef BCMOS_TRACE_IN_DEV_LOG */ |
| 384 | return BCM_ERR_NOT_SUPPORTED; |
| 385 | #endif |
| 386 | } |
| 387 | |
| 388 | #ifdef BCMOS_TRACE_IN_DEV_LOG |
| 389 | |
| 390 | /* Direct OS trace to dev_log */ |
| 391 | void bcmos_trace(bcmos_trace_level level, const char *format, ...) |
| 392 | { |
| 393 | va_list args; |
| 394 | |
| 395 | va_start(args, format); |
| 396 | if (bcmos_trace_dev_log_id[level] != DEV_LOG_INVALID_ID && dev_log.state == BCM_DEV_LOG_STATE_ENABLED) |
| 397 | { |
| 398 | if (level == BCMOS_TRACE_LEVEL_ERROR) |
| 399 | bcm_dev_log_vlog(bcmos_trace_dev_log_id[level], bcmos_trace_dev_log_level[level], BCM_LOG_FLAG_CALLER_FMT, format, args); |
| 400 | else |
| 401 | bcm_dev_log_vlog(bcmos_trace_dev_log_id[level], bcmos_trace_dev_log_level[level], 0, format, args); |
| 402 | } |
| 403 | |
| 404 | va_end(args); |
| 405 | } |
| 406 | |
| 407 | #endif |
| 408 | |
| 409 | #endif /* ENABLE_LOG */ |