Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#ifndef ZEPHYR_INCLUDE_LOGGING_LOG_CORE_H_
#define ZEPHYR_INCLUDE_LOGGING_LOG_CORE_H_

#include <zephyr/logging/log_msg.h>
#include <zephyr/logging/log_instance.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#include <zephyr/sys/util.h>

/* This header file keeps all macros and functions needed for creating logging
 * messages (macros like @ref LOG_ERR).
 */
#define LOG_LEVEL_NONE 0U
#define LOG_LEVEL_ERR  1U
#define LOG_LEVEL_WRN  2U
#define LOG_LEVEL_INF  3U
#define LOG_LEVEL_DBG  4U

#ifdef __cplusplus
extern "C" {
#endif

#ifndef CONFIG_LOG
#define CONFIG_LOG_DEFAULT_LEVEL 0U
#define CONFIG_LOG_MAX_LEVEL 0U
#endif

/* Id of local domain. */
#define Z_LOG_LOCAL_DOMAIN_ID 0

#define LOG_FUNCTION_PREFIX_MASK \
	(((uint32_t)IS_ENABLED(CONFIG_LOG_FUNC_NAME_PREFIX_ERR) << \
	  LOG_LEVEL_ERR) | \
	 ((uint32_t)IS_ENABLED(CONFIG_LOG_FUNC_NAME_PREFIX_WRN) << \
	  LOG_LEVEL_WRN) | \
	 ((uint32_t)IS_ENABLED(CONFIG_LOG_FUNC_NAME_PREFIX_INF) << \
	  LOG_LEVEL_INF) | \
	 ((uint32_t)IS_ENABLED(CONFIG_LOG_FUNC_NAME_PREFIX_DBG) << LOG_LEVEL_DBG))

/** @brief Macro for returning local level value if defined or default.
 *
 * Check @ref IS_ENABLED macro for detailed explanation of the trick.
 */
#define Z_LOG_RESOLVED_LEVEL(_level, _default) \
	Z_LOG_RESOLVED_LEVEL1(_level, _default)

#define Z_LOG_RESOLVED_LEVEL1(_level, _default) \
	__COND_CODE(_LOG_XXXX##_level, (_level), (_default))

#define _LOG_XXXX0  _LOG_YYYY,
#define _LOG_XXXX0U _LOG_YYYY,
#define _LOG_XXXX1  _LOG_YYYY,
#define _LOG_XXXX1U _LOG_YYYY,
#define _LOG_XXXX2  _LOG_YYYY,
#define _LOG_XXXX2U _LOG_YYYY,
#define _LOG_XXXX3  _LOG_YYYY,
#define _LOG_XXXX3U _LOG_YYYY,
#define _LOG_XXXX4  _LOG_YYYY,
#define _LOG_XXXX4U _LOG_YYYY,

/**
 * @brief Macro for conditional code generation if provided log level allows.
 *
 * Macro behaves similarly to standard \#if \#else \#endif clause. The
 * difference is that it is evaluated when used and not when header file is
 * included.
 *
 * @param _eval_level Evaluated level. If level evaluates to one of existing log
 *		      log level (1-4) then macro evaluates to _iftrue.
 * @param _iftrue     Code that should be inserted when evaluated to true. Note,
 *		      that parameter must be provided in brackets.
 * @param _iffalse    Code that should be inserted when evaluated to false.
 *		      Note, that parameter must be provided in brackets.
 */
#define Z_LOG_EVAL(_eval_level, _iftrue, _iffalse) \
	Z_LOG_EVAL1(_eval_level, _iftrue, _iffalse)

#define Z_LOG_EVAL1(_eval_level, _iftrue, _iffalse) \
	__COND_CODE(_LOG_ZZZZ##_eval_level, _iftrue, _iffalse)

#define _LOG_ZZZZ0  _LOG_YYYY,
#define _LOG_ZZZZ0U _LOG_YYYY,
#define _LOG_ZZZZ1  _LOG_YYYY,
#define _LOG_ZZZZ1U _LOG_YYYY,
#define _LOG_ZZZZ2  _LOG_YYYY,
#define _LOG_ZZZZ2U _LOG_YYYY,
#define _LOG_ZZZZ3  _LOG_YYYY,
#define _LOG_ZZZZ3U _LOG_YYYY,
#define _LOG_ZZZZ4  _LOG_YYYY,
#define _LOG_ZZZZ4U _LOG_YYYY,

/**
 *
 * @brief Macro for getting ID of current module.
 */
#define LOG_CURRENT_MODULE_ID() (__log_level != 0 ? \
	log_const_source_id(__log_current_const_data) : 0U)

/* Set of defines that are set to 1 if function name prefix is enabled for given level. */
#define Z_LOG_FUNC_PREFIX_0U 0
#define Z_LOG_FUNC_PREFIX_1U COND_CODE_1(CONFIG_LOG_FUNC_NAME_PREFIX_ERR, (1), (0))
#define Z_LOG_FUNC_PREFIX_2U COND_CODE_1(CONFIG_LOG_FUNC_NAME_PREFIX_WRN, (1), (0))
#define Z_LOG_FUNC_PREFIX_3U COND_CODE_1(CONFIG_LOG_FUNC_NAME_PREFIX_INF, (1), (0))
#define Z_LOG_FUNC_PREFIX_4U COND_CODE_1(CONFIG_LOG_FUNC_NAME_PREFIX_DBG, (1), (0))

/**
 * @brief Macro for optional injection of function name as first argument of
 *	  formatted string. COND_CODE_0() macro is used to handle no arguments
 *	  case.
 *
 * The purpose of this macro is to prefix string literal with format specifier
 * for function name and inject function name as first argument. In order to
 * handle string with no arguments _LOG_Z_EVAL is used.
 */
#define Z_LOG_STR_WITH_PREFIX2(...) \
	"%s: " GET_ARG_N(1, __VA_ARGS__), (const char *)__func__\
		COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__),\
			    (),\
			    (, GET_ARGS_LESS_N(1, __VA_ARGS__))\
			   )

