Logging in U-Boot¶
Introduction¶
U-Boot’s internal operation involves many different steps and actions. From setting up the board to displaying a start-up screen to loading an Operating System, there are many component parts each with many actions.
Most of the time this internal detail is not useful. Displaying it on the console would delay booting (U-Boot’s primary purpose) and confuse users.
But for digging into what is happening in a particular area, or for debugging a problem it is often useful to see what U-Boot is doing in more detail than is visible from the basic console output.
U-Boot’s logging feature aims to satisfy this goal for both users and developers.
Logging levels¶
There are a number logging levels available.
-
enum
log_level_t
¶ Log levels supported, ranging from most to least important
Constants
LOGL_EMERG
- 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 optimal 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
- Total number of valid log levels
LOGL_NONE
- Used to indicate that there is no valid log level
LOGL_LEVEL_MASK
- Mask for valid log levels
LOGL_FORCE_DEBUG
- Mask to force output due to LOG_DEBUG
LOGL_FIRST
- The first, most-important log level
LOGL_MAX
- The last, least-important log level
LOGL_CONT
- Use same log level as in previous call
Logging category¶
Logging can come from a wide variety of places within U-Boot. Each log message has a category which is intended to allow messages to be filtered according to their source.
-
enum
log_category_t
¶ Log categories supported.
Constants
LOGC_FIRST
- First log category
LOGC_NONE
- Default log category
LOGC_ARCH
- Related to arch-specific code
LOGC_BOARD
- Related to board-specific code
LOGC_CORE
- Related to core features (non-driver-model)
LOGC_DM
- Core driver-model
LOGC_DT
- Device-tree
LOGC_EFI
- EFI implementation
LOGC_ALLOC
- Memory allocation
LOGC_SANDBOX
- Related to the sandbox board
LOGC_BLOBLIST
- Bloblist
LOGC_DEVRES
- Device resources (
devres_...
functions) LOGC_ACPI
- Advanced Configuration and Power Interface (ACPI)
LOGC_BOOT
- Related to boot process / boot image processing
LOGC_COUNT
- Number of log categories
LOGC_END
- Sentinel value for lists of log categories
LOGC_CONT
- Use same category as in previous call
Description
Log categories between LOGC_FIRST
and LOGC_NONE
correspond to uclasses
(i.e. enum uclass_id
), but there are also some more generic categories.
Remember to update log_cat_name[] after adding a new category.
Enabling logging¶
The following options are used to enable logging at compile time:
- CONFIG_LOG - Enables the logging system
- CONFIG_LOG_MAX_LEVEL - Max log level to build (anything higher is compiled out)
- CONFIG_LOG_CONSOLE - Enable writing log records to the console
If CONFIG_LOG is not set, then no logging will be available.
The above have SPL and TPL versions also, e.g. CONFIG_SPL_LOG_MAX_LEVEL and CONFIG_TPL_LOG_MAX_LEVEL.
Temporary logging within a single file¶
Sometimes it is useful to turn on logging just in one file. You can use this
#define LOG_DEBUG
to enable building in of all logging statements in a single file. Put it at the top of the file, before any #includes.
To actually get U-Boot to output this you need to also set the default logging
level - e.g. set CONFIG_LOG_DEFAULT_LEVEL to 7 (LOGL_DEBUG
) or more.
Otherwise debug output is suppressed and will not be generated.
Using DEBUG¶
U-Boot has traditionally used a #define called DEBUG to enable debugging on a file-by-file basis. The debug() macro compiles to a printf() statement if DEBUG is enabled, and an empty statement if not.
With logging enabled, debug() statements are interpreted as logging output with a level of LOGL_DEBUG and a category of LOGC_NONE.
The logging facilities are intended to replace DEBUG, but if DEBUG is defined at the top of a file, then it takes precedence. This means that debug() statements will result in output to the console and this output will not be logged.
Logging statements¶
The main logging function is:
log(category, level, format_string, ...)
Also debug() and error() will generate log records - these use LOG_CATEGORY as the category, so you should #define this right at the top of the source file to ensure the category is correct.
You can also define CONFIG_LOG_ERROR_RETURN to enable the log_ret() macro. This can be used whenever your function returns an error value:
return log_ret(uclass_first_device(UCLASS_MMC, &dev));
This will write a log record when an error code is detected (a value < 0). This can make it easier to trace errors that are generated deep in the call stack.
Convenience functions¶
A number of convenience functions are available to shorten the code needed for logging:
- log_err(_fmt…)
- log_warning(_fmt…)
- log_notice(_fmt…)
- log_info(_fmt…)
- log_debug(_fmt…)
- log_content(_fmt…)
- log_io(_fmt…)
With these the log level is implicit in the name. The category is set by LOG_CATEGORY, which you can only define once per file, above all #includes, e.g.
#define LOG_CATEGORY LOGC_ALLOC
or
#define LOG_CATEGORY UCLASS_SPI
Remember that all uclasses IDs are log categories too.
Logging destinations¶
If logging information goes nowhere then it serves no purpose. U-Boot provides several possible determinations for logging information, all of which can be enabled or disabled independently:
- console - goes to stdout
- syslog - broadcast RFC 3164 messages to syslog servers on UDP port 514
The syslog driver sends the value of environmental variable ‘log_hostname’ as HOSTNAME if available.
Filters¶
Filters are attached to log drivers to control what those drivers emit. FIlters can either allow or deny a log message when they match it. Only records which are allowed by a filter make it to the driver.
Filters can be based on several criteria:
- minimum or maximum log level
- in a set of categories
- in a set of files
If no filters are attached to a driver then a default filter is used, which limits output to records with a level less than CONFIG_MAX_LOG_LEVEL.
Log command¶
The ‘log’ command provides access to several features:
- level - list log levels or set the default log level
- categories - list log categories
- drivers - list log drivers
- filter-list - list filters
- filter-add - add a new filter
- filter-remove - remove filters
- format - access the console log format
- rec - output a log record
Type ‘help log’ for details.
Log format¶
You can control the log format using the ‘log format’ command. The basic format is:
LEVEL.category,file.c:123-func() message
In the above, file.c:123 is the filename where the log record was generated and func() is the function name. By default (‘log format default’) only the message is displayed on the console. You can control which fields are present, but not the field order.
Adding Filters¶
To add new filters at runtime, use the ‘log filter-add’ command. For example, to suppress messages from the SPI and MMC subsystems, run:
log filter-add -D -c spi -c mmc
You will also need to add another filter to allow other messages (because the default filter no longer applies):
log filter-add -A -l info
Log levels may be either symbolic names (like above) or numbers. For example, to
disable all debug and above (log level 7) messages from drivers/core/lists.c
and drivers/core/ofnode.c
, run:
log filter-add -D -f drivers/core/lists.c,drivers/core/ofnode.c -L 7
To view active filters, use the ‘log filter-list’ command. Some example output is:
=> log filter-list
num policy level categories files
2 deny >= DEBUG drivers/core/lists.c,drivers/core/ofnode.c
0 deny <= IO spi
mmc
1 allow <= INFO
Note that filters are processed in-order from top to bottom, not in the order of their filter number. Filters are added to the top of the list if they deny when they match, and to the bottom if they allow when they match. For more information, consult the usage of the ‘log’ command, by running ‘help log’.
Code size¶
Code size impact depends largely on what is enabled. The following numbers are generated by ‘buildman -S’ for snow, which is a Thumb-2 board (all units in bytes):
This series: adds bss +20.0 data +4.0 rodata +4.0 text +44.0
CONFIG_LOG: bss -52.0 data +92.0 rodata -635.0 text +1048.0
CONFIG_LOG_MAX_LEVEL=7: bss +188.0 data +4.0 rodata +49183.0 text +98124.0
The last option turns every debug() statement into a logging call, which bloats the code hugely. The advantage is that it is then possible to enable all logging within U-Boot.
To Do¶
There are lots of useful additions that could be made. None of the below is implemented! If you do one, please add a test in test/log/log_test.c log filter-add -D -f drivers/core/lists.c,drivers/core/ofnode.c -l 6 Convenience functions to support setting the category:
- log_arch(level, format_string, …) - category LOGC_ARCH
- log_board(level, format_string, …) - category LOGC_BOARD
- log_core(level, format_string, …) - category LOGC_CORE
- log_dt(level, format_string, …) - category LOGC_DT
More logging destinations:
- device - goes to a device (e.g. serial)
- buffer - recorded in a memory buffer
Convert debug() statements in the code to log() statements
Support making printf() emit log statements at L_INFO level
Convert error() statements in the code to log() statements
Figure out what to do with BUG(), BUG_ON() and warn_non_spl()
Add a way to browse log records
Add a way to record log records for browsing using an external tool
Add commands to add and remove log devices
Allow sharing of printf format strings in log records to reduce storage size for large numbers of log records
Consider making log() calls emit an automatic newline, perhaps with a logn() function to avoid that
Passing log records through to linux (e.g. via device tree /chosen)
Provide a command to access the number of log records generated, and the number dropped due to them being generated before the log system was ready.
Add a printf() format string pragma so that log statements are checked properly
Add a command to delete existing log records.
Logging API¶
-
enum
log_level_t
Log levels supported, ranging from most to least important
Constants
LOGL_EMERG
- 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 optimal 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
- Total number of valid log levels
LOGL_NONE
- Used to indicate that there is no valid log level
LOGL_LEVEL_MASK
- Mask for valid log levels
LOGL_FORCE_DEBUG
- Mask to force output due to LOG_DEBUG
LOGL_FIRST
- The first, most-important log level
LOGL_MAX
- The last, least-important log level
LOGL_CONT
- Use same log level as in previous call
-
enum
log_category_t
Log categories supported.
Constants
LOGC_FIRST
- First log category
LOGC_NONE
- Default log category
LOGC_ARCH
- Related to arch-specific code
LOGC_BOARD
- Related to board-specific code
LOGC_CORE
- Related to core features (non-driver-model)
LOGC_DM
- Core driver-model
LOGC_DT
- Device-tree
LOGC_EFI
- EFI implementation
LOGC_ALLOC
- Memory allocation
LOGC_SANDBOX
- Related to the sandbox board
LOGC_BLOBLIST
- Bloblist
LOGC_DEVRES
- Device resources (
devres_...
functions) LOGC_ACPI
- Advanced Configuration and Power Interface (ACPI)
LOGC_BOOT
- Related to boot process / boot image processing
LOGC_COUNT
- Number of log categories
LOGC_END
- Sentinel value for lists of log categories
LOGC_CONT
- Use same category as in previous call
Description
Log categories between LOGC_FIRST
and LOGC_NONE
correspond to uclasses
(i.e. enum uclass_id
), but there are also some more generic categories.
Remember to update log_cat_name[] after adding a new category.
-
int
_log
(enum log_category_t cat, enum log_level_t level, const char * file, int line, const char * func, const char * fmt, ...)¶ Internal function to emit a new log record
Parameters
enum log_category_t cat
- Category of log record (indicating which subsystem generated it)
enum log_level_t level
- Level of log record (indicating its severity)
const char * file
- File name of file where log record was generated
int line
- Line number in file where log record was generated
const char * func
- Function where log record was generated
const char * 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
-
assert
(x)¶ assert expression is true
Parameters
x
- expression to test
Description
If the expression x evaluates to false and _DEBUG evaluates to true, a panic message is written and the system stalls. The value of _DEBUG is set to true if DEBUG is defined before including common.h.
The expression x is always executed irrespective of the value of _DEBUG.
-
struct
log_rec
¶ a single log record
Definition
struct log_rec {
enum log_category_t cat;
enum log_level_t level;
bool force_debug;
const char *file;
int line;
const char *func;
const char *msg;
};
Members
cat
- Category, representing a uclass or part of U-Boot
level
- Severity level, less severe is higher
force_debug
- Force output of debug
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)
Description
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.
Members marked as ‘allocated’ are allocated (e.g. via strdup()
) by the log
system.
TODO(sjg**chromium.org**): Compress this struct down a bit to reduce space, e.g. a single u32 for cat, level, line and force_debug
-
struct
log_driver
¶ a driver which accepts and processes log records
Definition
struct log_driver {
const char *name;
int (*emit)(struct log_device *ldev, struct log_rec *rec);
unsigned short flags;
};
Members
name
- Name of driver
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.
flags
- Initial value for flags (use LOGDF_ENABLE to enable on start-up)
-
struct
log_device
¶ an instance of a log driver
Definition
struct log_device {
unsigned short next_filter_num;
unsigned short flags;
struct log_driver *drv;
struct list_head filter_head;
struct list_head sibling_node;
};
Members
next_filter_num
- Sequence number of next filter filter added (0=no filters yet). This increments with each new filter on the device, but never decrements
flags
- Flags for this filter (enum log_device_flags)
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
Description
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).
-
enum
log_filter_flags
¶ Flags which modify a filter
Constants
LOGFF_HAS_CAT
- Filter has a category list
LOGFF_DENY
- Filter denies matching messages
LOGFF_LEVEL_MIN
- Filter’s level is a minimum, not a maximum
-
struct
log_filter
¶ criteria to filter out log messages
Definition
struct log_filter {
int filter_num;
int flags;
enum log_category_t cat_list[LOGF_MAX_CATEGORIES];
enum log_level_t level;
const char *file_list;
struct list_head sibling_node;
};
Members
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_END
). If empty then all categories are permitted. Up toLOGF_MAX_CATEGORIES
entries can be provided level
- Maximum (or minimum, if
LOGFF_MIN_LEVEL
) 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
Description
If a message matches all criteria, then it is allowed. If LOGFF_DENY is set, then it is denied instead.
-
const char *
log_get_cat_name
(enum log_category_t cat)¶ Get the name of a category
Parameters
enum log_category_t cat
- Category to look up
Return
- category name (which may be a uclass driver name) if found, or
- “<invalid>” if invalid, or “<missing>” if not found. All error responses begin with ‘<’.
-
enum log_category_t
log_get_cat_by_name
(const char * name)¶ Look up a category by name
Parameters
const char * name
- Name to look up
Return
Category, or LOGC_NONE
if not found
-
const char *
log_get_level_name
(enum log_level_t level)¶ Get the name of a log level
Parameters
enum log_level_t level
- Log level to look up
Return
Log level name (in ALL CAPS)
-
enum log_level_t
log_get_level_by_name
(const char * name)¶ Look up a log level by name
Parameters
const char * name
- Name to look up
Return
Log level, or LOGL_NONE
if not found
-
struct log_device *
log_device_find_by_name
(const char * drv_name)¶ Look up a log device by its driver’s name
Parameters
const char * drv_name
- Name of the driver
Return
the log device, or NULL
if not found
-
bool
log_has_cat
(enum log_category_t cat_list, enum log_category_t cat)¶ check if a log category exists within a list
Parameters
enum log_category_t cat_list
- List of categories to check, at most
LOGF_MAX_CATEGORIES
entries long, terminated byLC_END
if fewer enum log_category_t cat
- Category to search for
Return
true
if cat is in cat_list, else false
-
bool
log_has_file
(const char * file_list, const char * file)¶ check if a file is with a list
Parameters
const char * file_list
- List of files to check, separated by comma
const char * 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
-
int
log_add_filter_flags
(const char * drv_name, enum log_category_t cat_list, enum log_level_t level, const char * file_list, int flags)¶ Add a new filter to a log device, specifying flags
Parameters
const char * drv_name
- Driver name to add the filter to (since each driver only has a single device)
enum log_category_t cat_list
- List of categories to allow (terminated by
LOGC_END
). If empty then all categories are permitted. Up toLOGF_MAX_CATEGORIES
entries can be provided enum log_level_t level
- Maximum (or minimum, if
LOGFF_LEVEL_MIN
) log level to allow const char * file_list
- List of files to allow, separated by comma. If NULL then all files are permitted
int flags
- Flags for this filter (
LOGFF_...
)
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)¶ Add a new filter to a log device
Parameters
const char * drv_name
- Driver name to add the filter to (since each driver only has a single device)
enum log_category_t cat_list
- List of categories to allow (terminated by
LOGC_END
). If empty then all categories are permitted. Up toLOGF_MAX_CATEGORIES
entries can be provided enum log_level_t max_level
- Maximum log level to allow
const char * 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_remove_filter
(const char * drv_name, int filter_num)¶ Remove a filter from a log device
Parameters
const char * drv_name
- Driver name to remove the filter from (since each driver only has a single device)
int 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_device_set_enable
(struct log_driver * drv, bool enable)¶ Enable or disable a log device
Parameters
struct log_driver * drv
- Driver of device to enable
bool enable
- true to enable, false to disable return 0 if OK, -ENOENT if the driver was not found
Description
Devices are referenced by their driver, so use LOG_GET_DRIVER(name) to pass the driver to this function. For example if the driver is declared with LOG_DRIVER(wibble) then pass LOG_GET_DRIVER(wibble) here.
-
int
log_init
(void)¶ Set up the log system ready for use
Parameters
void
- no arguments
Return
0 if OK, -ENOMEM
if out of memory
-
int
log_get_default_format
(void)¶ get default log format
Parameters
void
- no arguments
Description
The default log format is configurable via
CONFIG_LOGF_FILE
, CONFIG_LOGF_LINE
, and CONFIG_LOGF_FUNC
.
Return
default log format