[PATCH 4/4] Introduce message logging support
Sascha Hauer
s.hauer at pengutronix.de
Tue Sep 30 07:22:06 PDT 2014
This adds a buffer for log messages and a 'dmesg' command to
print the messages. The log buffer is implemented as log objects
rather than a string buffer. This makes it easy to implement
limiting the messages, cleaning the buffer and timestamping
the messages.
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
commands/Kconfig | 7 +++
commands/Makefile | 1 +
commands/dmesg.c | 100 +++++++++++++++++++++++++++++++++++++
common/Kconfig | 3 ++
common/console_common.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++--
drivers/base/driver.c | 22 ---------
include/printk.h | 17 +++++++
7 files changed, 254 insertions(+), 25 deletions(-)
create mode 100644 commands/dmesg.c
diff --git a/commands/Kconfig b/commands/Kconfig
index 3a49baf..f0cd8b2 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -84,6 +84,13 @@ config CMD_DEVINFO
If called with a device path being the argument, devinfo shows more
default information about this device and its parameters.
+config CMD_DMESG
+ tristate
+ prompt "dmesg"
+ select LOGBUF
+ help
+ Print or control the log message buffer.
+
config CMD_DRVINFO
tristate
default y
diff --git a/commands/Makefile b/commands/Makefile
index 52b6137..608ff5e 100644
--- a/commands/Makefile
+++ b/commands/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_CMD_IOMEM) += iomemport.o
obj-$(CONFIG_CMD_LINUX_EXEC) += linux_exec.o
obj-$(CONFIG_CMD_AUTOMOUNT) += automount.o
obj-$(CONFIG_CMD_GLOBAL) += global.o
+obj-$(CONFIG_CMD_DMESG) += dmesg.o
obj-$(CONFIG_CMD_BASENAME) += basename.o
obj-$(CONFIG_CMD_DIRNAME) += dirname.o
obj-$(CONFIG_CMD_READLINK) += readlink.o
diff --git a/commands/dmesg.c b/commands/dmesg.c
new file mode 100644
index 0000000..b2bb334
--- /dev/null
+++ b/commands/dmesg.c
@@ -0,0 +1,100 @@
+/*
+ * dmesg.c - barebox logbuffer handling
+ *
+ * Copyright (c) 2014 Sascha Hauer <s.hauer at pengutronix.de>, Pengutronix
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <common.h>
+#include <malloc.h>
+#include <command.h>
+#include <globalvar.h>
+#include <environment.h>
+#include <getopt.h>
+#include <clock.h>
+
+static int do_dmesg(int argc, char *argv[])
+{
+ int opt, i;
+ int delete_buf = 0, emit = 0;
+ unsigned flags = 0;
+
+ while ((opt = getopt(argc, argv, "ctde")) > 0) {
+ switch (opt) {
+ case 'c':
+ delete_buf = 1;
+ break;
+ case 't':
+ flags |= BAREBOX_LOG_PRINT_TIME;
+ break;
+ case 'd':
+ flags |= BAREBOX_LOG_DIFF_TIME;
+ break;
+ case 'e':
+ emit = 1;
+ break;
+ default:
+ return COMMAND_ERROR_USAGE;
+ }
+ }
+
+ if (emit) {
+ char *buf;
+ int len = 0;
+
+ for (i = optind; i < argc; i++)
+ len += strlen(argv[i]) + 1;
+
+ buf = malloc(len + 2);
+ if (!buf)
+ return -ENOMEM;
+
+ len = 0;
+
+ for (i = optind; i < argc; i++)
+ len += sprintf(buf + len, "%s ", argv[i]);
+
+ *(buf + len) = '\n';
+ *(buf + len + 1) = 0;
+
+ pr_info(buf);
+
+ free(buf);
+
+ return 0;
+ }
+
+ log_print(flags);
+
+ if (delete_buf)
+ log_clean(10);
+
+ return 0;
+}
+
+BAREBOX_CMD_HELP_START(dmesg)
+BAREBOX_CMD_HELP_TEXT("Options:")
+BAREBOX_CMD_HELP_OPT ("-c", "Delete messages after printing them")
+BAREBOX_CMD_HELP_OPT ("-d", "Show a time delta to the last message")
+BAREBOX_CMD_HELP_OPT ("-e <msg>", "Emit a log message")
+BAREBOX_CMD_HELP_OPT ("-t", "Show timestamp informations")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(dmesg)
+ .cmd = do_dmesg,
+ BAREBOX_CMD_DESC("Print or control log messages")
+ BAREBOX_CMD_OPTS("[-cdet]")
+ BAREBOX_CMD_GROUP(CMD_GRP_INFO)
+ BAREBOX_CMD_HELP(cmd_dmesg_help)
+BAREBOX_CMD_END
diff --git a/common/Kconfig b/common/Kconfig
index 9cc96b7..4a84cfa 100644
--- a/common/Kconfig
+++ b/common/Kconfig
@@ -66,6 +66,9 @@ config UIMAGE
select CRC32
bool
+config LOGBUF
+ bool
+
config GLOBALVAR
bool
diff --git a/common/console_common.c b/common/console_common.c
index cc184df..cc25f97 100644
--- a/common/console_common.c
+++ b/common/console_common.c
@@ -27,6 +27,9 @@
#include <globalvar.h>
#include <magicvar.h>
#include <password.h>
+#include <clock.h>
+#include <malloc.h>
+#include <asm-generic/div64.h>
#ifndef CONFIG_CONSOLE_NONE
@@ -59,31 +62,151 @@ void console_allow_input(bool val)
int barebox_loglevel = CONFIG_DEFAULT_LOGLEVEL;
+LIST_HEAD(barebox_logbuf);
+static int barebox_logbuf_num_messages;
+static int barebox_log_max_messages = 1000;
+
+static void log_del(struct log_entry *log)
+{
+ free(log->msg);
+ list_del(&log->list);
+ free(log);
+ barebox_logbuf_num_messages--;
+}
+
+/**
+ * log_clean - delete log messages from buffer
+ *
+ * @limit: The maximum messages left in the buffer after
+ * calling this function.
+ *
+ * This function deletes all messages in the logbuf exeeding
+ * the limit.
+ */
+void log_clean(unsigned int limit)
+{
+ struct log_entry *log, *tmp;
+
+ if (list_empty(&barebox_logbuf))
+ return;
+
+ list_for_each_entry_safe(log, tmp, &barebox_logbuf, list) {
+ if (barebox_logbuf_num_messages <= limit)
+ break;
+ log_del(log);
+ }
+}
+
+void pr_puts(int level, const char *str)
+{
+ struct log_entry *log;
+
+ if (IS_ENABLED(CONFIG_LOGBUF)) {
+ if (barebox_log_max_messages > 0)
+ log_clean(barebox_log_max_messages - 1);
+
+ if (barebox_log_max_messages >= 0) {
+ log = xzalloc(sizeof(*log));
+ log->msg = xstrdup(str);
+ log->timestamp = get_time_ns();
+ log->level = level;
+ list_add_tail(&log->list, &barebox_logbuf);
+ barebox_logbuf_num_messages++;
+ }
+ }
+
+ if (level > barebox_loglevel)
+ return;
+
+ puts(str);
+}
+
int pr_print(int level, const char *fmt, ...)
{
va_list args;
uint i;
char printbuffer[CFG_PBSIZE];
- if (level > barebox_loglevel)
+ if (!IS_ENABLED(CONFIG_LOGBUF) && level > barebox_loglevel)
return 0;
va_start(args, fmt);
i = vsprintf(printbuffer, fmt, args);
va_end(args);
- /* Print the string */
- puts(printbuffer);
+ pr_puts(level, printbuffer);
return i;
}
+int dev_printf(int level, const struct device_d *dev, const char *format, ...)
+{
+ va_list args;
+ int ret = 0;
+ char printbuffer[CFG_PBSIZE];
+
+ if (!IS_ENABLED(CONFIG_LOGBUF) && level > barebox_loglevel)
+ return 0;
+
+ if (dev->driver && dev->driver->name)
+ ret += sprintf(printbuffer, "%s ", dev->driver->name);
+
+ ret += sprintf(printbuffer + ret, "%s: ", dev_name(dev));
+
+ va_start(args, format);
+
+ ret += vsprintf(printbuffer + ret, format, args);
+
+ va_end(args);
+
+ pr_puts(level, printbuffer);
+
+ return ret;
+}
+
static int loglevel_init(void)
{
+ if (IS_ENABLED(CONFIG_LOGBUF))
+ globalvar_add_simple_int("log_max_messages",
+ &barebox_log_max_messages, "%d");
+
return globalvar_add_simple_int("loglevel", &barebox_loglevel, "%d");
}
device_initcall(loglevel_init);
+void log_print(unsigned flags)
+{
+ struct log_entry *log;
+ unsigned long last = 0;
+
+ list_for_each_entry(log, &barebox_logbuf, list) {
+ uint64_t diff = log->timestamp - time_beginning;
+ unsigned long difful;
+
+ do_div(diff, 1000);
+ difful = diff;
+
+ if (!log->timestamp)
+ difful = 0;
+
+ if (flags & (BAREBOX_LOG_PRINT_TIME | BAREBOX_LOG_DIFF_TIME))
+ printf("[");
+
+ if (flags & BAREBOX_LOG_PRINT_TIME)
+ printf("%10luus", difful);
+
+ if (flags & BAREBOX_LOG_DIFF_TIME) {
+ printf(" < %10luus", difful - last);
+ last = difful;
+ }
+
+ if (flags & (BAREBOX_LOG_PRINT_TIME | BAREBOX_LOG_DIFF_TIME))
+ printf("] ");
+
+ printf("%s", log->msg);
+ }
+}
+
int printf(const char *fmt, ...)
{
va_list args;
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 2cf3ee6..9709415 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -377,28 +377,6 @@ const char *dev_id(const struct device_d *dev)
return buf;
}
-int dev_printf(int level, const struct device_d *dev, const char *format, ...)
-{
- va_list args;
- int ret = 0;
-
- if (level > barebox_loglevel)
- return 0;
-
- if (dev->driver && dev->driver->name)
- ret += printf("%s ", dev->driver->name);
-
- ret += printf("%s: ", dev_name(dev));
-
- va_start(args, format);
-
- ret += vprintf(format, args);
-
- va_end(args);
-
- return ret;
-}
-
void devices_shutdown(void)
{
struct device_d *dev;
diff --git a/include/printk.h b/include/printk.h
index 4543156..fb63586 100644
--- a/include/printk.h
+++ b/include/printk.h
@@ -72,4 +72,21 @@ int dev_printf(int level, const struct device_d *dev, const char *format, ...)
#define debug(fmt, arg...) __pr_printk(7, pr_fmt(fmt), ##arg)
#define pr_vdebug(fmt, arg...) __pr_printk(8, pr_fmt(fmt), ##arg)
+struct log_entry {
+ struct list_head list;
+ char *msg;
+ void *dummy;
+ uint64_t timestamp;
+ int level;
+};
+
+extern struct list_head barebox_logbuf;
+
+extern void log_clean(unsigned int limit);
+
+#define BAREBOX_LOG_PRINT_TIME (1 << 0)
+#define BAREBOX_LOG_DIFF_TIME (1 << 1)
+
+void log_print(unsigned flags);
+
#endif
--
2.1.0
More information about the barebox
mailing list