/* Macro handles case when no format string is provided: e.g. LOG_DBG().
 * Handling of format string is deferred to the next level macro.
 */
#define Z_LOG_STR_WITH_PREFIX(...) \
	COND_CODE_0(NUM_VA_ARGS_LESS_1(_, ##__VA_ARGS__), \
		("%s", (const char *)__func__), \
		(Z_LOG_STR_WITH_PREFIX2(__VA_ARGS__)))

/**
 * @brief Handle optional injection of function name as the first argument.
 *
 * Additionally, macro is handling the empty message case.
 */
#define Z_LOG_STR(_level, ...) \
	COND_CODE_1(UTIL_CAT(Z_LOG_FUNC_PREFIX_##_level), \
		(Z_LOG_STR_WITH_PREFIX(__VA_ARGS__)), (__VA_ARGS__))

#define Z_LOG_LEVEL_CHECK(_level, _check_level, _default_level) \
	(_level <= Z_LOG_RESOLVED_LEVEL(_check_level, _default_level))

#define Z_LOG_CONST_LEVEL_CHECK(_level)					    \
	(IS_ENABLED(CONFIG_LOG) &&					    \
	(Z_LOG_LEVEL_CHECK(_level, CONFIG_LOG_OVERRIDE_LEVEL, LOG_LEVEL_NONE) \
	||								    \
	((IS_ENABLED(CONFIG_LOG_OVERRIDE_LEVEL) == false) &&		    \
	(_level <= __log_level) &&					    \
	(_level <= CONFIG_LOG_MAX_LEVEL)				    \
	)								    \
	))

/*****************************************************************************/
/****************** Definitions used by minimal logging *********************/
/*****************************************************************************/
void z_log_minimal_hexdump_print(int level, const void *data, size_t size);
void z_log_minimal_vprintk(const char *fmt, va_list ap);
void z_log_minimal_printk(const char *fmt, ...);

#define Z_LOG_TO_PRINTK(_level, fmt, ...) do { \
	z_log_minimal_printk("%c: " fmt "\n", \
			     z_log_minimal_level_to_char(_level), \
			     ##__VA_ARGS__); \
} while (false)

#define Z_LOG_TO_VPRINTK(_level, fmt, valist) do { \
	z_log_minimal_printk("%c: ", z_log_minimal_level_to_char(_level)); \
	z_log_minimal_vprintk(fmt, valist); \
	z_log_minimal_printk("\n"); \
} while (false)

static inline char z_log_minimal_level_to_char(int level)
{
	switch (level) {
	case LOG_LEVEL_ERR:
		return 'E';
	case LOG_LEVEL_WRN:
		return 'W';
	case LOG_LEVEL_INF:
		return 'I';
	case LOG_LEVEL_DBG:
		return 'D';
	default:
		return '?';
	}
}

#define Z_LOG_INST(_inst) COND_CODE_1(CONFIG_LOG, (_inst), NULL)

/*****************************************************************************/
/****************** Macros for standard logging ******************************/
/*****************************************************************************/
/** @internal
 * @brief Generic logging macro.
 *
 * It checks against static levels (resolved at compile timer), runtime levels
 * and modes and dispatch to relevant processing path.
 *
 * @param _level Log message severity level.
 *
 * @param _inst Set to 1 for instance specific log message. 0 otherwise.
 *
 * @param _source Pointer to static source descriptor object. NULL when runtime filtering
 * is enabled.
 *
 * @param _dsource Pointer to dynamic source descriptor. NULL when runtime filtering
 * is disabled.
 *
 * @param ... String with arguments.
 */
#define Z_LOG2(_level, _inst, _source, _dsource, ...) do { \
	if (!Z_LOG_CONST_LEVEL_CHECK(_level)) { \
		break; \
	} \
	if (IS_ENABLED(CONFIG_LOG_MODE_MINIMAL)) { \
		Z_LOG_TO_PRINTK(_level, __VA_ARGS__); \
		break; \
	} \
	/* For instance logging check instance specific static level */ \
	if (_inst != 0 && !IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { \
		if (_level > ((struct log_source_const_data *)_source)->level) { \
			break; \
		} \
	} \
	\
	bool is_user_context = k_is_user_context(); \
	if (!IS_ENABLED(CONFIG_LOG_FRONTEND) && IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) && \
	    !is_user_context && _level > Z_LOG_RUNTIME_FILTER((_dsource)->filters)) { \
		break; \
	} \
	int _mode; \
	void *_src = IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) ? \
		(void *)_dsource : (void *)_source; \
	Z_LOG_MSG_CREATE(UTIL_NOT(IS_ENABLED(CONFIG_USERSPACE)), _mode, \
				  Z_LOG_LOCAL_DOMAIN_ID, _src, _level, NULL,\
			  0, __VA_ARGS__); \
	(void)_mode; \
	if (false) { \
		/* Arguments checker present but never evaluated.*/ \
		/* Placed here to ensure that __VA_ARGS__ are*/ \
		/* evaluated once when log is enabled.*/ \
		z_log_printf_arg_checker(__VA_ARGS__); \
	} \
} while (false)

