[RFC v2] Use register value in elf note NT_PRSTATUS to do backtrace

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Dave and list,

I guess you still remember that a few weeks ago Wen has sent a patch which intends
to use register values in NT_PRSTATUS notes for backtracing if no panic task was
found. Thanks for your review and the suggestions are very useful.

However, Wen was busy with other work for these days, so I'll continue with this
work and now the 2rd version patch is attached.

v2: Address review feedbacks.
1) Set up a flag in pc->flags2 and it's determined during the diskdump file init
   procedure.
2) Seperate code according to the that flag.
3) Also, we've done some tests on dumpfile of xen kernel and the trouble described
   in the previous mail was gone.

So we're looking forward to your feedback and if you still have any problems with
it, please tell us.

Thanks,
Wang Chao
>From 7463a0e53b6e210d27fda23bfc66addcffba1cab Mon Sep 17 00:00:00 2001
From: Wang Chao <wang.chao@xxxxxxxxxxxxxx>
Date: Fri, 8 Apr 2011 23:59:08 +0800
Subject: [PATCH] Use register value in elf note NT_PRSTATUS to do backtrace

We have a new hardware to do dump, and use makedumpfile to generate
vmcore. Our hardware can work when the OS is out of controll(for
example: dead loop). When we use crash to analyze the vmcore, bt can
not work, because there is no panic task.

