[Crash-utility] [PATCH] Use registers from LKCD header to get starting ESP register

Bernhard Walle bwalle at suse.de
Tue May 1 22:23:08 UTC 2007


On i386/x86_64, crash uses the ESP register to start its backtrace when
analysing LKCD dumps. It does this by guessing in get_lkcd_regs().  This patch
uses another approach vor v8 dumps: It uses the registers saved by LKCD in the
arch-specific dump header.

It fixed a problem here where I head a crash dump from a customer
using LKCD that showed a totally wrong backtrace in crash but lcrash
worked fine.

To implement this, the patch fetches the headers for all architectures and not
only to IA64. We need the arch-specific header, this is done by copying the
declaraction from the kernel patch (2.6.17, the latest) and using a separate
header file for each architecture.

Signed-off-by: Bernhard Walle <bwalle at suse.de>

---
 Makefile           |   31 +++++++++-
 defs.h             |    2 
 kernel.c           |   63 +++++++++++---------
 lkcd_dump_i386.c   |   32 ++++++++++
 lkcd_dump_i386.h   |   71 +++++++++++++++++++++++
 lkcd_dump_ia64.c   |   35 +++++++++++
 lkcd_dump_ia64.h   |   81 ++++++++++++++++++++++++++
 lkcd_dump_ppc.h    |   67 +++++++++++++++++++++
 lkcd_dump_v7.h     |  163 ++---------------------------------------------------
 lkcd_dump_v8.h     |   32 ++++++++++
 lkcd_dump_x86_64.c |   32 ++++++++++
 lkcd_dump_x86_64.h |   71 +++++++++++++++++++++++
 lkcd_fix_mem.c     |   18 -----
 lkcd_v8.c          |  140 ++++++++++++++++++++++++++++++++++++++++++++-
 14 files changed, 633 insertions(+), 205 deletions(-)

--- a/Makefile
+++ b/Makefile
@@ -97,6 +97,24 @@ OBJECT_FILES=main.o tools.o global_data.
  	xen_hyper.o xen_hyper_command.o xen_hyper_global_data.o \
 	xen_hyper_dump_tables.o
 
+
+# architecture specific LKCD files
+
+ifeq ($(ARCH), x86_64)
+CFILES 		+= lkcd_dump_x86_64.c
+OBJECT_FILES 	+= lkcd_dump_x86_64.o
+endif
+
+ifeq ($(ARCH), i386)
+CFILES		+= lkcd_dump_i386.c
+OBJECT_FILES	+= lkcd_dump_i386.o
+endif
+
+ifeq ($(ARCH), ia64)
+CFILES		+= lkcd_dump_ia64.c
+OBJECT_FILES	+= lkcd_dump_ia64.o
+endif
+
 # These are the current set of crash extensions sources.  They are not built
 # by default unless the third command line of the "all:" stanza is uncommented.
 # Alternatively, they can be built by entering "make extensions" from this
@@ -334,6 +352,15 @@ lkcd_v7.o: ${GENERIC_HFILES} ${LKCD_DUMP
 lkcd_v8.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_v8.c
 	cc -c ${CFLAGS} -DMCLX lkcd_v8.c ${WARNING_OPTIONS} ${WARNING_ERROR}
 
+lkcd_dump_x86_64.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_dump_x86_64.c
+	cc -c ${CFLAGS} -DMCLX lkcd_dump_x86_64.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
+lkcd_dump_i386.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_dump_i386.c
+	cc -c ${CFLAGS} -DMCLX lkcd_dump_i386.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
+lkcd_dump_ia64.o: ${GENERIC_HFILES} ${LKCD_DUMP_HFILES} lkcd_dump_ia64.c
+	cc -c ${CFLAGS} -DMCLX lkcd_dump_ia64.c ${WARNING_OPTIONS} ${WARNING_ERROR}
+
 net.o: ${GENERIC_HFILES} net.c
 	cc -c ${CFLAGS} net.c ${WARNING_OPTIONS} ${WARNING_ERROR}
 
--- a/defs.h
+++ b/defs.h
@@ -3790,7 +3790,7 @@ void ppc_dump_machdep_table(ulong);
  */
 
 ulong get_lkcd_switch_stack(ulong);
-int fix_addr_v8(int);
+int fix_addr_v8();
 int fix_addr_v7(int);
 
 /*
--- a/kernel.c
+++ b/kernel.c
@@ -2089,6 +2089,11 @@ get_lkcd_regs(struct bt_info *bt, ulong 
 		return;
 	}
 
+	/* try to get it from the header */
+	if (get_lkcd_regs_for_cpu(bt, eip, esp) == 0)
+		return;
+
+	/* if that fails: do guessing */
 	sysrq_eip = sysrq_esp = 0;
 
 	for (i = 0, up = (ulong *)bt->stackbuf; i < LONGS_PER_STACK; i++, up++){
@@ -2098,48 +2103,48 @@ get_lkcd_regs(struct bt_info *bt, ulong 
 			*esp = *(up-1);
 			return;
 		}
-                /* Begin 3PAR change -- required for our panic path */
+		/* Begin 3PAR change -- required for our panic path */
 		if (STREQ(sym, "dump_ipi") && INSTACK(*(up-1), bt)) {
 			*eip = *up;
 			*esp = *(up-1);
 			return;
 		}
 		/* End 3PAR change */
-                if (STREQ(sym, "panic") && INSTACK(*(up-1), bt)) {
-                        *eip = *up;
-                        *esp = *(up-1);
-                        return;
-                }
+		if (STREQ(sym, "panic") && INSTACK(*(up-1), bt)) {
+			*eip = *up;
+			*esp = *(up-1);
+			return;
+		}
 		/* Egenera */
-                if (STREQ(sym, "netdump_ipi")) {
-                        *eip = *up;
-                        *esp = bt->task + 
+		if (STREQ(sym, "netdump_ipi")) {
+			*eip = *up;
+			*esp = bt->task +
 				((char *)(up-1) - bt->stackbuf);
-                        return;
-                }
-                if (STREQ(sym, "smp_stop_cpu_interrupt")) {
-                        *eip = *up;
-                        *esp = bt->task + 
+			return;
+		}
+		if (STREQ(sym, "smp_stop_cpu_interrupt")) {
+			*eip = *up;
+			*esp = bt->task +
 				((char *)(up-1) - bt->stackbuf);
-                        return;
-                }
-                if (STREQ(sym, "stop_this_cpu")) {
-                        *eip = *up;
-                        *esp = bt->task + 
+			return;
+		}
+		if (STREQ(sym, "stop_this_cpu")) {
+			*eip = *up;
+			*esp = bt->task +
 				((char *)(up-1) - bt->stackbuf);
-                        return;
-                }
-                if (SYSRQ_TASK(bt->task) &&
-		    STREQ(sym, "smp_call_function_interrupt")) {
-                        sysrq_eip = *up;
-                        sysrq_esp = bt->task +
-                                ((char *)(up-1) - bt->stackbuf);
-                }
+			return;
+		}
+		if (SYSRQ_TASK(bt->task) &&
+				STREQ(sym, "smp_call_function_interrupt")) {
+			sysrq_eip = *up;
+			sysrq_esp = bt->task +
+				((char *)(up-1) - bt->stackbuf);
+		}
 	}
 
 	if (sysrq_eip) {
-        	*eip = sysrq_eip;
-        	*esp = sysrq_esp;
+		*eip = sysrq_eip;
+		*esp = sysrq_esp;
 		return;
 	}
 
--- /dev/null
+++ b/lkcd_dump_i386.c
@@ -0,0 +1,32 @@
+/* lkcd_dump_i386.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#define LKCD_COMMON
+#include "defs.h"
+#include "lkcd_dump_v8.h"
+
+
+int
+get_lkcd_regs_for_cpu_arch_i386(dump_header_asm_t *dha, int cpu, ulong *eip, ulong *esp)
+{
+	if (eip)
+		*eip = dha->dha_smp_regs[cpu].eip;
+	if (esp)
+		*esp = dha->dha_smp_regs[cpu].esp;
+
+	return 0;
+}
+
--- /dev/null
+++ b/lkcd_dump_i386.h
@@ -0,0 +1,71 @@
+/* lkcd_dump_i386.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+#ifndef LKCD_DUMP_I386_H
+#define LKCD_DUMP_I386_H
+
+#include <linux/ptrace.h>
+
+/* from kernel include/asm-i386/dump.h */
+#define DUMP_ASM_MAGIC_NUMBER	0xdeaddeadULL	/* magic number            */
+#define DUMP_ASM_VERSION_NUMBER	0x5	/* version number          */
+
+/*
+ * Structure: __dump_header_asm
+ *  Function: This is the header for architecture-specific stuff.  It
+ *            follows right after the dump header.
+ */
+typedef struct __dump_header_asm_i386 {
+	/* the dump magic number -- unique to verify dump is valid */
+	uint64_t	dha_magic_number;
+
+	/* the version number of this dump */
+	uint32_t	dha_version;
+
+	/* the size of this header (in case we can't read it) */
+	uint32_t	dha_header_size;
+
+	/* the esp for i386 systems */
+	uint32_t	dha_esp;
+
+	/* the eip for i386 systems */
+	uint32_t	dha_eip;
+
+	/* the dump registers */
+	struct pt_regs	dha_regs;
+
+	/* smp specific */
+	uint32_t	dha_smp_num_cpus;
+	uint32_t	dha_dumping_cpu;
+	struct pt_regs	dha_smp_regs[NR_CPUS];
+	uint32_t	dha_smp_current_task[NR_CPUS];
+	uint32_t	dha_stack[NR_CPUS];
+	uint32_t	dha_stack_ptr[NR_CPUS];
+} __attribute__((packed)) dump_header_asm_i386_t;
+
+/*
+ * CPU specific part of dump_header_asm_t
+ */
+typedef struct dump_CPU_info_i386 {
+	struct pt_regs	dha_smp_regs;
+	uint64_t	dha_smp_current_task;
+	uint64_t	dha_stack;
+	uint64_t	dha_stack_ptr;
+} __attribute__ ((packed)) dump_CPU_info_i386_t;
+
+
+typedef struct __dump_header_asm_i386 dump_header_asm_t;
+typedef struct dump_CPU_info_i386 dump_CPU_info_t;
+
+
+#endif /* LKCD_DUMP_I386_H */
+
--- /dev/null
+++ b/lkcd_dump_ia64.c
@@ -0,0 +1,35 @@
+/* lkcd_dump_x86_64.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#define LKCD_COMMON
+#include "defs.h"
+#include "lkcd_dump_v8.h"
+#include "lkcd_dump_ia64.h"
+
+
+/* reads the load address value for IA64 */
+
+int lkcd_dump_init_v8_ia64(int fd, dump_header_asm_t *dha)
+{
+    int ret;
+
+    ret = read(fd, &dha->dha_kernel_addr, sizeof(dha->dha_kernel_addr));
+    if (ret != sizeof(dha->dha_kernel_addr))
+        return -1;
+    else
+        return 0;
+}
+
--- /dev/null
+++ b/lkcd_dump_ia64.h
@@ -0,0 +1,81 @@
+/* lkcd_dump_ia64.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+#ifndef LKCD_DUMP_IA64_H
+#define LKCD_DUMP_IA64_H
+
+#define _ASM_IA64_FPU_H
+#include <linux/ptrace.h>
+
+#define DUMP_ASM_MAGIC_NUMBER     0xdeaddeadULL  /* magic number */
+#define DUMP_ASM_VERSION_NUMBER   0x5            /* version number          */
+
+/*
+ * Structure: dump_header_asm_t
+ *  Function: This is the header for architecture-specific stuff.  It
+ *            follows right after the dump header.
+ */
+/*typedef struct _dump_header_asm {*/
+
+typedef struct __dump_header_asm_ia64 {
+
+        /* the dump magic number -- unique to verify dump is valid */
+        uint64_t             dha_magic_number;
+
+        /* the version number of this dump */
+        uint32_t             dha_version;
+
+        /* the size of this header (in case we can't read it) */
+        uint32_t             dha_header_size;
+
+        /* pointer to pt_regs, (OLD: (struct pt_regs *, NEW: (uint64_t)) */
+	uint64_t             dha_pt_regs;
+
+	/* the dump registers */
+	struct pt_regs       dha_regs;
+
+        /* the rnat register saved after flushrs */
+        uint64_t             dha_rnat;
+
+	/* the pfs register saved after flushrs */
+	uint64_t             dha_pfs;
+
+	/* the bspstore register saved after flushrs */
+	uint64_t             dha_bspstore;
+
+	/* smp specific */
+	uint32_t	     dha_smp_num_cpus;
+	uint32_t	     dha_dumping_cpu;
+	struct pt_regs	     dha_smp_regs[NR_CPUS];
+	uint64_t	     dha_smp_current_task[NR_CPUS];
+	uint64_t	     dha_stack[NR_CPUS];
+	uint64_t	     dha_stack_ptr[NR_CPUS];
+
+	/* load address of kernel */
+        uint64_t             dha_kernel_addr;
+
+} __attribute__((packed)) dump_header_asm_ia64_t;
+
+
+struct dump_CPU_info_ia64 {
+	struct pt_regs	     dha_smp_regs;
+	uint64_t	     dha_smp_current_task;
+	uint64_t	     dha_stack;
+	uint64_t	     dha_stack_ptr;
+} __attribute__((packed)) dump_CPU_info_ia64_t;
+
+typedef struct dump_CPU_info_ia64 dump_CPU_info_t;
+typedef struct __dump_header_asm_ia64 dump_header_asm_t;
+
+int lkcd_dump_init_v8_ia64(int fd, dump_header_asm_t *dha);
+
+#endif /* LKCD_DUMP_IA64_H */
--- /dev/null
+++ b/lkcd_dump_ppc.h
@@ -0,0 +1,67 @@
+/* lkcd_dump_ppc.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+#ifndef LKCD_DUMP_PPC_H
+#define LKCD_DUMP_PPC_H
+
+#include <linux/ptrace.h>
+
+/* from kernel include/asm-i386/dump.h */
+#define DUMP_ASM_MAGIC_NUMBER	0xdeaddeadULL	/* magic number            */
+#define DUMP_ASM_VERSION_NUMBER	0x5	/* version number          */
+
+/*
+ * Structure: __dump_header_asm
+ *  Function: This is the header for architecture-specific stuff.  It
+ *            follows right after the dump header.
+ */
+struct __dump_header_asm_ppc {
+
+        /* the dump magic number -- unique to verify dump is valid */
+        uint64_t             dha_magic_number;
+
+        /* the version number of this dump */
+        uint32_t             dha_version;
+
+        /* the size of this header (in case we can't read it) */
+        uint32_t             dha_header_size;
+
+	/* the dump registers */
+	struct pt_regs       dha_regs;
+
+	/* smp specific */
+	uint32_t	     dha_smp_num_cpus;
+	int		     dha_dumping_cpu;
+	struct pt_regs	     dha_smp_regs[NR_CPUS];
+	uint64_t	     dha_smp_current_task[NR_CPUS];
+	uint64_t	     dha_stack[NR_CPUS];
+	uint64_t     	     dha_stack_ptr[NR_CPUS];
+} __attribute__((packed)) dump_header_asm_ppc_t;
+
+
+/*
+ * CPU specific part of dump_header_asm_t
+ */
+typedef struct dump_CPU_info_ppc {
+	struct pt_regs	dha_smp_regs;
+	uint64_t	dha_smp_current_task;
+	uint64_t	dha_stack;
+	uint64_t	dha_stack_ptr;
+} __attribute__ ((packed)) dump_CPU_info_ppc_t;
+
+
+typedef struct __dump_header_asm_ppc dump_header_asm_t;
+typedef struct dump_CPU_info_ppc dump_CPU_info_t;
+
+
+#endif /* LKCD_DUMP_PPC_H */
+
--- a/lkcd_dump_v7.h
+++ b/lkcd_dump_v7.h
@@ -228,165 +228,20 @@ typedef struct lkcdinfo_s {
 } lkcdinfo_t;
 
 
-#ifdef IA64
-
-#define DUMP_ASM_MAGIC_NUMBER     0xdeaddeadULL  /* magic number */
-
-struct pt_regs {
-	/* The following registers are saved by SAVE_MIN: */
-	unsigned long b6;		/* scratch */
-	unsigned long b7;		/* scratch */
-
-	unsigned long ar_csd;           /* used by cmp8xchg16 (scratch) */
-	unsigned long ar_ssd;           /* reserved for future use (scratch) */
-
-	unsigned long r8;		/* scratch (return value register 0) */
-	unsigned long r9;		/* scratch (return value register 1) */
-	unsigned long r10;		/* scratch (return value register 2) */
-	unsigned long r11;		/* scratch (return value register 3) */
-
-	unsigned long cr_ipsr;		/* interrupted task's psr */
-	unsigned long cr_iip;		/* interrupted task's instruction pointer */
-	unsigned long cr_ifs;		/* interrupted task's function state */
-
-	unsigned long ar_unat;		/* interrupted task's NaT register (preserved) */
-	unsigned long ar_pfs;		/* prev function state  */
-	unsigned long ar_rsc;		/* RSE configuration */
-	/* The following two are valid only if cr_ipsr.cpl > 0: */
-	unsigned long ar_rnat;		/* RSE NaT */
-	unsigned long ar_bspstore;	/* RSE bspstore */
-
-	unsigned long pr;		/* 64 predicate registers (1 bit each) */
-	unsigned long b0;		/* return pointer (bp) */
-	unsigned long loadrs;		/* size of dirty partition << 16 */
-
-	unsigned long r1;		/* the gp pointer */
-	unsigned long r12;		/* interrupted task's memory stack pointer */
-	unsigned long r13;		/* thread pointer */
-
-	unsigned long ar_fpsr;		/* floating point status (preserved) */
-	unsigned long r15;		/* scratch */
-
-	/* The remaining registers are NOT saved for system calls.  */
-
-	unsigned long r14;		/* scratch */
-	unsigned long r2;		/* scratch */
-	unsigned long r3;		/* scratch */
-
-	/* The following registers are saved by SAVE_REST: */
-	unsigned long r16;		/* scratch */
-	unsigned long r17;		/* scratch */
-	unsigned long r18;		/* scratch */
-	unsigned long r19;		/* scratch */
-	unsigned long r20;		/* scratch */
-	unsigned long r21;		/* scratch */
-	unsigned long r22;		/* scratch */
-	unsigned long r23;		/* scratch */
-	unsigned long r24;		/* scratch */
-	unsigned long r25;		/* scratch */
-	unsigned long r26;		/* scratch */
-	unsigned long r27;		/* scratch */
-	unsigned long r28;		/* scratch */
-	unsigned long r29;		/* scratch */
-	unsigned long r30;		/* scratch */
-	unsigned long r31;		/* scratch */
-
-	unsigned long ar_ccv;		/* compare/exchange value (scratch) */
-
-	/*
-	 * Floating point registers that the kernel considers scratch:
-	 */
-	struct ia64_fpreg f6;		/* scratch */
-	struct ia64_fpreg f7;		/* scratch */
-	struct ia64_fpreg f8;		/* scratch */
-	struct ia64_fpreg f9;		/* scratch */
-	struct ia64_fpreg f10;		/* scratch */
-	struct ia64_fpreg f11;		/* scratch */
-};
-
 /*
- * Structure: dump_header_asm_t
- *  Function: This is the header for architecture-specific stuff.  It
- *            follows right after the dump header.
+ * architecture specific data structures
  */
-typedef struct _dump_header_asm_s {
-
-        /* the dump magic number -- unique to verify dump is valid */
-        uint64_t             dha_magic_number;
-
-        /* the version number of this dump */
-        uint32_t             dha_version;
-
-        /* the size of this header (in case we can't read it) */
-        uint32_t             dha_header_size;
-
-        /* pointer to pt_regs */
-	struct pt_regs      *dha_pt_regs;
-
-	/* the dump registers */
-	struct pt_regs       dha_regs;
-
-        /* the rnat register saved after flushrs */
-        uint64_t             dha_rnat;
-
-	/* the pfs register saved after flushrs */
-	uint64_t             dha_pfs;
-
-	/* the bspstore register saved after flushrs */
-	uint64_t             dha_bspstore;
-
-	/* smp specific */
-	uint32_t	     dha_smp_num_cpus;
-	int		     dha_dumping_cpu;	
-	struct pt_regs	     dha_smp_regs[NR_CPUS];
-	void *		     dha_smp_current_task[NR_CPUS];
-	void *		     dha_stack[NR_CPUS];
-	void *		     dha_switch_stack[NR_CPUS];
-
-} dump_header_asm_t;
-
-#define NR_CPUS 32
-
-typedef struct _dump_header_asm_smp_s {
-
-        /* the dump magic number -- unique to verify dump is valid */
-        uint64_t             dha_magic_number;
-
-        /* the version number of this dump */
-        uint32_t             dha_version;
-
-        /* the size of this header (in case we can't read it) */
-        uint32_t             dha_header_size;
-
-        /* pointer to pt_regs */
-	struct pt_regs      *dha_pt_regs;
-
-	/* the dump registers */
-	struct pt_regs       dha_regs;
-
-        /* the rnat register saved after flushrs */
-        uint64_t             dha_rnat;
-
-	/* the pfs register saved after flushrs */
-	uint64_t             dha_pfs;
-
-	/* the bspstore register saved after flushrs */
-	uint64_t             dha_bspstore;
-
-	/* smp specific */
-	uint32_t	     dha_smp_num_cpus;
-	int		     dha_dumping_cpu;	
-	struct pt_regs	     dha_smp_regs[NR_CPUS];
-	void *		     dha_smp_current_task[NR_CPUS];
-	void *		     dha_stack[NR_CPUS];
-	void *		     dha_switch_stack[NR_CPUS];
-
-} dump_header_asm_smp_t;
-
 
+#if defined(IA64)
+#    include "lkcd_dump_ia64.h"
+#elif defined(X86_64)
+#    include "lkcd_dump_x86_64.h"
+#elif defined(X86)
+#    include "lkcd_dump_i386.h"
+#else
+#    error "Add support for your architecture here"
 #endif
 
-
 #ifdef __KERNEL__
 
 /*
--- a/lkcd_dump_v8.h
+++ b/lkcd_dump_v8.h
@@ -235,4 +235,36 @@ typedef struct lkcdinfo_s {
 	int             stack_offset;
 } lkcdinfo_t;
 
+
+#if defined(IA64)
+
+#   include "lkcd_dump_ia64.h"
+#   define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \
+        -1
+
+#elif defined(X86_64)
+
+#   include "lkcd_dump_x86_64.h"
+#   define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \
+	get_lkcd_regs_for_cpu_arch_x86_64(dha, cpu, eip, esp)
+
+#elif defined(X86)
+
+#   include "lkcd_dump_i386.h"
+#   define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \
+	get_lkcd_regs_for_cpu_arch_i386(dha, cpu, eip, esp)
+
+#elif defined(PPC) || defined(PPC64)
+
+#   include "lkcd_dump_ppc.h"
+#   define get_lkcd_regs_for_cpu_arch(dha, cpu, eip, esp) \
+        -1
+
+#else
+
+#   define NO_LKCD_DUMP_ARCH 1
+
+#endif
+
+
 #endif /* _DUMP_H */
--- /dev/null
+++ b/lkcd_dump_x86_64.c
@@ -0,0 +1,32 @@
+/* lkcd_dump_x86_64.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#define LKCD_COMMON
+#include "defs.h"
+#include "lkcd_dump_v8.h"
+
+
+int
+get_lkcd_regs_for_cpu_arch_x86_64(dump_header_asm_t *dha, int cpu, ulong *eip, ulong *esp)
+{
+	if (eip)
+		*eip = dha->dha_smp_regs[cpu].rip;
+	if (esp)
+		*esp = dha->dha_smp_regs[cpu].rsp;
+
+	return 0;
+}
+
--- /dev/null
+++ b/lkcd_dump_x86_64.h
@@ -0,0 +1,71 @@
+/* lkcd_dump_x86_64.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ */
+#ifndef LKCD_DUMP_X86_64_H
+#define LKCD_DUMP_X86_64_H
+
+#include <linux/ptrace.h>
+
+#define DUMP_HEADER_SIZE 0x100000
+
+
+/* from kernel include/asm-x86_64/dump.h */
+
+/* definitions */
+#define DUMP_ASM_MAGIC_NUMBER     0xdeaddeadULL  /* magic number            */
+#define DUMP_ASM_VERSION_NUMBER   0x2            /* version number          */
+
+
+/*
+ * Structure: dump_header_asm_t
+ *  Function: This is the header for architecture-specific stuff.  It
+ *            follows right after the dump header.
+ */
+typedef struct __dump_header_asm_x86_64 {
+
+        /* the dump magic number -- unique to verify dump is valid */
+        uint64_t             dha_magic_number;
+
+        /* the version number of this dump */
+        uint32_t             dha_version;
+
+        /* the size of this header (in case we can't read it) */
+        uint32_t             dha_header_size;
+
+	/* the dump registers */
+	struct pt_regs       dha_regs;
+
+	/* smp specific */
+	uint32_t	     dha_smp_num_cpus;
+	int		     dha_dumping_cpu;
+	struct pt_regs	     dha_smp_regs[NR_CPUS];
+	uint64_t	     dha_smp_current_task[NR_CPUS];
+	uint64_t	     dha_stack[NR_CPUS];
+	uint64_t	     dha_stack_ptr[NR_CPUS];
+} __attribute__((packed)) dump_header_asm_t_x86_64;
+
+
+/*
+ * CPU specific part of dump_header_asm_t
+ */
+typedef struct dump_CPU_info_x86_64 {
+	struct pt_regs	     dha_smp_regs;
+	uint64_t	     dha_smp_current_task;
+	uint64_t	     dha_stack;
+	uint64_t	     dha_stack_ptr;
+} __attribute__ ((packed)) dump_CPU_info_x86_64_t;
+
+typedef struct dump_CPU_info_x86_64 dump_CPU_info_t;
+typedef struct __dump_header_asm_x86_64 dump_header_asm_t;
+
+#endif /* LKCD_DUMP_X86_64_H */
+
--- a/lkcd_fix_mem.c
+++ b/lkcd_fix_mem.c
@@ -25,16 +25,8 @@
 static int fix_addr(dump_header_asm_t *); 
     
 int
-fix_addr_v8(int fd)
+fix_addr_v8(dump_header_asm_t *dha)
 {
-    static dump_header_asm_t dump_header_asm_v8 = { 0 };
-    dump_header_asm_t *dha;
-    dha = &dump_header_asm_v8;
-    
-    if (read(lkcd->fd, dha, sizeof(dump_header_asm_t)) !=
-	    sizeof(dump_header_asm_t))
-	return -1;
-    
     fix_addr(dha);
 
     return 0;
@@ -59,14 +51,6 @@ fix_addr_v7(int fd)
 static int
 fix_addr(dump_header_asm_t *dha)  
 {
-    
-
-    if (dha->dha_header_size != sizeof(dump_header_asm_t)) {
-	error(INFO, "LKCD machine specific dump header doesn't match crash version\n");
-	error(INFO, "traceback of currently executing threads may not work\n\n");
-    }
-    
-
     lkcd->dump_header_asm = dha;
     
 
--- a/lkcd_v8.c
+++ b/lkcd_v8.c
@@ -19,15 +19,120 @@
  */
 
 #define LKCD_COMMON
+#include <stddef.h>
 #include "defs.h"
 #include "lkcd_dump_v8.h"				/* REMIND */
 
 static dump_header_t dump_header_v8 = { 0 };
-// static dump_header_asm_t dump_header_asm_v8 = { 0 };
+#ifndef NO_LKCD_DUMP_ARCH
+static dump_header_asm_t dump_header_asm_v8 = { 0 };
+#endif
 static dump_page_t dump_page = { 0 };
 static void mclx_cache_page_headers_v8(void);
 static off_t lkcd_offset_to_first_page = LKCD_OFFSET_TO_FIRST_PAGE;
 
+#define STR(x)	#x
+
+#ifndef NO_LKCD_DUMP_ARCH
+int
+lkcd_dump_init_v8_arch(dump_header_t *dh, dump_header_asm_t *dha)
+{
+	off_t 			ret_of;
+	ssize_t 		ret_sz;
+	uint32_t 		hdr_size, offset, nr_cpus;
+	dump_header_asm_t 	arch_hdr;
+	char 			*hdr_buf = NULL;
+
+	ret_of = lseek(lkcd->fd, dh->dh_header_size +
+			offsetof(dump_header_asm_t, dha_header_size),
+			SEEK_SET);
+	if (ret_of < 0) {
+		perror("lseek failed in " __FILE__ ":" STR(__LINE__));
+		goto err;
+	}
+
+	ret_sz = read(lkcd->fd, (char *)&hdr_size, sizeof(hdr_size));
+	if (ret_sz != sizeof(hdr_size)) {
+		perror("Reading hdr_size failed in " __FILE__ ":" STR(__LINE__));
+		goto err;
+	}
+
+	ret_of = lseek(lkcd->fd, dh->dh_header_size, SEEK_SET);
+	if (ret_of < 0) {
+		perror("lseek failed in " __FILE__ ":" STR(__LINE__));
+		goto err;
+	}
+
+	hdr_buf = (char *)malloc(hdr_size);
+	if (!hdr_buf) {
+		perror("Could not allocate memory for dump header\n");
+		goto err;
+	}
+
+	ret_sz = read(lkcd->fd, (char *)hdr_buf, hdr_size);
+	if (ret_sz != hdr_size) {
+		perror("Could not read header " __FILE__ ":" STR(__LINE__));
+		goto err;
+	}
+
+
+	/*
+         * Though we have KL_NR_CPUS is 128, the header size is different
+         * CONFIG_NR_CPUS might be different in the kernel. Hence, need
+         * to find out how many CPUs are configured.
+         */
+        offset = offsetof(dump_header_asm_t, dha_smp_regs[0]);
+        nr_cpus = (hdr_size - offset) / sizeof(dump_CPU_info_t);
+
+	/* check for CPU overflow */
+	if (nr_cpus > NR_CPUS) {
+		fprintf(stderr, "CPU number too high %d (%s:%s)\n",
+				nr_cpus, __FILE__, __LINE__);
+		goto err;
+	}
+
+	/* parts that don't depend on the number of CPUs */
+	memcpy(&arch_hdr, (void *)hdr_buf, offset);
+
+	/* registers */
+	memcpy(&arch_hdr.dha_smp_regs, (void *)&hdr_buf[offset],
+			nr_cpus * sizeof(struct pt_regs));
+	offset += nr_cpus * sizeof(struct pt_regs);
+
+	/* current task */
+	memcpy(&arch_hdr.dha_smp_current_task, (void *)&hdr_buf[offset],
+			nr_cpus * sizeof(&arch_hdr.dha_smp_current_task[0]));
+	offset += nr_cpus * sizeof(&arch_hdr.dha_smp_current_task[0]);
+
+	/* stack */
+	memcpy(&arch_hdr.dha_stack, (void *)&hdr_buf[offset],
+			nr_cpus * sizeof(&arch_hdr.dha_stack[0]));
+	offset += nr_cpus * sizeof(&arch_hdr.dha_stack[0]);
+
+	/* stack_ptr */
+	memcpy(&arch_hdr.dha_stack_ptr, (void *)&hdr_buf[offset],
+			nr_cpus * sizeof(&arch_hdr.dha_stack_ptr[0]));
+	offset += nr_cpus * sizeof(&arch_hdr.dha_stack_ptr[0]);
+
+	if (arch_hdr.dha_magic_number != DUMP_ASM_MAGIC_NUMBER) {
+		fprintf(stderr, "Invalid magic number for x86_64\n");
+		goto err;
+	}
+
+#ifdef IA64
+	lkcd_dump_init_v8_ia64(lkcd->fd, &arch_hdr);
+#endif
+
+	memcpy(dha, &arch_hdr, sizeof(dump_header_asm_t));
+
+	return 0;
+
+err:
+	free(hdr_buf);
+	return -1;
+}
+#endif
+
 /*
  *  Verify and initialize the LKCD environment, storing the common data
  *  in the global lkcd_environment structure.
@@ -69,8 +174,16 @@ lkcd_dump_init_v8(FILE *fp, int fd, char
         lkcd->dump_header = dh;
 	if (lkcd->debug) 
 		dump_lkcd_environment(LKCD_DUMP_HEADER_ONLY);
+
+#ifndef NO_LKCD_DUMP_ARCH
+	if (lkcd_dump_init_v8_arch(dh, &dump_header_asm_v8) != 0) {
+		fprintf(stderr, "Warning: Failed to initialise "
+				"arch specific dump code\n");
+	}
+#endif
+
 #ifdef IA64
-	if ( (fix_addr_v8(fd) == -1) )
+	if ( (fix_addr_v8(&dump_header_asm_v8) == -1) )
 	    return FALSE;
 #endif
 
@@ -233,6 +346,29 @@ lkcd_dump_init_v8(FILE *fp, int fd, char
 	return TRUE;
 }
 
+int
+get_lkcd_regs_for_cpu(struct bt_info *bt, ulong *eip, ulong *esp)
+{
+	int cpu;
+
+	if (!bt || !bt->tc) {
+		fprintf(stderr, "get_lkcd_regs_for_cpu: invalid tc\n", cpu);
+		return -EINVAL;
+	}
+
+	cpu = bt->tc->processor;
+
+	if (cpu >= NR_CPUS) {
+		fprintf(stderr, "get_lkcd_regs_for_cpu, cpu (%d) too high\n", cpu);
+		return -EINVAL;
+	}
+
+#ifndef NO_LKCD_DUMP_ARCH
+	return get_lkcd_regs_for_cpu_arch(&dump_header_asm_v8, cpu, eip, esp);
+#else
+	return -ENOSYS;
+#endif
+}
 
 /*
  *  Return the current page's dp_size.




More information about the Crash-utility mailing list