Merge branch '2020-10-30-misc-changes'

- Additional log improvements
- SPI flash environment improvements
This commit is contained in:
Tom Rini 2020-10-30 14:17:23 -04:00
commit 7820052fde
28 changed files with 1632 additions and 549 deletions

View File

@ -758,6 +758,7 @@ T: git https://gitlab.denx.de/u-boot/u-boot.git
F: common/log*
F: cmd/log.c
F: doc/develop/logging.rst
F: lib/getopt.c
F: test/log/
F: test/py/tests/test_log.py

View File

@ -2239,6 +2239,7 @@ config CMD_KGDB
config CMD_LOG
bool "log - Generation, control and access to logging"
select LOG
select GETOPT
help
This provides access to logging features. It allows the output of
log data to be controlled to a limited extent (setting up the default

354
cmd/log.c
View File

@ -7,27 +7,298 @@
#include <common.h>
#include <command.h>
#include <dm.h>
#include <getopt.h>
#include <log.h>
#include <malloc.h>
static char log_fmt_chars[LOGF_COUNT] = "clFLfm";
static enum log_level_t parse_log_level(char *const arg)
{
enum log_level_t ret;
ulong level;
if (!strict_strtoul(arg, 10, &level)) {
if (level > _LOG_MAX_LEVEL) {
printf("Only log levels <= %d are supported\n",
_LOG_MAX_LEVEL);
return LOGL_NONE;
}
return level;
}
ret = log_get_level_by_name(arg);
if (ret == LOGL_NONE)
printf("Unknown log level \"%s\"\n", arg);
return ret;
}
static int do_log_level(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
if (argc > 1) {
long log_level = simple_strtol(argv[1], NULL, 10);
enum log_level_t log_level;
if (log_level < 0 || log_level > _LOG_MAX_LEVEL) {
printf("Only log levels <= %d are supported\n",
_LOG_MAX_LEVEL);
if (argc > 1) {
log_level = parse_log_level(argv[1]);
if (log_level == LOGL_NONE)
return CMD_RET_FAILURE;
}
gd->default_log_level = log_level;
} else {
printf("Default log level: %d\n", gd->default_log_level);
for (log_level = LOGL_FIRST; log_level <= _LOG_MAX_LEVEL;
log_level++)
printf("%s%s\n", log_get_level_name(log_level),
log_level == gd->default_log_level ?
" (default)" : "");
}
return 0;
return CMD_RET_SUCCESS;
}
static int do_log_categories(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
enum log_category_t cat;
const char *name;
for (cat = LOGC_FIRST; cat < LOGC_COUNT; cat++) {
name = log_get_cat_name(cat);
/*
* Invalid category names (e.g. <invalid> or <missing>) begin
* with '<'.
*/
if (name[0] == '<')
continue;
printf("%s\n", name);
}
return CMD_RET_SUCCESS;
}
static int do_log_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct log_device *ldev;
list_for_each_entry(ldev, &gd->log_head, sibling_node)
printf("%s\n", ldev->drv->name);
return CMD_RET_SUCCESS;
}
static int do_log_filter_list(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
int opt;
const char *drv_name = "console";
struct getopt_state gs;
struct log_filter *filt;
struct log_device *ldev;
getopt_init_state(&gs);
while ((opt = getopt(&gs, argc, argv, "d:")) > 0) {
switch (opt) {
case 'd':
drv_name = gs.arg;
break;
default:
return CMD_RET_USAGE;
}
}
if (gs.index != argc)
return CMD_RET_USAGE;
ldev = log_device_find_by_name(drv_name);
if (!ldev) {
printf("Could not find log device for \"%s\"\n", drv_name);
return CMD_RET_FAILURE;
}
/* <3> < 6 > <2+1 + 7 > < 16 > < unbounded... */
printf("num policy level categories files\n");
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
printf("%3d %6.6s %s %-7.7s ", filt->filter_num,
filt->flags & LOGFF_DENY ? "deny" : "allow",
filt->flags & LOGFF_LEVEL_MIN ? ">=" : "<=",
log_get_level_name(filt->level));
if (filt->flags & LOGFF_HAS_CAT) {
int i;
if (filt->cat_list[0] != LOGC_END)
printf("%16.16s %s\n",
log_get_cat_name(filt->cat_list[0]),
filt->file_list ? filt->file_list : "");
for (i = 1; i < LOGF_MAX_CATEGORIES &&
filt->cat_list[i] != LOGC_END; i++)
printf("%21c %16.16s\n", ' ',
log_get_cat_name(filt->cat_list[i]));
} else {
printf("%16c %s\n", ' ',
filt->file_list ? filt->file_list : "");
}
}
return CMD_RET_SUCCESS;
}
static int do_log_filter_add(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
bool level_set = false;
bool print_num = false;
bool type_set = false;
char *file_list = NULL;
const char *drv_name = "console";
int opt, err;
int cat_count = 0;
int flags = 0;
enum log_category_t cat_list[LOGF_MAX_CATEGORIES + 1];
enum log_level_t level = LOGL_MAX;
struct getopt_state gs;
getopt_init_state(&gs);
while ((opt = getopt(&gs, argc, argv, "Ac:d:Df:l:L:p")) > 0) {
switch (opt) {
case 'A':
#define do_type() do { \
if (type_set) { \
printf("Allow or deny set twice\n"); \
return CMD_RET_USAGE; \
} \
type_set = true; \
} while (0)
do_type();
break;
case 'c': {
enum log_category_t cat;
if (cat_count >= LOGF_MAX_CATEGORIES) {
printf("Too many categories\n");
return CMD_RET_FAILURE;
}
cat = log_get_cat_by_name(gs.arg);
if (cat == LOGC_NONE) {
printf("Unknown category \"%s\"\n", gs.arg);
return CMD_RET_FAILURE;
}
cat_list[cat_count++] = cat;
break;
}
case 'd':
drv_name = gs.arg;
break;
case 'D':
do_type();
flags |= LOGFF_DENY;
break;
case 'f':
file_list = gs.arg;
break;
case 'l':
#define do_level() do { \
if (level_set) { \
printf("Log level set twice\n"); \
return CMD_RET_USAGE; \
} \
level = parse_log_level(gs.arg); \
if (level == LOGL_NONE) \
return CMD_RET_FAILURE; \
level_set = true; \
} while (0)
do_level();
break;
case 'L':
do_level();
flags |= LOGFF_LEVEL_MIN;
break;
case 'p':
print_num = true;
break;
default:
return CMD_RET_USAGE;
}
}
if (gs.index != argc)
return CMD_RET_USAGE;
cat_list[cat_count] = LOGC_END;
err = log_add_filter_flags(drv_name, cat_count ? cat_list : NULL, level,
file_list, flags);
if (err < 0) {
printf("Could not add filter (err = %d)\n", err);
return CMD_RET_FAILURE;
} else if (print_num) {
printf("%d\n", err);
}
return CMD_RET_SUCCESS;
}
static int do_log_filter_remove(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
bool all = false;
int opt, err;
ulong filter_num;
const char *drv_name = "console";
struct getopt_state gs;
getopt_init_state(&gs);
while ((opt = getopt(&gs, argc, argv, "ad:")) > 0) {
switch (opt) {
case 'a':
all = true;
break;
case 'd':
drv_name = gs.arg;
break;
default:
return CMD_RET_USAGE;
}
}
if (all) {
struct log_filter *filt, *tmp_filt;
struct log_device *ldev;
if (gs.index != argc)
return CMD_RET_USAGE;
ldev = log_device_find_by_name(drv_name);
if (!ldev) {
printf("Could not find log device for \"%s\"\n",
drv_name);
return CMD_RET_FAILURE;
}
list_for_each_entry_safe(filt, tmp_filt, &ldev->filter_head,
sibling_node) {
list_del(&filt->sibling_node);
free(filt);
}
} else {
if (gs.index + 1 != argc)
return CMD_RET_USAGE;
if (strict_strtoul(argv[gs.index], 10, &filter_num)) {
printf("Invalid filter number \"%s\"\n", argv[gs.index]);
return CMD_RET_FAILURE;
}
err = log_remove_filter(drv_name, filter_num);
if (err) {
printf("Could not remove filter (err = %d)\n", err);
return CMD_RET_FAILURE;
}
}
return CMD_RET_SUCCESS;
}
static int do_log_format(struct cmd_tbl *cmdtp, int flag, int argc,
@ -103,39 +374,31 @@ static int do_log_rec(struct cmd_tbl *cmdtp, int flag, int argc,
return 0;
}
static struct cmd_tbl log_sub[] = {
U_BOOT_CMD_MKENT(level, CONFIG_SYS_MAXARGS, 1, do_log_level, "", ""),
#ifdef CONFIG_LOG_TEST
U_BOOT_CMD_MKENT(test, 2, 1, do_log_test, "", ""),
#endif
U_BOOT_CMD_MKENT(format, CONFIG_SYS_MAXARGS, 1, do_log_format, "", ""),
U_BOOT_CMD_MKENT(rec, CONFIG_SYS_MAXARGS, 1, do_log_rec, "", ""),
};
static int do_log(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
struct cmd_tbl *cp;
if (argc < 2)
return CMD_RET_USAGE;
/* drop initial "log" arg */
argc--;
argv++;
cp = find_cmd_tbl(argv[0], log_sub, ARRAY_SIZE(log_sub));
if (cp)
return cp->cmd(cmdtp, flag, argc, argv);
return CMD_RET_USAGE;
}
#ifdef CONFIG_SYS_LONGHELP
static char log_help_text[] =
"level - get/set log level\n"
#ifdef CONFIG_LOG_TEST
"log test - run log tests\n"
#endif
"level [<level>] - get/set log level\n"
"categories - list log categories\n"
"drivers - list log drivers\n"
"log filter-list [OPTIONS] - list all filters for a log driver\n"
"\t-d <driver> - Specify the log driver to list filters from; defaults\n"
"\t to console\n"
"log filter-add [OPTIONS] - add a new filter to a driver\n"
"\t-A - Allow messages matching this filter; mutually exclusive with -D\n"
"\t This is the default.\n"
"\t-c <category> - Category to match; may be specified multiple times\n"
"\t-d <driver> - Specify the log driver to add the filter to; defaults\n"
"\t to console\n"
"\t-D - Deny messages matching this filter; mutually exclusive with -A\n"
"\t-f <files_list> - A comma-separated list of files to match\n"
"\t-l <level> - Match log levels less than or equal to <level>;\n"
"\t mutually-exclusive with -L\n"
"\t-L <level> - Match log levels greather than or equal to <level>;\n"
"\t mutually-exclusive with -l\n"
"\t-p - Print the filter number on success\n"
"log filter-remove [OPTIONS] [<num>] - Remove filter number <num>\n"
"\t-a - Remove ALL filters\n"
"\t-d <driver> - Specify the log driver to remove the filter from;\n"
"\t defaults to console\n"
"log format <fmt> - set log output format. <fmt> is a string where\n"
"\teach letter indicates something that should be displayed:\n"
"\tc=category, l=level, F=file, L=line number, f=function, m=msg\n"
@ -145,7 +408,14 @@ static char log_help_text[] =
;
#endif
U_BOOT_CMD(
log, CONFIG_SYS_MAXARGS, 1, do_log,
"log system", log_help_text
U_BOOT_CMD_WITH_SUBCMDS(log, "log system", log_help_text,
U_BOOT_SUBCMD_MKENT(level, 2, 1, do_log_level),
U_BOOT_SUBCMD_MKENT(categories, 1, 1, do_log_categories),
U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_log_drivers),
U_BOOT_SUBCMD_MKENT(filter-list, 3, 1, do_log_filter_list),
U_BOOT_SUBCMD_MKENT(filter-add, CONFIG_SYS_MAXARGS, 1,
do_log_filter_add),
U_BOOT_SUBCMD_MKENT(filter-remove, 4, 1, do_log_filter_remove),
U_BOOT_SUBCMD_MKENT(format, 2, 1, do_log_format),
U_BOOT_SUBCMD_MKENT(rec, 7, 1, do_log_rec),
);

View File

@ -13,7 +13,7 @@
DECLARE_GLOBAL_DATA_PTR;
static const char *log_cat_name[] = {
static const char *const log_cat_name[] = {
"none",
"arch",
"board",
@ -31,7 +31,7 @@ static const char *log_cat_name[] = {
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
"log_cat_name size");
static const char *log_level_name[] = {
static const char *const log_level_name[] = {
"EMERG",
"ALERT",
"CRIT",
@ -99,7 +99,7 @@ enum log_level_t log_get_level_by_name(const char *name)
return LOGL_NONE;
}
static struct log_device *log_device_find_by_name(const char *drv_name)
struct log_device *log_device_find_by_name(const char *drv_name)
{
struct log_device *ldev;
@ -111,15 +111,7 @@ static struct log_device *log_device_find_by_name(const char *drv_name)
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)
bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
{
int i;
@ -131,16 +123,7 @@ static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
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)
bool log_has_file(const char *file_list, const char *file)
{
int file_len = strlen(file);
const char *s, *p;
@ -179,15 +162,25 @@ static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
}
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
if (rec->level > filt->max_level)
if (filt->flags & LOGFF_LEVEL_MIN) {
if (rec->level < filt->level)
continue;
} else if (rec->level > filt->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;
if (filt->flags & LOGFF_DENY)
return false;
else
return true;
}
return false;
@ -263,8 +256,9 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
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)
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)
{
struct log_filter *filt;
struct log_device *ldev;
@ -278,6 +272,7 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
if (!filt)
return -ENOMEM;
filt->flags = flags;
if (cat_list) {
filt->flags |= LOGFF_HAS_CAT;
for (i = 0; ; i++) {
@ -290,16 +285,20 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
break;
}
}
filt->max_level = max_level;
filt->level = level;
if (file_list) {
filt->file_list = strdup(file_list);
if (!filt->file_list) {
ret = ENOMEM;
ret = -ENOMEM;
goto err;
}
}
filt->filter_num = ldev->next_filter_num++;
list_add_tail(&filt->sibling_node, &ldev->filter_head);
/* Add deny filters to the beginning of the list */
if (flags & LOGFF_DENY)
list_add(&filt->sibling_node, &ldev->filter_head);
else
list_add_tail(&filt->sibling_node, &ldev->filter_head);
return filt->filter_num;

View File

@ -58,6 +58,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y

View File

@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y

View File

@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y

View File

@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
CONFIG_MULTI_DTB_FIT=y
CONFIG_ENV_OVERWRITE=y
CONFIG_ENV_IS_IN_SPI_FLASH=y
CONFIG_ENV_SPI_EARLY=y
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
CONFIG_VERSION_VARIABLE=y

8
doc/api/getopt.rst Normal file
View File

@ -0,0 +1,8 @@
.. SPDX-License-Identifier: GPL-2.0+
.. Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
Option Parsing
==============
.. kernel-doc:: include/getopt.h
:internal:

View File

@ -8,6 +8,7 @@ U-Boot API documentation
dfu
efi
getopt
linker_lists
pinctrl
rng

View File

@ -21,26 +21,13 @@ 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:
There are a number logging levels available.
* 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
To continue a log message in a separate call of function log() use
* LOGL_CONT - Use same log level as in previous call
.. kernel-doc:: include/log.h
:identifiers: log_level_t
Logging category
----------------
@ -49,19 +36,8 @@ 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
To continue a log message in a separate call of function log() use
* LOGC_CONT - Use same category as in previous call
.. kernel-doc:: include/log.h
:identifiers: log_category_t
Enabling logging
----------------
@ -78,7 +54,6 @@ 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
--------------------------------------
@ -89,12 +64,52 @@ 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. This overrides any log-level setting
in U-Boot, including CONFIG_LOG_DEFAULT_LEVEL, but just for that file.
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 (:c:type:`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:
.. code-block:: c
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:
.. code-block:: c
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:
@ -122,36 +137,6 @@ or
Remember that all uclasses IDs are log categories too.
Log command
-----------
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
--------------------
@ -165,9 +150,40 @@ enabled or disabled independently:
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::
@ -175,50 +191,43 @@ 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.
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
~~~~~~~~~~~~~~
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::
Filters are attached to log drivers to control what those drivers emit. Only
records that pass through the filter make it to the driver.
log filter-add -D -c spi -c mmc
Filters can be based on several criteria:
You will also need to add another filter to allow other messages (because the
default filter no longer applies)::
* maximum log level
* in a set of categories
* in a set of files
log filter-add -A -l info
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 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
Logging statements
------------------
To view active filters, use the 'log filter-list' command. Some example output
is::
The main logging function is:
.. code-block:: c
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:
.. code-block:: c
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.
=> 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
---------
@ -235,13 +244,12 @@ 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
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
@ -262,25 +270,15 @@ 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
@ -291,9 +289,9 @@ 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 delete existing log records.
Add a command to add new log records and delete existing records.
Provide additional log() functions - e.g. logc() to specify the category
Logging API
-----------
.. kernel-doc:: include/log.h
:internal:

8
env/Kconfig vendored
View File

@ -376,6 +376,14 @@ config ENV_SPI_MODE
Value of the SPI work mode for environment.
See include/spi.h for value.
config ENV_SPI_EARLY
bool "Access Environment in SPI flashes before relocation"
depends on ENV_IS_IN_SPI_FLASH
help
Enable this if you want to use Environment in SPI flash
before relocation. Call env_init() and than you can use
env_get_f() for accessing Environment variables.
config ENV_IS_IN_UBI
bool "Environment in a UBI volume"
depends on !CHAIN_OF_TRUST

42
env/common.c vendored
View File

@ -141,12 +141,11 @@ int env_import(const char *buf, int check, int flags)
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
static unsigned char env_flags;
int env_import_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail,
int flags)
int env_check_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail)
{
int crc1_ok, crc2_ok;
env_t *ep, *tmp_env1, *tmp_env2;
env_t *tmp_env1, *tmp_env2;
tmp_env1 = (env_t *)buf1;
tmp_env2 = (env_t *)buf2;
@ -159,14 +158,13 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
}
if (buf1_read_fail && buf2_read_fail) {
env_set_default("bad env area", 0);
return -EIO;
} else if (!buf1_read_fail && buf2_read_fail) {
gd->env_valid = ENV_VALID;
return env_import((char *)tmp_env1, 1, flags);
return -EINVAL;
} else if (buf1_read_fail && !buf2_read_fail) {
gd->env_valid = ENV_REDUND;
return env_import((char *)tmp_env2, 1, flags);
return -ENOENT;
}
crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
@ -175,7 +173,6 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
tmp_env2->crc;
if (!crc1_ok && !crc2_ok) {
env_set_default("bad CRC", 0);
return -ENOMSG; /* needed for env_load() */
} else if (crc1_ok && !crc2_ok) {
gd->env_valid = ENV_VALID;
@ -195,12 +192,37 @@ int env_import_redund(const char *buf1, int buf1_read_fail,
gd->env_valid = ENV_VALID;
}
return 0;
}
int env_import_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail,
int flags)
{
env_t *ep;
int ret;
ret = env_check_redund(buf1, buf1_read_fail, buf2, buf2_read_fail);
if (ret == -EIO) {
env_set_default("bad env area", 0);
return -EIO;
} else if (ret == -EINVAL) {
return env_import((char *)buf1, 1, flags);
} else if (ret == -ENOENT) {
return env_import((char *)buf2, 1, flags);
} else if (ret == -ENOMSG) {
env_set_default("bad CRC", 0);
return -ENOMSG;
}
if (gd->env_valid == ENV_VALID)
ep = tmp_env1;
ep = (env_t *)buf1;
else
ep = tmp_env2;
ep = (env_t *)buf2;
env_flags = ep->flags;
return env_import((char *)ep, 0, flags);
}
#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */

100
env/sf.c vendored
View File

@ -287,7 +287,10 @@ __weak void *env_sf_get_env_addr(void)
#endif
#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
static int env_sf_init(void)
/*
* check if Environment on CONFIG_ENV_ADDR is valid.
*/
static int env_sf_init_addr(void)
{
env_t *env_ptr = (env_t *)env_sf_get_env_addr();
@ -303,12 +306,103 @@ static int env_sf_init(void)
}
#endif
#if defined(CONFIG_ENV_SPI_EARLY)
/*
* early load environment from SPI flash (before relocation)
* and check if it is valid.
*/
static int env_sf_init_early(void)
{
int ret;
int read1_fail;
int read2_fail;
int crc1_ok;
env_t *tmp_env2 = NULL;
env_t *tmp_env1;
/*
* if malloc is not ready yet, we cannot use
* this part yet.
*/
if (!gd->malloc_limit)
return -ENOENT;
tmp_env1 = (env_t *)memalign(ARCH_DMA_MINALIGN,
CONFIG_ENV_SIZE);
if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
tmp_env2 = (env_t *)memalign(ARCH_DMA_MINALIGN,
CONFIG_ENV_SIZE);
if (!tmp_env1 || !tmp_env2)
goto out;
ret = setup_flash_device();
if (ret)
goto out;
read1_fail = spi_flash_read(env_flash, CONFIG_ENV_OFFSET,
CONFIG_ENV_SIZE, tmp_env1);
if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT)) {
read2_fail = spi_flash_read(env_flash,
CONFIG_ENV_OFFSET_REDUND,
CONFIG_ENV_SIZE, tmp_env2);
ret = env_check_redund((char *)tmp_env1, read1_fail,
(char *)tmp_env2, read2_fail);
if (ret == -EIO || ret == -ENOMSG)
goto err_read;
if (gd->env_valid == ENV_VALID)
gd->env_addr = (unsigned long)&tmp_env1->data;
else
gd->env_addr = (unsigned long)&tmp_env2->data;
} else {
if (read1_fail)
goto err_read;
crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
tmp_env1->crc;
if (!crc1_ok)
goto err_read;
/* if valid -> this is our env */
gd->env_valid = ENV_VALID;
gd->env_addr = (unsigned long)&tmp_env1->data;
}
return 0;
err_read:
spi_flash_free(env_flash);
env_flash = NULL;
free(tmp_env1);
if (IS_ENABLED(CONFIG_SYS_REDUNDAND_ENVIRONMENT))
free(tmp_env2);
out:
/* env is not valid. always return 0 */
gd->env_valid = ENV_INVALID;
return 0;
}
#endif
static int env_sf_init(void)
{
#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
return env_sf_init_addr();
#elif defined(CONFIG_ENV_SPI_EARLY)
return env_sf_init_early();
#endif
/*
* we must return with 0 if there is nothing done,
* else env_set_inited() get not called in env_init()
*/
return 0;
}
U_BOOT_ENV_LOCATION(sf) = {
.location = ENVL_SPI_FLASH,
ENV_NAME("SPIFlash")
.load = env_sf_load,
.save = CONFIG_IS_ENABLED(SAVEENV) ? ENV_SAVE_PTR(env_sf_save) : NULL,
#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
.init = env_sf_init,
#endif
};

View File

@ -318,6 +318,24 @@ int env_import(const char *buf, int check, int flags);
*/
int env_export(struct environment_s *env_out);
/**
* env_check_redund() - check the two redundant environments
* and find out, which is the valid one.
*
* @buf1: First environment (struct environemnt_s *)
* @buf1_read_fail: 0 if buf1 is valid, non-zero if invalid
* @buf2: Second environment (struct environemnt_s *)
* @buf2_read_fail: 0 if buf2 is valid, non-zero if invalid
* @return 0 if OK,
* -EIO if no environment is valid,
* -EINVAL if read of second entry is good
* -ENOENT if read of first entry is good
* -ENOMSG if the CRC was bad
*/
int env_check_redund(const char *buf1, int buf1_read_fail,
const char *buf2, int buf2_read_fail);
/**
* env_import_redund() - Select and import one of two redundant environments
*

130
include/getopt.h Normal file
View File

@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* getopt.h - a simple getopt(3) implementation.
*
* Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
* Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*/
#ifndef __GETOPT_H
#define __GETOPT_H
/**
* struct getopt_state - Saved state across getopt() calls
*/
struct getopt_state {
/**
* @index: Index of the next unparsed argument of @argv. If getopt() has
* parsed all of @argv, then @index will equal @argc.
*/
int index;
/* private: */
/** @arg_index: Index within the current argument */
int arg_index;
union {
/* public: */
/**
* @opt: Option being parsed when an error occurs. @opt is only
* valid when getopt() returns ``?`` or ``:``.
*/
int opt;
/**
* @arg: The argument to an option, NULL if there is none. @arg
* is only valid when getopt() returns an option character.
*/
char *arg;
/* private: */
};
};
/**
* getopt_init_state() - Initialize a &struct getopt_state
* @gs: The state to initialize
*
* This must be called before using @gs with getopt().
*/
void getopt_init_state(struct getopt_state *gs);
int __getopt(struct getopt_state *gs, int argc, char *const argv[],
const char *optstring, bool silent);
/**
* getopt() - Parse short command-line options
* @gs: Internal state and out-of-band return arguments. This must be
* initialized with getopt_init_context() beforehand.
* @argc: Number of arguments, not including the %NULL terminator
* @argv: Argument list, terminated by %NULL
* @optstring: Option specification, as described below
*
* getopt() parses short options. Short options are single characters. They may
* be followed by a required argument or an optional argument. Arguments to
* options may occur in the same argument as an option (like ``-larg``), or
* in the following argument (like ``-l arg``). An argument containing
* options begins with a ``-``. If an option expects no arguments, then it may
* be immediately followed by another option (like ``ls -alR``).
*
* @optstring is a list of accepted options. If an option is followed by ``:``
* in @optstring, then it expects a mandatory argument. If an option is followed
* by ``::`` in @optstring, it expects an optional argument. @gs.arg points
* to the argument, if one is parsed.
*
* getopt() stops parsing options when it encounters the first non-option
* argument, when it encounters the argument ``--``, or when it runs out of
* arguments. For example, in ``ls -l foo -R``, option parsing will stop when
* getopt() encounters ``foo``, if ``l`` does not expect an argument. However,
* the whole list of arguments would be parsed if ``l`` expects an argument.
*
* An example invocation of getopt() might look like::
*
* char *argv[] = { "program", "-cbx", "-a", "foo", "bar", 0 };
* int opt, argc = ARRAY_SIZE(argv) - 1;
* struct getopt_state gs;
*
* getopt_init_state(&gs);
* while ((opt = getopt(&gs, argc, argv, "a::b:c")) != -1)
* printf("opt = %c, index = %d, arg = \"%s\"\n", opt, gs.index, gs.arg);
* printf("%d argument(s) left\n", argc - gs.index);
*
* and would produce an output of::
*
* opt = c, index = 1, arg = "<NULL>"
* opt = b, index = 2, arg = "x"
* opt = a, index = 4, arg = "foo"
* 1 argument(s) left
*
* For further information, refer to the getopt(3) man page.
*
* Return:
* * An option character if an option is found. @gs.arg is set to the
* argument if there is one, otherwise it is set to ``NULL``.
* * ``-1`` if there are no more options, if a non-option argument is
* encountered, or if an ``--`` argument is encountered.
* * ``'?'`` if we encounter an option not in @optstring. @gs.opt is set to
* the unknown option.
* * ``':'`` if an argument is required, but no argument follows the
* option. @gs.opt is set to the option missing its argument.
*
* @gs.index is always set to the index of the next unparsed argument in @argv.
*/
static inline int getopt(struct getopt_state *gs, int argc,
char *const argv[], const char *optstring)
{
return __getopt(gs, argc, argv, optstring, false);
}
/**
* getopt_silent() - Parse short command-line options silently
* @gs: State
* @argc: Argument count
* @argv: Argument list
* @optstring: Option specification
*
* Same as getopt(), except no error messages are printed.
*/
static inline int getopt_silent(struct getopt_state *gs, int argc,
char *const argv[], const char *optstring)
{
return __getopt(gs, argc, argv, optstring, true);
}
#endif /* __GETOPT_H */

View File

@ -17,56 +17,92 @@
struct cmd_tbl;
/** Log levels supported, ranging from most to least important */
/**
* enum log_level_t - 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_EMERG: U-Boot is unstable */
LOGL_EMERG = 0,
/** @LOGL_ALERT: Action must be taken immediately */
LOGL_ALERT,
/** @LOGL_CRIT: Critical conditions */
LOGL_CRIT,
/** @LOGL_ERR: Error that prevents something from working */
LOGL_ERR,
/** @LOGL_WARNING: Warning may prevent optimial operation */
LOGL_WARNING,
/** @LOGL_NOTICE: Normal but significant condition, printf() */
LOGL_NOTICE,
/** @LOGL_INFO: General information message */
LOGL_INFO,
/** @LOGL_DEBUG: Basic debug-level message */
LOGL_DEBUG,
/** @LOGL_DEBUG_CONTENT: Debug message showing full message content */
LOGL_DEBUG_CONTENT,
/** @LOGL_DEBUG_IO: Debug message showing hardware I/O access */
LOGL_DEBUG_IO,
/** @LOGL_COUNT: Total number of valid log levels */
LOGL_COUNT,
/** @LOGL_NONE: Used to indicate that there is no valid log level */
LOGL_NONE,
LOGL_LEVEL_MASK = 0xf, /* Mask for valid log levels */
LOGL_FORCE_DEBUG = 0x10, /* Mask to force output due to LOG_DEBUG */
/** @LOGL_LEVEL_MASK: Mask for valid log levels */
LOGL_LEVEL_MASK = 0xf,
/** @LOGL_FORCE_DEBUG: Mask to force output due to LOG_DEBUG */
LOGL_FORCE_DEBUG = 0x10,
/** @LOGL_FIRST: The first, most-important log level */
LOGL_FIRST = LOGL_EMERG,
/** @LOGL_MAX: The last, least-important log level */
LOGL_MAX = LOGL_DEBUG_IO,
LOGL_CONT = -1, /* Use same log level as in previous call */
/** @LOGL_CONT: Use same log level as in previous call */
LOGL_CONT = -1,
};
/**
* 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 - Log categories supported.
*
* 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.
*/
enum log_category_t {
/** @LOGC_FIRST: First log category */
LOGC_FIRST = 0, /* First part mirrors UCLASS_... */
/** @LOGC_NONE: Default log category */
LOGC_NONE = UCLASS_COUNT, /* First number is after all uclasses */
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) */
/* Advanced Configuration and Power Interface (ACPI) */
/** @LOGC_ARCH: Related to arch-specific code */
LOGC_ARCH,
/** @LOGC_BOARD: Related to board-specific code */
LOGC_BOARD,
/** @LOGC_CORE: Related to core features (non-driver-model) */
LOGC_CORE,
/** @LOGC_DM: Core driver-model */
LOGC_DM,
/** @LOGC_DT: Device-tree */
LOGC_DT,
/** @LOGC_EFI: EFI implementation */
LOGC_EFI,
/** @LOGC_ALLOC: Memory allocation */
LOGC_ALLOC,
/** @LOGC_SANDBOX: Related to the sandbox board */
LOGC_SANDBOX,
/** @LOGC_BLOBLIST: Bloblist */
LOGC_BLOBLIST,
/** @LOGC_DEVRES: Device resources (``devres_...`` functions) */
LOGC_DEVRES,
/** @LOGC_ACPI: Advanced Configuration and Power Interface (ACPI) */
LOGC_ACPI,
LOGC_COUNT, /* Number of log categories */
LOGC_END, /* Sentinel value for a list of log categories */
LOGC_CONT = -1, /* Use same category as in previous call */
/** @LOGC_COUNT: Number of log categories */
LOGC_COUNT,
/** @LOGC_END: Sentinel value for lists of log categories */
LOGC_END,
/** @LOGC_CONT: Use same category as in previous call */
LOGC_CONT = -1,
};
/* Helper to cast a uclass ID to a log category */
@ -85,7 +121,7 @@ static inline int log_uc_cat(enum uclass_id id)
* @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
* 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, ...)
@ -240,7 +276,7 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
* full pathname as it may be huge. Only use this when the user should be
* warning, similar to BUG_ON() in linux.
*
* @return true if assertion succeeded (condition is true), else false
* Return: true if assertion succeeded (condition is true), else false
*/
#define assert_noisy(x) \
({ bool _val = (x); \
@ -324,8 +360,9 @@ enum log_device_flags {
*/
struct log_driver {
const char *name;
/**
* emit() - emit a log record
* @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.
@ -361,21 +398,32 @@ enum {
LOGF_MAX_CATEGORIES = 5, /* maximum categories per filter */
};
/**
* enum log_filter_flags - Flags which modify a filter
*/
enum log_filter_flags {
LOGFF_HAS_CAT = 1 << 0, /* Filter has a category list */
/** @LOGFF_HAS_CAT: Filter has a category list */
LOGFF_HAS_CAT = 1 << 0,
/** @LOGFF_DENY: Filter denies matching messages */
LOGFF_DENY = 1 << 1,
/** @LOGFF_LEVEL_MIN: Filter's level is a minimum, not a maximum */
LOGFF_LEVEL_MIN = 1 << 2,
};
/**
* struct log_filter - criterial to filter out log messages
*
* If a message matches all criteria, then it is allowed. If LOGFF_DENY is set,
* then it is denied instead.
*
* @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
* @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 to %LOGF_MAX_CATEGORIES entries
* can be provided
* @max_level: Maximum log level to allow
* @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
@ -384,7 +432,7 @@ struct log_filter {
int filter_num;
int flags;
enum log_category_t cat_list[LOGF_MAX_CATEGORIES];
enum log_level_t max_level;
enum log_level_t level;
const char *file_list;
struct list_head sibling_node;
};
@ -400,8 +448,9 @@ struct log_filter {
* log_get_cat_name() - Get the name of a category
*
* @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
* 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 '<'.
*/
const char *log_get_cat_name(enum log_category_t cat);
@ -409,7 +458,7 @@ const char *log_get_cat_name(enum log_category_t cat);
* log_get_cat_by_name() - Look up a category by name
*
* @name: Name to look up
* @return category ID, or LOGC_NONE if not found
* Return: Category, or %LOGC_NONE if not found
*/
enum log_category_t log_get_cat_by_name(const char *name);
@ -417,7 +466,7 @@ enum log_category_t log_get_cat_by_name(const char *name);
* log_get_level_name() - Get the name of a log level
*
* @level: Log level to look up
* @return log level name (in ALL CAPS)
* Return: Log level name (in ALL CAPS)
*/
const char *log_get_level_name(enum log_level_t level);
@ -425,10 +474,41 @@ const char *log_get_level_name(enum log_level_t level);
* log_get_level_by_name() - Look up a log level by name
*
* @name: Name to look up
* @return log level ID, or LOGL_NONE if not found
* Return: Log level, or %LOGL_NONE if not found
*/
enum log_level_t log_get_level_by_name(const char *name);
/**
* log_device_find_by_name() - Look up a log device by its driver's name
*
* @drv_name: Name of the driver
* Return: the log device, or %NULL if not found
*/
struct log_device *log_device_find_by_name(const char *drv_name);
/**
* 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``
*/
bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat);
/**
* 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``
*/
bool log_has_file(const char *file_list, const char *file);
/* Log format flags (bit numbers) for gd->log_fmt. See log_fmt_chars */
enum log_fmt {
LOGF_CAT = 0,
@ -445,22 +525,49 @@ enum log_fmt {
/* Handle the 'log test' command */
int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
/**
* log_add_filter_flags() - Add a new filter to a log device, specifying flags
*
* @drv_name: Driver name to add the filter to (since each driver only has a
* single device)
* @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 to %LOGF_MAX_CATEGORIES entries
* can be provided
* @level: Maximum (or minimum, if %LOGFF_LEVEL_MIN) 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_flags(const char *drv_name, enum log_category_t cat_list[],
enum log_level_t level, const char *file_list,
int flags);
/**
* 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
* @cat_list: List of categories to allow (terminated by %LOGC_END). 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
* @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
* 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);
static inline int log_add_filter(const char *drv_name,
enum log_category_t cat_list[],
enum log_level_t max_level,
const char *file_list)
{
return log_add_filter_flags(drv_name, cat_list, max_level, file_list,
0);
}
/**
* log_remove_filter() - Remove a filter from a log device
@ -468,8 +575,9 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
* @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
* 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);
@ -490,7 +598,7 @@ int log_device_set_enable(struct log_driver *drv, bool enable);
/**
* log_init() - Set up the log system ready for use
*
* @return 0 if OK, -ENOMEM if out of memory
* Return: 0 if OK, -%ENOMEM if out of memory
*/
int log_init(void);
#else
@ -504,7 +612,7 @@ static inline int log_init(void)
* log_get_default_format() - get default log format
*
* The default log format is configurable via
* CONFIG_LOGF_FILE, CONFIG_LOGF_LINE, CONFIG_LOGF_FUNC.
* %CONFIG_LOGF_FILE, %CONFIG_LOGF_LINE, and %CONFIG_LOGF_FUNC.
*
* Return: default log format
*/

View File

@ -10,7 +10,10 @@
#include <test/test.h>
#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
/* Declare a new logging test */
#define LOG_TEST(_name) UNIT_TEST(_name, 0, log_test)
#define LOG_TEST_FLAGS(_name, _flags) UNIT_TEST(_name, _flags, log_test)
#endif /* __TEST_LOG_H__ */

View File

@ -550,6 +550,11 @@ config SPL_HEXDUMP
This enables functions for printing dumps of binary data in
SPL.
config GETOPT
bool "Enable getopt"
help
This enables functions for parsing command-line options.
config OF_LIBFDT
bool "Enable the FDT library"
default y if OF_CONTROL

View File

@ -106,6 +106,7 @@ obj-y += string.o
obj-y += tables_csum.o
obj-y += time.o
obj-y += hexdump.o
obj-$(CONFIG_GETOPT) += getopt.o
obj-$(CONFIG_TRACE) += trace.o
obj-$(CONFIG_LIB_UUID) += uuid.o
obj-$(CONFIG_LIB_RAND) += rand.o

125
lib/getopt.c Normal file
View File

@ -0,0 +1,125 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* getopt.c - a simple getopt(3) implementation. See getopt.h for explanation.
*
* Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
* Copyright (c) 2007 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
*/
#define LOG_CATEGORY LOGC_CORE
#include <common.h>
#include <getopt.h>
#include <log.h>
void getopt_init_state(struct getopt_state *gs)
{
gs->index = 1;
gs->arg_index = 1;
}
int __getopt(struct getopt_state *gs, int argc, char *const argv[],
const char *optstring, bool silent)
{
char curopt; /* current option character */
const char *curoptp; /* pointer to the current option in optstring */
while (1) {
log_debug("arg_index: %d index: %d\n", gs->arg_index,
gs->index);
/* `--` indicates the end of options */
if (gs->arg_index == 1 && argv[gs->index] &&
!strcmp(argv[gs->index], "--")) {
gs->index++;
return -1;
}
/* Out of arguments */
if (gs->index >= argc)
return -1;
/* Can't parse non-options */
if (*argv[gs->index] != '-')
return -1;
/* We have found an option */
curopt = argv[gs->index][gs->arg_index];
if (curopt)
break;
/*
* no more options in current argv[] element; try the next one
*/
gs->index++;
gs->arg_index = 1;
}
/* look up current option in optstring */
curoptp = strchr(optstring, curopt);
if (!curoptp) {
if (!silent)
printf("%s: invalid option -- %c\n", argv[0], curopt);
gs->opt = curopt;
gs->arg_index++;
return '?';
}
if (*(curoptp + 1) != ':') {
/* option with no argument. Just return it */
gs->arg = NULL;
gs->arg_index++;
return curopt;
}
if (*(curoptp + 1) && *(curoptp + 2) == ':') {
/* optional argument */
if (argv[gs->index][gs->arg_index + 1]) {
/* optional argument with directly following arg */
gs->arg = argv[gs->index++] + gs->arg_index + 1;
gs->arg_index = 1;
return curopt;
}
if (gs->index + 1 == argc) {
/* We are at the last argv[] element */
gs->arg = NULL;
gs->index++;
return curopt;
}
if (*argv[gs->index + 1] != '-') {
/*
* optional argument with arg in next argv[] element
*/
gs->index++;
gs->arg = argv[gs->index++];
gs->arg_index = 1;
return curopt;
}
/* no optional argument found */
gs->arg = NULL;
gs->arg_index = 1;
gs->index++;
return curopt;
}
if (argv[gs->index][gs->arg_index + 1]) {
/* required argument with directly following arg */
gs->arg = argv[gs->index++] + gs->arg_index + 1;
gs->arg_index = 1;
return curopt;
}
gs->index++;
gs->arg_index = 1;
if (gs->index >= argc || argv[gs->index][0] == '-') {
if (!silent)
printf("option requires an argument -- %c\n", curopt);
gs->opt = curopt;
return ':';
}
gs->arg = argv[gs->index++];
return curopt;
}

View File

@ -14,3 +14,4 @@ obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
obj-$(CONFIG_UT_LIB_RSA) += rsa.o
obj-$(CONFIG_AES) += test_aes.o
obj-$(CONFIG_GETOPT) += getopt.o

123
test/lib/getopt.c Normal file
View File

@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
*
* Portions of these tests were inspired by glibc's posix/bug-getopt1.c and
* posix/tst-getopt-cancel.c
*/
#include <common.h>
#include <getopt.h>
#include <test/lib.h>
#include <test/test.h>
#include <test/ut.h>
static int do_test_getopt(struct unit_test_state *uts, int line,
struct getopt_state *gs, const char *optstring,
int args, char *argv[], int expected_count,
int expected[])
{
int opt;
getopt_init_state(gs);
for (int i = 0; i < expected_count; i++) {
opt = getopt_silent(gs, args, argv, optstring);
if (expected[i] != opt) {
/*
* Fudge the line number so we can tell which test
* failed
*/
ut_failf(uts, __FILE__, line, __func__,
"expected[i] == getopt()",
"Expected '%c' (%d) with i=%d, got '%c' (%d)",
expected[i], expected[i], i, opt, opt);
return CMD_RET_FAILURE;
}
}
opt = getopt_silent(gs, args, argv, optstring);
if (opt != -1) {
ut_failf(uts, __FILE__, line, __func__,
"getopt() != -1",
"Expected -1, got '%c' (%d)", opt, opt);
return CMD_RET_FAILURE;
}
return 0;
}
#define test_getopt(optstring, argv, expected) do { \
int ret = do_test_getopt(uts, __LINE__, &gs, optstring, \
ARRAY_SIZE(argv) - 1, argv, \
ARRAY_SIZE(expected), expected); \
if (ret) \
return ret; \
} while (0)
static int lib_test_getopt(struct unit_test_state *uts)
{
struct getopt_state gs;
/* Happy path */
test_getopt("ab:c",
((char *[]){ "program", "-cb", "x", "-a", "foo", 0 }),
((int []){ 'c', 'b', 'a' }));
ut_asserteq(4, gs.index);
/* Make sure we pick up the optional argument */
test_getopt("a::b:c",
((char *[]){ "program", "-cbx", "-a", "foo", 0 }),
((int []){ 'c', 'b', 'a' }));
ut_asserteq(4, gs.index);
/* Test required arguments */
test_getopt("a:b", ((char *[]){ "program", "-a", 0 }),
((int []){ ':' }));
ut_asserteq('a', gs.opt);
test_getopt("a:b", ((char *[]){ "program", "-b", "-a", 0 }),
((int []){ 'b', ':' }));
ut_asserteq('a', gs.opt);
/* Test invalid arguments */
test_getopt("ab:c", ((char *[]){ "program", "-d", 0 }),
((int []){ '?' }));
ut_asserteq('d', gs.opt);
/* Test arg */
test_getopt("a::b:c",
((char *[]){ "program", "-a", 0 }),
((int []){ 'a' }));
ut_asserteq(2, gs.index);
ut_assertnull(gs.arg);
test_getopt("a::b:c",
((char *[]){ "program", "-afoo", 0 }),
((int []){ 'a' }));
ut_asserteq(2, gs.index);
ut_assertnonnull(gs.arg);
ut_asserteq_str("foo", gs.arg);
test_getopt("a::b:c",
((char *[]){ "program", "-a", "foo", 0 }),
((int []){ 'a' }));
ut_asserteq(3, gs.index);
ut_assertnonnull(gs.arg);
ut_asserteq_str("foo", gs.arg);
test_getopt("a::b:c",
((char *[]){ "program", "-bfoo", 0 }),
((int []){ 'b' }));
ut_asserteq(2, gs.index);
ut_assertnonnull(gs.arg);
ut_asserteq_str("foo", gs.arg);
test_getopt("a::b:c",
((char *[]){ "program", "-b", "foo", 0 }),
((int []){ 'b' }));
ut_asserteq(3, gs.index);
ut_assertnonnull(gs.arg);
ut_asserteq_str("foo", gs.arg);
return 0;
}
LIB_TEST(lib_test_getopt, 0);

View File

@ -3,6 +3,7 @@
# Copyright (c) 2017 Google, Inc
obj-$(CONFIG_LOG_TEST) += log_test.o
obj-$(CONFIG_CMD_LOG) += log_filter.o
ifdef CONFIG_UT_LOG

108
test/log/log_filter.c Normal file
View File

@ -0,0 +1,108 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
*/
#include <common.h>
#include <console.h>
#include <log.h>
#include <test/log.h>
#include <test/ut.h>
DECLARE_GLOBAL_DATA_PTR;
/* Test invalid options */
static int log_test_filter_invalid(struct unit_test_state *uts)
{
ut_asserteq(1, run_command("log filter-add -AD", 0));
ut_asserteq(1, run_command("log filter-add -l1 -L1", 0));
ut_asserteq(1, run_command("log filter-add -l1 -L1", 0));
ut_asserteq(1, run_command("log filter-add -lfoo", 0));
ut_asserteq(1, run_command("log filter-add -cfoo", 0));
ut_asserteq(1, run_command("log filter-add -ccore -ccore -ccore -ccore "
"-ccore -ccore", 0));
return 0;
}
LOG_TEST_FLAGS(log_test_filter_invalid, UT_TESTF_CONSOLE_REC);
/* Test adding and removing filters */
static int log_test_filter(struct unit_test_state *uts)
{
bool any_found = false;
bool filt1_found = false;
bool filt2_found = false;
char cmd[32];
struct log_filter *filt;
struct log_device *ldev;
ulong filt1, filt2;
#define create_filter(args, filter_num) do {\
ut_assertok(console_record_reset_enable()); \
ut_assertok(run_command("log filter-add -p " args, 0)); \
ut_assert_skipline(); \
ut_assertok(strict_strtoul(uts->actual_str, 10, &(filter_num))); \
ut_assert_console_end(); \
} while (0)
create_filter("", filt1);
create_filter("-DL warning -cmmc -cspi -ffile", filt2);
ldev = log_device_find_by_name("console");
ut_assertnonnull(ldev);
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
if (filt->filter_num == filt1) {
filt1_found = true;
ut_asserteq(0, filt->flags);
ut_asserteq(LOGL_MAX, filt->level);
ut_assertnull(filt->file_list);
} else if (filt->filter_num == filt2) {
filt2_found = true;
ut_asserteq(LOGFF_HAS_CAT | LOGFF_DENY |
LOGFF_LEVEL_MIN, filt->flags);
ut_asserteq(true, log_has_cat(filt->cat_list,
log_uc_cat(UCLASS_MMC)));
ut_asserteq(true, log_has_cat(filt->cat_list,
log_uc_cat(UCLASS_SPI)));
ut_asserteq(LOGL_WARNING, filt->level);
ut_asserteq_str("file", filt->file_list);
}
}
ut_asserteq(true, filt1_found);
ut_asserteq(true, filt2_found);
#define remove_filter(filter_num) do { \
ut_assertok(console_record_reset_enable()); \
snprintf(cmd, sizeof(cmd), "log filter-remove %lu", filter_num); \
ut_assertok(run_command(cmd, 0)); \
ut_assert_console_end(); \
} while (0)
remove_filter(filt1);
remove_filter(filt2);
filt1_found = false;
filt2_found = false;
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
if (filt->filter_num == filt1)
filt1_found = true;
else if (filt->filter_num == filt2)
filt2_found = true;
}
ut_asserteq(false, filt1_found);
ut_asserteq(false, filt2_found);
create_filter("", filt1);
create_filter("", filt2);
ut_assertok(console_record_reset_enable());
ut_assertok(run_command("log filter-remove -a", 0));
ut_assert_console_end();
list_for_each_entry(filt, &ldev->filter_head, sibling_node)
any_found = true;
ut_asserteq(false, any_found);
return 0;
}
LOG_TEST_FLAGS(log_test_filter, UT_TESTF_CONSOLE_REC);

View File

@ -9,12 +9,17 @@
#include <common.h>
#include <command.h>
#include <log.h>
#include <test/log.h>
#include <test/ut.h>
DECLARE_GLOBAL_DATA_PTR;
/* emit some sample log records in different ways, for testing */
static int log_run(enum uclass_id cat, const char *file)
static int do_log_run(int cat, const char *file)
{
int i;
gd->log_fmt = LOGF_TEST;
debug("debug\n");
for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
log(cat, i, "log %d\n", i);
@ -22,203 +27,358 @@ static int log_run(enum uclass_id cat, const char *file)
i);
}
gd->log_fmt = log_get_default_format();
return 0;
}
static int log_test(int testnum)
#define log_run_cat(cat) do_log_run(cat, "file")
#define log_run_file(file) do_log_run(UCLASS_SPI, file)
#define log_run() do_log_run(UCLASS_SPI, "file")
#define EXPECT_LOG BIT(0)
#define EXPECT_DIRECT BIT(1)
#define EXPECT_EXTRA BIT(2)
static int do_check_log_entries(struct unit_test_state *uts, int flags, int min,
int max)
{
int ret;
int i;
printf("test %d\n", testnum);
switch (testnum) {
case 0: {
/* Check a category filter using the first category */
enum log_category_t cat_list[] = {
log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
LOGC_NONE, LOGC_END
};
ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
if (ret < 0)
return ret;
log_run(UCLASS_MMC, "file");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 1: {
/* Check a category filter using the second category */
enum log_category_t cat_list[] = {
log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI), LOGC_END
};
ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 2: {
/* Check a category filter that should block log entries */
enum log_category_t cat_list[] = {
log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
};
ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 3: {
/* Check a passing file filter */
ret = log_add_filter("console", NULL, LOGL_MAX, "file");
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 4: {
/* Check a failing file filter */
ret = log_add_filter("console", NULL, LOGL_MAX, "file");
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file2");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 5: {
/* Check a passing file filter (second in list) */
ret = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file2");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 6: {
/* Check a passing file filter */
ret = log_add_filter("console", NULL, LOGL_MAX,
"file,file2,log/log_test.c");
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file2");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 7: {
/* Check a log level filter */
ret = log_add_filter("console", NULL, LOGL_WARNING, NULL);
if (ret < 0)
return ret;
log_run(UCLASS_SPI, "file");
ret = log_remove_filter("console", ret);
if (ret < 0)
return ret;
break;
}
case 8: {
/* Check two filters, one of which passes everything */
int filt1, filt2;
ret = log_add_filter("console", NULL, LOGL_WARNING, NULL);
if (ret < 0)
return ret;
filt1 = ret;
ret = log_add_filter("console", NULL, LOGL_MAX, NULL);
if (ret < 0)
return ret;
filt2 = ret;
log_run(UCLASS_SPI, "file");
ret = log_remove_filter("console", filt1);
if (ret < 0)
return ret;
ret = log_remove_filter("console", filt2);
if (ret < 0)
return ret;
break;
}
case 9: {
/* Check three filters, which together pass everything */
int filt1, filt2, filt3;
ret = log_add_filter("console", NULL, LOGL_MAX, "file)");
if (ret < 0)
return ret;
filt1 = ret;
ret = log_add_filter("console", NULL, LOGL_MAX, "file2");
if (ret < 0)
return ret;
filt2 = ret;
ret = log_add_filter("console", NULL, LOGL_MAX,
"log/log_test.c");
if (ret < 0)
return ret;
filt3 = ret;
log_run(UCLASS_SPI, "file2");
ret = log_remove_filter("console", filt1);
if (ret < 0)
return ret;
ret = log_remove_filter("console", filt2);
if (ret < 0)
return ret;
ret = log_remove_filter("console", filt3);
if (ret < 0)
return ret;
break;
}
case 10: {
log_err("level %d\n", LOGL_EMERG);
log_err("level %d\n", LOGL_ALERT);
log_err("level %d\n", LOGL_CRIT);
log_err("level %d\n", LOGL_ERR);
log_warning("level %d\n", LOGL_WARNING);
log_notice("level %d\n", LOGL_NOTICE);
log_info("level %d\n", LOGL_INFO);
log_debug("level %d\n", LOGL_DEBUG);
log_content("level %d\n", LOGL_DEBUG_CONTENT);
log_io("level %d\n", LOGL_DEBUG_IO);
break;
}
case 11:
log_err("default\n");
ret = log_device_set_enable(LOG_GET_DRIVER(console), false);
log_err("disabled\n");
ret = log_device_set_enable(LOG_GET_DRIVER(console), true);
log_err("enabled\n");
break;
}
for (i = min; i <= max; i++) {
if (flags & EXPECT_LOG)
ut_assert_nextline("do_log_run() log %d", i);
if (flags & EXPECT_DIRECT)
ut_assert_nextline("func() _log %d", i);
}
if (flags & EXPECT_EXTRA)
for (; i <= LOGL_MAX ; i++)
ut_assert_nextline("func() _log %d", i);
ut_assert_console_end();
return 0;
}
int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
#define check_log_entries_flags_levels(flags, min, max) do {\
int ret = do_check_log_entries(uts, flags, min, max); \
if (ret) \
return ret; \
} while (0)
#define check_log_entries_flags(flags) \
check_log_entries_flags_levels(flags, LOGL_FIRST, _LOG_MAX_LEVEL)
#define check_log_entries() check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT)
#define check_log_entries_extra() \
check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT | EXPECT_EXTRA)
#define check_log_entries_none() check_log_entries_flags(0)
/* Check a category filter using the first category */
int log_test_cat_allow(struct unit_test_state *uts)
{
enum log_category_t cat_list[] = {
log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
LOGC_NONE, LOGC_END
};
int filt;
filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
ut_assert(filt >= 0);
ut_assertok(console_record_reset_enable());
log_run_cat(UCLASS_MMC);
check_log_entries_extra();
ut_assertok(console_record_reset_enable());
log_run_cat(UCLASS_SPI);
check_log_entries_extra();
ut_assertok(log_remove_filter("console", filt));
return 0;
}
LOG_TEST_FLAGS(log_test_cat_allow, UT_TESTF_CONSOLE_REC);
/* Check a category filter that should block log entries */
int log_test_cat_deny_implicit(struct unit_test_state *uts)
{
enum log_category_t cat_list[] = {
log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
};
int filt;
filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
ut_assert(filt >= 0);
ut_assertok(console_record_reset_enable());
log_run_cat(UCLASS_SPI);
check_log_entries_none();
ut_assertok(log_remove_filter("console", filt));
return 0;
}
LOG_TEST_FLAGS(log_test_cat_deny_implicit, UT_TESTF_CONSOLE_REC);
/* Check passing and failing file filters */
int log_test_file(struct unit_test_state *uts)
{
int filt;
filt = log_add_filter("console", NULL, LOGL_MAX, "file");
ut_assert(filt >= 0);
ut_assertok(console_record_reset_enable());
log_run_file("file");
check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA);
ut_assertok(console_record_reset_enable());
log_run_file("file2");
check_log_entries_none();
ut_assertok(log_remove_filter("console", filt));
return 0;
}
LOG_TEST_FLAGS(log_test_file, UT_TESTF_CONSOLE_REC);
/* Check a passing file filter (second in list) */
int log_test_file_second(struct unit_test_state *uts)
{
int filt;
filt = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
ut_assert(filt >= 0);
ut_assertok(console_record_reset_enable());
log_run_file("file2");
check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA);
ut_assertok(log_remove_filter("console", filt));
return 0;
}
LOG_TEST_FLAGS(log_test_file_second, UT_TESTF_CONSOLE_REC);
/* Check a passing file filter (middle of list) */
int log_test_file_mid(struct unit_test_state *uts)
{
int filt;
filt = log_add_filter("console", NULL, LOGL_MAX,
"file,file2,log/log_test.c");
ut_assert(filt >= 0);
ut_assertok(console_record_reset_enable());
log_run_file("file2");
check_log_entries_extra();
ut_assertok(log_remove_filter("console", filt));
return 0;
}
LOG_TEST_FLAGS(log_test_file_mid, UT_TESTF_CONSOLE_REC);
/* Check a log level filter */
int log_test_level(struct unit_test_state *uts)
{
int filt;
filt = log_add_filter("console", NULL, LOGL_WARNING, NULL);
ut_assert(filt >= 0);
ut_assertok(console_record_reset_enable());
log_run();
check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT, LOGL_FIRST,
LOGL_WARNING);
ut_assertok(log_remove_filter("console", filt));
return 0;
}
LOG_TEST_FLAGS(log_test_level, UT_TESTF_CONSOLE_REC);
/* Check two filters, one of which passes everything */
int log_test_double(struct unit_test_state *uts)
{
int filt1, filt2;
filt1 = log_add_filter("console", NULL, LOGL_WARNING, NULL);
ut_assert(filt1 >= 0);
filt2 = log_add_filter("console", NULL, LOGL_MAX, NULL);
ut_assert(filt2 >= 0);
ut_assertok(console_record_reset_enable());
log_run();
check_log_entries_extra();
ut_assertok(log_remove_filter("console", filt1));
ut_assertok(log_remove_filter("console", filt2));
return 0;
}
LOG_TEST_FLAGS(log_test_double, UT_TESTF_CONSOLE_REC);
/* Check three filters, which together pass everything */
int log_test_triple(struct unit_test_state *uts)
{
int filt1, filt2, filt3;
filt1 = log_add_filter("console", NULL, LOGL_MAX, "file)");
ut_assert(filt1 >= 0);
filt2 = log_add_filter("console", NULL, LOGL_MAX, "file2");
ut_assert(filt2 >= 0);
filt3 = log_add_filter("console", NULL, LOGL_MAX, "log/log_test.c");
ut_assert(filt3 >= 0);
ut_assertok(console_record_reset_enable());
log_run_file("file2");
check_log_entries_extra();
ut_assertok(log_remove_filter("console", filt1));
ut_assertok(log_remove_filter("console", filt2));
ut_assertok(log_remove_filter("console", filt3));
return 0;
}
LOG_TEST_FLAGS(log_test_triple, UT_TESTF_CONSOLE_REC);
int do_log_test_helpers(struct unit_test_state *uts)
{
int i;
ut_assertok(console_record_reset_enable());
log_err("level %d\n", LOGL_EMERG);
log_err("level %d\n", LOGL_ALERT);
log_err("level %d\n", LOGL_CRIT);
log_err("level %d\n", LOGL_ERR);
log_warning("level %d\n", LOGL_WARNING);
log_notice("level %d\n", LOGL_NOTICE);
log_info("level %d\n", LOGL_INFO);
log_debug("level %d\n", LOGL_DEBUG);
log_content("level %d\n", LOGL_DEBUG_CONTENT);
log_io("level %d\n", LOGL_DEBUG_IO);
for (i = LOGL_EMERG; i <= _LOG_MAX_LEVEL; i++)
ut_assert_nextline("%s() level %d", __func__, i);
ut_assert_console_end();
return 0;
}
int log_test_helpers(struct unit_test_state *uts)
{
int testnum = 0;
int ret;
if (argc > 1)
testnum = simple_strtoul(argv[1], NULL, 10);
ret = log_test(testnum);
if (ret)
printf("Test failure (err=%d)\n", ret);
return ret ? CMD_RET_FAILURE : 0;
gd->log_fmt = LOGF_TEST;
ret = do_log_test_helpers(uts);
gd->log_fmt = log_get_default_format();
return ret;
}
LOG_TEST_FLAGS(log_test_helpers, UT_TESTF_CONSOLE_REC);
int do_log_test_disable(struct unit_test_state *uts)
{
ut_assertok(console_record_reset_enable());
log_err("default\n");
ut_assert_nextline("%s() default", __func__);
ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), false));
log_err("disabled\n");
ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), true));
log_err("enabled\n");
ut_assert_nextline("%s() enabled", __func__);
ut_assert_console_end();
return 0;
}
int log_test_disable(struct unit_test_state *uts)
{
int ret;
gd->log_fmt = LOGF_TEST;
ret = do_log_test_disable(uts);
gd->log_fmt = log_get_default_format();
return ret;
}
LOG_TEST_FLAGS(log_test_disable, UT_TESTF_CONSOLE_REC);
/* Check denying based on category */
int log_test_cat_deny(struct unit_test_state *uts)
{
int filt1, filt2;
enum log_category_t cat_list[] = {
log_uc_cat(UCLASS_SPI), LOGC_END
};
filt1 = log_add_filter("console", cat_list, LOGL_MAX, NULL);
ut_assert(filt1 >= 0);
filt2 = log_add_filter_flags("console", cat_list, LOGL_MAX, NULL,
LOGFF_DENY);
ut_assert(filt2 >= 0);
ut_assertok(console_record_reset_enable());
log_run_cat(UCLASS_SPI);
check_log_entries_none();
ut_assertok(log_remove_filter("console", filt1));
ut_assertok(log_remove_filter("console", filt2));
return 0;
}
LOG_TEST_FLAGS(log_test_cat_deny, UT_TESTF_CONSOLE_REC);
/* Check denying based on file */
int log_test_file_deny(struct unit_test_state *uts)
{
int filt1, filt2;
filt1 = log_add_filter("console", NULL, LOGL_MAX, "file");
ut_assert(filt1 >= 0);
filt2 = log_add_filter_flags("console", NULL, LOGL_MAX, "file",
LOGFF_DENY);
ut_assert(filt2 >= 0);
ut_assertok(console_record_reset_enable());
log_run_file("file");
check_log_entries_none();
ut_assertok(log_remove_filter("console", filt1));
ut_assertok(log_remove_filter("console", filt2));
return 0;
}
LOG_TEST_FLAGS(log_test_file_deny, UT_TESTF_CONSOLE_REC);
/* Check denying based on level */
int log_test_level_deny(struct unit_test_state *uts)
{
int filt1, filt2;
filt1 = log_add_filter("console", NULL, LOGL_INFO, NULL);
ut_assert(filt1 >= 0);
filt2 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL,
LOGFF_DENY);
ut_assert(filt2 >= 0);
ut_assertok(console_record_reset_enable());
log_run();
check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT,
LOGL_WARNING + 1, _LOG_MAX_LEVEL);
ut_assertok(log_remove_filter("console", filt1));
ut_assertok(log_remove_filter("console", filt2));
return 0;
}
LOG_TEST_FLAGS(log_test_level_deny, UT_TESTF_CONSOLE_REC);
/* Check matching based on minimum level */
int log_test_min(struct unit_test_state *uts)
{
int filt1, filt2;
filt1 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL,
LOGFF_LEVEL_MIN);
ut_assert(filt1 >= 0);
filt2 = log_add_filter_flags("console", NULL, LOGL_INFO, NULL,
LOGFF_DENY | LOGFF_LEVEL_MIN);
ut_assert(filt2 >= 0);
ut_assertok(console_record_reset_enable());
log_run();
check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT,
LOGL_WARNING, LOGL_INFO - 1);
ut_assertok(log_remove_filter("console", filt1));
ut_assertok(log_remove_filter("console", filt2));
return 0;
}
LOG_TEST_FLAGS(log_test_min, UT_TESTF_CONSOLE_REC);

View File

@ -8,8 +8,6 @@
#ifndef __SYSLOG_TEST_H
#define __SYSLOG_TEST_H
#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
/**
* struct sb_log_env - private data for sandbox ethernet driver
*

View File

@ -10,110 +10,6 @@ and checks that the output is correct.
import pytest
LOGL_FIRST, LOGL_WARNING, LOGL_INFO = (0, 4, 6)
@pytest.mark.buildconfigspec('cmd_log')
def test_log(u_boot_console):
"""Test that U-Boot logging works correctly."""
def check_log_entries(lines, mask, max_level=LOGL_INFO):
"""Check that the expected log records appear in the output
Args:
lines: iterator containing lines to check
mask: bit mask to select which lines to check for:
bit 0: standard log line
bit 1: _log line
max_level: maximum log level to expect in the output
"""
for i in range(max_level):
if mask & 1:
assert 'log_run() log %d' % i == next(lines)
if mask & 3:
assert 'func() _log %d' % i == next(lines)
def run_test(testnum):
"""Run a particular test number (the 'log test' command)
Args:
testnum: Test number to run
Returns:
iterator containing the lines output from the command
"""
output = u_boot_console.run_command('log format fm')
assert output == ''
with cons.log.section('basic'):
output = u_boot_console.run_command('log test %d' % testnum)
split = output.replace('\r', '').splitlines()
lines = iter(split)
assert 'test %d' % testnum == next(lines)
return lines
def test0():
lines = run_test(0)
check_log_entries(lines, 3)
def test1():
lines = run_test(1)
check_log_entries(lines, 3)
def test2():
lines = run_test(2)
def test3():
lines = run_test(3)
check_log_entries(lines, 2)
def test4():
lines = run_test(4)
assert next(lines, None) == None
def test5():
lines = run_test(5)
check_log_entries(lines, 2)
def test6():
lines = run_test(6)
check_log_entries(lines, 3)
def test7():
lines = run_test(7)
check_log_entries(lines, 3, LOGL_WARNING)
def test8():
lines = run_test(8)
check_log_entries(lines, 3)
def test9():
lines = run_test(9)
check_log_entries(lines, 3)
def test10():
lines = run_test(10)
for i in range(7):
assert 'log_test() level %d' % i == next(lines)
def test11():
"""Test use of log_device_set_enable()"""
lines = run_test(11)
assert 'log_test() default'
# disabled should not be displayed
assert 'log_test() enabled'
# TODO(sjg@chromium.org): Consider structuring this as separate tests
cons = u_boot_console
test0()
test1()
test2()
test3()
test4()
test5()
test6()
test7()
test8()
test9()
test10()
test11()
@pytest.mark.buildconfigspec('cmd_log')
def test_log_format(u_boot_console):
"""Test the 'log format' and 'log rec' commands"""