We have provide the value of register in the vmcore(the format is
elf_prstatus, it is same with normal kdump's vmcore). So we can use
it when we do not find panic task.

Based on previous patch from wency@xxxxxxxxxxxxxx
---
 defs.h     |    6 ++++
 diskdump.c |   92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 netdump.c  |   81 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 task.c     |    2 +
 x86.c      |   11 +++++++
 x86_64.c   |    9 ++++++
 6 files changed, 194 insertions(+), 7 deletions(-)

diff --git a/defs.h b/defs.h
index d01ace7..ffee605 100755
--- a/defs.h
+++ b/defs.h
@@ -428,7 +428,9 @@ struct program_context {
 	char *kvmdump_mapfile;          /* storage of physical to file offsets */
 	ulonglong flags2;               /* flags overrun */  
 #define FLAT          (0x1ULL)
+#define ELF_NOTES     (0x2ULL)
 #define FLAT_FORMAT() (pc->flags2 & FLAT)
+#define ELF_NOTES_VALID() (pc->flags2 & ELF_NOTES)
 	char *cleanup;
 	char *namelist_orig;
 	char *namelist_debug_orig;
@@ -1433,6 +1435,7 @@ struct offset_table {                    /* stash of commonly-used offsets */
 	long prio_array_queue;
 	long user_regs_struct_ebp;
 	long user_regs_struct_esp;
+	long user_regs_struct_eip;
 	long user_regs_struct_rip;
 	long user_regs_struct_cs;
 	long user_regs_struct_eflags;
@@ -4604,6 +4607,9 @@ ulong *diskdump_flags;
 int is_partial_diskdump(void);
 int dumpfile_is_split(void);
 void show_split_dumpfiles(void);
+void x86_process_elf_notes(void *, unsigned long);
+void *diskdump_get_prstatus_percpu(int);
+void map_cpus_to_prstatus_kdump_cmprs(void);
 
 /*
  * makedumpfile.c
diff --git a/diskdump.c b/diskdump.c
index 9a2f37f..84992ba 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -49,6 +49,7 @@ struct diskdump_data {
 	int	byte, bit;
 	char	*compressed_page;	/* copy of compressed page data */
 	char	*curbufptr;		/* ptr to uncompressed page buffer */
+	unsigned char *notes_buf;	/* copy of notes */
 
 	/* page cache */
 	struct page_cache_hdr {		/* header for each cached page */
@@ -67,6 +68,8 @@ struct diskdump_data {
 
 static struct diskdump_data diskdump_data = { 0 };
 static struct diskdump_data *dd = &diskdump_data;
+static unsigned char *nt_prstatus_percpu[NR_CPUS];
+static size_t num_prstatus_notes = 0;
 static int get_dump_level(void);
 
 ulong *diskdump_flags = &diskdump_data.flags;
@@ -84,6 +87,40 @@ int dumpfile_is_split(void)
 	return KDUMP_SPLIT();
 }
 
+void
+map_cpus_to_prstatus_kdump_cmprs(void)
+{
+	void **nt_ptr;
+	int online, i, j, nrcpus;
+	size_t size;
+
+	if (!(online = get_cpus_online()) || (online == kt->cpus))
+		return;
+
+	if (CRASHDEBUG(1))
+		error(INFO,
+		    "cpus: %d online: %d NT_PRSTATUS notes: %d (remapping)\n",
+			kt->cpus, online, num_prstatus_notes);
+
+	size = NR_CPUS * sizeof(void *);
+
+	nt_ptr = (void **)GETBUF(size);
+	BCOPY(nt_prstatus_percpu, nt_ptr, size);
+	BZERO(nt_prstatus_percpu, size);
+
+	/*
+	 *  Re-populate the array with the notes mapping to online cpus
+	 */
+	nrcpus = (kt->kernel_NR_CPUS ? kt->kernel_NR_CPUS : NR_CPUS);
+
+	for (i = 0, j = 0; i < nrcpus; i++) {
+		if (in_cpu_map(ONLINE, i))
+			nt_prstatus_percpu[i] = nt_ptr[j++];
+	}
+
+	FREEBUF(nt_ptr);
+}
+
 static void add_diskdump_data(char* name)
 {
 #define DDL_SIZE 16
@@ -185,12 +222,50 @@ static int open_dump_file(char *file)
 	return TRUE;
 }
 
+void x86_process_elf_notes(void *note_ptr, unsigned long size_note)
+{
+	Elf32_Nhdr *note32 = NULL;
+	Elf64_Nhdr *note64 = NULL;
+	size_t tot, len = 0;
+	static int num = 0;
+
+	for (tot = 0; tot < size_note; tot += len) {
+		if (machine_type("X86_64")) {
+			note64 = note_ptr + tot;
+
+			if (note64->n_type == NT_PRSTATUS) {
+				nt_prstatus_percpu[num] = (void *)note64;
+				num++;
+			}
+
+			len = sizeof(Elf64_Nhdr);
+			len = roundup(len + note64->n_namesz, 4);
+			len = roundup(len + note64->n_descsz, 4);
+		} else if (machine_type("X86")) {
+			note32 = note_ptr + tot;
+
+			if (note32->n_type == NT_PRSTATUS) {
+				nt_prstatus_percpu[num] = (void *)note32;
+				num++;
+			}
+
+			len = sizeof(Elf32_Nhdr);
+			len = roundup(len + note32->n_namesz, 4);
+			len = roundup(len + note32->n_descsz, 4);
+		}
+	}
+
+	if (num > 0) {
+		pc->flags2 |= ELF_NOTES;
+		num_prstatus_notes = num;
+	}
+}
+
 static int read_dump_header(char *file)
 {
 	struct disk_dump_header *header = NULL;
 	struct disk_dump_sub_header *sub_header = NULL;
 	struct kdump_sub_header *sub_header_kdump = NULL;
-	unsigned char *notes_buf = NULL;
 	size_t size;
 	int bitmap_len;
 	int block_size = (int)sysconf(_SC_PAGESIZE);
@@ -398,12 +473,12 @@ restart:
 		size = sub_header_kdump->size_note;
 		offset = sub_header_kdump->offset_note;
 
-		if ((notes_buf = malloc(size)) == NULL)
+		if ((dd->notes_buf = malloc(size)) == NULL)
 			error(FATAL, "compressed kdump: cannot malloc notes"
 				" buffer\n");
 
 		if (FLAT_FORMAT()) {
-			if (!read_flattened_format(dd->dfd, offset, notes_buf, size)) {
+			if (!read_flattened_format(dd->dfd, offset, dd->notes_buf, size)) {
 				error(INFO, "compressed kdump: cannot read notes data"
 					"\n");
 				goto err;
@@ -413,14 +488,14 @@ restart:
 				error(INFO, "compressed kdump: cannot lseek notes data\n");
 				goto err;
 			}
-			if (read(dd->dfd, notes_buf, size) < size) {
+			if (read(dd->dfd, dd->notes_buf, size) < size) {
 				error(INFO, "compressed kdump: cannot read notes data"
 					"\n");
 				goto err;
 			}
 		}
 
-		machdep->process_elf_notes(notes_buf, size);
+		machdep->process_elf_notes(dd->notes_buf, size);
 	}
 
 	/* For split dumpfile */
@@ -468,8 +543,6 @@ err:
 		free(sub_header);
 	if (sub_header_kdump)
 		free(sub_header_kdump);
-	if (notes_buf)
-		free(notes_buf);
 	if (dd->bitmap)
 		free(dd->bitmap);
 	if (dd->dumpable_bitmap)
@@ -1226,3 +1299,8 @@ show_split_dumpfiles(void)
 			fprintf(fp, "\n");
 	}
 }
+
+void *diskdump_get_prstatus_percpu(int cpu)
+{
+	return nt_prstatus_percpu[cpu];
+}
diff --git a/netdump.c b/netdump.c
index 7916df1..07cae6b 100644
--- a/netdump.c
+++ b/netdump.c
@@ -39,6 +39,7 @@ static physaddr_t xen_kdump_p2m(physaddr_t);
 static void check_dumpfile_size(char *);
 static int proc_kcore_init_32(FILE *fp);
 static int proc_kcore_init_64(FILE *fp);
+static char * get_regs_from_note(char *, ulong *, ulong *);
 
 #define ELFSTORE 1
 #define ELFREAD  0
@@ -2170,6 +2171,39 @@ get_netdump_regs(struct bt_info *bt, ulong *eip, ulong *esp)
 	}
 }
 
+/* get regs from elf note, and return the address of user_regs. */
+static char * get_regs_from_note(char *note, ulong *ip, ulong *sp)
+{
+	Elf32_Nhdr *note32;
+	Elf64_Nhdr *note64;
+	size_t len;
+	char *user_regs;
+	long offset_sp, offset_ip;
+
+	if (machine_type("X86_64")) {
+		note64 = (Elf64_Nhdr *)note;
+		len = sizeof(Elf64_Nhdr);
+		len = roundup(len + note64->n_namesz, 4);
+		len = roundup(len + note64->n_descsz, 4);
+		offset_sp = OFFSET(user_regs_struct_rsp);
+		offset_ip = OFFSET(user_regs_struct_rip);
+	} else if (machine_type("X86")) {
+		note32 = (Elf32_Nhdr *)note;
+		len = sizeof(Elf32_Nhdr);
+		len = roundup(len + note32->n_namesz, 4);
+		len = roundup(len + note32->n_descsz, 4);
+		offset_sp = OFFSET(user_regs_struct_esp);
+		offset_ip = OFFSET(user_regs_struct_eip);
+	} else
+		return NULL;
+
+	user_regs = note + len - SIZE(user_regs_struct) - sizeof(long);
+	*sp = ULONG(user_regs + offset_sp);
+	*ip = ULONG(user_regs + offset_ip);
+
+	return user_regs;
+}
+
 struct x86_64_user_regs_struct {
         unsigned long r15,r14,r13,r12,rbp,rbx,r11,r10;
         unsigned long r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
@@ -2186,6 +2220,7 @@ get_netdump_regs_x86_64(struct bt_info *bt, ulong *ripp, ulong *rspp)
         size_t len;
         char *user_regs;
 	ulong regs_size, rsp_offset, rip_offset;
+	ulong rip, rsp;
 
         if (is_task_active(bt->task)) 
                 bt->flags |= BT_DUMPFILE_SEARCH;
@@ -2232,6 +2267,28 @@ get_netdump_regs_x86_64(struct bt_info *bt, ulong *ripp, ulong *rspp)
 		bt->machdep = (void *)user_regs;
 	}
 
+	if (ELF_NOTES_VALID() && (bt->flags & BT_DUMPFILE_SEARCH) && DISKDUMP_DUMPFILE()) {
+		note = (Elf64_Nhdr *)
+			diskdump_get_prstatus_percpu(bt->tc->processor);
+		if (note == NULL)
+			goto skip_notes;
+
+		user_regs = get_regs_from_note((char *)note, &rip, &rsp);
+
+		if (CRASHDEBUG(1))
+			netdump_print("ELF prstatus rsp: %lx rip: %lx\n",
+				rsp, rip);
+
+		*rspp = rsp;
+		*ripp = rip;
+
+		if (*ripp && *rspp)
+			bt->flags |= BT_KDUMP_ELF_REGS;
+
+		bt->machdep = (void *)user_regs;
+	}
+
+skip_notes:
         machdep->get_stack_frame(bt, ripp, rspp);
 }
 
@@ -2423,6 +2480,30 @@ next_sysrq:
                 goto retry;
         }
 
