Commit e9c8d49d54cbbc7b219a1637d2994de7448b767d

Authored by Simon Glass
Committed by Tom Rini
1 parent c5404b64fb

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>

Showing 6 changed files with 555 additions and 0 deletions Side-by-side Diff

... ... @@ -310,6 +310,13 @@
310 310 T: git git://git.denx.de/u-boot-i2c.git
311 311 F: drivers/i2c/
312 312  
  313 +LOGGING
  314 +M: Simon Glass <sjg@chromium.org>
  315 +S: Maintained
  316 +T: git git://git.denx.de/u-boot.git
  317 +F: common/log.c
  318 +F: cmd/log.c
  319 +
313 320 MICROBLAZE
314 321 M: Michal Simek <monstr@monstr.eu>
315 322 S: Maintained
... ... @@ -420,6 +420,62 @@
420 420  
421 421 endmenu
422 422  
  423 +menu "Logging"
  424 +
  425 +config LOG
  426 + bool "Enable logging support"
  427 + help
  428 + This enables support for logging of status and debug messages. These
  429 + can be displayed on the console, recorded in a memory buffer, or
  430 + discarded if not needed. Logging supports various categories and
  431 + levels of severity.
  432 +
  433 +config SPL_LOG
  434 + bool "Enable logging support in SPL"
  435 + help
  436 + This enables support for logging of status and debug messages. These
  437 + can be displayed on the console, recorded in a memory buffer, or
  438 + discarded if not needed. Logging supports various categories and
  439 + levels of severity.
  440 +
  441 +config LOG_MAX_LEVEL
  442 + int "Maximum log level to record"
  443 + depends on LOG
  444 + default 5
  445 + help
  446 + This selects the maximum log level that will be recorded. Any value
  447 + higher than this will be ignored. If possible log statements below
  448 + this level will be discarded at build time. Levels:
  449 +
  450 + 0 - panic
  451 + 1 - critical
  452 + 2 - error
  453 + 3 - warning
  454 + 4 - note
  455 + 5 - info
  456 + 6 - detail
  457 + 7 - debug
  458 +
  459 +config SPL_LOG_MAX_LEVEL
  460 + int "Maximum log level to record in SPL"
  461 + depends on SPL_LOG
  462 + default 3
  463 + help
  464 + This selects the maximum log level that will be recorded. Any value
  465 + higher than this will be ignored. If possible log statements below
  466 + this level will be discarded at build time. Levels:
  467 +
  468 + 0 - panic
  469 + 1 - critical
  470 + 2 - error
  471 + 3 - warning
  472 + 4 - note
  473 + 5 - info
  474 + 6 - detail
  475 + 7 - debug
  476 +
  477 +endmenu
  478 +
423 479 config DEFAULT_FDT_FILE
424 480 string "Default fdt file"
425 481 help
... ... @@ -128,6 +128,7 @@
128 128 obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o
129 129 obj-$(CONFIG_CMD_DFU) += dfu.o
130 130 obj-y += command.o
  131 +obj-$(CONFIG_$(SPL_)LOG) += log.o