#define Z_LOG(_level, ...) \
	Z_LOG2(_level, 0, __log_current_const_data, __log_current_dynamic_data, __VA_ARGS__)

#define Z_LOG_INSTANCE(_level, _inst, ...) \
	Z_LOG2(_level, 1, \
		COND_CODE_1(CONFIG_LOG_RUNTIME_FILTERING, (NULL), (Z_LOG_INST(_inst))), \
		(struct log_source_dynamic_data *)COND_CODE_1( \
						CONFIG_LOG_RUNTIME_FILTERING, \
						(Z_LOG_INST(_inst)), (NULL)), \
		__VA_ARGS__)

/*****************************************************************************/
/****************** Macros for hexdump logging *******************************/
/*****************************************************************************/
/** @internal
 * @brief Generic logging macro.
 *
 * It checks against static levels (resolved at compile timer), runtime levels
 * and modes and dispatch to relevant processing path.
 *
 * @param _level Log message severity level.
 *
 * @param _inst Set to 1 for instance specific log message. 0 otherwise.
 *
 * @param _source Pointer to static source descriptor object. NULL when runtime filtering
 * is enabled.
 *
 * @param _dsource Pointer to dynamic source descriptor. NULL when runtime filtering
 * is disabled.
 *
 * @param _data Hexdump data;
 *
 * @param _len Hexdump data length.
 *
 * @param ... String.
 */
