ARM: rockchip: add last_log support
author黄涛 <huangtao@rock-chips.com>
Tue, 1 Apr 2014 02:23:08 +0000 (10:23 +0800)
committer黄涛 <huangtao@rock-chips.com>
Tue, 1 Apr 2014 02:23:08 +0000 (10:23 +0800)
arch/arm/common/fiq_debugger.c
arch/arm/mach-rockchip/Kconfig
arch/arm/mach-rockchip/Makefile
kernel/printk.c

index 5e16e8007cc2e2e7b18c4e99ee422f6020e33e65..17b3b8b454764010def79c50f131c749bfae710f 100755 (executable)
@@ -253,21 +253,130 @@ static void dump_kernel_log(struct fiq_debugger_state *state)
 #ifdef CONFIG_RK_LAST_LOG
 #include <linux/ctype.h>
 extern char *last_log_get(unsigned *size);
-static void dump_last_kernel_log(struct fiq_debugger_state *state)
+
+enum log_flags {
+       LOG_NOCONS      = 1,    /* already flushed, do not print to console */
+       LOG_NEWLINE     = 2,    /* text ended with a newline */
+       LOG_PREFIX      = 4,    /* text started with a prefix */
+       LOG_CONT        = 8,    /* text is a fragment of a continuation line */
+};
+
+struct log {
+       u64 ts_nsec;            /* timestamp in nanoseconds */
+       u16 len;                /* length of entire record */
+       u16 text_len;           /* length of text buffer */
+       u16 dict_len;           /* length of dictionary buffer */
+       u8 facility;            /* syslog facility */
+       u8 flags:5;             /* internal record flags */
+       u8 level:3;             /* syslog level */
+};
+
+static void dump_last_kernel_log_raw(struct fiq_debugger_state *state)
 {
-       unsigned size, i, c;
+       unsigned size, i, c, w = 0;
        char *s = last_log_get(&size);
-
        for (i = 0; i < size; i++) {
                if (i % 1024 == 0)
                        wdt_keepalive();
                c = s[i];
+               if (w % 80 == 79) {
+                       debug_putc(state, '\r');
+                       debug_putc(state, '\n');
+                       w = 0;
+               }
                if (c == '\n') {
-                       debug_putc(state->pdev, '\r');
-                       debug_putc(state->pdev, c);
+                       debug_putc(state, '\r');
+                       debug_putc(state, c);
+                       w = 0;
                } else if (isascii(c) && isprint(c)) {
-                       debug_putc(state->pdev, c);
+                       debug_putc(state, c);
+                       w++;
+               }
+       }
+}
+
+static void dump_last_kernel_log(struct fiq_debugger_state *state)
+{
+       unsigned size, i, c;
+       char *s = last_log_get(&size);
+       unsigned int prev = 0;
+
+       for (;;) {
+               size_t sz;
+               struct log *l;
+               bool prefix = true;
+               bool newline = true;
+               const char *text;
+               unsigned int text_size;
+
+               sz = sizeof(struct log);
+               if (size < sz)
+                       break;
+               l = (struct log *)s;
+               size -= sz;
+               s += sz;
+
+               if (l->len == 0)
+                       break;
+
+               sz = l->len - sz;
+               text = s;
+               text_size = l->text_len;
+               if (size < sz)
+                       break;
+               size -= sz;
+               s += sz;
+
+               if ((prev & LOG_CONT) && !(l->flags & LOG_PREFIX))
+                       prefix = false;
+
+               if (l->flags & LOG_CONT) {
+                       if ((prev & LOG_CONT) && !(prev & LOG_NEWLINE))
+                               prefix = false;
+
+                       if (!(l->flags & LOG_NEWLINE))
+                               newline = false;
                }
+
+               do {
+                       char *next = memchr(text, '\n', text_size);
+                       size_t text_len;
+
+                       if (next) {
+                               text_len = next - text;
+                               next++;
+                               text_size -= next - text;
+                       } else {
+                               text_len = text_size;
+                       }
+
+                       if (prefix) {
+                               char buf[32];
+                               size_t len = 0;
+                               unsigned long rem_nsec = do_div(l->ts_nsec, 1000000000);
+                               unsigned int prefix = (l->facility << 3) | l->level;
+
+                               len += snprintf(buf, sizeof(buf), "<%u>", prefix);
+                               len += snprintf(buf + len, sizeof(buf) - len, "[%5lu.%06lu] ", (unsigned long)l->ts_nsec, rem_nsec / 1000);
+                               debug_puts(state, buf);
+                       }
+                       for (i = 0; i < text_len; i++) {
+                               c = text[i];
+                               if (c == '\n') {
+                                       debug_putc(state, '\r');
+                                       debug_putc(state, c);
+                               } else if (isascii(c) && isprint(c)) {
+                                       debug_putc(state, c);
+                               }
+                       }
+                       if (next || newline) {
+                               debug_putc(state, '\r');
+                               debug_putc(state, '\n');
+                       }
+                       prefix = true;
+                       text = next;
+               } while (text);
+               wdt_keepalive();
        }
 }
 #endif
