log: Add an implementation of logging
Add the logging header file and implementation with some configuration options to control it. Signed-off-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		
							parent
							
								
									c5404b64fb
								
							
						
					
					
						commit
						e9c8d49d54
					
				|  | @ -310,6 +310,13 @@ S:	Maintained | ||||||
| T:	git git://git.denx.de/u-boot-i2c.git | T:	git git://git.denx.de/u-boot-i2c.git | ||||||
| F:	drivers/i2c/ | F:	drivers/i2c/ | ||||||
| 
 | 
 | ||||||
|  | LOGGING | ||||||
|  | M:	Simon Glass <sjg@chromium.org> | ||||||
|  | S:	Maintained | ||||||
|  | T:	git git://git.denx.de/u-boot.git | ||||||
|  | F:	common/log.c | ||||||
|  | F:	cmd/log.c | ||||||
|  | 
 | ||||||
| MICROBLAZE | MICROBLAZE | ||||||
| M:	Michal Simek <monstr@monstr.eu> | M:	Michal Simek <monstr@monstr.eu> | ||||||
| S:	Maintained | S:	Maintained | ||||||
|  |  | ||||||
|  | @ -420,6 +420,62 @@ config SYS_STDIO_DEREGISTER | ||||||
| 
 | 
 | ||||||
| endmenu | endmenu | ||||||
| 
 | 
 | ||||||
|  | menu "Logging" | ||||||
|  | 
 | ||||||
|  | config LOG | ||||||
|  | 	bool "Enable logging support" | ||||||
|  | 	help | ||||||
|  | 	  This enables support for logging of status and debug messages. These | ||||||
|  | 	  can be displayed on the console, recorded in a memory buffer, or | ||||||
|  | 	  discarded if not needed. Logging supports various categories and | ||||||
|  | 	  levels of severity. | ||||||
|  | 
 | ||||||
|  | config SPL_LOG | ||||||
|  | 	bool "Enable logging support in SPL" | ||||||
|  | 	help | ||||||
|  | 	  This enables support for logging of status and debug messages. These | ||||||
|  | 	  can be displayed on the console, recorded in a memory buffer, or | ||||||
|  | 	  discarded if not needed. Logging supports various categories and | ||||||
|  | 	  levels of severity. | ||||||
|  | 
 | ||||||
|  | config LOG_MAX_LEVEL | ||||||
|  | 	int "Maximum log level to record" | ||||||
|  | 	depends on LOG | ||||||
|  | 	default 5 | ||||||
|  | 	help | ||||||
|  | 	  This selects the maximum log level that will be recorded. Any value | ||||||
|  | 	  higher than this will be ignored. If possible log statements below | ||||||
|  | 	  this level will be discarded at build time. Levels: | ||||||
|  | 
 | ||||||
|  | 	    0 - panic | ||||||
|  | 	    1 - critical | ||||||
|  | 	    2 - error | ||||||
|  | 	    3 - warning | ||||||
|  | 	    4 - note | ||||||
|  | 	    5 - info | ||||||
|  | 	    6 - detail | ||||||
|  | 	    7 - debug | ||||||
|  | 
 | ||||||
|  | config SPL_LOG_MAX_LEVEL | ||||||
|  | 	int "Maximum log level to record in SPL" | ||||||
|  | 	depends on SPL_LOG | ||||||
|  | 	default 3 | ||||||
|  | 	help | ||||||
|  | 	  This selects the maximum log level that will be recorded. Any value | ||||||
|  | 	  higher than this will be ignored. If possible log statements below | ||||||
|  | 	  this level will be discarded at build time. Levels: | ||||||
|  | 
 | ||||||
|  | 	    0 - panic | ||||||
|  | 	    1 - critical | ||||||
|  | 	    2 - error | ||||||
|  | 	    3 - warning | ||||||
|  | 	    4 - note | ||||||
|  | 	    5 - info | ||||||
|  | 	    6 - detail | ||||||
|  | 	    7 - debug | ||||||
|  | 
 | ||||||
|  | endmenu | ||||||
|  | 
 | ||||||