131 132 obj-y += s_record.o
132 133 obj-y += xyzModem.o
  1 +/*
  2 + * Logging support
  3 + *
  4 + * Copyright (c) 2017 Google, Inc
  5 + * Written by Simon Glass <sjg@chromium.org>
  6 + *
  7 + * SPDX-License-Identifier: GPL-2.0+
  8 + */
  9 +
  10 +#include <common.h>
  11 +#include <log.h>
  12 +#include <malloc.h>
  13 +
  14 +DECLARE_GLOBAL_DATA_PTR;
  15 +
  16 +static struct log_device *log_device_find_by_name(const char *drv_name)
  17 +{
  18 + struct log_device *ldev;
  19 +
  20 + list_for_each_entry(ldev, &gd->log_head, sibling_node) {
  21 + if (!strcmp(drv_name, ldev->drv->name))
  22 + return ldev;
  23 + }
  24 +
  25 + return NULL;
  26 +}
  27 +
  28 +/**
  29 + * log_has_cat() - check if a log category exists within a list
  30 + *
  31 + * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries
  32 + * long, terminated by LC_END if fewer
  33 + * @cat: Category to search for
  34 + * @return true if @cat is in @cat_list, else false
  35 + */
  36 +static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
  37 +{
  38 + int i;
  39 +
  40 + for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) {
  41 + if (cat_list[i] == cat)
  42 + return true;
  43 + }
  44 +
  45 + return false;
  46 +}
  47 +
  48 +/**
  49 + * log_has_file() - check if a file is with a list
  50 + *
  51 + * @file_list: List of files to check, separated by comma
  52 + * @file: File to check for. This string is matched against the end of each
  53 + * file in the list, i.e. ignoring any preceding path. The list is
  54 + * intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c
  55 + * @return true if @file is in @file_list, else false
  56 + */
  57 +static bool log_has_file(const char *file_list, const char *file)
  58 +{
  59 + int file_len = strlen(file);
  60 + const char *s, *p;
  61 + int substr_len;
  62 +
  63 + for (s = file_list; *s; s = p + (*p != '\0')) {
  64 + p = strchrnul(s, ',');
  65 + substr_len = p - s;
  66 + if (file_len >= substr_len &&
  67 + !strncmp(file + file_len - substr_len, s, substr_len))
  68 + return true;
  69 + }
  70 +
  71 + return false;
  72 +}
  73 +
  74 +/**
  75 + * log_passes_filters() - check if a log record passes the filters for a device
  76 + *
  77 + * @ldev: Log device to check
  78 + * @rec: Log record to check
  79 + * @return true if @rec is not blocked by the filters in @ldev, false if it is
  80 + */
  81 +static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
  82 +{
  83 + struct log_filter *filt;
  84 +
  85 + /* If there are no filters, filter on the default log level */
  86 + if (list_empty(&ldev->filter_head)) {
  87 + if (rec->level > gd->default_log_level)
  88 + return false;
  89 + return true;
  90 + }
  91 +
  92 + list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
  93 + if (rec->level > filt->max_level)
  94 + continue;
  95 + if ((filt->flags & LOGFF_HAS_CAT) &&
  96 + !log_has_cat(filt->cat_list, rec->cat))
  97 + continue;
  98 + if (filt->file_list &&
  99 + !log_has_file(filt->file_list, rec->file))
  100 + continue;
  101 + return true;
  102 + }
  103 +
  104 + return false;
  105 +}
  106 +
  107 +/**
  108 + * log_dispatch() - Send a log record to all log devices for processing
  109 + *
  110 + * The log record is sent to each log device in turn, skipping those which have
  111 + * filters which block the record
  112 + *
  113 + * @rec: Log record to dispatch
  114 + * @return 0 (meaning success)
  115 + */
  116 +static int log_dispatch(struct log_rec *rec)
  117 +{
  118 + struct log_device *ldev;
  119 +
  120 + list_for_each_entry(ldev, &gd->log_head, sibling_node) {
  121 + if (log_passes_filters(ldev, rec))
  122 + ldev->drv->emit(ldev, rec);
  123 + }
  124 +
  125 + return 0;
  126 +}
  127 +
  128 +int _log(enum log_category_t cat, enum log_level_t level, const char *file,
  129 + int line, const char *func, const char *fmt, ...)
  130 +{
  131 + char buf[CONFIG_SYS_CBSIZE];
  132 + struct log_rec rec;
  133 + va_list args;
  134 +
  135 + rec.cat = cat;
  136 + rec.level = level;
  137 + rec.file = file;
  138 + rec.line = line;
  139 + rec.func = func;
  140 + va_start(args, fmt);
  141 + vsnprintf(buf, sizeof(buf), fmt, args);
  142 + va_end(args);
  143 + rec.msg = buf;
  144 + if (!gd || !(gd->flags & GD_FLG_LOG_READY)) {
  145 + if (gd)
  146 + gd->log_drop_count++;
  147 + return -ENOSYS;
  148 + }
  149 + log_dispatch(&rec);
  150 +
  151 + return 0;
  152 +}
  153 +
  154 +int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
  155 + enum log_level_t max_level, const char *file_list)
  156 +{
  157 + struct log_filter *filt;
  158 + struct log_device *ldev;
  159 + int i;
  160 +
  161 + ldev = log_device_find_by_name(drv_name);
  162 + if (!ldev)
  163 + return -ENOENT;
  164 + filt = (struct log_filter *)calloc(1, sizeof(*filt));
  165 + if (!filt)
  166 + return -ENOMEM;
  167 +
  168 + if (cat_list) {
  169 + filt->flags |= LOGFF_HAS_CAT;
  170 + for (i = 0; ; i++) {
  171 + if (i == ARRAY_SIZE(filt->cat_list))
  172 + return -ENOSPC;
  173 + filt->cat_list[i] = cat_list[i];
  174 + if (cat_list[i] == LOGC_END)
  175 + break;
  176 + }
  177 + }
  178 + filt->max_level = max_level;
  179 + if (file_list) {
  180 + filt->file_list = strdup(file_list);
  181 + if (!filt->file_list)
  182 + goto nomem;
  183 + }
  184 + filt->filter_num = ldev->next_filter_num++;
  185 + list_add_tail(&filt->sibling_node, &ldev->filter_head);
  186 +
  187 + return filt->filter_num;
  188 +
  189 +nomem:
  190 + free(filt);
  191 + return -ENOMEM;
  192 +}
  193 +
  194 +int log_remove_filter(const char *drv_name, int filter_num)
  195 +{
  196 + struct log_filter *filt;
  197 + struct log_device *ldev;
  198 +
  199 + ldev = log_device_find_by_name(drv_name);
  200 + if (!ldev)
  201 + return -ENOENT;
  202 +
  203 + list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
  204 + if (filt->filter_num == filter_num) {
  205 + list_del(&filt->sibling_node);
  206 + free(filt);
  207 +
  208 + return 0;
  209 + }
  210 + }
  211 +
  212 + return -ENOENT;
  213 +}
  214 +
  215 +int log_init(void)
  216 +{
  217 + struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
  218 + const int count = ll_entry_count(struct log_driver, log_driver);
  219 + struct log_driver *end = drv + count;
  220 +
  221 + /*
  222 + * We cannot add runtime data to the driver since it is likely stored
  223 + * in rodata. Instead, set up a 'device' corresponding to each driver.
  224 + * We only support having a single device.
  225 + */
  226 + INIT_LIST_HEAD((struct list_head *)&gd->log_head);
  227 + while (drv < end) {
  228 + struct log_device *ldev;
  229 +
  230 + ldev = calloc(1, sizeof(*ldev));
  231 + if (!ldev) {
  232 + debug("%s: Cannot allocate memory\n", __func__);
  233 + return -ENOMEM;
  234 + }
  235 + INIT_LIST_HEAD(&ldev->filter_head);
  236 + ldev->drv = drv;
  237 + list_add_tail(&ldev->sibling_node,
  238 + (struct list_head *)&gd->log_head);
  239 + drv++;
  240 + }
  241 + gd->default_log_level = LOGL_INFO;
  242 +
  243 + return 0;
  244 +}
include/asm-generic/global_data.h
... ... @@ -114,6 +114,11 @@
114 114 struct bootstage_data *bootstage; /* Bootstage information */
115 115 struct bootstage_data *new_bootstage; /* Relocated bootstage info */
116 116 #endif
  117 +#ifdef CONFIG_LOG
  118 + int log_drop_count; /* Number of dropped log messages */
  119 + int default_log_level; /* For devices with no filters */
  120 + struct list_head log_head; /* List of struct log_device */
  121 +#endif
117 122 } gd_t;
118 123 #endif
119 124  
... ... @@ -10,6 +10,94 @@
10 10 #ifndef __LOG_H
11 11 #define __LOG_H
12 12  
  13 +#include <dm/uclass-id.h>
  14 +#include <linux/list.h>
  15 +
  16 +/** Log levels supported, ranging from most to least important */
  17 +enum log_level_t {
  18 + LOGL_EMERG = 0, /*U-Boot is unstable */
  19 + LOGL_ALERT, /* Action must be taken immediately */
  20 + LOGL_CRIT, /* Critical conditions */
  21 + LOGL_ERR, /* Error that prevents something from working */
  22 + LOGL_WARNING, /* Warning may prevent optimial operation */
  23 + LOGL_NOTICE, /* Normal but significant condition, printf() */
  24 + LOGL_INFO, /* General information message */
  25 + LOGL_DEBUG, /* Basic debug-level message */
  26 + LOGL_DEBUG_CONTENT, /* Debug message showing full message content */
  27 + LOGL_DEBUG_IO, /* Debug message showing hardware I/O access */
  28 +
  29 + LOGL_COUNT,
  30 + LOGL_FIRST = LOGL_EMERG,
  31 + LOGL_MAX = LOGL_DEBUG,
  32 +};
  33 +
  34 +/**
  35 + * Log categories supported. Most of these correspond to uclasses (i.e.
  36 + * enum uclass_id) but there are also some more generic categories
  37 + */
  38 +enum log_category_t {
  39 + LOGC_FIRST = 0, /* First part mirrors UCLASS_... */
  40 +
  41 + LOGC_NONE = UCLASS_COUNT,
  42 + LOGC_ARCH,
  43 + LOGC_BOARD,
  44 + LOGC_CORE,
  45 + LOGC_DT,
  46 +
  47 + LOGC_COUNT,
  48 + LOGC_END,
  49 +};
  50 +
  51 +/* Helper to cast a uclass ID to a log category */
  52 +static inline int log_uc_cat(enum uclass_id id)
  53 +{
  54 + return (enum log_category_t)id;
  55 +}
  56 +
  57 +/**
  58 + * _log() - Internal function to emit a new log record
  59 + *
  60 + * @cat: Category of log record (indicating which subsystem generated it)
  61 + * @level: Level of log record (indicating its severity)
  62 + * @file: File name of file where log record was generated
  63 + * @line: Line number in file where log record was generated
  64 + * @func: Function where log record was generated
  65 + * @fmt: printf() format string for log record
  66 + * @...: Optional parameters, according to the format string @fmt
  67 + * @return 0 if log record was emitted, -ve on error
  68 + */
  69 +int _log(enum log_category_t cat, enum log_level_t level, const char *file,
  70 + int line, const char *func, const char *fmt, ...);
  71 +
  72 +/* Define this at the top of a file to add a prefix to debug messages */
  73 +#ifndef pr_fmt
  74 +#define pr_fmt(fmt) fmt
  75 +#endif
  76 +
  77 +/* Use a default category if this file does not supply one */
  78 +#ifndef LOG_CATEGORY
  79 +#define LOG_CATEGORY LOGC_NONE
  80 +#endif
  81 +
  82 +/*
  83 + * This header may be including when CONFIG_LOG is disabled, in which case
  84 + * CONFIG_LOG_MAX_LEVEL is not defined. Add a check for this.
  85 + */
  86 +#if CONFIG_IS_ENABLED(LOG)
  87 +#define _LOG_MAX_LEVEL CONFIG_VAL(LOG_MAX_LEVEL)
  88 +#else
  89 +#define _LOG_MAX_LEVEL LOGL_INFO
  90 +#endif
  91 +
  92 +/* Emit a log record if the level is less that the maximum */
  93 +#define log(_cat, _level, _fmt, _args...) ({ \
  94 + int _l = _level; \
  95 + if (_l <= _LOG_MAX_LEVEL) \
  96 + _log((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \
  97 + __func__, \
  98 + pr_fmt(_fmt), ##_args); \
  99 + })
  100 +