+	if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) {
+		Elf32_Nhdr *note;
+		char *user_regs;
+		ulong ip, sp;
+
+		note = (Elf32_Nhdr *)
+			diskdump_get_prstatus_percpu(bt->tc->processor);
+		if (note == NULL)
+			goto skip_notes;
+
+		user_regs = get_regs_from_note((char *)note, &ip, &sp);
+		bt->flags |= BT_KDUMP_ELF_REGS;
+		if (is_kernel_text(ip) &&
+		    (((sp >= GET_STACKBASE(bt->task)) &&
+		      (sp < GET_STACKTOP(bt->task))) ||
+		    in_alternate_stack(bt->tc->processor, sp))) {
+			*eip = ip;
+			*esp = sp;
+			bt->flags |= BT_KERNEL_SPACE;
+			return;
+		}
+	}
+
+skip_notes:
 	if (CRASHDEBUG(1))
 		error(INFO, 
     "get_netdump_regs_x86: cannot find anything useful (task: %lx)\n", bt->task);
diff --git a/task.c b/task.c
index 266b8de..8d4d02b 100755
--- a/task.c
+++ b/task.c
@@ -458,6 +458,8 @@ task_init(void)
 	else {
 		if (KDUMP_DUMPFILE())
 			map_cpus_to_prstatus();
+		else if (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE())
+			map_cpus_to_prstatus_kdump_cmprs();
 		please_wait("determining panic task");
 		set_context(get_panic_context(), NO_PID);
 		please_wait_done();
diff --git a/x86.c b/x86.c
index c5cf010..4425ee1 100755
--- a/x86.c
+++ b/x86.c
@@ -1704,6 +1704,9 @@ x86_init(int when)
 
 	switch (when)
 	{
+	case SETUP_ENV:
+		machdep->process_elf_notes = x86_process_elf_notes;
+		break;
 	case PRE_SYMTAB:
 		machdep->verify_symbol = x86_verify_symbol;
                 if (pc->flags & KERNEL_DEBUG_QUERY)
@@ -1797,6 +1800,12 @@ x86_init(int when)
 		else
 			MEMBER_OFFSET_INIT(user_regs_struct_esp,
 				"user_regs_struct", "sp");
+		if (MEMBER_EXISTS("user_regs_struct", "eip"))
+			MEMBER_OFFSET_INIT(user_regs_struct_eip,
+				"user_regs_struct", "eip");
+		else
+			MEMBER_OFFSET_INIT(user_regs_struct_eip,
+				"user_regs_struct", "ip");
 		if (!VALID_STRUCT(user_regs_struct)) {
 			/*  Use this hardwired version -- sometimes the 
 			 *  debuginfo doesn't pick this up even though
@@ -1817,6 +1826,8 @@ x86_init(int when)
 				offsetof(struct x86_user_regs_struct, ebp);
 			ASSIGN_OFFSET(user_regs_struct_esp) =
 				offsetof(struct x86_user_regs_struct, esp);
+			ASSIGN_OFFSET(user_regs_struct_eip) =
+				offsetof(struct x86_user_regs_struct, eip);
 		}
 		MEMBER_OFFSET_INIT(thread_struct_cr3, "thread_struct", "cr3");
 		STRUCT_SIZE_INIT(cpuinfo_x86, "cpuinfo_x86");
diff --git a/x86_64.c b/x86_64.c
index eaf6373..6639cf2 100755
--- a/x86_64.c
+++ b/x86_64.c
@@ -124,6 +124,9 @@ x86_64_init(int when)
 
 	switch (when)
 	{
+	case SETUP_ENV:
+		machdep->process_elf_notes = x86_process_elf_notes;
+		break;
 	case PRE_SYMTAB:
 		machdep->verify_symbol = x86_64_verify_symbol;
                 machdep->machspec = &x86_64_machine_specific;
@@ -4043,6 +4046,12 @@ x86_64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *rip, ulong *rsp)
 				goto skip_stage;
 			}
 		}
+	} else if (ELF_NOTES_VALID()) {
+		user_regs = bt->machdep;
+		ur_rip = ULONG(user_regs +
+			OFFSET(user_regs_struct_rip));
+		ur_rsp = ULONG(user_regs +
+			OFFSET(user_regs_struct_rsp));
 	}
 
 	panic = FALSE;
-- 
1.7.1

--
Crash-utility mailing list
Crash-utility@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/crash-utility

[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux