Merge git://git.denx.de/u-boot-dm

This commit is contained in:
Tom Rini 2018-02-04 08:30:31 -05:00
commit 211a3a23b9
12 changed files with 351 additions and 6 deletions

View file

@ -10,6 +10,8 @@
#include <dm.h>
#include <log.h>
static char log_fmt_chars[LOGF_COUNT] = "clFLfm";
static int do_log_level(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
@ -21,11 +23,85 @@ static int do_log_level(cmd_tbl_t *cmdtp, int flag, int argc,
return 0;
}
static int do_log_format(cmd_tbl_t *cmdtp, int flag, int argc,
char * const argv[])
{
int i;
if (argc > 1) {
const char *str = argv[1];
if (!strcmp(str, "default")) {
gd->log_fmt = LOGF_DEFAULT;
} else if (!strcmp(str, "all")) {
gd->log_fmt = LOGF_ALL;
} else {
gd->log_fmt = 0;
for (; *str; str++) {
char *ptr = strchr(log_fmt_chars, *str);
if (!ptr) {
printf("Invalid log char '%c'\n", *str);
return CMD_RET_FAILURE;
}
gd->log_fmt |= 1 << (ptr - log_fmt_chars);
}
}
} else {
printf("Log format: ");
for (i = 0; i < LOGF_COUNT; i++) {
if (gd->log_fmt & (1 << i))
printf("%c", log_fmt_chars[i]);
}
printf("\n");
}
return 0;
}
static int do_log_rec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
enum log_category_t cat;
enum log_level_t level;
const char *file;
uint line;
const char *func;
const char *msg;
char *end;
if (argc < 7)
return CMD_RET_USAGE;
cat = log_get_cat_by_name(argv[1]);
level = simple_strtoul(argv[2], &end, 10);
if (end == argv[2]) {
level = log_get_level_by_name(argv[2]);
if (level == LOGL_NONE) {
printf("Invalid log level '%s'\n", argv[2]);
return CMD_RET_USAGE;
}
}
if (level >= LOGL_MAX) {
printf("Invalid log level %u\n", level);
return CMD_RET_USAGE;
}
file = argv[3];
line = simple_strtoul(argv[4], NULL, 10);
func = argv[5];
msg = argv[6];
if (_log(cat, level, file, line, func, "%s\n", msg))
return CMD_RET_FAILURE;
return 0;
}
static cmd_tbl_t 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(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
@ -52,6 +128,12 @@ static char log_help_text[] =
#ifdef CONFIG_LOG_TEST
"log test - run log tests\n"
#endif
"log format <fmt> - set log output format. <fmt> is a string where\n"
"\teach letter indicates something that should be displayed:\n"
"\tc=category, l=level, F=file, L=line number, f=function, m=msg\n"
"\tor 'default', equivalent to 'fm', or 'all' for all\n"
"log rec <category> <level> <file> <line> <func> <message> - "
"output a log record"
;
#endif

View file

@ -504,6 +504,19 @@ config LOG_TEST
in various different ways to test that the logging system works
correctly with varoius settings.
config LOG_ERROR_RETURN
bool "Log all functions which return an error"
depends on LOG
help
When an error is returned in U-Boot it is sometimes difficult to
figure out the root cause. For eaxmple, reading from SPI flash may
fail due to a problem in the SPI controller or due to the flash part
not returning the expected information. This option changes
log_ret() to log any errors it sees. With this option disabled,
log_ret() is a nop.
You can add log_ret() to all functions which return an error code.
endmenu
config DEFAULT_FDT_FILE

View file

@ -10,9 +10,77 @@
#include <common.h>
#include <log.h>
#include <malloc.h>
#include <dm/uclass.h>
DECLARE_GLOBAL_DATA_PTR;
static const char *log_cat_name[LOGC_COUNT - LOGC_NONE] = {
"none",
"arch",
"board",
"core",
"driver-model",
"device-tree",
"efi",
};
static const char *log_level_name[LOGL_COUNT] = {
"EMERG",
"ALERT",
"CRIT",
"ERR",
"WARNING",
"NOTICE",
"INFO",
"DEBUG",
"CONTENT",
"IO",
};
const char *log_get_cat_name(enum log_category_t cat)
{
if (cat > LOGC_COUNT)
return "invalid";
if (cat >= LOGC_NONE)
return log_cat_name[cat - LOGC_NONE];
return uclass_get_name((enum uclass_id)cat);
}
enum log_category_t log_get_cat_by_name(const char *name)
{
enum uclass_id id;
int i;
for (i = LOGC_NONE; i < LOGC_COUNT; i++)
if (!strcmp(name, log_cat_name[i - LOGC_NONE]))
return i;
id = uclass_get_by_name(name);
if (id != UCLASS_INVALID)
return (enum log_category_t)id;
return LOGC_NONE;
}
const char *log_get_level_name(enum log_level_t level)
{
if (level >= LOGL_COUNT)
return "INVALID";
return log_level_name[level];
}
enum log_level_t log_get_level_by_name(const char *name)
{
int i;
for (i = 0; i < LOGL_COUNT; i++) {
if (!strcasecmp(log_level_name[i], name))
return i;
}
return LOGL_NONE;
}
static struct log_device *log_device_find_by_name(const char *drv_name)
{
struct log_device *ldev;
@ -240,6 +308,7 @@ int log_init(void)
}
gd->flags |= GD_FLG_LOG_READY;
gd->default_log_level = LOGL_INFO;
gd->log_fmt = LOGF_DEFAULT;
return 0;
}

