Subject: [PATCH] powerpc: add dmesg command to xmon

From: Olaf Hering <[email-masked]>
Date: Sun, 28 May 2006 12:46:21 +0200

Based on work by Linas Vepstas:
dump dmesg buffer in xmon, with a new 'D' command.
Use kallsyms to lookup the symbols to avoid touching generic code in printk.c

Signed-off-by: Olaf Hering <[email-masked]>

---
 arch/powerpc/xmon/xmon.c |  112 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

Index: linux-2.6/arch/powerpc/xmon/xmon.c
===================================================================
--- linux-2.6.orig/arch/powerpc/xmon/xmon.c
+++ linux-2.6/arch/powerpc/xmon/xmon.c
@@ -143,6 +143,7 @@ void dump_segments(void);
 static void symbol_lookup(void);
 static void xmon_print_symbol(unsigned long address, const char *mid,
 			      const char *after);
+static void xmon_show_dmesg(void);
 static const char *getvecname(unsigned long vec);
 
 extern int print_insn_powerpc(unsigned long, unsigned long, int);
@@ -192,6 +193,7 @@ Commands:\n\
   df	dump float values\n\
   dd	dump double values\n\
   dr	dump stream of raw bytes\n\
+  D	show dmesg (printk) buffer\n\
   e	print exception information\n\
   f	flush cache\n\
   la	lookup symbol+offset of specified address\n\
@@ -781,6 +783,9 @@ cmds(struct pt_regs *excp)
 		case 'd':
 			dump();
 			break;
+		case 'D':
+			xmon_show_dmesg();
+			break;
 		case 'l':
 			symbol_lookup();
 			break;
@@ -2488,6 +2493,113 @@ static void xmon_print_symbol(unsigned l
 	printf("%s", after);
 }
 
+#ifdef CONFIG_KALLSYMS
+static const char *xmon_log_buf;
+static int xmon_log_buf_len;
+static unsigned long xmon_log_end, xmon_logged_chars;
+
+static int xmon_init_dmesg(void)
+{
+	const char **p;
+	const int *i;
+	const unsigned long *l;
+
+	if (!xmon_log_buf && setjmp(bus_error_jmp) == 0) {
+		catch_memory_errors = 1;
+		sync();
+
+		p = (const char **)kallsyms_lookup_name("log_buf");
+		if (p) {
+			xmon_log_buf = *p;
+			sync();
+			__delay(200);
+		}
+
+		i = (const int *)kallsyms_lookup_name("log_buf_len");
+		if (i) {
+			xmon_log_buf_len = *i;
+			sync();
+			__delay(200);
+		}
+
+		l = (const unsigned long *)kallsyms_lookup_name("log_end");
+		if (l) {
+			xmon_log_end = *l;
+			sync();
+			__delay(200);
+		}
+
+		l = (const unsigned long *)kallsyms_lookup_name("logged_chars");
+		if (l) {
+			xmon_logged_chars = *l;
+			sync();
+			__delay(200);
+		}
+
+	}
+	catch_memory_errors = 0;
+	return !!xmon_log_buf;
+}
+#else
+static inline int xmon_init_dmesg(void)
+{
+	return 0;
+}
+#endif
+
+static void xmon_show_dmesg(void)
+{
+	if (xmon_init_dmesg()) {
+		const char *p_start, *p_end, *l_start, *l_end, *start, *end;
+		char c;
+
+		p_start = xmon_log_buf;
+		p_end = p_start + xmon_log_buf_len;
+		l_start = p_start + xmon_log_end - (xmon_logged_chars < xmon_log_buf_len ? xmon_logged_chars : xmon_log_buf_len);
+		l_end = p_start + xmon_log_end;
+		if (l_start == l_end)
+			return;
+		start = p_start + (l_start - p_start) % xmon_log_buf_len;
+		end = p_start + (l_end - p_start) % xmon_log_buf_len;
+		c = '\0';
+		while (1) {
+			const char *p;
+			int chars = 0;
+			if (!*start) {
+				while (!*start) {
+					start++;
+					if (start < p_start)
+						start = p_end - 1;
+					else if (start >= p_end)
+						start = p_start;
+					if (start == end)
+						break;
+				}
+				if (start == end)
+					break;
+			}
+			p = start;
+			while (*start && chars < 200) {
+				c = *start;
+				chars++;
+				start++;
+				if (start < p_start)
+					start = p_end - 1;
+				else if (start >= p_end)
+					start = p_start;
+				if (start == end)
+					break;
+			}
+			if (chars)
+				printf("%.*s", chars, p);
+			if (start == end)
+				break;
+		}
+		if (c != '\n')
+			printf("\n");
+	}
+}
+
 #ifdef CONFIG_PPC64
 static void dump_slb(void)
 {