| 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, in increasing order of verbosity: |
| |
| LOGL_EMERG - Printed before U-Boot halts |
| LOGL_ALERT - Indicates action must be taken immediate or U-Boot will crash |
| LOGL_CRIT - Indicates a critical error that will cause boot failure |
| LOGL_ERR - Indicates an error that may cause boot failure |
| LOGL_WARNING - Warning about an unexpected condition |
| LOGL_NOTE - Important information about progress |
| LOGL_INFO - Information about normal boot progress |
| LOGL_DEBUG - Debug information (useful for debugging a driver or subsystem) |
| LOGL_DEBUG_CONTENT - Debug message showing full message content |
| LOGL_DEBUG_IO - Debug message showing hardware I/O access |
| |
| |
| 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. |
| |
| The following main categories are defined: |
| |
| LOGC_NONE - Unknown category (e.g. a debug() statement) |
| UCLASS_... - Related to a particular uclass (e.g. UCLASS_USB) |
| LOGC_ARCH - Related to architecture-specific code |
| LOGC_BOARD - Related to board-specific code |
| LOGC_CORE - Related to core driver-model support |
| LOGC_DT - Related to device tree control |
| LOGC_EFI - Related to EFI implementation |
| |
| |
| Enabling logging |
| ---------------- |
| |
| The following options are used to enable logging at compile time: |
| |
| CONFIG_LOG - Enables the logging system |
| CONFIG_MAX_LOG_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 versions also, e.g. CONFIG_SPL_MAX_LOG_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. |
| |
| |
| 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: |
| |
| #define LOG_CATEGORY LOGC_ALLOC |
| |
| or |
| |
| #define LOG_CATEGORY UCLASS_SPI |
| |
| Remember that all uclasses IDs are log categories too. |
| |
| |
| Log commands |
| ------------ |
| |
| The 'log' command provides access to several features: |
| |
| level - access the default log level |
| format - access the console log format |
| rec - output a log record |
| test - run tests |
| |
| Type 'help log' for details. |
| |
| |
| 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 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. |
| |
| 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 |
| function name and message are displayed on the console. You can control which |
| fields are present, but not the field order. |
| |
| |
| Filters |
| ------- |
| |
| Filters are attached to log drivers to control what those drivers emit. Only |
| records that pass through the filter make it to the driver. |
| |
| Filters can be based on several criteria: |
| |
| - 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_LOG_MAX_LEVEL. |
| |
| |
| 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. |
| |
| |
| 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/py/tests/test_log.py |
| |
| 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 a 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() |
| |
| Figure out what to do with assert() |
| |
| 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 filters |
| |
| 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 |
| |
| Add a command-line option to sandbox to set the default logging level |
| |
| Convert core driver model code to use logging |
| |
| Convert uclasses to use logging with the correct category |
| |
| 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 |
| |
| Enhance the log console driver to show level / category / file / line |
| information |
| |
| Add a command to add new log records and delete existing records. |
| |
| Provide additional log() functions - e.g. logc() to specify the category |
| |
| -- |
| Simon Glass <sjg@chromium.org> |
| 15-Sep-17 |