#define Z_LOG_HEXDUMP2(_level, _inst, _source, _dsource, _data, _len, ...) do { \
	const char *_str = GET_ARG_N(1, __VA_ARGS__); \
	if (!Z_LOG_CONST_LEVEL_CHECK(_level)) {	\
		break; \
	} \
	/* For instance logging check instance specific static level */ \
	if (_inst & !IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING)) { \
		if (_level > ((struct log_source_const_data *)_source)->level) { \
			break; \
		} \
	} \
	bool is_user_context = k_is_user_context(); \
	uint32_t filters = IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) ? \
						(_dsource)->filters : 0;\
	\
	if (IS_ENABLED(CONFIG_LOG_MODE_MINIMAL)) { \
		Z_LOG_TO_PRINTK(_level, "%s", _str); \
		z_log_minimal_hexdump_print(_level, \
					    (const char *)_data, _len);\
		break; \
	} \
	if (!IS_ENABLED(CONFIG_LOG_FRONTEND) && IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) && \
	    !is_user_context && _level > Z_LOG_RUNTIME_FILTER(filters)) { \
		break; \
	} \
	int mode; \
	void *_src = IS_ENABLED(CONFIG_LOG_RUNTIME_FILTERING) ? \
		(void *)_dsource : (void *)_source; \
	Z_LOG_MSG_CREATE(UTIL_NOT(IS_ENABLED(CONFIG_USERSPACE)), mode, \
				  Z_LOG_LOCAL_DOMAIN_ID, _src, _level, \
			  _data, _len, \
			COND_CODE_0(NUM_VA_ARGS_LESS_1(_, ##__VA_ARGS__), \
				(), \
			  (COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), \
				  ("%s", __VA_ARGS__), (__VA_ARGS__)))));\
} while (false)

#define Z_LOG_HEXDUMP(_level, _data, _length, ...) \
	Z_LOG_HEXDUMP2(_level, 0, \
		      __log_current_const_data, \
		      __log_current_dynamic_data, \
		      _data, _length, __VA_ARGS__)

#define Z_LOG_HEXDUMP_INSTANCE(_level, _inst, _data, _length, _str) \
	Z_LOG_HEXDUMP2(_level, 1, \
		COND_CODE_1(CONFIG_LOG_RUNTIME_FILTERING, (NULL), (Z_LOG_INST(_inst))), \
		(struct log_source_dynamic_data *)COND_CODE_1( \
						CONFIG_LOG_RUNTIME_FILTERING, \
						(Z_LOG_INST(_inst)), (NULL)), \
		_data, _length, _str)

/*****************************************************************************/
/****************** Filtering macros *****************************************/
/*****************************************************************************/

/** @brief Number of bits used to encode log level. */
#define LOG_LEVEL_BITS 3U

/** @brief Filter slot size. */
#define LOG_FILTER_SLOT_SIZE LOG_LEVEL_BITS

/** @brief Number of slots in one word. */
#define LOG_FILTERS_NUM_OF_SLOTS (32 / LOG_FILTER_SLOT_SIZE)

/** @brief Slot mask. */
#define LOG_FILTER_SLOT_MASK (BIT(LOG_FILTER_SLOT_SIZE) - 1U)

/** @brief Bit offset of a slot.
 *
 *  @param _id Slot ID.
 */
#define LOG_FILTER_SLOT_SHIFT(_id) (LOG_FILTER_SLOT_SIZE * (_id))

#define LOG_FILTER_SLOT_GET(_filters, _id) \
	((*(_filters) >> LOG_FILTER_SLOT_SHIFT(_id)) & LOG_FILTER_SLOT_MASK)

#define LOG_FILTER_SLOT_SET(_filters, _id, _filter)		     \
	do {							     \
		*(_filters) &= ~(LOG_FILTER_SLOT_MASK <<	     \
				 LOG_FILTER_SLOT_SHIFT(_id));	     \
		*(_filters) |= ((_filter) & LOG_FILTER_SLOT_MASK) << \
			       LOG_FILTER_SLOT_SHIFT(_id);	     \
	} while (false)

#define LOG_FILTER_AGGR_SLOT_IDX 0

#define LOG_FILTER_AGGR_SLOT_GET(_filters) \
	LOG_FILTER_SLOT_GET(_filters, LOG_FILTER_AGGR_SLOT_IDX)

#define LOG_FILTER_FIRST_BACKEND_SLOT_IDX 1

