Merge branch '2020-10-10-log-improvements'
authorTom Rini <trini@konsulko.com>
Sun, 11 Oct 2020 19:22:05 +0000 (15:22 -0400)
committerTom Rini <trini@konsulko.com>
Sun, 11 Oct 2020 19:22:05 +0000 (15:22 -0400)
- Assorted improvements to our log functionality.

15 files changed:
common/log.c
common/log_console.c
common/log_syslog.c
doc/README.log [deleted file]
include/log.h
include/net.h
lib/Kconfig
net/eth-uclass.c
net/net.c
test/log/Makefile
test/log/log_test.c
test/log/syslog_test.c
test/log/syslog_test.h [new file with mode: 0644]
test/log/syslog_test_ndebug.c [new file with mode: 0644]
test/py/tests/test_log.py

index 9a5f100..1b10f6f 100644 (file)
@@ -157,6 +157,9 @@ static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
 {
        struct log_filter *filt;
 
+       if (rec->force_debug)
+               return true;
+
        /* If there are no filters, filter on the default log level */
        if (list_empty(&ldev->filter_head)) {
                if (rec->level > gd->default_log_level)
@@ -204,7 +207,8 @@ static int log_dispatch(struct log_rec *rec)
        /* Emit message */
        processing_msg = 1;
        list_for_each_entry(ldev, &gd->log_head, sibling_node) {
-               if (log_passes_filters(ldev, rec))
+               if ((ldev->flags & LOGDF_ENABLE) &&
+                   log_passes_filters(ldev, rec))
                        ldev->drv->emit(ldev, rec);
        }
        processing_msg = 0;
@@ -219,7 +223,8 @@ int _log(enum log_category_t cat, enum log_level_t level, const char *file,
        va_list args;
 
        rec.cat = cat;
-       rec.level = level;
+       rec.level = level & LOGL_LEVEL_MASK;
+       rec.force_debug = level & LOGL_FORCE_DEBUG;
        rec.file = file;
        rec.line = line;
        rec.func = func;
@@ -303,6 +308,44 @@ int log_remove_filter(const char *drv_name, int filter_num)
        return -ENOENT;
 }
 
+/**
+ * log_find_device_by_drv() - Find a device by its driver
+ *
+ * @drv: Log driver
+ * @return Device associated with that driver, or NULL if not found
+ */
+static struct log_device *log_find_device_by_drv(struct log_driver *drv)
+{
+       struct log_device *ldev;
+
+       list_for_each_entry(ldev, &gd->log_head, sibling_node) {
+               if (ldev->drv == drv)
+                       return ldev;
+       }
+       /*
+        * It is quite hard to pass an invalid driver since passing an unknown
+        * LOG_GET_DRIVER(xxx) would normally produce a compilation error. But
+        * it is possible to pass NULL, for example, so this
+        */
+
+       return NULL;
+}
+
+int log_device_set_enable(struct log_driver *drv, bool enable)
+{
+       struct log_device *ldev;
+
+       ldev = log_find_device_by_drv(drv);
+       if (!ldev)
+               return -ENOENT;
+       if (enable)
+               ldev->flags |= LOGDF_ENABLE;
+       else
+               ldev->flags &= ~LOGDF_ENABLE;
+
+       return 0;
+}
+
 int log_init(void)
 {
        struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
@@ -325,6 +368,7 @@ int log_init(void)
                }
                INIT_LIST_HEAD(&ldev->filter_head);
                ldev->drv = drv;
+               ldev->flags = drv->flags;
                list_add_tail(&ldev->sibling_node,
                              (struct list_head *)&gd->log_head);
                drv++;
index bb3f846..8776fd4 100644 (file)
@@ -44,4 +44,5 @@ static int log_console_emit(struct log_device *ldev, struct log_rec *rec)
 LOG_DRIVER(console) = {
        .name   = "console",
        .emit   = log_console_emit,
+       .flags  = LOGDF_ENABLE,
 };
index 2ae703f..4eb0915 100644 (file)
@@ -39,7 +39,9 @@ static int log_syslog_emit(struct log_device *ldev, struct log_rec *rec)
        char *log_hostname;
 
        /* Setup packet buffers */
-       net_init();
+       ret = net_init();
+       if (ret)
+               return ret;
        /* Disable hardware and put it into the reset state */
        eth_halt();
        /* Set current device according to environment variables */
diff --git a/doc/README.log b/doc/README.log
deleted file mode 100644 (file)
index ba83882..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-Logging in U-Boot
-=================
-
-Introduction
-------------
-
-U-Boot's internal operation involves many different steps and actions. From
-setting up the board to displaying a start-up screen to loading an Operating
-System, there are many component parts each with many actions.
-
-Most of the time this internal detail is not useful. Displaying it on the
-console would delay booting (U-Boot's primary purpose) and confuse users.
-
-But for digging into what is happening in a particular area, or for debugging
-a problem it is often useful to see what U-Boot is doing in more detail than
-is visible from the basic console output.
-
-U-Boot's logging feature aims to satisfy this goal for both users and
-developers.
-
-
-Logging levels
---------------
-
-There are a number logging levels available, in increasing order of verbosity:
-
-   LOGL_EMERG  - Printed before U-Boot halts
-   LOGL_ALERT  - Indicates action must be taken immediate or U-Boot will crash
-   LOGL_CRIT   - Indicates a critical error that will cause boot failure
-   LOGL_ERR    - Indicates an error that may cause boot failure
-   LOGL_WARNING        - Warning about an unexpected condition
-   LOGL_NOTE   - Important information about progress
-   LOGL_INFO   - Information about normal boot progress
-   LOGL_DEBUG  - Debug information (useful for debugging a driver or subsystem)
-   LOGL_DEBUG_CONTENT  - Debug message showing full message content
-   LOGL_DEBUG_IO       - Debug message showing hardware I/O access
-
-
-Logging category
-----------------
-
-Logging can come from a wide variety of places within U-Boot. Each log message
-has a category which is intended to allow messages to be filtered according to
-their source.
-
-The following main categories are defined:
-
-   LOGC_NONE   - Unknown category (e.g. a debug() statement)
-   UCLASS_...  - Related to a particular uclass (e.g. UCLASS_USB)
-   LOGC_ARCH   - Related to architecture-specific code
-   LOGC_BOARD  - Related to board-specific code
-   LOGC_CORE   - Related to core driver-model support
-   LOGC_DT     - Related to device tree control
-   LOGC_EFI    - Related to EFI implementation
-
-
-Enabling logging
-----------------
-
-The following options are used to enable logging at compile time:
-
-   CONFIG_LOG          - Enables the logging system
-   CONFIG_LOG_MAX_LEVEL - Max log level to build (anything higher is compiled
-                               out)
-   CONFIG_LOG_CONSOLE  - Enable writing log records to the console
-
-If CONFIG_LOG is not set, then no logging will be available.
-
-The above have SPL and TPL versions also, e.g. CONFIG_SPL_LOG_MAX_LEVEL and
-CONFIG_TPL_LOG_MAX_LEVEL.
-
-
-Temporary logging within a single file
---------------------------------------
-
-Sometimes it is useful to turn on logging just in one file. You can use this:
-
-   #define LOG_DEBUG
-
-to enable building in of all logging statements in a single file. Put it at
-the top of the file, before any #includes.
-
-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 (LOGL_DEBUG) or more. Otherwise
-debug output is suppressed and will not be generated.
-
-
-Convenience functions
----------------------
-
-A number of convenience functions are available to shorten the code needed
-for logging:
-
-       log_err(_fmt...)
-       log_warning(_fmt...)
-       log_notice(_fmt...)
-       log_info(_fmt...)
-       log_debug(_fmt...)
-       log_content(_fmt...)
-       log_io(_fmt...)
-
-With these the log level is implicit in the name. The category is set by
-LOG_CATEGORY, which you can only define once per file, above all #includes:
-
-       #define LOG_CATEGORY LOGC_ALLOC
-
-or
-
-       #define LOG_CATEGORY UCLASS_SPI
-
-Remember that all uclasses IDs are log categories too.
-
-
-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
------------
-
-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
---------------------
-
-If logging information goes nowhere then it serves no purpose. U-Boot provides
-several possible determinations for logging information, all of which can be
-enabled or disabled independently:
-
-   console - goes to stdout
-   syslog - broadcast RFC 3164 messages to syslog servers on UDP port 514
-
-The syslog driver sends the value of environmental variable 'log_hostname' as
-HOSTNAME if available.
-
-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
--------
-
-Filters are attached to log drivers to control what those drivers emit. Only
-records that pass through the filter make it to the driver.
-
-Filters can be based on several criteria:
-
-   - 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_LOG_MAX_LEVEL.
-
-
-Logging statements
-------------------
-
-The main logging function is:
-
-   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:
-
-   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
----------
-
-Code size impact depends largely on what is enabled. The following numbers are
-generated by 'buildman -S' for snow, which is a Thumb-2 board (all units in
-bytes):
-
-This series: adds bss +20.0 data +4.0 rodata +4.0 text +44.0
-CONFIG_LOG: bss -52.0 data +92.0 rodata -635.0 text +1048.0
-CONFIG_LOG_MAX_LEVEL=7: bss +188.0 data +4.0 rodata +49183.0 text +98124.0
-
-The last option turns every debug() statement into a logging call, which
-bloats the code hugely. The advantage is that it is then possible to enable
-all logging within U-Boot.
-
-
-To Do
------
-
-There are lots of useful additions that could be made. None of the below is
-implemented! If you do one, please add a test in test/py/tests/test_log.py
-
-Convenience functions to support setting the category:
-
-   log_arch(level, format_string, ...) - category LOGC_ARCH
-   log_board(level, format_string, ...) - category LOGC_BOARD
-   log_core(level, format_string, ...) - category LOGC_CORE
-   log_dt(level, format_string, ...) - category LOGC_DT
-
-More logging destinations:
-
-   device - goes to a device (e.g. serial)
-   buffer - recorded in a memory buffer
-
-Convert debug() statements in the code to log() statements
-
-Support making printf() emit log statements a L_INFO level
-
-Convert error() statements in the code to log() statements
-
-Figure out what to do with BUG(), BUG_ON() and warn_non_spl()
-
-Figure out what to do with assert()
-
-Add a way to browse log records
-
-Add a way to record log records for browsing using an external tool
-
-Add commands to add and remove filters
-
-Add commands to add and remove log devices
-
-Allow sharing of printf format strings in log records to reduce storage size
-for large numbers of log records
-
-Add a command-line option to sandbox to set the default logging level
-
-Convert core driver model code to use logging
-
-Convert uclasses to use logging with the correct category
-
-Consider making log() calls emit an automatic newline, perhaps with a logn()
-   function to avoid that
-
-Passing log records through to linux (e.g. via device tree /chosen)
-
-Provide a command to access the number of log records generated, and the
-number dropped due to them being generated before the log system was ready.
-
-Add a printf() format string pragma so that log statements are checked properly
-
-Enhance the log console driver to show level / category / file / line
-information
-
-Add a command to add new log records and delete existing records.
-
-Provide additional log() functions - e.g. logc() to specify the category
-
---
-Simon Glass <sjg@chromium.org>
-15-Sep-17
index 2859ce1..4acc087 100644 (file)
@@ -33,6 +33,9 @@ enum log_level_t {
        LOGL_COUNT,
        LOGL_NONE,
 
+       LOGL_LEVEL_MASK = 0xf,  /* Mask for valid log levels */
+       LOGL_FORCE_DEBUG = 0x10, /* Mask to force output due to LOG_DEBUG */
+
        LOGL_FIRST = LOGL_EMERG,
        LOGL_MAX = LOGL_DEBUG_IO,
 };
@@ -133,7 +136,7 @@ static inline int _log_nop(enum log_category_t cat, enum log_level_t level,
 
 #if CONFIG_IS_ENABLED(LOG)
 #ifdef LOG_DEBUG
-#define _LOG_DEBUG     1
+#define _LOG_DEBUG     LOGL_FORCE_DEBUG
 #else
 #define _LOG_DEBUG     0
 #endif
@@ -141,9 +144,11 @@ static inline int _log_nop(enum log_category_t cat, enum log_level_t level,
 /* Emit a log record if the level is less that the maximum */
 #define log(_cat, _level, _fmt, _args...) ({ \
        int _l = _level; \
-       if (CONFIG_IS_ENABLED(LOG) && (_l <= _LOG_MAX_LEVEL || _LOG_DEBUG)) \
-               _log((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \
-                     __func__, \
+       if (CONFIG_IS_ENABLED(LOG) && \
+           (_LOG_DEBUG != 0 || _l <= _LOG_MAX_LEVEL)) \
+               _log((enum log_category_t)(_cat), \
+                    (enum log_level_t)(_l | _LOG_DEBUG), __FILE__, \
+                    __LINE__, __func__, \
                      pr_fmt(_fmt), ##_args); \
        })
 #else
@@ -279,8 +284,12 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
  * Memebers marked as 'allocated' are allocated (e.g. via strdup()) by the log
  * system.
  *
+ * TODO(sjg@chromium.org): Compress this struct down a bit to reduce space, e.g.
+ * a single u32 for cat, level, line and force_debug
+ *
  * @cat: Category, representing a uclass or part of U-Boot
  * @level: Severity level, less severe is higher
+ * @force_debug: Force output of debug
  * @file: Name of file where the log record was generated (not allocated)
  * @line: Line number where the log record was generated
  * @func: Function where the log record was generated (not allocated)
@@ -289,6 +298,7 @@ void __assert_fail(const char *assertion, const char *file, unsigned int line,
 struct log_rec {
        enum log_category_t cat;
        enum log_level_t level;
+       bool force_debug;
        const char *file;
        int line;
        const char *func;
@@ -297,10 +307,16 @@ struct log_rec {
 
 struct log_device;
 
+enum log_device_flags {
+       LOGDF_ENABLE            = BIT(0),       /* Device is enabled */
+};
+
 /**
  * struct log_driver - a driver which accepts and processes log records
  *
  * @name: Name of driver
+ * @emit: Method to call to emit a log record via this device
+ * @flags: Initial value for flags (use LOGDF_ENABLE to enable on start-up)
  */
 struct log_driver {
        const char *name;
@@ -311,6 +327,7 @@ struct log_driver {
         * for processing. The filter is checked before calling this function.
         */
        int (*emit)(struct log_device *ldev, struct log_rec *rec);
+       unsigned short flags;
 };
 
 /**
@@ -323,12 +340,14 @@ struct log_driver {
  * @next_filter_num: Seqence number of next filter filter added (0=no filters
  *     yet). This increments with each new filter on the device, but never
  *     decrements
+ * @flags: Flags for this filter (enum log_device_flags)
  * @drv: Pointer to driver for this device
  * @filter_head: List of filters for this device
  * @sibling_node: Next device in the list of all devices
  */
 struct log_device {
-       int next_filter_num;
+       unsigned short next_filter_num;
+       unsigned short flags;
        struct log_driver *drv;
        struct list_head filter_head;
        struct list_head sibling_node;
@@ -369,6 +388,10 @@ struct log_filter {
 #define LOG_DRIVER(_name) \
        ll_entry_declare(struct log_driver, _name, log_driver)
 
+/* Get a pointer to a given driver */
+#define LOG_GET_DRIVER(__name)                                         \
+       ll_entry_get(struct log_driver, __name, log_driver)
+
 /**
  * log_get_cat_name() - Get the name of a category
  *
@@ -446,6 +469,19 @@ int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
  */
 int log_remove_filter(const char *drv_name, int filter_num);
 
+/**
+ * log_device_set_enable() - Enable or disable a log device
+ *
+ * Devices are referenced by their driver, so use LOG_GET_DRIVER(name) to pass
+ * the driver to this function. For example if the driver is declared with
+ * LOG_DRIVER(wibble) then pass LOG_GET_DRIVER(wibble) here.
+ *
+ * @drv: Driver of device to enable
+ * @enable: true to enable, false to disable
+ * @return 0 if OK, -ENOENT if the driver was not found
+ */
+int log_device_set_enable(struct log_driver *drv, bool enable);
+
 #if CONFIG_IS_ENABLED(LOG)
 /**
  * log_init() - Set up the log system ready for use
index 2191071..778acf7 100644 (file)
@@ -593,7 +593,7 @@ extern int net_ntp_time_offset;                     /* offset time from UTC */
 #endif
 
 /* Initialize the network adapter */
-void net_init(void);
+int net_init(void);
 int net_loop(enum proto_t);
 
 /* Load failed.         Start again. */
index 8efb154..37aae73 100644 (file)
@@ -542,6 +542,14 @@ config HEXDUMP
        help
          This enables functions for printing dumps of binary data.
 
+config SPL_HEXDUMP
+       bool "Enable hexdump in SPL"
+       depends on HEXDUMP
+       default y
+       help
+         This enables functions for printing dumps of binary data in
+         SPL.
+
 config OF_LIBFDT
        bool "Enable the FDT library"
        default y if OF_CONTROL
index 396418e..4424d59 100644 (file)
@@ -75,6 +75,9 @@ struct udevice *eth_get_dev(void)
        struct eth_uclass_priv *uc_priv;
 
        uc_priv = eth_get_uclass_priv();
+       if (!uc_priv)
+               return NULL;
+
        if (!uc_priv->current)
                eth_errno = uclass_first_device(UCLASS_ETH,
                                    &uc_priv->current);
index 197fde3..ad7e3b3 100644 (file)
--- a/net/net.c
+++ b/net/net.c
@@ -338,12 +338,19 @@ void net_auto_load(void)
        tftp_start(TFTPGET);
 }
 
-static void net_init_loop(void)
+static int net_init_loop(void)
 {
        if (eth_get_dev())
                memcpy(net_ethaddr, eth_get_ethaddr(), 6);
+       else
+               /*
+                * Not ideal, but there's no way to get the actual error, and I
+                * don't feel like fixing all the users of eth_get_dev to deal
+                * with errors.
+                */
+               return -ENONET;
 
-       return;
+       return 0;
 }
 
 static void net_clear_handlers(void)
@@ -358,7 +365,7 @@ static void net_cleanup_loop(void)
        net_clear_handlers();
 }
 
-void net_init(void)
+int net_init(void)
 {
        static int first_call = 1;
 
@@ -381,7 +388,7 @@ void net_init(void)
                first_call = 0;
        }
 
-       net_init_loop();
+       return net_init_loop();
 }
 
 /**********************************************************************/
index 4c92550..52e2f7b 100644 (file)
@@ -10,6 +10,7 @@ obj-y += test-main.o
 
 ifdef CONFIG_SANDBOX
 obj-$(CONFIG_LOG_SYSLOG) += syslog_test.o
+obj-$(CONFIG_LOG_SYSLOG) += syslog_test_ndebug.o
 endif
 
 ifndef CONFIG_LOG
index 4245372..6a60ff6 100644 (file)
@@ -196,12 +196,18 @@ static int log_test(int testnum)
                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;
        }
 
        return 0;
 }
 
-#ifdef CONFIG_LOG_TEST
 int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 {
        int testnum = 0;
@@ -216,4 +222,3 @@ int do_log_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 
        return ret ? CMD_RET_FAILURE : 0;
 }
-#endif
index 120a8b2..febaca6 100644 (file)
 #include <test/suites.h>
 #include <test/ut.h>
 #include <asm/eth.h>
+#include <syslog_test.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
-#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
-
-/**
- * struct sb_log_env - private data for sandbox ethernet driver
- *
- * This structure is used for the private data of the sandbox ethernet
- * driver.
- *
- * @expected:  string expected to be written by the syslog driver
- * @uts:       unit test state
- */
-struct sb_log_env {
-       const char *expected;
-       struct unit_test_state *uts;
-};
-
-/**
- * sb_log_tx_handler() - transmit callback function
- *
- * This callback function is invoked when a network package is sent using the
- * sandbox Ethernet driver. The private data of the driver holds a sb_log_env
- * structure with the unit test state and the expected UDP payload.
- *
- * The following checks are executed:
- *
- * * the Ethernet packet indicates a IP broadcast message
- * * the IP header is for a local UDP broadcast message to port 514
- * * the UDP payload matches the expected string
- *
- * After testing the pointer to the expected string is set to NULL to signal
- * that the callback function has been called.
- *
- * @dev:       sandbox ethernet device
- * @packet:    Ethernet packet
- * @len:       length of Ethernet packet
- * Return:     0 = success
- */
-static int sb_log_tx_handler(struct udevice *dev, void *packet,
-                            unsigned int len)
+int sb_log_tx_handler(struct udevice *dev, void *packet, unsigned int len)
 {
        struct eth_sandbox_priv *priv = dev_get_priv(dev);
        struct sb_log_env *env = priv->priv;
@@ -93,6 +56,20 @@ static int sb_log_tx_handler(struct udevice *dev, void *packet,
        return 0;
 }
 
+int syslog_test_setup(struct unit_test_state *uts)
+{
+       ut_assertok(log_device_set_enable(LOG_GET_DRIVER(syslog), true));
+
+       return 0;
+}
+
+int syslog_test_finish(struct unit_test_state *uts)
+{
+       ut_assertok(log_device_set_enable(LOG_GET_DRIVER(syslog), false));
+
+       return 0;
+}
+
 /**
  * log_test_syslog_err() - test log_err() function
  *
@@ -104,6 +81,7 @@ static int log_test_syslog_err(struct unit_test_state *uts)
        int old_log_level = gd->default_log_level;
        struct sb_log_env env;
 
+       ut_assertok(syslog_test_setup(uts));
        gd->log_fmt = LOGF_TEST;
        gd->default_log_level = LOGL_INFO;
        env_set("ethact", "eth@10002000");
@@ -119,6 +97,7 @@ static int log_test_syslog_err(struct unit_test_state *uts)
        sandbox_eth_set_tx_handler(0, NULL);
        gd->default_log_level = old_log_level;
        gd->log_fmt = log_get_default_format();
+       ut_assertok(syslog_test_finish(uts));
 
        return 0;
 }
@@ -135,6 +114,7 @@ static int log_test_syslog_warning(struct unit_test_state *uts)
        int old_log_level = gd->default_log_level;
        struct sb_log_env env;
 
+       ut_assertok(syslog_test_setup(uts));
        gd->log_fmt = LOGF_TEST;
        gd->default_log_level = LOGL_INFO;
        env_set("ethact", "eth@10002000");
@@ -151,6 +131,7 @@ static int log_test_syslog_warning(struct unit_test_state *uts)
        ut_assertnull(env.expected);
        gd->default_log_level = old_log_level;
        gd->log_fmt = log_get_default_format();
+       ut_assertok(syslog_test_finish(uts));
 
        return 0;
 }
@@ -167,6 +148,7 @@ static int log_test_syslog_notice(struct unit_test_state *uts)
        int old_log_level = gd->default_log_level;
        struct sb_log_env env;
 
+       ut_assertok(syslog_test_setup(uts));
        gd->log_fmt = LOGF_TEST;
        gd->default_log_level = LOGL_INFO;
        env_set("ethact", "eth@10002000");
@@ -183,6 +165,7 @@ static int log_test_syslog_notice(struct unit_test_state *uts)
        ut_assertnull(env.expected);
        gd->default_log_level = old_log_level;
        gd->log_fmt = log_get_default_format();
+       ut_assertok(syslog_test_finish(uts));
 
        return 0;
 }
@@ -199,6 +182,7 @@ static int log_test_syslog_info(struct unit_test_state *uts)
        int old_log_level = gd->default_log_level;
        struct sb_log_env env;
 
+       ut_assertok(syslog_test_setup(uts));
        gd->log_fmt = LOGF_TEST;
        gd->default_log_level = LOGL_INFO;
        env_set("ethact", "eth@10002000");
@@ -215,6 +199,7 @@ static int log_test_syslog_info(struct unit_test_state *uts)
        ut_assertnull(env.expected);
        gd->default_log_level = old_log_level;
        gd->log_fmt = log_get_default_format();
+       ut_assertok(syslog_test_finish(uts));
 
        return 0;
 }
@@ -231,6 +216,7 @@ static int log_test_syslog_debug(struct unit_test_state *uts)
        int old_log_level = gd->default_log_level;
        struct sb_log_env env;
 
+       ut_assertok(syslog_test_setup(uts));
        gd->log_fmt = LOGF_TEST;
        gd->default_log_level = LOGL_DEBUG;
        env_set("ethact", "eth@10002000");
@@ -247,42 +233,8 @@ static int log_test_syslog_debug(struct unit_test_state *uts)
        ut_assertnull(env.expected);
        gd->default_log_level = old_log_level;
        gd->log_fmt = log_get_default_format();
+       ut_assertok(syslog_test_finish(uts));
 
        return 0;
 }
 LOG_TEST(log_test_syslog_debug);
-
-/**
- * log_test_syslog_nodebug() - test logging level filter
- *
- * Verify that log_debug() does not lead to a log message if the logging level
- * is set to LOGL_INFO.
- *
- * @uts:       unit test state
- * Return:     0 = success
- */
-static int log_test_syslog_nodebug(struct unit_test_state *uts)
-{
-       int old_log_level = gd->default_log_level;
-       struct sb_log_env env;
-
-       gd->log_fmt = LOGF_TEST;
-       gd->default_log_level = LOGL_INFO;
-       env_set("ethact", "eth@10002000");
-       env_set("log_hostname", "sandbox");
-       env.expected = "<7>sandbox uboot: log_test_syslog_nodebug() "
-                      "testing log_debug\n";
-       env.uts = uts;
-       sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
-       /* Used by ut_assert macros in the tx_handler */
-       sandbox_eth_set_priv(0, &env);
-       log_debug("testing %s\n", "log_debug");
-       sandbox_eth_set_tx_handler(0, NULL);
-       /* Check that the callback function was not called */
-       ut_assertnonnull(env.expected);
-       gd->default_log_level = old_log_level;
-       gd->log_fmt = log_get_default_format();
-
-       return 0;
-}
-LOG_TEST(log_test_syslog_nodebug);
diff --git a/test/log/syslog_test.h b/test/log/syslog_test.h
new file mode 100644 (file)
index 0000000..1310257
--- /dev/null
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Header file for logging tests
+ */
+
+#ifndef __SYSLOG_TEST_H
+#define __SYSLOG_TEST_H
+
+#define LOGF_TEST (BIT(LOGF_FUNC) | BIT(LOGF_MSG))
+
+/**
+ * struct sb_log_env - private data for sandbox ethernet driver
+ *
+ * This structure is used for the private data of the sandbox ethernet
+ * driver.
+ *
+ * @expected:  string expected to be written by the syslog driver
+ * @uts:       unit test state
+ */
+struct sb_log_env {
+       const char *expected;
+       struct unit_test_state *uts;
+};
+
+/**
+ * sb_log_tx_handler() - transmit callback function
+ *
+ * This callback function is invoked when a network package is sent using the
+ * sandbox Ethernet driver. The private data of the driver holds a sb_log_env
+ * structure with the unit test state and the expected UDP payload.
+ *
+ * The following checks are executed:
+ *
+ * * the Ethernet packet indicates a IP broadcast message
+ * * the IP header is for a local UDP broadcast message to port 514
+ * * the UDP payload matches the expected string
+ *
+ * After testing the pointer to the expected string is set to NULL to signal
+ * that the callback function has been called.
+ *
+ * @dev:       sandbox ethernet device
+ * @packet:    Ethernet packet
+ * @len:       length of Ethernet packet
+ * Return:     0 = success
+ */
+int sb_log_tx_handler(struct udevice *dev, void *packet, unsigned int len);
+
+/**
+ * syslog_test_setup() - Enable syslog logging ready for tests
+ *
+ * @uts: Test state
+ * @return 0 if OK, -ENOENT if the syslog log driver is not found
+ */
+int syslog_test_setup(struct unit_test_state *uts);
+
+/**
+ * syslog_test_finish() - Disable syslog logging after tests
+ *
+ * @uts: Test state
+ * @return 0 if OK, -ENOENT if the syslog log driver is not found
+ */
+int syslog_test_finish(struct unit_test_state *uts);
+
+#endif
diff --git a/test/log/syslog_test_ndebug.c b/test/log/syslog_test_ndebug.c
new file mode 100644 (file)
index 0000000..c7f5a60
--- /dev/null
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Logging function tests for CONFIG_LOG_SYSLOG=y.
+ *
+ * Invoke the test with: ./u-boot -d arch/sandbox/dts/test.dtb
+ */
+
+#include <common.h>
+#include <dm/device.h>
+#include <hexdump.h>
+#include <test/log.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <asm/eth.h>
+#include <syslog_test.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * log_test_syslog_nodebug() - test logging level filter
+ *
+ * Verify that log_debug() does not lead to a log message if the logging level
+ * is set to LOGL_INFO.
+ *
+ * @uts:       unit test state
+ * Return:     0 = success
+ */
+static int log_test_syslog_nodebug(struct unit_test_state *uts)
+{
+       int old_log_level = gd->default_log_level;
+       struct sb_log_env env;
+
+       ut_assertok(syslog_test_setup(uts));
+       gd->log_fmt = LOGF_TEST;
+       gd->default_log_level = LOGL_INFO;
+       env_set("ethact", "eth@10002000");
+       env_set("log_hostname", "sandbox");
+       env.expected = "<7>sandbox uboot: log_test_syslog_nodebug() "
+                      "testing log_debug\n";
+       env.uts = uts;
+       sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+       /* Used by ut_assert macros in the tx_handler */
+       sandbox_eth_set_priv(0, &env);
+       log_debug("testing %s\n", "log_debug");
+       sandbox_eth_set_tx_handler(0, NULL);
+       /* Check that the callback function was not called */
+       ut_assertnonnull(env.expected);
+       gd->default_log_level = old_log_level;
+       gd->log_fmt = log_get_default_format();
+       ut_assertok(syslog_test_finish(uts));
+
+       return 0;
+}
+LOG_TEST(log_test_syslog_nodebug);
index ddc28f1..275f938 100644 (file)
@@ -92,6 +92,13 @@ def test_log(u_boot_console):
         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()
@@ -105,6 +112,7 @@ def test_log(u_boot_console):
     test8()
     test9()
     test10()
+    test11()
 
 @pytest.mark.buildconfigspec('cmd_log')
 def test_log_format(u_boot_console):