[U-Boot] [PATCH 06/13] log: Add an implemention of logging

Masahiro Yamada yamada.masahiro at socionext.com
Wed Sep 27 17:11:57 UTC 2017


Hi Simon,


2017-09-27 4:10 GMT+09:00 Simon Glass <sjg at chromium.org>:
> Hi Masahiro,
>
> On 20 September 2017 at 11:19, Masahiro Yamada
> <yamada.masahiro at socionext.com> wrote:
>> Hi Simon,
>>
>>
>> 2017-09-20 22:49 GMT+09:00 Simon Glass <sjg at chromium.org>:
>>> Hi Masahiro,
>>>
>>> On 19 September 2017 at 20:51, Masahiro Yamada
>>> <yamada.masahiro at socionext.com> wrote:
>>>> Hi Simon,
>>>>
>>>>
>>>> 2017-09-17 6:23 GMT+09:00 Simon Glass <sjg at chromium.org>:
>>>>
>>>>>
>>>>> +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.
>>>>
>>>>
>>>> Please note CONFIG_IS_ENABLED(LOG) is never enabled for TPL_BUILD.
>>>>
>>>> Since commit f1c6e1922eb57f4a212c09709801a1cc7920ffa9,
>>>> CONFIG_IS_ENABLED(LOG) is expanded to CONFIG_TPL_LOG
>>>> when building for TPL.
>>>>
>>>> Since that commit, if you add SPL_ prefixed option,
>>>> you need to add a TPL_ one as well.
>>>>
>>>> I cannot believe why such a commit was accepted.
>>>
>>> Well either way is strange. it is strange that SPL is enabled for TPL
>>> when really they are separate.
>>>
>>> We could revert that commit. But how do you think all of this SPL/TPL
>>> control should actually work? What is intended?
>>>
>>> But I'm OK with not having logging in TPL until we need it.
>>
>> I will explain it in another mail.
>>
>>
>>>>
>>>>
>>>>
>>>>
>>>>> +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
>>>>
>>>>
>>>> Please do not invent our own for U-Boot.
>>>> Just use Linux log level.
>>>>
>>>>                         0 (KERN_EMERG)          system is unusable
>>>>                         1 (KERN_ALERT)          action must be taken immediately
>>>>                         2 (KERN_CRIT)           critical conditions
>>>>                         3 (KERN_ERR)            error conditions
>>>>                         4 (KERN_WARNING)        warning conditions
>>>>                         5 (KERN_NOTICE)         normal but significant condition
>>>>                         6 (KERN_INFO)           informational
>>>>                         7 (KERN_DEBUG)          debug-level messages
>>>
>>> Yes I looked hard at that. The first three seem hard to distinguish in
>>> U-Boot, but we can keep them I suppose. But most of my problem is with
>>> the last two. INFO is what I plan to use for normal printf() output.
>>> DEBUG is obviously for debugging and often involves vaste amounts of
>>> stuff (e.g. logging every access to an MMC peripheral). We need
>>> something in between. It could list the accesses to device at a high
>>> level (e.g API calls) but not every little register access.
>>>
>>> So I don't think the Linux levels are suitable at the high end. We
>>> could go up to 8 I suppose, instead of trying to save one at the
>>> bottom?
>>
>>
>> In fact, Linux has one more for debug.
>>  dev_vdbg() is widely used in Linux.
>>
>> If you like, we can add one more level:
>>
>>                          8 (KERN_VDEBUG)           verbose debug messages
>>
>>
>> Perhaps, logging every access to an MMC peripheral
>> might belong to the vdbg level.
>
> I like the idea of having a log level for message contents (bytes) and
> another for I/O access. So I will add two more in v2.
>
>>
>>
>>
>> BTW, what do you mean "INFO is what I plan to use for normal printf() output"
>>
>> Is that mean printf() is equivalent to pr_info()?
>> If loglevel is 6 or smaller, will all print() be silent?
>> If so, probably we can not use command line interface.
>
> I mean that I want to (later) add a feature that logs normal printf()
> output. If the console is silent then it would still be logged. Maybe
> one day log functions will be used instead of printf(), but for now
> this provides a useful way to make things wok.


IMHO, printf() should be used for command line interface.

All SoC files, board files should use pr_*(),
and drivers dev_*().



>>
>>
>>
>>
>>
>>>>
>>>>
>>>>
>>>>
>>>>> +
>>>>> +/**
>>>>> + * 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++;
>>>>> +       INIT_LIST_HEAD(&filt->sibling_node);
>>>>> +       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;
>>>>> +}
>>>>
>>>>
>>>> I am not sure if this luxury filter is needed.
>>>> After all, the purpose is debugging.
>>>> The printf() debugging is useful, but only during testing.
>>>> Once quality code is established, most of debug message should
>>>> generally be removed from upstream code.
>>>
>>> But not logging, and this is the point. It is very useful to have
>>> basic logging information recorded for every boot, and the normal
>>> printf() output is not detailed enough. For example for verified boot
>>> I want to see details about boot failures and what went wrong (key
>>> verification, etc.).
>>
>> This should be warn level, (or notice at least)
>> not info.
>
> Sure, but the the fact that it failed is not very useful. We know
> that. What is useful is the events leading up to that failure and
> those are probably not written to the console. For example if a key to
> verify was it because the key was invalid, the image was not found,
> the TPM had wrong data, ...?
>
>>
>>
>>> So I expect that at least INFO (and probably
>>> DETAIL) logging would be useful to record for such a system, even if
>>> it does not appear on the console (in fact the console would normally
>>> be disabled in such a system).
>>
>> OK.  Logging is useful, but your example will be used
>> with info level.
>>
>> I still do not understand the necessity of
>> category / file filtering.
>
> Category is for knowing which subsystem generated the log message. It
> makes no sense to log MMC messages if you are hunting for an error in
> USB. It clutters up the log. Also, it allows a post-processing tool to
> filter log messages in a sensible, human-friendly way, even if both
> categories are recorded.


I do not think we have much volume for notice or lower loglevel.

The categorization is useful for debug level
and you can see the right thing to do
for example, in drivers/i2c/Makefile of Linux.

ccflags-$(CONFIG_I2C_DEBUG_CORE) := -DDEBUG



This is already established in Linux

[1] Create an entry for CONFIG_FOO_DEBUG in Kconfig
[2] Add ccflags-$(CONFIG_FOO_DEBUG) := -DDEBUG

You can do likewise for DM-core, EFI, or whatever.




> File filtering is useful I think in the interim, while nothing uses
> categories. We can filter on particular files to pick out the messages
> from those files. Even when we do have proper categories everywhere,
> it might still be useful. I am not sure yet.


File filter is pointless because we are generally
supposed to add #define DEBUG to each file
to make pr_dbg() effective.




-- 
Best Regards
Masahiro Yamada


More information about the U-Boot mailing list