mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-10-06 19:40:42 +09:00
Merge branch '2020-10-30-misc-changes'
- Additional log improvements - SPI flash environment improvements
This commit is contained in:
commit
7820052fde
@ -758,6 +758,7 @@ T: git https://gitlab.denx.de/u-boot/u-boot.git
|
|||||||
F: common/log*
|
F: common/log*
|
||||||
F: cmd/log.c
|
F: cmd/log.c
|
||||||
F: doc/develop/logging.rst
|
F: doc/develop/logging.rst
|
||||||
|
F: lib/getopt.c
|
||||||
F: test/log/
|
F: test/log/
|
||||||
F: test/py/tests/test_log.py
|
F: test/py/tests/test_log.py
|
||||||
|
|
||||||
|
@ -2239,6 +2239,7 @@ config CMD_KGDB
|
|||||||
config CMD_LOG
|
config CMD_LOG
|
||||||
bool "log - Generation, control and access to logging"
|
bool "log - Generation, control and access to logging"
|
||||||
select LOG
|
select LOG
|
||||||
|
select GETOPT
|
||||||
help
|
help
|
||||||
This provides access to logging features. It allows the output of
|
This provides access to logging features. It allows the output of
|
||||||
log data to be controlled to a limited extent (setting up the default
|
log data to be controlled to a limited extent (setting up the default
|
||||||
|
354
cmd/log.c
354
cmd/log.c
@ -7,27 +7,298 @@
|
|||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <command.h>
|
#include <command.h>
|
||||||
#include <dm.h>
|
#include <dm.h>
|
||||||
|
#include <getopt.h>
|
||||||
#include <log.h>
|
#include <log.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
static char log_fmt_chars[LOGF_COUNT] = "clFLfm";
|
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,
|
static int do_log_level(struct cmd_tbl *cmdtp, int flag, int argc,
|
||||||
char *const argv[])
|
char *const argv[])
|
||||||
{
|
{
|
||||||
if (argc > 1) {
|
enum log_level_t log_level;
|
||||||
long log_level = simple_strtol(argv[1], NULL, 10);
|
|
||||||
|
|
||||||
if (log_level < 0 || log_level > _LOG_MAX_LEVEL) {
|
if (argc > 1) {
|
||||||
printf("Only log levels <= %d are supported\n",
|
log_level = parse_log_level(argv[1]);
|
||||||
_LOG_MAX_LEVEL);
|
|
||||||
|
if (log_level == LOGL_NONE)
|
||||||
return CMD_RET_FAILURE;
|
return CMD_RET_FAILURE;
|
||||||
}
|
|
||||||
gd->default_log_level = log_level;
|
gd->default_log_level = log_level;
|
||||||
} else {
|
} 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,
|
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;
|
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
|
#ifdef CONFIG_SYS_LONGHELP
|
||||||
static char log_help_text[] =
|
static char log_help_text[] =
|
||||||
"level - get/set log level\n"
|
"level [<level>] - get/set log level\n"
|
||||||
#ifdef CONFIG_LOG_TEST
|
"categories - list log categories\n"
|
||||||
"log test - run log tests\n"
|
"drivers - list log drivers\n"
|
||||||
#endif
|
"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"
|
"log format <fmt> - set log output format. <fmt> is a string where\n"
|
||||||
"\teach letter indicates something that should be displayed:\n"
|
"\teach letter indicates something that should be displayed:\n"
|
||||||
"\tc=category, l=level, F=file, L=line number, f=function, m=msg\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
|
#endif
|
||||||
|
|
||||||
U_BOOT_CMD(
|
U_BOOT_CMD_WITH_SUBCMDS(log, "log system", log_help_text,
|
||||||
log, CONFIG_SYS_MAXARGS, 1, do_log,
|
U_BOOT_SUBCMD_MKENT(level, 2, 1, do_log_level),
|
||||||
"log system", log_help_text
|
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),
|
||||||
);
|
);
|
||||||
|
53
common/log.c
53
common/log.c
@ -13,7 +13,7 @@
|
|||||||
|
|
||||||
DECLARE_GLOBAL_DATA_PTR;
|
DECLARE_GLOBAL_DATA_PTR;
|
||||||
|
|
||||||
static const char *log_cat_name[] = {
|
static const char *const log_cat_name[] = {
|
||||||
"none",
|
"none",
|
||||||
"arch",
|
"arch",
|
||||||
"board",
|
"board",
|
||||||
@ -31,7 +31,7 @@ static const char *log_cat_name[] = {
|
|||||||
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
|
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
|
||||||
"log_cat_name size");
|
"log_cat_name size");
|
||||||
|
|
||||||
static const char *log_level_name[] = {
|
static const char *const log_level_name[] = {
|
||||||
"EMERG",
|
"EMERG",
|
||||||
"ALERT",
|
"ALERT",
|
||||||
"CRIT",
|
"CRIT",
|
||||||
@ -99,7 +99,7 @@ enum log_level_t log_get_level_by_name(const char *name)
|
|||||||
return LOGL_NONE;
|
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;
|
struct log_device *ldev;
|
||||||
|
|
||||||
@ -111,15 +111,7 @@ static struct log_device *log_device_find_by_name(const char *drv_name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -131,16 +123,7 @@ static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool log_has_file(const char *file_list, const char *file)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
int file_len = strlen(file);
|
int file_len = strlen(file);
|
||||||
const char *s, *p;
|
const char *s, *p;
|
||||||
@ -179,14 +162,24 @@ static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
|
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;
|
continue;
|
||||||
|
} else if (rec->level > filt->level) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if ((filt->flags & LOGFF_HAS_CAT) &&
|
if ((filt->flags & LOGFF_HAS_CAT) &&
|
||||||
!log_has_cat(filt->cat_list, rec->cat))
|
!log_has_cat(filt->cat_list, rec->cat))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (filt->file_list &&
|
if (filt->file_list &&
|
||||||
!log_has_file(filt->file_list, rec->file))
|
!log_has_file(filt->file_list, rec->file))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (filt->flags & LOGFF_DENY)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,8 +256,9 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
|
int log_add_filter_flags(const char *drv_name, enum log_category_t cat_list[],
|
||||||
enum log_level_t max_level, const char *file_list)
|
enum log_level_t level, const char *file_list,
|
||||||
|
int flags)
|
||||||
{
|
{
|
||||||
struct log_filter *filt;
|
struct log_filter *filt;
|
||||||
struct log_device *ldev;
|
struct log_device *ldev;
|
||||||
@ -278,6 +272,7 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
|
|||||||
if (!filt)
|
if (!filt)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
filt->flags = flags;
|
||||||
if (cat_list) {
|
if (cat_list) {
|
||||||
filt->flags |= LOGFF_HAS_CAT;
|
filt->flags |= LOGFF_HAS_CAT;
|
||||||
for (i = 0; ; i++) {
|
for (i = 0; ; i++) {
|
||||||
@ -290,15 +285,19 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filt->max_level = max_level;
|
filt->level = level;
|
||||||
if (file_list) {
|
if (file_list) {
|
||||||
filt->file_list = strdup(file_list);
|
filt->file_list = strdup(file_list);
|
||||||
if (!filt->file_list) {
|
if (!filt->file_list) {
|
||||||
ret = ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
filt->filter_num = ldev->next_filter_num++;
|
filt->filter_num = ldev->next_filter_num++;
|
||||||
|
/* 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);
|
list_add_tail(&filt->sibling_node, &ldev->filter_head);
|
||||||
|
|
||||||
return filt->filter_num;
|
return filt->filter_num;
|
||||||
|
@ -58,6 +58,7 @@ CONFIG_DTB_RESELECT=y
|
|||||||
CONFIG_MULTI_DTB_FIT=y
|
CONFIG_MULTI_DTB_FIT=y
|
||||||
CONFIG_ENV_OVERWRITE=y
|
CONFIG_ENV_OVERWRITE=y
|
||||||
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
||||||
|
CONFIG_ENV_SPI_EARLY=y
|
||||||
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
||||||
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
||||||
CONFIG_VERSION_VARIABLE=y
|
CONFIG_VERSION_VARIABLE=y
|
||||||
|
@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
|
|||||||
CONFIG_MULTI_DTB_FIT=y
|
CONFIG_MULTI_DTB_FIT=y
|
||||||
CONFIG_ENV_OVERWRITE=y
|
CONFIG_ENV_OVERWRITE=y
|
||||||
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
||||||
|
CONFIG_ENV_SPI_EARLY=y
|
||||||
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
||||||
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
||||||
CONFIG_VERSION_VARIABLE=y
|
CONFIG_VERSION_VARIABLE=y
|
||||||
|
@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
|
|||||||
CONFIG_MULTI_DTB_FIT=y
|
CONFIG_MULTI_DTB_FIT=y
|
||||||
CONFIG_ENV_OVERWRITE=y
|
CONFIG_ENV_OVERWRITE=y
|
||||||
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
||||||
|
CONFIG_ENV_SPI_EARLY=y
|
||||||
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
||||||
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
||||||
CONFIG_VERSION_VARIABLE=y
|
CONFIG_VERSION_VARIABLE=y
|
||||||
|
@ -56,6 +56,7 @@ CONFIG_DTB_RESELECT=y
|
|||||||
CONFIG_MULTI_DTB_FIT=y
|
CONFIG_MULTI_DTB_FIT=y
|
||||||
CONFIG_ENV_OVERWRITE=y
|
CONFIG_ENV_OVERWRITE=y
|
||||||
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
CONFIG_ENV_IS_IN_SPI_FLASH=y
|
||||||
|
CONFIG_ENV_SPI_EARLY=y
|
||||||
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
CONFIG_SYS_REDUNDAND_ENVIRONMENT=y
|
||||||
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
CONFIG_SYS_RELOC_GD_ENV_ADDR=y
|
||||||
CONFIG_VERSION_VARIABLE=y
|
CONFIG_VERSION_VARIABLE=y
|
||||||
|
8
doc/api/getopt.rst
Normal file
8
doc/api/getopt.rst
Normal 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:
|
@ -8,6 +8,7 @@ U-Boot API documentation
|
|||||||
|
|
||||||
dfu
|
dfu
|
||||||
efi
|
efi
|
||||||
|
getopt
|
||||||
linker_lists
|
linker_lists
|
||||||
pinctrl
|
pinctrl
|
||||||
rng
|
rng
|
||||||
|
@ -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
|
U-Boot's logging feature aims to satisfy this goal for both users and
|
||||||
developers.
|
developers.
|
||||||
|
|
||||||
|
|
||||||
Logging levels
|
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
|
.. kernel-doc:: include/log.h
|
||||||
* LOGL_ALERT - Indicates action must be taken immediate or U-Boot will crash
|
:identifiers: log_level_t
|
||||||
* 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
|
|
||||||
|
|
||||||
Logging category
|
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
|
has a category which is intended to allow messages to be filtered according to
|
||||||
their source.
|
their source.
|
||||||
|
|
||||||
The following main categories are defined:
|
.. kernel-doc:: include/log.h
|
||||||
|
:identifiers: log_category_t
|
||||||
* 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
|
|
||||||
|
|
||||||
Enabling logging
|
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
|
The above have SPL and TPL versions also, e.g. CONFIG_SPL_LOG_MAX_LEVEL and
|
||||||
CONFIG_TPL_LOG_MAX_LEVEL.
|
CONFIG_TPL_LOG_MAX_LEVEL.
|
||||||
|
|
||||||
|
|
||||||
Temporary logging within a single file
|
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
|
#define LOG_DEBUG
|
||||||
|
|
||||||
to enable building in of all logging statements in a single file. Put it at
|
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
|
the top of the file, before any #includes.
|
||||||
in U-Boot, including CONFIG_LOG_DEFAULT_LEVEL, but just for that file.
|
|
||||||
|
|
||||||
|
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
|
Convenience functions
|
||||||
---------------------
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
A number of convenience functions are available to shorten the code needed
|
A number of convenience functions are available to shorten the code needed
|
||||||
for logging:
|
for logging:
|
||||||
@ -122,36 +137,6 @@ or
|
|||||||
|
|
||||||
Remember that all uclasses IDs are log categories too.
|
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
|
Logging destinations
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
@ -165,9 +150,40 @@ enabled or disabled independently:
|
|||||||
The syslog driver sends the value of environmental variable 'log_hostname' as
|
The syslog driver sends the value of environmental variable 'log_hostname' as
|
||||||
HOSTNAME if available.
|
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
|
Log format
|
||||||
----------
|
~~~~~~~~~~
|
||||||
|
|
||||||
You can control the log format using the 'log format' command. The basic
|
You can control the log format using the 'log format' command. The basic
|
||||||
format is::
|
format is::
|
||||||
@ -175,50 +191,43 @@ format is::
|
|||||||
LEVEL.category,file.c:123-func() message
|
LEVEL.category,file.c:123-func() message
|
||||||
|
|
||||||
In the above, file.c:123 is the filename where the log record was generated and
|
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
|
func() is the function name. By default ('log format default') only the message
|
||||||
function name and message are displayed on the console. You can control which
|
is displayed on the console. You can control which fields are present, but not
|
||||||
fields are present, but not the field order.
|
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
|
log filter-add -D -c spi -c mmc
|
||||||
records that pass through the filter make it to the driver.
|
|
||||||
|
|
||||||
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
|
log filter-add -A -l info
|
||||||
* 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
|
Log levels may be either symbolic names (like above) or numbers. For example, to
|
||||||
limits output to records with a level less than CONFIG_MAX_LOG_LEVEL.
|
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:
|
=> log filter-list
|
||||||
|
num policy level categories files
|
||||||
.. code-block:: c
|
2 deny >= DEBUG drivers/core/lists.c,drivers/core/ofnode.c
|
||||||
|
0 deny <= IO spi
|
||||||
log(category, level, format_string, ...)
|
mmc
|
||||||
|
1 allow <= INFO
|
||||||
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.
|
|
||||||
|
|
||||||
|
Note that filters are processed in-order from top to bottom, not in the order of
|
||||||
|
their filter number. Filters are added to the top of the list if they deny when
|
||||||
|
they match, and to the bottom if they allow when they match. For more
|
||||||
|
information, consult the usage of the 'log' command, by running 'help log'.
|
||||||
|
|
||||||
Code size
|
Code size
|
||||||
---------
|
---------
|
||||||
@ -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
|
bloats the code hugely. The advantage is that it is then possible to enable
|
||||||
all logging within U-Boot.
|
all logging within U-Boot.
|
||||||
|
|
||||||
|
|
||||||
To Do
|
To Do
|
||||||
-----
|
-----
|
||||||
|
|
||||||
There are lots of useful additions that could be made. None of the below is
|
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:
|
Convenience functions to support setting the category:
|
||||||
|
|
||||||
* log_arch(level, format_string, ...) - category LOGC_ARCH
|
* 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 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 browse log records
|
||||||
|
|
||||||
Add a way to record log records for browsing using an external tool
|
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
|
Add commands to add and remove log devices
|
||||||
|
|
||||||
Allow sharing of printf format strings in log records to reduce storage size
|
Allow sharing of printf format strings in log records to reduce storage size
|
||||||
for large numbers of log records
|
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()
|
Consider making log() calls emit an automatic newline, perhaps with a logn()
|
||||||
function to avoid that
|
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
|
Add a printf() format string pragma so that log statements are checked properly
|
||||||
|
|
||||||
Enhance the log console driver to show level / category / file / line
|
Add a command to delete existing log records.
|
||||||
information
|
|
||||||
|
|
||||||
Add a command to add new log records and delete existing records.
|
Logging API
|
||||||
|
-----------
|
||||||
Provide additional log() functions - e.g. logc() to specify the category
|
.. kernel-doc:: include/log.h
|
||||||
|
:internal:
|
||||||
|
8
env/Kconfig
vendored
8
env/Kconfig
vendored
@ -376,6 +376,14 @@ config ENV_SPI_MODE
|
|||||||
Value of the SPI work mode for environment.
|
Value of the SPI work mode for environment.
|
||||||
See include/spi.h for value.
|
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
|
config ENV_IS_IN_UBI
|
||||||
bool "Environment in a UBI volume"
|
bool "Environment in a UBI volume"
|
||||||
depends on !CHAIN_OF_TRUST
|
depends on !CHAIN_OF_TRUST
|
||||||
|
42
env/common.c
vendored
42
env/common.c
vendored
@ -141,12 +141,11 @@ int env_import(const char *buf, int check, int flags)
|
|||||||
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
|
#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT
|
||||||
static unsigned char env_flags;
|
static unsigned char env_flags;
|
||||||
|
|
||||||
int env_import_redund(const char *buf1, int buf1_read_fail,
|
int env_check_redund(const char *buf1, int buf1_read_fail,
|
||||||
const char *buf2, int buf2_read_fail,
|
const char *buf2, int buf2_read_fail)
|
||||||
int flags)
|
|
||||||
{
|
{
|
||||||
int crc1_ok, crc2_ok;
|
int crc1_ok, crc2_ok;
|
||||||
env_t *ep, *tmp_env1, *tmp_env2;
|
env_t *tmp_env1, *tmp_env2;
|
||||||
|
|
||||||
tmp_env1 = (env_t *)buf1;
|
tmp_env1 = (env_t *)buf1;
|
||||||
tmp_env2 = (env_t *)buf2;
|
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) {
|
if (buf1_read_fail && buf2_read_fail) {
|
||||||
env_set_default("bad env area", 0);
|
|
||||||
return -EIO;
|
return -EIO;
|
||||||
} else if (!buf1_read_fail && buf2_read_fail) {
|
} else if (!buf1_read_fail && buf2_read_fail) {
|
||||||
gd->env_valid = ENV_VALID;
|
gd->env_valid = ENV_VALID;
|
||||||
return env_import((char *)tmp_env1, 1, flags);
|
return -EINVAL;
|
||||||
} else if (buf1_read_fail && !buf2_read_fail) {
|
} else if (buf1_read_fail && !buf2_read_fail) {
|
||||||
gd->env_valid = ENV_REDUND;
|
gd->env_valid = ENV_REDUND;
|
||||||
return env_import((char *)tmp_env2, 1, flags);
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
crc1_ok = crc32(0, tmp_env1->data, ENV_SIZE) ==
|
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;
|
tmp_env2->crc;
|
||||||
|
|
||||||
if (!crc1_ok && !crc2_ok) {
|
if (!crc1_ok && !crc2_ok) {
|
||||||
env_set_default("bad CRC", 0);
|
|
||||||
return -ENOMSG; /* needed for env_load() */
|
return -ENOMSG; /* needed for env_load() */
|
||||||
} else if (crc1_ok && !crc2_ok) {
|
} else if (crc1_ok && !crc2_ok) {
|
||||||
gd->env_valid = ENV_VALID;
|
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;
|
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)
|
if (gd->env_valid == ENV_VALID)
|
||||||
ep = tmp_env1;
|
ep = (env_t *)buf1;
|
||||||
else
|
else
|
||||||
ep = tmp_env2;
|
ep = (env_t *)buf2;
|
||||||
|
|
||||||
env_flags = ep->flags;
|
env_flags = ep->flags;
|
||||||
|
|
||||||
return env_import((char *)ep, 0, flags);
|
return env_import((char *)ep, 0, flags);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
|
#endif /* CONFIG_SYS_REDUNDAND_ENVIRONMENT */
|
||||||
|
100
env/sf.c
vendored
100
env/sf.c
vendored
@ -287,7 +287,10 @@ __weak void *env_sf_get_env_addr(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
|
#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();
|
env_t *env_ptr = (env_t *)env_sf_get_env_addr();
|
||||||
|
|
||||||
@ -303,12 +306,103 @@ static int env_sf_init(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#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) = {
|
U_BOOT_ENV_LOCATION(sf) = {
|
||||||
.location = ENVL_SPI_FLASH,
|
.location = ENVL_SPI_FLASH,
|
||||||
ENV_NAME("SPIFlash")
|
ENV_NAME("SPIFlash")
|
||||||
.load = env_sf_load,
|
.load = env_sf_load,
|
||||||
.save = CONFIG_IS_ENABLED(SAVEENV) ? ENV_SAVE_PTR(env_sf_save) : NULL,
|
.save = CONFIG_IS_ENABLED(SAVEENV) ? ENV_SAVE_PTR(env_sf_save) : NULL,
|
||||||
#if defined(INITENV) && (CONFIG_ENV_ADDR != 0x0)
|
|
||||||
.init = env_sf_init,
|
.init = env_sf_init,
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
@ -318,6 +318,24 @@ int env_import(const char *buf, int check, int flags);
|
|||||||
*/
|
*/
|
||||||
int env_export(struct environment_s *env_out);
|
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
|
* env_import_redund() - Select and import one of two redundant environments
|
||||||
*
|
*
|
||||||
|
130
include/getopt.h
Normal file
130
include/getopt.h
Normal 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 */
|
218
include/log.h
218
include/log.h
@ -17,56 +17,92 @@
|
|||||||
|
|
||||||
struct cmd_tbl;
|
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 {
|
enum log_level_t {
|
||||||
LOGL_EMERG = 0, /* U-Boot is unstable */
|
/** @LOGL_EMERG: U-Boot is unstable */
|
||||||
LOGL_ALERT, /* Action must be taken immediately */
|
LOGL_EMERG = 0,
|
||||||
LOGL_CRIT, /* Critical conditions */
|
/** @LOGL_ALERT: Action must be taken immediately */
|
||||||
LOGL_ERR, /* Error that prevents something from working */
|
LOGL_ALERT,
|
||||||
LOGL_WARNING, /* Warning may prevent optimial operation */
|
/** @LOGL_CRIT: Critical conditions */
|
||||||
LOGL_NOTICE, /* Normal but significant condition, printf() */
|
LOGL_CRIT,
|
||||||
LOGL_INFO, /* General information message */
|
/** @LOGL_ERR: Error that prevents something from working */
|
||||||
LOGL_DEBUG, /* Basic debug-level message */
|
LOGL_ERR,
|
||||||
LOGL_DEBUG_CONTENT, /* Debug message showing full message content */
|
/** @LOGL_WARNING: Warning may prevent optimial operation */
|
||||||
LOGL_DEBUG_IO, /* Debug message showing hardware I/O access */
|
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_COUNT,
|
||||||
|
/** @LOGL_NONE: Used to indicate that there is no valid log level */
|
||||||
LOGL_NONE,
|
LOGL_NONE,
|
||||||
|
|
||||||
LOGL_LEVEL_MASK = 0xf, /* Mask for valid log levels */
|
/** @LOGL_LEVEL_MASK: Mask for valid log levels */
|
||||||
LOGL_FORCE_DEBUG = 0x10, /* Mask to force output due to LOG_DEBUG */
|
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_FIRST = LOGL_EMERG,
|
||||||
|
/** @LOGL_MAX: The last, least-important log level */
|
||||||
LOGL_MAX = LOGL_DEBUG_IO,
|
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 log_category_t - Log categories supported.
|
||||||
* enum uclass_id) but there are also some more generic categories.
|
*
|
||||||
|
* 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.
|
* Remember to update log_cat_name[] after adding a new category.
|
||||||
*/
|
*/
|
||||||
enum log_category_t {
|
enum log_category_t {
|
||||||
|
/** @LOGC_FIRST: First log category */
|
||||||
LOGC_FIRST = 0, /* First part mirrors UCLASS_... */
|
LOGC_FIRST = 0, /* First part mirrors UCLASS_... */
|
||||||
|
|
||||||
|
/** @LOGC_NONE: Default log category */
|
||||||
LOGC_NONE = UCLASS_COUNT, /* First number is after all uclasses */
|
LOGC_NONE = UCLASS_COUNT, /* First number is after all uclasses */
|
||||||
LOGC_ARCH, /* Related to arch-specific code */
|
/** @LOGC_ARCH: Related to arch-specific code */
|
||||||
LOGC_BOARD, /* Related to board-specific code */
|
LOGC_ARCH,
|
||||||
LOGC_CORE, /* Related to core features (non-driver-model) */
|
/** @LOGC_BOARD: Related to board-specific code */
|
||||||
LOGC_DM, /* Core driver-model */
|
LOGC_BOARD,
|
||||||
LOGC_DT, /* Device-tree */
|
/** @LOGC_CORE: Related to core features (non-driver-model) */
|
||||||
LOGC_EFI, /* EFI implementation */
|
LOGC_CORE,
|
||||||
LOGC_ALLOC, /* Memory allocation */
|
/** @LOGC_DM: Core driver-model */
|
||||||
LOGC_SANDBOX, /* Related to the sandbox board */
|
LOGC_DM,
|
||||||
LOGC_BLOBLIST, /* Bloblist */
|
/** @LOGC_DT: Device-tree */
|
||||||
LOGC_DEVRES, /* Device resources (devres_... functions) */
|
LOGC_DT,
|
||||||
/* Advanced Configuration and Power Interface (ACPI) */
|
/** @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_ACPI,
|
||||||
|
|
||||||
LOGC_COUNT, /* Number of log categories */
|
/** @LOGC_COUNT: Number of log categories */
|
||||||
LOGC_END, /* Sentinel value for a list of log categories */
|
LOGC_COUNT,
|
||||||
LOGC_CONT = -1, /* Use same category as in previous call */
|
/** @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 */
|
/* 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
|
* @func: Function where log record was generated
|
||||||
* @fmt: printf() format string for log record
|
* @fmt: printf() format string for log record
|
||||||
* @...: Optional parameters, according to the format string @fmt
|
* @...: 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 _log(enum log_category_t cat, enum log_level_t level, const char *file,
|
||||||
int line, const char *func, const char *fmt, ...)
|
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
|
* full pathname as it may be huge. Only use this when the user should be
|
||||||
* warning, similar to BUG_ON() in linux.
|
* 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) \
|
#define assert_noisy(x) \
|
||||||
({ bool _val = (x); \
|
({ bool _val = (x); \
|
||||||
@ -324,8 +360,9 @@ enum log_device_flags {
|
|||||||
*/
|
*/
|
||||||
struct log_driver {
|
struct log_driver {
|
||||||
const char *name;
|
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
|
* Called by the log system to pass a log record to a particular driver
|
||||||
* for processing. The filter is checked before calling this function.
|
* for processing. The filter is checked before calling this function.
|
||||||
@ -361,21 +398,32 @@ enum {
|
|||||||
LOGF_MAX_CATEGORIES = 5, /* maximum categories per filter */
|
LOGF_MAX_CATEGORIES = 5, /* maximum categories per filter */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enum log_filter_flags - Flags which modify a filter
|
||||||
|
*/
|
||||||
enum log_filter_flags {
|
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
|
* 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
|
* @filter_num: Sequence number of this filter. This is returned when adding a
|
||||||
* new filter, and must be provided when removing a previously added
|
* new filter, and must be provided when removing a previously added
|
||||||
* filter.
|
* filter.
|
||||||
* @flags: Flags for this filter (LOGFF_...)
|
* @flags: Flags for this filter (``LOGFF_...``)
|
||||||
* @cat_list: List of categories to allow (terminated by LOGC_none). If empty
|
* @cat_list: List of categories to allow (terminated by %LOGC_END). If empty
|
||||||
* then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries
|
* then all categories are permitted. Up to %LOGF_MAX_CATEGORIES entries
|
||||||
* can be provided
|
* 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
|
* @file_list: List of files to allow, separated by comma. If NULL then all
|
||||||
* files are permitted
|
* files are permitted
|
||||||
* @sibling_node: Next filter in the list of filters for this log device
|
* @sibling_node: Next filter in the list of filters for this log device
|
||||||
@ -384,7 +432,7 @@ struct log_filter {
|
|||||||
int filter_num;
|
int filter_num;
|
||||||
int flags;
|
int flags;
|
||||||
enum log_category_t cat_list[LOGF_MAX_CATEGORIES];
|
enum log_category_t cat_list[LOGF_MAX_CATEGORIES];
|
||||||
enum log_level_t max_level;
|
enum log_level_t level;
|
||||||
const char *file_list;
|
const char *file_list;
|
||||||
struct list_head sibling_node;
|
struct list_head sibling_node;
|
||||||
};
|
};
|
||||||
@ -400,8 +448,9 @@ struct log_filter {
|
|||||||
* log_get_cat_name() - Get the name of a category
|
* log_get_cat_name() - Get the name of a category
|
||||||
*
|
*
|
||||||
* @cat: Category to look up
|
* @cat: Category to look up
|
||||||
* @return category name (which may be a uclass driver name) if found, or
|
* Return: category name (which may be a uclass driver name) if found, or
|
||||||
* "<invalid>" if invalid, or "<missing>" if not found
|
* "<invalid>" if invalid, or "<missing>" if not found. All error
|
||||||
|
* responses begin with '<'.
|
||||||
*/
|
*/
|
||||||
const char *log_get_cat_name(enum log_category_t cat);
|
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
|
* log_get_cat_by_name() - Look up a category by name
|
||||||
*
|
*
|
||||||
* @name: Name to look up
|
* @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);
|
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
|
* log_get_level_name() - Get the name of a log level
|
||||||
*
|
*
|
||||||
* @level: Log level to look up
|
* @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);
|
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
|
* log_get_level_by_name() - Look up a log level by name
|
||||||
*
|
*
|
||||||
* @name: Name to look up
|
* @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);
|
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 */
|
/* Log format flags (bit numbers) for gd->log_fmt. See log_fmt_chars */
|
||||||
enum log_fmt {
|
enum log_fmt {
|
||||||
LOGF_CAT = 0,
|
LOGF_CAT = 0,
|
||||||
@ -445,22 +525,49 @@ enum log_fmt {
|
|||||||
/* Handle the 'log test' command */
|
/* Handle the 'log test' command */
|
||||||
int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
|
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
|
* 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
|
* @drv_name: Driver name to add the filter to (since each driver only has a
|
||||||
* single device)
|
* single device)
|
||||||
* @cat_list: List of categories to allow (terminated by LOGC_none). If empty
|
* @cat_list: List of categories to allow (terminated by %LOGC_END). If empty
|
||||||
* then all categories are permitted. Up to LOGF_MAX_CATEGORIES entries
|
* then all categories are permitted. Up to %LOGF_MAX_CATEGORIES entries
|
||||||
* can be provided
|
* can be provided
|
||||||
* @max_level: Maximum log level to allow
|
* @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
|
* files are permitted
|
||||||
* @return the sequence number of the new filter (>=0) if the filter was added,
|
* Return:
|
||||||
* or a -ve value on error
|
* 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[],
|
static inline int log_add_filter(const char *drv_name,
|
||||||
enum log_level_t max_level, const char *file_list);
|
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
|
* 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
|
* @drv_name: Driver name to remove the filter from (since each driver only has
|
||||||
* a single device)
|
* a single device)
|
||||||
* @filter_num: Filter number to remove (as returned by log_add_filter())
|
* @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
|
* Return:
|
||||||
* filter number was not found
|
* 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);
|
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
|
* 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);
|
int log_init(void);
|
||||||
#else
|
#else
|
||||||
@ -504,7 +612,7 @@ static inline int log_init(void)
|
|||||||
* log_get_default_format() - get default log format
|
* log_get_default_format() - get default log format
|
||||||
*
|
*
|
||||||
* The default log format is configurable via
|
* 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
|
* Return: default log format
|
||||||
*/
|
*/
|
||||||
|
@ -10,7 +10,10 @@
|
|||||||
|
|
||||||
#include <test/test.h>
|
#include <test/test.h>
|
||||||
|
|
||||||
|
#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
|
||||||
|
|
||||||
/* Declare a new logging test */
|
/* Declare a new logging test */
|
||||||
#define LOG_TEST(_name) UNIT_TEST(_name, 0, log_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__ */
|
#endif /* __TEST_LOG_H__ */
|
||||||
|
@ -550,6 +550,11 @@ config SPL_HEXDUMP
|
|||||||
This enables functions for printing dumps of binary data in
|
This enables functions for printing dumps of binary data in
|
||||||
SPL.
|
SPL.
|
||||||
|
|
||||||
|
config GETOPT
|
||||||
|
bool "Enable getopt"
|
||||||
|
help
|
||||||
|
This enables functions for parsing command-line options.
|
||||||
|
|
||||||
config OF_LIBFDT
|
config OF_LIBFDT
|
||||||
bool "Enable the FDT library"
|
bool "Enable the FDT library"
|
||||||
default y if OF_CONTROL
|
default y if OF_CONTROL
|
||||||
|
@ -106,6 +106,7 @@ obj-y += string.o
|
|||||||
obj-y += tables_csum.o
|
obj-y += tables_csum.o
|
||||||
obj-y += time.o
|
obj-y += time.o
|
||||||
obj-y += hexdump.o
|
obj-y += hexdump.o
|
||||||
|
obj-$(CONFIG_GETOPT) += getopt.o
|
||||||
obj-$(CONFIG_TRACE) += trace.o
|
obj-$(CONFIG_TRACE) += trace.o
|
||||||
obj-$(CONFIG_LIB_UUID) += uuid.o
|
obj-$(CONFIG_LIB_UUID) += uuid.o
|
||||||
obj-$(CONFIG_LIB_RAND) += rand.o
|
obj-$(CONFIG_LIB_RAND) += rand.o
|
||||||
|
125
lib/getopt.c
Normal file
125
lib/getopt.c
Normal 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;
|
||||||
|
}
|
@ -14,3 +14,4 @@ obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
|
|||||||
obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
|
obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
|
||||||
obj-$(CONFIG_UT_LIB_RSA) += rsa.o
|
obj-$(CONFIG_UT_LIB_RSA) += rsa.o
|
||||||
obj-$(CONFIG_AES) += test_aes.o
|
obj-$(CONFIG_AES) += test_aes.o
|
||||||
|
obj-$(CONFIG_GETOPT) += getopt.o
|
||||||
|
123
test/lib/getopt.c
Normal file
123
test/lib/getopt.c
Normal 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);
|
@ -3,6 +3,7 @@
|
|||||||
# Copyright (c) 2017 Google, Inc
|
# Copyright (c) 2017 Google, Inc
|
||||||
|
|
||||||
obj-$(CONFIG_LOG_TEST) += log_test.o
|
obj-$(CONFIG_LOG_TEST) += log_test.o
|
||||||
|
obj-$(CONFIG_CMD_LOG) += log_filter.o
|
||||||
|
|
||||||
ifdef CONFIG_UT_LOG
|
ifdef CONFIG_UT_LOG
|
||||||
|
|
||||||
|
108
test/log/log_filter.c
Normal file
108
test/log/log_filter.c
Normal 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);
|
@ -9,12 +9,17 @@
|
|||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <command.h>
|
#include <command.h>
|
||||||
#include <log.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 */
|
/* 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;
|
int i;
|
||||||
|
|
||||||
|
gd->log_fmt = LOGF_TEST;
|
||||||
debug("debug\n");
|
debug("debug\n");
|
||||||
for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
|
for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
|
||||||
log(cat, i, "log %d\n", i);
|
log(cat, i, "log %d\n", i);
|
||||||
@ -22,168 +27,217 @@ static int log_run(enum uclass_id cat, const char *file)
|
|||||||
i);
|
i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gd->log_fmt = log_get_default_format();
|
||||||
return 0;
|
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)
|
||||||
int ret;
|
#define log_run() do_log_run(UCLASS_SPI, "file")
|
||||||
|
|
||||||
printf("test %d\n", testnum);
|
#define EXPECT_LOG BIT(0)
|
||||||
switch (testnum) {
|
#define EXPECT_DIRECT BIT(1)
|
||||||
case 0: {
|
#define EXPECT_EXTRA BIT(2)
|
||||||
/* Check a category filter using the first category */
|
|
||||||
|
static int do_check_log_entries(struct unit_test_state *uts, int flags, int min,
|
||||||
|
int max)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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[] = {
|
enum log_category_t cat_list[] = {
|
||||||
log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
|
log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
|
||||||
LOGC_NONE, LOGC_END
|
LOGC_NONE, LOGC_END
|
||||||
};
|
};
|
||||||
|
int filt;
|
||||||
|
|
||||||
ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
|
filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
|
||||||
if (ret < 0)
|
ut_assert(filt >= 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);
|
ut_assertok(console_record_reset_enable());
|
||||||
if (ret < 0)
|
log_run_cat(UCLASS_MMC);
|
||||||
return ret;
|
check_log_entries_extra();
|
||||||
log_run(UCLASS_SPI, "file");
|
|
||||||
ret = log_remove_filter("console", ret);
|
ut_assertok(console_record_reset_enable());
|
||||||
if (ret < 0)
|
log_run_cat(UCLASS_SPI);
|
||||||
return ret;
|
check_log_entries_extra();
|
||||||
break;
|
|
||||||
}
|
ut_assertok(log_remove_filter("console", filt));
|
||||||
case 2: {
|
return 0;
|
||||||
/* Check a category filter that should block log entries */
|
}
|
||||||
|
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[] = {
|
enum log_category_t cat_list[] = {
|
||||||
log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
|
log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
|
||||||
};
|
};
|
||||||
|
int filt;
|
||||||
|
|
||||||
ret = log_add_filter("console", cat_list, LOGL_MAX, NULL);
|
filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
|
||||||
if (ret < 0)
|
ut_assert(filt >= 0);
|
||||||
return ret;
|
|
||||||
log_run(UCLASS_SPI, "file");
|
ut_assertok(console_record_reset_enable());
|
||||||
ret = log_remove_filter("console", ret);
|
log_run_cat(UCLASS_SPI);
|
||||||
if (ret < 0)
|
check_log_entries_none();
|
||||||
return ret;
|
|
||||||
break;
|
ut_assertok(log_remove_filter("console", filt));
|
||||||
}
|
return 0;
|
||||||
case 3: {
|
}
|
||||||
/* Check a passing file filter */
|
LOG_TEST_FLAGS(log_test_cat_deny_implicit, UT_TESTF_CONSOLE_REC);
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX, "file");
|
|
||||||
if (ret < 0)
|
/* Check passing and failing file filters */
|
||||||
return ret;
|
int log_test_file(struct unit_test_state *uts)
|
||||||
log_run(UCLASS_SPI, "file");
|
{
|
||||||
ret = log_remove_filter("console", ret);
|
int filt;
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
filt = log_add_filter("console", NULL, LOGL_MAX, "file");
|
||||||
break;
|
ut_assert(filt >= 0);
|
||||||
}
|
|
||||||
case 4: {
|
ut_assertok(console_record_reset_enable());
|
||||||
/* Check a failing file filter */
|
log_run_file("file");
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX, "file");
|
check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA);
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
ut_assertok(console_record_reset_enable());
|
||||||
log_run(UCLASS_SPI, "file2");
|
log_run_file("file2");
|
||||||
ret = log_remove_filter("console", ret);
|
check_log_entries_none();
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
ut_assertok(log_remove_filter("console", filt));
|
||||||
break;
|
return 0;
|
||||||
}
|
}
|
||||||
case 5: {
|
LOG_TEST_FLAGS(log_test_file, UT_TESTF_CONSOLE_REC);
|
||||||
/* Check a passing file filter (second in list) */
|
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
|
/* Check a passing file filter (second in list) */
|
||||||
if (ret < 0)
|
int log_test_file_second(struct unit_test_state *uts)
|
||||||
return ret;
|
{
|
||||||
log_run(UCLASS_SPI, "file2");
|
int filt;
|
||||||
ret = log_remove_filter("console", ret);
|
|
||||||
if (ret < 0)
|
filt = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
|
||||||
return ret;
|
ut_assert(filt >= 0);
|
||||||
break;
|
|
||||||
}
|
ut_assertok(console_record_reset_enable());
|
||||||
case 6: {
|
log_run_file("file2");
|
||||||
/* Check a passing file filter */
|
check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA);
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX,
|
|
||||||
|
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");
|
"file,file2,log/log_test.c");
|
||||||
if (ret < 0)
|
ut_assert(filt >= 0);
|
||||||
return ret;
|
|
||||||
log_run(UCLASS_SPI, "file2");
|
ut_assertok(console_record_reset_enable());
|
||||||
ret = log_remove_filter("console", ret);
|
log_run_file("file2");
|
||||||
if (ret < 0)
|
check_log_entries_extra();
|
||||||
return ret;
|
|
||||||
break;
|
ut_assertok(log_remove_filter("console", filt));
|
||||||
}
|
return 0;
|
||||||
case 7: {
|
}
|
||||||
/* Check a log level filter */
|
LOG_TEST_FLAGS(log_test_file_mid, UT_TESTF_CONSOLE_REC);
|
||||||
ret = log_add_filter("console", NULL, LOGL_WARNING, NULL);
|
|
||||||
if (ret < 0)
|
/* Check a log level filter */
|
||||||
return ret;
|
int log_test_level(struct unit_test_state *uts)
|
||||||
log_run(UCLASS_SPI, "file");
|
{
|
||||||
ret = log_remove_filter("console", ret);
|
int filt;
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
filt = log_add_filter("console", NULL, LOGL_WARNING, NULL);
|
||||||
break;
|
ut_assert(filt >= 0);
|
||||||
}
|
|
||||||
case 8: {
|
ut_assertok(console_record_reset_enable());
|
||||||
/* Check two filters, one of which passes everything */
|
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;
|
int filt1, filt2;
|
||||||
|
|
||||||
ret = log_add_filter("console", NULL, LOGL_WARNING, NULL);
|
filt1 = log_add_filter("console", NULL, LOGL_WARNING, NULL);
|
||||||
if (ret < 0)
|
ut_assert(filt1 >= 0);
|
||||||
return ret;
|
filt2 = log_add_filter("console", NULL, LOGL_MAX, NULL);
|
||||||
filt1 = ret;
|
ut_assert(filt2 >= 0);
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX, NULL);
|
|
||||||
if (ret < 0)
|
ut_assertok(console_record_reset_enable());
|
||||||
return ret;
|
log_run();
|
||||||
filt2 = ret;
|
check_log_entries_extra();
|
||||||
log_run(UCLASS_SPI, "file");
|
|
||||||
ret = log_remove_filter("console", filt1);
|
ut_assertok(log_remove_filter("console", filt1));
|
||||||
if (ret < 0)
|
ut_assertok(log_remove_filter("console", filt2));
|
||||||
return ret;
|
return 0;
|
||||||
ret = log_remove_filter("console", filt2);
|
}
|
||||||
if (ret < 0)
|
LOG_TEST_FLAGS(log_test_double, UT_TESTF_CONSOLE_REC);
|
||||||
return ret;
|
|
||||||
break;
|
/* Check three filters, which together pass everything */
|
||||||
}
|
int log_test_triple(struct unit_test_state *uts)
|
||||||
case 9: {
|
{
|
||||||
/* Check three filters, which together pass everything */
|
|
||||||
int filt1, filt2, filt3;
|
int filt1, filt2, filt3;
|
||||||
|
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX, "file)");
|
filt1 = log_add_filter("console", NULL, LOGL_MAX, "file)");
|
||||||
if (ret < 0)
|
ut_assert(filt1 >= 0);
|
||||||
return ret;
|
filt2 = log_add_filter("console", NULL, LOGL_MAX, "file2");
|
||||||
filt1 = ret;
|
ut_assert(filt2 >= 0);
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX, "file2");
|
filt3 = log_add_filter("console", NULL, LOGL_MAX, "log/log_test.c");
|
||||||
if (ret < 0)
|
ut_assert(filt3 >= 0);
|
||||||
return ret;
|
|
||||||
filt2 = ret;
|
ut_assertok(console_record_reset_enable());
|
||||||
ret = log_add_filter("console", NULL, LOGL_MAX,
|
log_run_file("file2");
|
||||||
"log/log_test.c");
|
check_log_entries_extra();
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
ut_assertok(log_remove_filter("console", filt1));
|
||||||
filt3 = ret;
|
ut_assertok(log_remove_filter("console", filt2));
|
||||||
log_run(UCLASS_SPI, "file2");
|
ut_assertok(log_remove_filter("console", filt3));
|
||||||
ret = log_remove_filter("console", filt1);
|
return 0;
|
||||||
if (ret < 0)
|
}
|
||||||
return ret;
|
LOG_TEST_FLAGS(log_test_triple, UT_TESTF_CONSOLE_REC);
|
||||||
ret = log_remove_filter("console", filt2);
|
|
||||||
if (ret < 0)
|
int do_log_test_helpers(struct unit_test_state *uts)
|
||||||
return ret;
|
{
|
||||||
ret = log_remove_filter("console", filt3);
|
int i;
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
ut_assertok(console_record_reset_enable());
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 10: {
|
|
||||||
log_err("level %d\n", LOGL_EMERG);
|
log_err("level %d\n", LOGL_EMERG);
|
||||||
log_err("level %d\n", LOGL_ALERT);
|
log_err("level %d\n", LOGL_ALERT);
|
||||||
log_err("level %d\n", LOGL_CRIT);
|
log_err("level %d\n", LOGL_CRIT);
|
||||||
@ -194,31 +248,137 @@ static int log_test(int testnum)
|
|||||||
log_debug("level %d\n", LOGL_DEBUG);
|
log_debug("level %d\n", LOGL_DEBUG);
|
||||||
log_content("level %d\n", LOGL_DEBUG_CONTENT);
|
log_content("level %d\n", LOGL_DEBUG_CONTENT);
|
||||||
log_io("level %d\n", LOGL_DEBUG_IO);
|
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 = LOGL_EMERG; i <= _LOG_MAX_LEVEL; i++)
|
||||||
|
ut_assert_nextline("%s() level %d", __func__, i);
|
||||||
|
ut_assert_console_end();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
int log_test_helpers(struct unit_test_state *uts)
|
||||||
{
|
{
|
||||||
int testnum = 0;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (argc > 1)
|
gd->log_fmt = LOGF_TEST;
|
||||||
testnum = simple_strtoul(argv[1], NULL, 10);
|
ret = do_log_test_helpers(uts);
|
||||||
|
gd->log_fmt = log_get_default_format();
|
||||||
ret = log_test(testnum);
|
return ret;
|
||||||
if (ret)
|
|
||||||
printf("Test failure (err=%d)\n", ret);
|
|
||||||
|
|
||||||
return ret ? CMD_RET_FAILURE : 0;
|
|
||||||
}
|
}
|
||||||
|
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);
|
||||||
|
@ -8,8 +8,6 @@
|
|||||||
#ifndef __SYSLOG_TEST_H
|
#ifndef __SYSLOG_TEST_H
|
||||||
#define __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
|
* struct sb_log_env - private data for sandbox ethernet driver
|
||||||
*
|
*
|
||||||
|
@ -10,110 +10,6 @@ and checks that the output is correct.
|
|||||||
|
|
||||||
import pytest
|
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')
|
@pytest.mark.buildconfigspec('cmd_log')
|
||||||
def test_log_format(u_boot_console):
|
def test_log_format(u_boot_console):
|
||||||
"""Test the 'log format' and 'log rec' commands"""
|
"""Test the 'log format' and 'log rec' commands"""
|
||||||
|
Loading…
Reference in New Issue
Block a user