Subject: [PATCH] add syslog printing to xmon debugger.
From: Linas Vepstas <linas@austin.ibm.com>
Patch-mainline: Not yet


This patch 'dmesg'/printk log buffer printing to xmon.  I find this
useful because crashes are almost always preceeded by interesting
printk's.   This patch is simple & straightforward, except for one
possibly controversial aspect: it embeds a small snippet in
kernel/printk.c to return the location of the syslog.  This is
needed because kallsyms and even CONFIG_KALLSYMS_ALL is not enough
to reveal the location of log_buf.   This code is about 90%
cut-n-paste of earlier code from Keith Owens.

Signed-off-by: Olaf Hering <olh@suse.de>
---

 arch/powerpc/xmon/xmon.c |   57 +++++++++++++++++++++++++++++++++++++++++++++++
 kernel/printk.c          |   15 ++++++++++++
 2 files changed, 72 insertions(+)

--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -138,6 +138,7 @@ static struct bpt *in_breakpoint_table(u
 static int  do_step(struct pt_regs *);
 static void bpt_cmds(void);
 static void cacheflush(void);
+static void xmon_show_dmesg(void);
 static int  cpu_cmd(void);
 static void csum(void);
 static void bootcmds(void);
@@ -194,6 +195,7 @@ Commands:\n\
 #endif
   "\
   C	checksum\n\
+  D	show dmesg (printk) buffer\n\
   d	dump bytes\n\
   di	dump instructions\n\
   df	dump float values\n\
@@ -828,6 +830,9 @@ cmds(struct pt_regs *excp)
 		case 'd':
 			dump();
 			break;
+		case 'D':
+			xmon_show_dmesg();
+			break;
 		case 'l':
 			symbol_lookup();
 			break;
@@ -2599,6 +2604,58 @@ static void xmon_print_symbol(unsigned l
 	printf("%s", after);
 }
 
+extern void debugger_syslog_data(char *syslog_data[4]);
+#define SYSLOG_WRAP(p) if (p < syslog_data[0]) p = syslog_data[1]-1; \
+	else if (p >= syslog_data[1]) p = syslog_data[0];
+
+static void xmon_show_dmesg(void)
+{
+	char *syslog_data[4], *start, *end, c;
+	int logsize;
+
+	/* syslog_data[0,1] physical start, end+1.
+	 * syslog_data[2,3] logical start, end+1.
+	 */
+	debugger_syslog_data(syslog_data);
+	if (syslog_data[2] == syslog_data[3])
+		return;
+	logsize = syslog_data[1] - syslog_data[0];
+	start = syslog_data[0] + (syslog_data[2] - syslog_data[0]) % logsize;
+	end = syslog_data[0] + (syslog_data[3] - syslog_data[0]) % logsize;
+
+	/* Do a line at a time (max 200 chars) to reduce overhead */
+	c = '\0';
+	while(1) {
+		char *p;
+		int chars = 0;
+		if (!*start) {
+			while (!*start) {
+				++start;
+				SYSLOG_WRAP(start);
+				if (start == end)
+					break;
+			}
+			if (start == end)
+				break;
+		}
+		p = start;
+		while (*start && chars < 200) {
+			c = *start;
+			++chars;
+			++start;
+			SYSLOG_WRAP(start);
+			if (start == end || c == '\n')
+				break;
+		}
+		if (chars)
+			printf("%.*s", chars, p);
+		if (start == end)
+			break;
+	}
+	if (c != '\n')
+		printf("\n");
+}
+
 #ifdef CONFIG_PPC_BOOK3S_64
 static void dump_slb(void)
 {
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -413,6 +413,21 @@ SYSCALL_DEFINE3(syslog, int, type, char
 	return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
 }
 
+#ifdef CONFIG_DEBUG_KERNEL
+/* Its very handy to be able to view the syslog buffer during debug.
+ * But do_syslog() uses locks so it cannot be used during debugging.
+ * Instead, provide the start and end of the physical and logical logs.
+ * This is equivalent to do_syslog(3).
+ */
+void debugger_syslog_data(char *syslog_data[4])
+{
+	syslog_data[0] = log_buf;
+	syslog_data[1] = log_buf + log_buf_len;
+	syslog_data[2] = log_buf + log_end - (logged_chars < log_buf_len ? logged_chars : log_buf_len);
+	syslog_data[3] = log_buf + log_end;
+}
+#endif   /* CONFIG_DEBUG_KERNEL */
+
 /*
  * Call the console drivers on a range of log_buf
  */