@@ -741,6 +850,7 @@ static char cmd_buf[][16] = {
                {"kmsg"},
 #ifdef CONFIG_RK_LAST_LOG
                {"last_kmsg"},
+               {"last_kmsg_raw"},
 #endif
                {"version"},
                {"sleep"},
@@ -770,6 +880,7 @@ static void debug_help(struct fiq_debugger_state *state)
                                " version       Kernel version\n");
 #ifdef CONFIG_RK_LAST_LOG
        debug_printf(state,     " last_kmsg     Last kernel log\n");
+       debug_printf(state,     " last_kmsg_raw Last kernel log raw\n");
 #endif
        debug_printf(state,     " sleep         Allow sleep while in FIQ\n"
                                " nosleep       Disable sleep while in FIQ\n"
@@ -850,6 +961,8 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state,
        } else if (!strcmp(cmd, "kmsg")) {
                dump_kernel_log(state);
 #ifdef CONFIG_RK_LAST_LOG
+       } else if (!strcmp(cmd, "last_kmsg_raw")) {
+               dump_last_kernel_log_raw(state);
        } else if (!strcmp(cmd, "last_kmsg")) {
                dump_last_kernel_log(state);
 #endif
index 4f8e4e76c3e1b4d6529733a4fac1c931f243cb9f..6fe6ad0ff4e278cdc118258fc42a0fae37b251ff 100755 (executable)
@@ -22,6 +22,13 @@ config ARCH_ROCKCHIP
 
 if ARCH_ROCKCHIP
 
+config RK_LAST_LOG
+       bool "Save the last kernel log on /proc/last_log"
+       depends on DEBUG_KERNEL && PRINTK
+       default y
+       help
+         It is only intended for debugging.
+
 config RK_DEBUG_UART
        int "Debug UART"
        default 2
index 34a12fbe9f18eb9e0379c7403d285c5af809b0d6..ce6bf257f39828128813167aea8ceb7474072a97 100755 (executable)
@@ -9,6 +9,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o
 obj-$(CONFIG_SMP) += platsmp.o
 obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
 obj-$(CONFIG_FIQ_DEBUGGER) += rk_fiq_debugger.o
+obj-$(CONFIG_RK_LAST_LOG) += last_log.o
 obj-$(CONFIG_DVFS) += dvfs.o
 obj-$(CONFIG_RK_VCODEC) += vcodec_service.o
 obj-$(CONFIG_RK_VPU) += vpu_service.o
index 51cc2e98029021fb9f8aa4267501b5412a41915e..a6b2f1fc21c10f37ca9b0e8675cf4d3b71814002 100644 (file)
@@ -1707,6 +1707,21 @@ asmlinkage int printk(const char *fmt, ...)
 }
 EXPORT_SYMBOL(printk);
 
+#ifdef CONFIG_RK_LAST_LOG
+void __init switch_log_buf(char *new_log_buf, unsigned size)
+{
+       unsigned long flags;
+
+       if (!new_log_buf || log_buf_len > size)
+               return;
+
+       raw_spin_lock_irqsave(&logbuf_lock, flags);
+       memcpy(new_log_buf, log_buf, min(log_buf_len, size));
+       log_buf = new_log_buf;
+       log_buf_len = size;
+       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+}
+#endif /* CONFIG_RK_LAST_LOG */
 #else /* CONFIG_PRINTK */
 
 #define LOG_LINE_MAX           0