/* Return aggregated (highest) level for all enabled backends, e.g. if there
 * are 3 active backends, one backend is set to get INF logs from a module and
 * two other backends are set for ERR, returned level is INF.
 */
#define Z_LOG_RUNTIME_FILTER(_filter) \
	LOG_FILTER_SLOT_GET(&_filter, LOG_FILTER_AGGR_SLOT_IDX)

/** @brief Log level value used to indicate log entry that should not be
 *	   formatted (raw string).
 */
#define LOG_LEVEL_INTERNAL_RAW_STRING LOG_LEVEL_NONE

TYPE_SECTION_START_EXTERN(struct log_source_const_data, log_const);
TYPE_SECTION_END_EXTERN(struct log_source_const_data, log_const);

/** @brief Create message for logging printk-like string or a raw string.
 *
 * Part of printk string processing is appending of carriage return after any
 * new line character found in the string. If it is not desirable then @p _is_raw
 * can be set to 1 to indicate raw string. This information is stored in the source
 * field which is not used for its typical purpose in this case.
 *
 * @param _is_raw	Set to 1 to indicate raw string, set to 0 to indicate printk.
 * @param ...		Format string with arguments.
 */
#define Z_LOG_PRINTK(_is_raw, ...) do { \
	if (!IS_ENABLED(CONFIG_LOG)) { \
		break; \
	} \
	if (IS_ENABLED(CONFIG_LOG_MODE_MINIMAL)) { \
		z_log_minimal_printk(__VA_ARGS__); \
		break; \
	} \
	int _mode; \
	if (0) {\
		z_log_printf_arg_checker(__VA_ARGS__); \
	} \
	Z_LOG_MSG_CREATE(!IS_ENABLED(CONFIG_USERSPACE), _mode, \
			  Z_LOG_LOCAL_DOMAIN_ID, (uintptr_t)_is_raw, \
			  LOG_LEVEL_INTERNAL_RAW_STRING, NULL, 0, __VA_ARGS__);\
} while (0)

/** @brief Get index of the log source based on the address of the constant data
 *         associated with the source.
 *
 * @param data Address of the constant data.
 *
 * @return Source ID.
 */
static inline uint32_t log_const_source_id(
				const struct log_source_const_data *data)
{
	return ((const uint8_t *)data - (uint8_t *)TYPE_SECTION_START(log_const))/
			sizeof(struct log_source_const_data);
}

TYPE_SECTION_START_EXTERN(struct log_source_dynamic_data, log_dynamic);
TYPE_SECTION_END_EXTERN(struct log_source_dynamic_data, log_dynamic);

/** @brief Creates name of variable and section for runtime log data.
 *
 *  @param _name Name.
 */
#define LOG_ITEM_DYNAMIC_DATA(_name) UTIL_CAT(log_dynamic_, _name)

#define LOG_INSTANCE_DYNAMIC_DATA(_module_name, _inst) \
	LOG_ITEM_DYNAMIC_DATA(Z_LOG_INSTANCE_FULL_NAME(_module_name, _inst))

/** @brief Get index of the log source based on the address of the dynamic data
 *         associated with the source.
 *
 * @param data Address of the dynamic data.
 *
 * @return Source ID.
 */
static inline uint32_t log_dynamic_source_id(struct log_source_dynamic_data *data)
{
	return ((uint8_t *)data - (uint8_t *)TYPE_SECTION_START(log_dynamic))/
			sizeof(struct log_source_dynamic_data);
}

/** @brief Dummy function to trigger log messages arguments type checking. */
static inline __printf_like(1, 2)
void z_log_printf_arg_checker(const char *fmt, ...)
{
	ARG_UNUSED(fmt);
}

/**
 * @brief Writes a generic log message to the logging v2.
 *
 * @note This function is intended to be used when porting other log systems.
 *
 * @param level          Log level..
 * @param fmt            String to format.
 * @param ap             Pointer to arguments list.
 */
static inline void log2_generic(uint8_t level, const char *fmt, va_list ap)
{
	z_log_msg_runtime_vcreate(Z_LOG_LOCAL_DOMAIN_ID, NULL, level,
				   NULL, 0, 0, fmt, ap);
}

#ifdef __cplusplus
}
#endif

#endif /* ZEPHYR_INCLUDE_LOGGING_LOG_CORE_H_ */