View file

@ -10,9 +10,34 @@
#include <common.h>
#include <log.h>
DECLARE_GLOBAL_DATA_PTR;
static int log_console_emit(struct log_device *ldev, struct log_rec *rec)
{
puts(rec->msg);
int fmt = gd->log_fmt;
/*
* The output format is designed to give someone a fighting chance of
* figuring out which field is which:
* - level is in CAPS
* - cat is lower case and ends with comma
* - file normally has a .c extension and ends with a colon
* - line is integer and ends with a -
* - function is an identifier and ends with ()
* - message has a space before it unless it is on its own
*/
if (fmt & (1 << LOGF_LEVEL))
printf("%s.", log_get_level_name(rec->level));
if (fmt & (1 << LOGF_CAT))
printf("%s,", log_get_cat_name(rec->cat));
if (fmt & (1 << LOGF_FILE))
printf("%s:", rec->file);
if (fmt & (1 << LOGF_LINE))
printf("%d-", rec->line);
if (fmt & (1 << LOGF_FUNC))
printf("%s()", rec->func);
if (fmt & (1 << LOGF_MSG))
printf("%s%s", fmt != (1 << LOGF_MSG) ? " " : "", rec->msg);
return 0;
}

View file

@ -18,6 +18,7 @@ CONFIG_PRE_CONSOLE_BUFFER=y
CONFIG_PRE_CON_BUF_ADDR=0x100000
CONFIG_LOG=y
CONFIG_LOG_MAX_LEVEL=6
CONFIG_LOG_ERROR_RETURN=y
CONFIG_CMD_CPU=y
CONFIG_CMD_LICENSE=y
CONFIG_CMD_BOOTZ=y

View file

@ -51,6 +51,7 @@ The following main categories are defined:
LOGC_BOARD - Related to board-specific code
LOGC_CORE - Related to core driver-model support
LOGC_DT - Related to device tree control
LOGC_EFI - Related to EFI implementation
Enabling logging
@ -68,6 +69,19 @@ If CONFIG_LOG is not set, then no logging will be available.
The above have SPL versions also, e.g. CONFIG_SPL_MAX_LOG_LEVEL.
Log commands
------------
The 'log' command provides access to several features:
level - access the default log level
format - access the console log format
rec - output a log record
test - run tests
Type 'help log' for details.
Using DEBUG
-----------
@ -94,6 +108,20 @@ enabled or disabled independently:
console - goes to stdout
Log format
----------
You can control the log format using the 'log format' command. The basic
format is:
LEVEL.category,file.c:123-func() message
In the above, file.c:123 is the filename where the log record was generated and
func() is the function name. By default ('log format default') only the
function name and message are displayed on the console. You can control which
fields are present, but not the field order.
Filters
-------
@ -121,6 +149,14 @@ Also debug() and error() will generate log records - these use LOG_CATEGORY
as the category, so you should #define this right at the top of the source
file to ensure the category is correct.
You can also define CONFIG_LOG_ERROR_RETURN to enable the log_ret() macro. This
can be used whenever your function returns an error value:
return log_ret(uclass_first_device(UCLASS_MMC, &dev));
This will write a log record when an error code is detected (a value < 0). This
can make it easier to trace errors that are generated deep in the call stack.
Code size
---------

View file