| config DEFAULT_FDT_FILE | config DEFAULT_FDT_FILE | ||||||
| 	string "Default fdt file" | 	string "Default fdt file" | ||||||
| 	help | 	help | ||||||
|  |  | ||||||
|  | @ -128,5 +128,6 @@ obj-y += cli.o | ||||||
| obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o | obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o | ||||||
| obj-$(CONFIG_CMD_DFU) += dfu.o | obj-$(CONFIG_CMD_DFU) += dfu.o | ||||||
| obj-y += command.o | obj-y += command.o | ||||||
|  | obj-$(CONFIG_$(SPL_)LOG) += log.o | ||||||
| obj-y += s_record.o | obj-y += s_record.o | ||||||
| obj-y += xyzModem.o | obj-y += xyzModem.o | ||||||
|  |  | ||||||
|  | @ -0,0 +1,244 @@ | ||||||
|  | /*
 | ||||||
|  |  * Logging support | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2017 Google, Inc | ||||||
|  |  * Written by Simon Glass <sjg@chromium.org> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier:	GPL-2.0+ | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <common.h> | ||||||
|  | #include <log.h> | ||||||
|  | #include <malloc.h> | ||||||
|  | 
 | ||||||
|  | DECLARE_GLOBAL_DATA_PTR; | ||||||
|  | 
 | ||||||
|  | static struct log_device *log_device_find_by_name(const char *drv_name) | ||||||
|  | { | ||||||
|  | 	struct log_device *ldev; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(ldev, &gd->log_head, sibling_node) { | ||||||
|  | 		if (!strcmp(drv_name, ldev->drv->name)) | ||||||
|  | 			return ldev; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * log_has_cat() - check if a log category exists within a list | ||||||
|  |  * | ||||||
|  |  * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries | ||||||
|  |  *	long, terminated by LC_END if fewer | ||||||
|  |  * @cat: Category to search for | ||||||
|  |  * @return true if @cat is in @cat_list, else false | ||||||
|  |  */ | ||||||
|  | static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat) | ||||||
|  | { | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) { | ||||||
|  | 		if (cat_list[i] == cat) | ||||||
|  | 			return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * log_has_file() - check if a file is with a list | ||||||
|  |  * | ||||||
|  |  * @file_list: List of files to check, separated by comma | ||||||
|  |  * @file: File to check for. This string is matched against the end of each | ||||||
|  |  *	file in the list, i.e. ignoring any preceding path. The list is | ||||||
|  |  *	intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c | ||||||
|  |  * @return true if @file is in @file_list, else false | ||||||
|  |  */ | ||||||
|  | static bool log_has_file(const char *file_list, const char *file) | ||||||
|  | { | ||||||
|  | 	int file_len = strlen(file); | ||||||
|  | 	const char *s, *p; | ||||||
|  | 	int substr_len; | ||||||
|  | 
 | ||||||
|  | 	for (s = file_list; *s; s = p + (*p != '\0')) { | ||||||
|  | 		p = strchrnul(s, ','); | ||||||
|  | 		substr_len = p - s; | ||||||
|  | 		if (file_len >= substr_len && | ||||||
|  | 		    !strncmp(file + file_len - substr_len, s, substr_len)) | ||||||
|  | 			return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * log_passes_filters() - check if a log record passes the filters for a device | ||||||
|  |  * | ||||||
|  |  * @ldev: Log device to check | ||||||
|  |  * @rec: Log record to check | ||||||
|  |  * @return true if @rec is not blocked by the filters in @ldev, false if it is | ||||||
|  |  */ | ||||||
|  | static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec) | ||||||
|  | { | ||||||
|  | 	struct log_filter *filt; | ||||||
|  | 
 | ||||||
|  | 	/* If there are no filters, filter on the default log level */ | ||||||
|  | 	if (list_empty(&ldev->filter_head)) { | ||||||
|  | 		if (rec->level > gd->default_log_level) | ||||||
|  | 			return false; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(filt, &ldev->filter_head, sibling_node) { | ||||||
|  | 		if (rec->level > filt->max_level) | ||||||
|  | 			continue; | ||||||
|  | 		if ((filt->flags & LOGFF_HAS_CAT) && | ||||||
|  | 		    !log_has_cat(filt->cat_list, rec->cat)) | ||||||
|  | 			continue; | ||||||
|  | 		if (filt->file_list && | ||||||
|  | 		    !log_has_file(filt->file_list, rec->file)) | ||||||
|  | 			continue; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * log_dispatch() - Send a log record to all log devices for processing | ||||||
|  |  * | ||||||
|  |  * The log record is sent to each log device in turn, skipping those which have | ||||||
|  |  * filters which block the record | ||||||
|  |  * | ||||||
|  |  * @rec: Log record to dispatch | ||||||
|  |  * @return 0 (meaning success) | ||||||
|  |  */ | ||||||
|  | static int log_dispatch(struct log_rec *rec) | ||||||
|  | { | ||||||
|  | 	struct log_device *ldev; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(ldev, &gd->log_head, sibling_node) { | ||||||
|  | 		if (log_passes_filters(ldev, rec)) | ||||||
|  | 			ldev->drv->emit(ldev, rec); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int _log(enum log_category_t cat, enum log_level_t level, const char *file, | ||||||
|  | 	 int line, const char *func, const char *fmt, ...) | ||||||
|  | { | ||||||
|  | 	char buf[CONFIG_SYS_CBSIZE]; | ||||||
|  | 	struct log_rec rec; | ||||||
|  | 	va_list args; | ||||||
|  | 
 | ||||||
|  | 	rec.cat = cat; | ||||||
|  | 	rec.level = level; | ||||||
|  | 	rec.file = file; | ||||||
|  | 	rec.line = line; | ||||||
|  | 	rec.func = func; | ||||||
|  | 	va_start(args, fmt); | ||||||
|  | 	vsnprintf(buf, sizeof(buf), fmt, args); | ||||||
|  | 	va_end(args); | ||||||
|  | 	rec.msg = buf; | ||||||
|  | 	if (!gd || !(gd->flags & GD_FLG_LOG_READY)) { | ||||||
|  | 		if (gd) | ||||||
|  | 			gd->log_drop_count++; | ||||||
|  | 		return -ENOSYS; | ||||||
|  | 	} | ||||||
|  | 	log_dispatch(&rec); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int log_add_filter(const char *drv_name, enum log_category_t cat_list[], | ||||||
|  | 		   enum log_level_t max_level, const char *file_list) | ||||||
|  | { | ||||||
|  | 	struct log_filter *filt; | ||||||
|  | 	struct log_device *ldev; | ||||||
|  | 	int i; | ||||||
|  | 
 | ||||||
|  | 	ldev = log_device_find_by_name(drv_name); | ||||||
|  | 	if (!ldev) | ||||||
|  | 		return -ENOENT; | ||||||
|  | 	filt = (struct log_filter *)calloc(1, sizeof(*filt)); | ||||||
|  | 	if (!filt) | ||||||
|  | 		return -ENOMEM; | ||||||
|  | 
 | ||||||
|  | 	if (cat_list) { | ||||||
|  | 		filt->flags |= LOGFF_HAS_CAT; | ||||||
|  | 		for (i = 0; ; i++) { | ||||||
|  | 			if (i == ARRAY_SIZE(filt->cat_list)) | ||||||
|  | 				return -ENOSPC; | ||||||
|  | 			filt->cat_list[i] = cat_list[i]; | ||||||
|  | 			if (cat_list[i] == LOGC_END) | ||||||
|  | 				break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	filt->max_level = max_level; | ||||||
|  | 	if (file_list) { | ||||||
|  | 		filt->file_list = strdup(file_list); | ||||||
|  | 		if (!filt->file_list) | ||||||
|  | 			goto nomem; | ||||||
|  | 	} | ||||||
|  | 	filt->filter_num = ldev->next_filter_num++; | ||||||
|  | 	list_add_tail(&filt->sibling_node, &ldev->filter_head); | ||||||
|  | 
 | ||||||
|  | 	return filt->filter_num; | ||||||
|  | 
 | ||||||
|  | nomem: | ||||||
|  | 	free(filt); | ||||||
|  | 	return -ENOMEM; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int log_remove_filter(const char *drv_name, int filter_num) | ||||||
|  | { | ||||||
|  | 	struct log_filter *filt; | ||||||
|  | 	struct log_device *ldev; | ||||||
|  | 
 | ||||||
|  | 	ldev = log_device_find_by_name(drv_name); | ||||||
|  | 	if (!ldev) | ||||||
|  | 		return -ENOENT; | ||||||
|  | 
 | ||||||
|  | 	list_for_each_entry(filt, &ldev->filter_head, sibling_node) { | ||||||
|  | 		if (filt->filter_num == filter_num) { | ||||||
|  | 			list_del(&filt->sibling_node); | ||||||
|  | 			free(filt); | ||||||
|  | 
 | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return -ENOENT; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int log_init(void) | ||||||
|  | { | ||||||
|  | 	struct log_driver *drv = ll_entry_start(struct log_driver, log_driver); | ||||||
|  | 	const int count = ll_entry_count(struct log_driver, log_driver); | ||||||
|  | 	struct log_driver *end = drv + count; | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * We cannot add runtime data to the driver since it is likely stored | ||||||
|  | 	 * in rodata. Instead, set up a 'device' corresponding to each driver. | ||||||
|  | 	 * We only support having a single device. | ||||||
|  | 	 */ | ||||||
|  | 	INIT_LIST_HEAD((struct list_head *)&gd->log_head); | ||||||
|  | 	while (drv < end) { | ||||||
|  | 		struct log_device *ldev; | ||||||
|  | 
 | ||||||
|  | 		ldev = calloc(1, sizeof(*ldev)); | ||||||
|  | 		if (!ldev) { | ||||||
|  | 			debug("%s: Cannot allocate memory\n", __func__); | ||||||
|  | 			return -ENOMEM; | ||||||
|  | 		} | ||||||
|  | 		INIT_LIST_HEAD(&ldev->filter_head); | ||||||
|  | 		ldev->drv = drv; | ||||||
|  | 		list_add_tail(&ldev->sibling_node, | ||||||
|  | 			      (struct list_head *)&gd->log_head); | ||||||
|  | 		drv++; | ||||||
|  | 	} | ||||||
|  | 	gd->default_log_level = LOGL_INFO; | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | @ -114,6 +114,11 @@ typedef struct global_data { | ||||||
| 	struct bootstage_data *bootstage;	/* Bootstage information */ | 	struct bootstage_data *bootstage;	/* Bootstage information */ | ||||||
| 	struct bootstage_data *new_bootstage;	/* Relocated bootstage info */ | 	struct bootstage_data *new_bootstage;	/* Relocated bootstage info */ | ||||||
| #endif | #endif | ||||||
|  | #ifdef CONFIG_LOG | ||||||
|  | 	int log_drop_count;		/* Number of dropped log messages */ | ||||||
|  | 	int default_log_level;		/* For devices with no filters */ | ||||||
|  | 	struct list_head log_head;	/* List of struct log_device */ | ||||||
|  | #endif | ||||||
| } gd_t; | } gd_t; | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										242
									
								
								include/log.h
								
								
								
								
							
							
						
						
									
										242
									
								
								include/log.h
								
								
								
								
							|  | @ -10,6 +10,94 @@ | ||||||
| #ifndef __LOG_H | #ifndef __LOG_H | ||||||
| #define __LOG_H | #define __LOG_H | ||||||
| 
 | 
 | ||||||
|  | #include <dm/uclass-id.h> | ||||||
|  | #include <linux/list.h> | ||||||
|  | 
 | ||||||
|  | /** Log levels supported, ranging from most to least important */ | ||||||
|  | enum log_level_t { | ||||||
|  | 	LOGL_EMERG = 0,		/*U-Boot is unstable */ | ||||||
|  | 	LOGL_ALERT,		/* Action must be taken immediately */ | ||||||
|  | 	LOGL_CRIT,		/* Critical conditions */ | ||||||
|  | 	LOGL_ERR,		/* Error that prevents something from working */ | ||||||
|  | 	LOGL_WARNING,		/* Warning may prevent optimial operation */ | ||||||
|  | 	LOGL_NOTICE,		/* Normal but significant condition, printf() */ | ||||||
|  | 	LOGL_INFO,		/* General information message */ | ||||||
|  | 	LOGL_DEBUG,		/* Basic debug-level message */ | ||||||
|  | 	LOGL_DEBUG_CONTENT,	/* Debug message showing full message content */ | ||||||
|  | 	LOGL_DEBUG_IO,		/* Debug message showing hardware I/O access */ | ||||||
|  | 
 | ||||||
|  | 	LOGL_COUNT, | ||||||
|  | 	LOGL_FIRST = LOGL_EMERG, | ||||||
|  | 	LOGL_MAX = LOGL_DEBUG, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Log categories supported. Most of these correspond to uclasses (i.e. | ||||||
|  |  * enum uclass_id) but there are also some more generic categories | ||||||
|  |  */ | ||||||
|  | enum log_category_t { | ||||||
|  | 	LOGC_FIRST = 0,	/* First part mirrors UCLASS_... */ | ||||||
|  | 
 | ||||||
|  | 	LOGC_NONE = UCLASS_COUNT, | ||||||
|  | 	LOGC_ARCH, | ||||||
|  | 	LOGC_BOARD, | ||||||
|  | 	LOGC_CORE, | ||||||
|  | 	LOGC_DT, | ||||||
|  | 
 | ||||||
|  | 	LOGC_COUNT, | ||||||
|  | 	LOGC_END, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Helper to cast a uclass ID to a log category */ | ||||||
|  | static inline int log_uc_cat(enum uclass_id id) | ||||||
|  | { | ||||||
|  | 	return (enum log_category_t)id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * _log() - Internal function to emit a new log record | ||||||
|  |  * | ||||||
|  |  * @cat: Category of log record (indicating which subsystem generated it) | ||||||
|  |  * @level: Level of log record (indicating its severity) | ||||||
|  |  * @file: File name of file where log record was generated | ||||||
|  |  * @line: Line number in file where log record was generated | ||||||
|  |  * @func: Function where log record was generated | ||||||
|  |  * @fmt: printf() format string for log record | ||||||
|  |  * @...: Optional parameters, according to the format string @fmt | ||||||
|  |  * @return 0 if log record was emitted, -ve on error | ||||||
|  |  */ | ||||||
|  | int _log(enum log_category_t cat, enum log_level_t level, const char *file, | ||||||
|  | 	 int line, const char *func, const char *fmt, ...); | ||||||
|  | 
 | ||||||
|  | /* Define this at the top of a file to add a prefix to debug messages */ | ||||||
|  | #ifndef pr_fmt | ||||||
|  | #define pr_fmt(fmt) fmt | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* Use a default category if this file does not supply one */ | ||||||
|  | #ifndef LOG_CATEGORY | ||||||
|  | #define LOG_CATEGORY LOGC_NONE | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * This header may be including when CONFIG_LOG is disabled, in which case | ||||||
|  |  * CONFIG_LOG_MAX_LEVEL is not defined. Add a check for this. | ||||||
|  |  */ | ||||||
|  | #if CONFIG_IS_ENABLED(LOG) | ||||||
|  | #define _LOG_MAX_LEVEL CONFIG_VAL(LOG_MAX_LEVEL) | ||||||
|  | #else | ||||||
|  | #define _LOG_MAX_LEVEL LOGL_INFO | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* Emit a log record if the level is less that the maximum */ | ||||||
|  | #define log(_cat, _level, _fmt, _args...) ({ \ | ||||||
|  | 	int _l = _level; \ | ||||||
|  | 	if (_l <= _LOG_MAX_LEVEL) \ | ||||||
|  | 		_log((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \ | ||||||
|  | 		      __func__, \ | ||||||
|  | 		      pr_fmt(_fmt), ##_args); \ | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
| #ifdef DEBUG | #ifdef DEBUG | ||||||
| #define _DEBUG	1 | #define _DEBUG	1 | ||||||
| #else | #else | ||||||
|  | @ -22,6 +110,16 @@ | ||||||
| #define _SPL_BUILD	0 | #define _SPL_BUILD	0 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #if !_DEBUG && CONFIG_IS_ENABLED(LOG) | ||||||
|  | 
 | ||||||
|  | #define debug_cond(cond, fmt, args...)			\ | ||||||
|  | 	do {						\ | ||||||
|  | 		if (1)					\ | ||||||
|  | 			log(LOG_CATEGORY, LOGL_DEBUG, fmt, ##args); \ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
|  | #else /* _DEBUG */ | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Output a debug text when condition "cond" is met. The "cond" should be |  * Output a debug text when condition "cond" is met. The "cond" should be | ||||||
|  * computed by a preprocessor in the best case, allowing for the best |  * computed by a preprocessor in the best case, allowing for the best | ||||||
|  | @ -33,6 +131,8 @@ | ||||||
| 			printf(pr_fmt(fmt), ##args);	\ | 			printf(pr_fmt(fmt), ##args);	\ | ||||||
| 	} while (0) | 	} while (0) | ||||||
| 
 | 
 | ||||||
|  | #endif /* _DEBUG */ | ||||||
|  | 
 | ||||||
| /* Show a message if DEBUG is defined in a file */ | /* Show a message if DEBUG is defined in a file */ | ||||||
| #define debug(fmt, args...)			\ | #define debug(fmt, args...)			\ | ||||||
| 	debug_cond(_DEBUG, fmt, ##args) | 	debug_cond(_DEBUG, fmt, ##args) | ||||||
|  | @ -56,4 +156,146 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line, | ||||||
| 	({ if (!(x) && _DEBUG) \ | 	({ if (!(x) && _DEBUG) \ | ||||||
| 		__assert_fail(#x, __FILE__, __LINE__, __func__); }) | 		__assert_fail(#x, __FILE__, __LINE__, __func__); }) | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct log_rec - a single log record | ||||||
|  |  * | ||||||
|  |  * Holds information about a single record in the log | ||||||
|  |  * | ||||||
|  |  * Members marked as 'not allocated' are stored as pointers and the caller is | ||||||
|  |  * responsible for making sure that the data pointed to is not overwritten. | ||||||
|  |  * Memebers marked as 'allocated' are allocated (e.g. via strdup()) by the log | ||||||
|  |  * system. | ||||||
|  |  * | ||||||
|  |  * @cat: Category, representing a uclass or part of U-Boot | ||||||
|  |  * @level: Severity level, less severe is higher | ||||||
|  |  * @file: Name of file where the log record was generated (not allocated) | ||||||
|  |  * @line: Line number where the log record was generated | ||||||
|  |  * @func: Function where the log record was generated (not allocated) | ||||||
|  |  * @msg: Log message (allocated) | ||||||
|  |  */ | ||||||
|  | struct log_rec { | ||||||
|  | 	enum log_category_t cat; | ||||||
|  | 	enum log_level_t level; | ||||||
|  | 	const char *file; | ||||||
|  | 	int line; | ||||||
|  | 	const char *func; | ||||||
|  | 	const char *msg; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct log_device; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct log_driver - a driver which accepts and processes log records | ||||||
|  |  * | ||||||
|  |  * @name: Name of driver | ||||||
|  |  */ | ||||||
|  | struct log_driver { | ||||||
|  | 	const char *name; | ||||||
|  | 	/**
 | ||||||
|  | 	 * emit() - emit a log record | ||||||
|  | 	 * | ||||||
|  | 	 * Called by the log system to pass a log record to a particular driver | ||||||
|  | 	 * for processing. The filter is checked before calling this function. | ||||||
|  | 	 */ | ||||||
|  | 	int (*emit)(struct log_device *ldev, struct log_rec *rec); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct log_device - an instance of a log driver | ||||||
|  |  * | ||||||
|  |  * Since drivers are set up at build-time we need to have a separate device for | ||||||
|  |  * the run-time aspects of drivers (currently just a list of filters to apply | ||||||
|  |  * to records send to this device). | ||||||
|  |  * | ||||||
|  |  * @next_filter_num: Seqence number of next filter filter added (0=no filters | ||||||
|  |  *	yet). This increments with each new filter on the device, but never | ||||||
|  |  *	decrements | ||||||
|  |  * @drv: Pointer to driver for this device | ||||||
|  |  * @filter_head: List of filters for this device | ||||||
|  |  * @sibling_node: Next device in the list of all devices | ||||||
|  |  */ | ||||||
|  | struct log_device { | ||||||
|  | 	int next_filter_num; | ||||||
|  | 	struct log_driver *drv; | ||||||
|  | 	struct list_head filter_head; | ||||||
|  | 	struct list_head sibling_node; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  | 	LOGF_MAX_CATEGORIES = 5,	/* maximum categories per filter */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum log_filter_flags { | ||||||
|  | 	LOGFF_HAS_CAT		= 1 << 0,	/* Filter has a category list */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct log_filter - criterial to filter out log messages | ||||||
|  |  * | ||||||
|  |  * @filter_num: Sequence number of this filter.  This is returned when adding a | ||||||
|  |  *	new filter, and must be provided when removing a previously added | ||||||
|  |  *	filter. | ||||||
|  |  * @flags: Flags for this filter (LOGFF_...) | ||||||
|  |  * @cat_list: List of categories to allow (terminated by LOGC_none). If empty | ||||||
|  |  *	then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries | ||||||
|  |  *	can be provided | ||||||
|  |  * @max_level: Maximum log level to allow | ||||||
|  |  * @file_list: List of files to allow, separated by comma. If NULL then all | ||||||
|  |  *	files are permitted | ||||||
|  |  * @sibling_node: Next filter in the list of filters for this log device | ||||||
|  |  */ | ||||||
|  | struct log_filter { | ||||||
|  | 	int filter_num; | ||||||
|  | 	int flags; | ||||||
|  | 	enum log_category_t cat_list[LOGF_MAX_CATEGORIES]; | ||||||
|  | 	enum log_level_t max_level; | ||||||
|  | 	const char *file_list; | ||||||
|  | 	struct list_head sibling_node; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define LOG_DRIVER(_name) \ | ||||||
|  | 	ll_entry_declare(struct log_driver, _name, log_driver) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * log_add_filter() - Add a new filter to a log device | ||||||
|  |  * | ||||||
|  |  * @drv_name: Driver name to add the filter to (since each driver only has a | ||||||
|  |  *	single device) | ||||||
|  |  * @cat_list: List of categories to allow (terminated by LOGC_none). If empty | ||||||
|  |  *	then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries | ||||||
|  |  *	can be provided | ||||||
|  |  * @max_level: Maximum log level to allow | ||||||
|  |  * @file_list: List of files to allow, separated by comma. If NULL then all | ||||||
|  |  *	files are permitted | ||||||
|  |  * @return the sequence number of the new filter (>=0) if the filter was added, | ||||||
|  |  *	or a -ve value on error | ||||||
|  |  */ | ||||||
|  | int log_add_filter(const char *drv_name, enum log_category_t cat_list[], | ||||||
|  | 		   enum log_level_t max_level, const char *file_list); | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * log_remove_filter() - Remove a filter from a log device | ||||||
|  |  * | ||||||
|  |  * @drv_name: Driver name to remove the filter from (since each driver only has | ||||||
|  |  *	a single device) | ||||||
|  |  * @filter_num: Filter number to remove (as returned by log_add_filter()) | ||||||
|  |  * @return 0 if the filter was removed, -ENOENT if either the driver or the | ||||||
|  |  *	filter number was not found | ||||||
|  |  */ | ||||||
|  | int log_remove_filter(const char *drv_name, int filter_num); | ||||||
|  | 
 | ||||||
|  | #if CONFIG_IS_ENABLED(LOG) | ||||||
|  | /**
 | ||||||
|  |  * log_init() - Set up the log system ready for use | ||||||
|  |  * | ||||||
|  |  * @return 0 if OK, -ENOMEM if out of memory | ||||||
|  |  */ | ||||||
|  | int log_init(void); | ||||||
|  | #else | ||||||
|  | static inline int log_init(void) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue