blob: 8c8cc2594343a02b4c2205d211f835b800cc359c [file] [log] [blame]
Shad Ansari2f7f9be2017-06-07 13:34:53 -07001/*
2<:copyright-BRCM:2016:DUAL/GPL:standard
3
4 Broadcom Proprietary and Confidential.(c) 2016 Broadcom
5 All Rights Reserved
6
7Unless you and Broadcom execute a separate written software license
8agreement governing use of this software, this software is licensed
9to you under the terms of the GNU General Public License version 2
10(the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
11with 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
22Not withstanding the above, under no circumstances may you combine
23this software in any way with any other Broadcom software provided
24under a license other than the GPL, without Broadcom's express prior
25written 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
36static bcmos_timer dev_log_timer;
37
38/* Store timestamp per every rate-limited log ID. */
39static uint64_t rate_limit_id2timestamp[DEV_LOG_RATE_LIMIT_ID__NUM_OF];
40
41const 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*/
68static 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*/
100static 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
121static 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
128uint8_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
140static 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
183static 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
294void 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 */
306void 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
319void 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
336static 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
345static 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/********************************************************************************************/
366bcmos_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 */
391void 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 */