]> git.sur5r.net Git - u-boot/commitdiff
Merge git://git.denx.de/u-boot-dm
authorTom Rini <trini@konsulko.com>
Sun, 4 Feb 2018 13:30:31 +0000 (08:30 -0500)
committerTom Rini <trini@konsulko.com>
Sun, 4 Feb 2018 13:30:31 +0000 (08:30 -0500)
12 files changed:
cmd/log.c
common/Kconfig
common/log.c
common/log_console.c
configs/sandbox_defconfig
doc/README.log
drivers/core/uclass.c
include/asm-generic/global_data.h
include/dm/uclass.h
include/log.h
test/dm/core.c
test/py/tests/test_log.py

index abc523b497192cd84c41fea10ddbb575def68b5a..12bac0e03c54ede4409e48f7112df04f6bc2fbb0 100644 (file)
--- a/cmd/log.c
+++ b/cmd/log.c
@@ -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
 
index 21e067c8582035fa96899eaa868a791479f0ef5c..dcab69d3215a05f5919d1bf65dd16ba3ced8964a 100644 (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
index 45e46dd5209118fcffd0333c3a91bdbeb78bee6a..680a60f86e819888e2891085d9a8179f4052d8b3 100644 (file)
 #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;
 }
index 5af73bd8be4f2c8f5c3abc6a9d7ba25ba83ea12e..290273307836265facbed6b110e644939235cbd7 100644 (file)
 #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;
 }
index 7efb4ebf117b07e3e62526ae3bc9ce7b0fee68f2..41a2e34235dd02d1ba52a15ebd106b9eef5383d1 100644 (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
index f653fe7d79711945855a9c25b8e3ffa2ecf55898..dc9e2deec50602e4f8d53bbd00a3ba7b33a30792 100644 (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
 ---------
index f5e406792209686a897c7395dc80d616f082036f..1aedaa08f0c2ee4058d9ddf9054ee03db45937a0 100644 (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;
index fd8cd45b050e44f41273135066ab45742130958b..1de67e8e8f56d9aaa2deea47c1daf455375bb43a 100644 (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
index 709f661f200b27607bcff6701377fad73271637a..3a01abc239ea9d48d869361c871939634cf24fa3 100644 (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
  *
index 8083b64831d9208726777753d6ed6955173a3812..20dc5289c7140866e33cf0d73db16dd765557d96 100644 (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[]);
 
index 50ee41b9e24ec7cad54b92a0808334a7886c86fb..052bf8fffbff7d7bd321a65a74ee70d587dfde65 100644 (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);
index fa9a25e8dc0c6c254750513151fd57e3120511cc..76f9236412c47dbda6d33a189cf26a475196724a 100644 (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')