@ -158,6 +158,20 @@ const char *uclass_get_name(enum uclass_id id)
return uc->uc_drv->name;
}
enum uclass_id uclass_get_by_name(const char *name)
{
int i;
for (i = 0; i < UCLASS_COUNT; i++) {
struct uclass_driver *uc_drv = lists_uclass_lookup(i);
if (uc_drv && !strcmp(uc_drv->name, name))
return i;
}
return UCLASS_INVALID;
}
int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
{
struct uclass *uc;

View file

@ -119,6 +119,7 @@ typedef struct global_data {
int log_drop_count; /* Number of dropped log messages */
int default_log_level; /* For devices with no filters */
struct list_head log_head; /* List of struct log_device */
int log_fmt; /* Mask containing log format info */
#endif
} gd_t;
#endif

View file

@ -127,6 +127,14 @@ int uclass_get(enum uclass_id key, struct uclass **ucp);
*/
const char *uclass_get_name(enum uclass_id id);
/**
* uclass_get_by_name() - Look up a uclass by its driver name
*
* @name: Name to look up
* @returns the associated uclass ID, or UCLASS_INVALID if not found
*/
enum uclass_id uclass_get_by_name(const char *name);
/**
* uclass_get_device() - Get a uclass device based on an ID and index
*

View file

@ -27,8 +27,10 @@ enum log_level_t {
LOGL_DEBUG_IO, /* Debug message showing hardware I/O access */
LOGL_COUNT,
LOGL_NONE,
LOGL_FIRST = LOGL_EMERG,
LOGL_MAX = LOGL_DEBUG,
LOGL_MAX = LOGL_DEBUG_IO,
};
/**
@ -42,7 +44,9 @@ enum log_category_t {
LOGC_ARCH,
LOGC_BOARD,
LOGC_CORE,
LOGC_DT,
LOGC_DM, /* Core driver-model */
LOGC_DT, /* Device-tree */
LOGL_EFI, /* EFI implementation */
LOGC_COUNT,
LOGC_END,
@ -156,6 +160,17 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
({ if (!(x) && _DEBUG) \
__assert_fail(#x, __FILE__, __LINE__, __func__); })
#ifdef CONFIG_LOG_ERROR_RETURN
#define log_ret(_ret) ({ \
int __ret = (_ret); \
if (__ret < 0) \
log(LOG_CATEGORY, LOGL_ERR, "returning err=%d\n", __ret); \
__ret; \
})
#else
#define log_ret(_ret) (_ret)
#endif
/**
* struct log_rec - a single log record
*
@ -256,6 +271,52 @@ struct log_filter {
#define LOG_DRIVER(_name) \
ll_entry_declare(struct log_driver, _name, log_driver)
/**
* log_get_cat_name() - Get the name of a category
*
* @cat: Category to look up
* @return category name (which may be a uclass driver name)
*/
const char *log_get_cat_name(enum log_category_t cat);
/**
* log_get_cat_by_name() - Look up a category by name
*
* @name: Name to look up
* @return category ID, or LOGC_NONE if not found
*/
enum log_category_t log_get_cat_by_name(const char *name);
/**
* log_get_level_name() - Get the name of a log level
*
* @level: Log level to look up
* @return log level name (in ALL CAPS)
*/
const char *log_get_level_name(enum log_level_t level);
/**
* log_get_level_by_name() - Look up a log level by name
*
* @name: Name to look up
* @return log level ID, or LOGL_NONE if not found
*/
enum log_level_t log_get_level_by_name(const char *name);
/* Log format flags (bit numbers) for gd->log_fmt. See log_fmt_chars */
enum log_fmt {
LOGF_CAT = 0,
LOGF_LEVEL,
LOGF_FILE,
LOGF_LINE,
LOGF_FUNC,
LOGF_MSG,
LOGF_COUNT,
LOGF_DEFAULT = (1 << LOGF_FUNC) | (1 << LOGF_MSG),
LOGF_ALL = 0x3f,
};
/* Handle the 'log test' command */
int do_log_test(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]);

View file

@ -862,3 +862,12 @@ static int dm_test_device_get_uclass_id(struct unit_test_state *uts)
return 0;
}
DM_TEST(dm_test_device_get_uclass_id, DM_TESTF_SCAN_PDATA);
static int dm_test_uclass_names(struct unit_test_state *uts)
{
ut_asserteq_str("test", uclass_get_name(UCLASS_TEST));
ut_asserteq(UCLASS_TEST, uclass_get_by_name("test"));
return 0;
}
DM_TEST(dm_test_uclass_names, DM_TESTF_SCAN_PDATA);

View file

@ -28,9 +28,9 @@ def test_log(u_boot_console):
"""
for i in range(max_level):
if mask & 1:
assert 'log %d' % i == lines.next()
assert 'log_run() log %d' % i == lines.next()
if mask & 3:
assert '_log %d' % i == lines.next()
assert 'func() _log %d' % i == lines.next()
def run_test(testnum):
"""Run a particular test number (the 'log test' command)
@ -40,7 +40,6 @@ def test_log(u_boot_console):
Returns:
iterator containing the lines output from the command
"""
with cons.log.section('basic'):
output = u_boot_console.run_command('log test %d' % testnum)
split = output.replace('\r', '').splitlines()
@ -99,3 +98,30 @@ def test_log(u_boot_console):
test7()
test8()
test9()
@pytest.mark.buildconfigspec('log')
def test_log_format(u_boot_console):
"""Test the 'log format' and 'log rec' commands"""
def run_with_format(fmt, expected_output):
"""Set up the log format and then write a log record
Args:
fmt: Format to use for 'log format'
expected_output: Expected output from the 'log rec' command
"""
output = cons.run_command('log format %s' % fmt)
assert output == ''
output = cons.run_command('log rec arch notice file.c 123 func msg')
assert output == expected_output
cons = u_boot_console
with cons.log.section('format'):
run_with_format('all', 'NOTICE.arch,file.c:123-func() msg')
output = cons.run_command('log format')
assert output == 'Log format: clFLfm'
run_with_format('fm', 'func() msg')
run_with_format('clfm', 'NOTICE.arch,func() msg')
run_with_format('FLfm', 'file.c:123-func() msg')
run_with_format('lm', 'NOTICE. msg')
run_with_format('m', 'msg')