13 101 #ifdef DEBUG
14 102 #define _DEBUG 1
15 103 #else
... ... @@ -22,6 +110,16 @@
22 110 #define _SPL_BUILD 0
23 111 #endif
24 112  
  113 +#if !_DEBUG && CONFIG_IS_ENABLED(LOG)
  114 +
  115 +#define debug_cond(cond, fmt, args...) \
  116 + do { \
  117 + if (1) \
  118 + log(LOG_CATEGORY, LOGL_DEBUG, fmt, ##args); \
  119 + } while (0)
  120 +
  121 +#else /* _DEBUG */
  122 +
25 123 /*
26 124 * Output a debug text when condition "cond" is met. The "cond" should be
27 125 * computed by a preprocessor in the best case, allowing for the best
... ... @@ -33,6 +131,8 @@
33 131 printf(pr_fmt(fmt), ##args); \
34 132 } while (0)
35 133  
  134 +#endif /* _DEBUG */
  135 +
36 136 /* Show a message if DEBUG is defined in a file */
37 137 #define debug(fmt, args...) \
38 138 debug_cond(_DEBUG, fmt, ##args)
... ... @@ -55,6 +155,148 @@
55 155 #define assert(x) \
56 156 ({ if (!(x) && _DEBUG) \
57 157 __assert_fail(#x, __FILE__, __LINE__, __func__); })
  158 +
  159 +/**
  160 + * struct log_rec - a single log record
  161 + *
  162 + * Holds information about a single record in the log
  163 + *
  164 + * Members marked as 'not allocated' are stored as pointers and the caller is
  165 + * responsible for making sure that the data pointed to is not overwritten.
  166 + * Memebers marked as 'allocated' are allocated (e.g. via strdup()) by the log
  167 + * system.
  168 + *
  169 + * @cat: Category, representing a uclass or part of U-Boot
  170 + * @level: Severity level, less severe is higher
  171 + * @file: Name of file where the log record was generated (not allocated)
  172 + * @line: Line number where the log record was generated
  173 + * @func: Function where the log record was generated (not allocated)
  174 + * @msg: Log message (allocated)
  175 + */
  176 +struct log_rec {
  177 + enum log_category_t cat;
  178 + enum log_level_t level;
  179 + const char *file;
  180 + int line;
  181 + const char *func;
  182 + const char *msg;
  183 +};
  184 +
  185 +struct log_device;
  186 +
  187 +/**
  188 + * struct log_driver - a driver which accepts and processes log records
  189 + *
  190 + * @name: Name of driver
  191 + */
  192 +struct log_driver {
  193 + const char *name;
  194 + /**
  195 + * emit() - emit a log record
  196 + *
  197 + * Called by the log system to pass a log record to a particular driver
  198 + * for processing. The filter is checked before calling this function.
  199 + */
  200 + int (*emit)(struct log_device *ldev, struct log_rec *rec);
  201 +};
  202 +
  203 +/**
  204 + * struct log_device - an instance of a log driver
  205 + *
  206 + * Since drivers are set up at build-time we need to have a separate device for
  207 + * the run-time aspects of drivers (currently just a list of filters to apply
  208 + * to records send to this device).
  209 + *
  210 + * @next_filter_num: Seqence number of next filter filter added (0=no filters
  211 + * yet). This increments with each new filter on the device, but never
  212 + * decrements
  213 + * @drv: Pointer to driver for this device
  214 + * @filter_head: List of filters for this device
  215 + * @sibling_node: Next device in the list of all devices
  216 + */
  217 +struct log_device {
  218 + int next_filter_num;
  219 + struct log_driver *drv;
  220 + struct list_head filter_head;
  221 + struct list_head sibling_node;
  222 +};
  223 +
  224 +enum {
  225 + LOGF_MAX_CATEGORIES = 5, /* maximum categories per filter */
  226 +};
  227 +
  228 +enum log_filter_flags {
  229 + LOGFF_HAS_CAT = 1 << 0, /* Filter has a category list */
  230 +};
  231 +
  232 +/**
  233 + * struct log_filter - criterial to filter out log messages
  234 + *
  235 + * @filter_num: Sequence number of this filter. This is returned when adding a
  236 + * new filter, and must be provided when removing a previously added
  237 + * filter.
  238 + * @flags: Flags for this filter (LOGFF_...)
  239 + * @cat_list: List of categories to allow (terminated by LOGC_none). If empty
  240 + * then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries
  241 + * can be provided
  242 + * @max_level: Maximum log level to allow
  243 + * @file_list: List of files to allow, separated by comma. If NULL then all
  244 + * files are permitted
  245 + * @sibling_node: Next filter in the list of filters for this log device
  246 + */
  247 +struct log_filter {
  248 + int filter_num;
  249 + int flags;
  250 + enum log_category_t cat_list[LOGF_MAX_CATEGORIES];
  251 + enum log_level_t max_level;
  252 + const char *file_list;
  253 + struct list_head sibling_node;
  254 +};
  255 +
  256 +#define LOG_DRIVER(_name) \
  257 + ll_entry_declare(struct log_driver, _name, log_driver)
  258 +
  259 +/**
  260 + * log_add_filter() - Add a new filter to a log device
  261 + *
  262 + * @drv_name: Driver name to add the filter to (since each driver only has a
  263 + * single device)
  264 + * @cat_list: List of categories to allow (terminated by LOGC_none). If empty
  265 + * then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries
  266 + * can be provided
  267 + * @max_level: Maximum log level to allow
  268 + * @file_list: List of files to allow, separated by comma. If NULL then all
  269 + * files are permitted
  270 + * @return the sequence number of the new filter (>=0) if the filter was added,
  271 + * or a -ve value on error
  272 + */
  273 +int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
  274 + enum log_level_t max_level, const char *file_list);
  275 +
  276 +/**
  277 + * log_remove_filter() - Remove a filter from a log device
  278 + *
  279 + * @drv_name: Driver name to remove the filter from (since each driver only has
  280 + * a single device)
  281 + * @filter_num: Filter number to remove (as returned by log_add_filter())
  282 + * @return 0 if the filter was removed, -ENOENT if either the driver or the
  283 + * filter number was not found
  284 + */
  285 +int log_remove_filter(const char *drv_name, int filter_num);
  286 +
  287 +#if CONFIG_IS_ENABLED(LOG)
  288 +/**
  289 + * log_init() - Set up the log system ready for use
  290 + *
  291 + * @return 0 if OK, -ENOMEM if out of memory
  292 + */
  293 +int log_init(void);
  294 +#else
  295 +static inline int log_init(void)
  296 +{
  297 + return 0;
  298 +}
  299 +#endif
58 300  
59 301 #endif