FAQ Search Today's Posts Mark Forums Read
» Video Reviews

» Linux Archive

Linux-archive is a website aiming to archive linux email lists and to make them easily accessible for linux users/developers.


» Sponsor

» Partners

» Sponsor

Go Back   Linux Archive > Redhat > Crash Utility

 
 
LinkBack Thread Tools
 
Old 04-26-2011, 03:15 PM
Dave Anderson
 
Default Use register value in elf note NT_PRSTATUS to do backtrace

----- Original Message -----
> 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

Hello Wang,

I haven't had a chance to run a complete test of this latest patch,
but I do have several suggestions.

diskdump.c:

Put the nt_prstatus_percpu array pointer and the num_prstatus_notes
into the diskdump_data structure -- like you did with the notes_buf.
Make nt_prstatus_percpu a pointer that gets allocated and initialized
only if necessary -- because if it's not used, it's a waste of space.

In read_dump_header(), when dd->notes_buf is malloc()'d, malloc() the
dd->nt_prstatus_percpu array.

And at the bottom of read_dump_header(), free both the dd->notes_buf
and the dd->nt_prstatus_percpu array if they exist, under the "err:"
label:

err:
...
if (dd->notes_buf)
free(dd->notes_buf);
if (dd->nt_prstatus_percpu)
free(dd->nt_prstatus_percpu;

In order to be able to debug/understand why things may fail, especially
due to the exclusion of prstatus notes of offline cpus, it would be
very helpful to be able to see the dd->nt_prstatus pointers for
each cpu. Similar to what is done with the vmcoreinfo data, can
you add a section to __diskdump_dump_header():

if (dh->header_version >= 3) {
fprintf(fp, " offset_vmcoreinfo: %lx
",
(ulong)dd->sub_header_kdump->offset_vmcoreinfo);
fprintf(fp, " size_vmcoreinfo: %lu
",
dd->sub_header_kdump->size_vmcoreinfo);
if (dd->sub_header_kdump->offset_vmcoreinfo &&
dd->sub_header_kdump->size_vmcoreinfo) {
dump_vmcoreinfo(fp);
}
}
if (dh->header_version >= 4) {
fprintf(fp, " offset_note: %lx
",
(ulong)dd->sub_header_kdump->offset_note);
fprintf(fp, " size_note: %lu
",
dd->sub_header_kdump->size_note);
here ==================>
}

Call a function that dumps the file offset of each cpu's nt_prstatus data.
Then, if necessary, any cpu's register set can be read with "rd -f".


netdump.c:

In get_netdump_regs_x86(), both of these settings are incorrect:

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;
}

Neither of these flags should be set there. BT_KDUMP_ELF_REGS
is only applicable to x86_64, and BT_KERNEL_SPACE is only
appicable to kvmdump dumpfiles.

Finally, in the interest of paranoia, give the user the capability
of *not* using this facility. In main.c, create a "--no_elf_notes"
option (similar to "--zero_excluded"), and have it set a NO_ELF_NOTES
bit in the globally-accessible "*diskdump_flags". As an example, see
how ZERO_EXCLUDED is set, used, and accessed. Then in read_dump_header()
skip the new ELF setup if that flag is set. It will not be necessary
to go as creating a new environment variable like "zero_excluded", but
it would be nice to be able to restrict its use on the crash invocation
command line.

Thanks,
Dave

--
Crash-utility mailing list
Crash-utility@redhat.com
https://www.redhat.com/mailman/listinfo/crash-utility
 
Old 05-06-2011, 09:16 AM
Wang Chao
 
Default Use register value in elf note NT_PRSTATUS to do backtrace

Hi Dave and all,

Thanks a lot for your suggestion.

According to the feedbacks, I made the following main changes since v2:
1) Add a new option --no_elf_notes. If specified, it'll skip processing
elf notes in kdump-compressed dump files.
2) Dump notes related information including total number of notes, their
buffers' pointer value and finally offset in the dump file.
3) Move nt_prstatus_percpu and nt_prstatus_percpu to diskdump_data struct.
However, bt command may need sp/ip from the notes later, so I guess
the buffer should not be freed after diskdump_read_header().

So the v3 patch is attached and we're looking forward to your opinions.

Thanks,
Wang Chao

Sent on 2011-4-26 23:15, Dave Anderson wrote:
>
>
> ----- Original Message -----
>> 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
>
> Hello Wang,
>
> I haven't had a chance to run a complete test of this latest patch,
> but I do have several suggestions.
>
> diskdump.c:
>
> Put the nt_prstatus_percpu array pointer and the num_prstatus_notes
> into the diskdump_data structure -- like you did with the notes_buf.
> Make nt_prstatus_percpu a pointer that gets allocated and initialized
> only if necessary -- because if it's not used, it's a waste of space.
>
> In read_dump_header(), when dd->notes_buf is malloc()'d, malloc() the
> dd->nt_prstatus_percpu array.
>
> And at the bottom of read_dump_header(), free both the dd->notes_buf
> and the dd->nt_prstatus_percpu array if they exist, under the "err:"
> label:
>
> err:
> ...
> if (dd->notes_buf)
> free(dd->notes_buf);
> if (dd->nt_prstatus_percpu)
> free(dd->nt_prstatus_percpu;
>
> In order to be able to debug/understand why things may fail, especially
> due to the exclusion of prstatus notes of offline cpus, it would be
> very helpful to be able to see the dd->nt_prstatus pointers for
> each cpu. Similar to what is done with the vmcoreinfo data, can
> you add a section to __diskdump_dump_header():
>
> if (dh->header_version >= 3) {
> fprintf(fp, " offset_vmcoreinfo: %lx
",
> (ulong)dd->sub_header_kdump->offset_vmcoreinfo);
> fprintf(fp, " size_vmcoreinfo: %lu
",
> dd->sub_header_kdump->size_vmcoreinfo);
> if (dd->sub_header_kdump->offset_vmcoreinfo &&
> dd->sub_header_kdump->size_vmcoreinfo) {
> dump_vmcoreinfo(fp);
> }
> }
> if (dh->header_version >= 4) {
> fprintf(fp, " offset_note: %lx
",
> (ulong)dd->sub_header_kdump->offset_note);
> fprintf(fp, " size_note: %lu
",
> dd->sub_header_kdump->size_note);
> here ==================>
> }
>
> Call a function that dumps the file offset of each cpu's nt_prstatus data.
> Then, if necessary, any cpu's register set can be read with "rd -f".
>
>
> netdump.c:
>
> In get_netdump_regs_x86(), both of these settings are incorrect:
>
> 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;
> }
>
> Neither of these flags should be set there. BT_KDUMP_ELF_REGS
> is only applicable to x86_64, and BT_KERNEL_SPACE is only
> appicable to kvmdump dumpfiles.
>
> Finally, in the interest of paranoia, give the user the capability
> of *not* using this facility. In main.c, create a "--no_elf_notes"
> option (similar to "--zero_excluded"), and have it set a NO_ELF_NOTES
> bit in the globally-accessible "*diskdump_flags". As an example, see
> how ZERO_EXCLUDED is set, used, and accessed. Then in read_dump_header()
> skip the new ELF setup if that flag is set. It will not be necessary
> to go as creating a new environment variable like "zero_excluded", but
> it would be nice to be able to restrict its use on the crash invocation
> command line.
>
> Thanks,
> Dave
>
>
>From 92fc495b4cd67837e22eec5f4a4bf309d580f2d4 Mon Sep 17 00:00:00 2001
From: Wang Chao <wang.chao@cn.fujitsu.com>
Date: Mon, 18 Apr 2011 09:51:21 +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@cn.fujitsu.com
---
defs.h | 7 +++
diskdump.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++
main.c | 4 ++
netdump.c | 79 +++++++++++++++++++++++++++
task.c | 2 +
x86.c | 11 ++++
x86_64.c | 9 +++
7 files changed, 289 insertions(+), 0 deletions(-)

diff --git a/defs.h b/defs.h
index d01ace7..744a5a9 100755
--- a/defs.h
+++ b/defs.h
@@ -242,6 +242,7 @@ struct number_option {
#define ERROR_EXCLUDED (0x4)
#define ZERO_EXCLUDED (0x8)
#define DUMPFILE_SPLIT (0x10)
+#define NO_ELF_NOTES (0x20)
#define DISKDUMP_VALID() (dd->flags & DISKDUMP_LOCAL)
#define KDUMP_CMPRS_VALID() (dd->flags & KDUMP_CMPRS_LOCAL)
#define KDUMP_SPLIT() (dd->flags & DUMPFILE_SPLIT)
@@ -428,7 +429,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 +1436,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 +4608,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..70a7275 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -49,6 +49,8 @@ struct diskdump_data {
int byte, bit;
char *compressed_page; /* copy of compressed page data */
char *curbufptr; /* ptr to uncompressed page buffer */
+ void **nt_prstatus_percpu;
+ uint num_prstatus_notes;

/* page cache */
struct page_cache_hdr { /* header for each cached page */
@@ -84,6 +86,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)
",
+ kt->cpus, online, dd->num_prstatus_notes);
+
+ size = NR_CPUS * sizeof(void *);
+
+ nt_ptr = (void **)GETBUF(size);
+ BCOPY(dd->nt_prstatus_percpu, nt_ptr, size);
+ BZERO(dd->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))
+ dd->nt_prstatus_percpu[i] = nt_ptr[j++];
+ }
+
+ FREEBUF(nt_ptr);
+}
+
static void add_diskdump_data(char* name)
{
#define DDL_SIZE 16
@@ -185,6 +221,58 @@ 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;
+ int num = 0;
+
+ /* --no_elf_notes is specified at invocation command line, so skip process elf notes data. */
+ if (dd->flags & NO_ELF_NOTES)
+ return;
+
+ for (tot = 0; tot < size_note; tot += len) {
+ if (machine_type("X86_64")) {
+ note64 = note_ptr + tot;
+
+ len = sizeof(Elf64_Nhdr);
+ len = roundup(len + note64->n_namesz, 4);
+ len = roundup(len + note64->n_descsz, 4);
+
+ if (note64->n_type == NT_PRSTATUS) {
+ if ((dd->nt_prstatus_percpu[num] = malloc(len)) == NULL) {
+ error(FATAL, "compressed kdump: cannot malloc buffer"
+ " for NR_PRSTATUS notes
");
+ }
+ BCOPY((void *)note64, dd->nt_prstatus_percpu[num], len);
+ num++;
+ }
+
+ } else if (machine_type("X86")) {
+ note32 = note_ptr + tot;
+
+ len = sizeof(Elf32_Nhdr);
+ len = roundup(len + note32->n_namesz, 4);
+ len = roundup(len + note32->n_descsz, 4);
+
+ if (note32->n_type == NT_PRSTATUS) {
+ if ((dd->nt_prstatus_percpu[num] = malloc(len)) == NULL) {
+ error(FATAL, "compressed kdump: cannot malloc buffer"
+ " for NR_PRSTATUS notes
");
+ }
+ BCOPY((void *)note32, dd->nt_prstatus_percpu[num], len);
+ num++;
+ }
+ }
+ }
+
+ if (num > 0) {
+ pc->flags2 |= ELF_NOTES;
+ dd->num_prstatus_notes = num;
+ }
+}
+
static int read_dump_header(char *file)
{
struct disk_dump_header *header = NULL;
@@ -402,6 +490,10 @@ restart:
error(FATAL, "compressed kdump: cannot malloc notes"
" buffer
");

+ if ((dd->nt_prstatus_percpu = malloc(NR_CPUS * sizeof(void*))) == NULL)
+ error(FATAL, "compressed kdump: cannot malloc pointer"
+ " to NT_PRSTATUS notes
");
+
if (FLAT_FORMAT()) {
if (!read_flattened_format(dd->dfd, offset, notes_buf, size)) {
error(INFO, "compressed kdump: cannot read notes data"
@@ -913,6 +1005,77 @@ err:
return;
}

+static void dump_nt_prstatus_offset(FILE *fp)
+{
+ unsigned char *notes_buf;
+ struct kdump_sub_header *sub_header_kdump = dd->sub_header_kdump;
+ size_t size;
+ off_t offset;
+ Elf32_Nhdr *note32 = NULL;
+ Elf64_Nhdr *note64 = NULL;
+ size_t tot, len = 0;
+
+ if (KDUMP_CMPRS_VALID() && (dd->header->header_version >= 4) &&
+ (sub_header_kdump->offset_note) &&
+ (sub_header_kdump->size_note) && (machdep->process_elf_notes)) {
+ size = sub_header_kdump->size_note;
+ offset = sub_header_kdump->offset_note;
+
+ if ((notes_buf = malloc(size)) == NULL)
+ error(FATAL, "compressed kdump: cannot malloc notes"
+ " buffer
");
+
+ if (FLAT_FORMAT()) {
+ if (!read_flattened_format(dd->dfd, offset, notes_buf, size)) {
+ error(INFO, "compressed kdump: cannot read notes data"
+ "
");
+ goto err;
+ }
+ } else {
+ if (lseek(dd->dfd, offset, SEEK_SET) == (off_t)-1) {
+ error(INFO, "compressed kdump: cannot lseek notes data
");
+ goto err;
+ }
+ if (read(dd->dfd, notes_buf, size) < size) {
+ error(INFO, "compressed kdump: cannot read notes data"
+ "
");
+ goto err;
+ }
+ }
+
+ fprintf(fp, " NT_PRSTATUS_offset: ");
+ for (tot = 0; tot < size; tot += len) {
+ if (machine_type("X86_64")) {
+ note64 = (void *)notes_buf + tot;
+ len = sizeof(Elf64_Nhdr);
+ len = roundup(len + note64->n_namesz, 4);
+ len = roundup(len + note64->n_descsz, 4);
+
+ if (note64->n_type == NT_PRSTATUS)
+ fprintf(fp, "%s%lx
",
+ (tot == 0) ? "" : " ",
+ offset + tot);
+
+ } else if (machine_type("X86")) {
+ note32 = (void *)notes_buf + tot;
+ len = sizeof(Elf32_Nhdr);
+ len = roundup(len + note32->n_namesz, 4);
+ len = roundup(len + note32->n_descsz, 4);
+
+ if (note32->n_type == NT_PRSTATUS)
+ fprintf(fp, "%s%lx
",
+ (tot == 0) ? "" : " ",
+ offset + tot);
+ }
+ }
+ }
+
+err:
+ if (notes_buf)
+ free(notes_buf);
+ return;
+}
+
/*
* This function is dump-type independent, and could be used
* to dump the diskdump_data structure contents and perhaps
@@ -942,6 +1105,8 @@ __diskdump_memory_dump(FILE *fp)
fprintf(fp, "%sERROR_EXCLUDED", others++ ? "|" : "");
if (dd->flags & ZERO_EXCLUDED)
fprintf(fp, "%sZERO_EXCLUDED", others++ ? "|" : "");
+ if (dd->flags & NO_ELF_NOTES)
+ fprintf(fp, "%sNO_ELF_NOTES", others++ ? "|" : "");
fprintf(fp, ") %s
", FLAT_FORMAT() ? "[FLAT]" : "");
fprintf(fp, " dfd: %d
", dd->dfd);
fprintf(fp, " ofp: %lx
", (ulong)dd->ofp);
@@ -1101,6 +1266,13 @@ __diskdump_memory_dump(FILE *fp)
(ulong)dd->sub_header_kdump->offset_note);
fprintf(fp, " size_note: %lu
",
dd->sub_header_kdump->size_note);
+ fprintf(fp, " number_of_notes: %d
",
+ dd->num_prstatus_notes);
+ for (i = 0; i < dd->num_prstatus_notes; i++) {
+ fprintf(fp, " notes[%d]: %lx
",
+ i, (ulong)dd->nt_prstatus_percpu[i]);
+ }
+ dump_nt_prstatus_offset(fp);
}
fprintf(fp, "
");
} else
@@ -1226,3 +1398,8 @@ show_split_dumpfiles(void)
fprintf(fp, "
");
}
}
+
+void *diskdump_get_prstatus_percpu(int cpu)
+{
+ return dd->nt_prstatus_percpu[cpu];
+}
diff --git a/main.c b/main.c
index 8c30fed..85a1531 100755
--- a/main.c
+++ b/main.c
@@ -62,6 +62,7 @@ static struct option long_options[] = {
{"mod", required_argument, 0, 0},
{"kvmhost", required_argument, 0, 0},
{"kvmio", required_argument, 0, 0},
+ {"no_elf_notes", 0, 0, 0},
{0, 0, 0, 0}
};

@@ -177,6 +178,9 @@ main(int argc, char **argv)
else if (STREQ(long_options[option_index].name, "zero_excluded"))
*diskdump_flags |= ZERO_EXCLUDED;

+ else if (STREQ(long_options[option_index].name, "no_elf_notes"))
+ *diskdump_flags |= NO_ELF_NOTES;
+
else if (STREQ(long_options[option_index].name, "no_panic"))
tt->flags |= PANIC_TASK_NOT_FOUND;

diff --git a/netdump.c b/netdump.c
index 7916df1..918de0f 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
",
+ 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,28 @@ 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);
+ 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;
+ return;
+ }
+ }
+
+skip_notes:
if (CRASHDEBUG(1))
error(INFO,
"get_netdump_regs_x86: cannot find anything useful (task: %lx)
", 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.3

--
Crash-utility mailing list
Crash-utility@redhat.com
https://www.redhat.com/mailman/listinfo/crash-utility
 
Old 05-06-2011, 04:11 PM
Dave Anderson
 
Default Use register value in elf note NT_PRSTATUS to do backtrace

Hello Wang,

I've re-studied this section, and I still have a few more issues
to resolve with this patch. But this should be the final review,
and I will wait for your v4 update before releasing 5.1.5.

> 1) Add a new option --no_elf_notes. If specified, it'll skip processing
> elf notes in kdump-compressed dump files.

OK good -- but please move the (dd->flags & NO_ELF_NOTES) check from
x86_process_elf_notes() into read_dump_header() so as to prevent the
allocation of any buffers if they're never going to be used, i.e.:

/* process elf notes data */
if (KDUMP_CMPRS_VALID() && !(dd->flags & NO_ELF_NOTES) &&
(dd->header->header_version >= 4) &&

And please make the --no_elf_notes option only applicable to x86 and x86_64
in order to prevent it from being applied to the s390x arch:

else if (STREQ(long_options[option_index].name, "no_elf_notes")) {
if (machine_type("X86") || machine_type("X86_64"))
*diskdump_flags |= NO_ELF_NOTES;
else
error(INFO,
"--no_elf_notes is only applicable to ",
"the X86 and X86_64 architectures.
");
}

> 2) Dump notes related information including total number of notes, their
> buffers' pointer value and finally offset in the dump file.
> 3) Move nt_prstatus_percpu and nt_prstatus_percpu to diskdump_data struct.
> However, bt command may need sp/ip from the notes later, so I guess
> the buffer should not be freed after diskdump_read_header().

I wish I had understood the s390x implementation a bit better when I
reviewed your v2 patch. Currently read_dump_header() malloc's a
single "notes_buf" buffer and copies the complete ELF notes section
from the dumpfile into it. I believe (but am not completely sure) that
the s390x code references pieces of that buffer, and that's why they
originally kept the "notes_buf" buffer permanently allocated. That
being the case, the "notes_buf" pointer should be moved to the
diskdump header.

Anyway, in the case of x86/x86_64, your patch:

(1) malloc's dd->nt_prstatus_percpu as an array of pointers,
(2) malloc's a bunch of individual per-cpu ELF prstatus buffers, and
then copies the ELF data from the "notes_buf" buffer into
each per-cpu buffer.
(4) puts a pointer to each per-cpu buffer into each dd->nt_prstatus_percpu
pointer.

But given that the original "notes_buf" buffer is permanent, your patch
should only have to store per-cpu pointers into the original note_buf
buffer -- rather than redundantly malloc'ing a bunch of new buffers that
contain copies of what's already permanently available. So I would suggest
setting it up like this:

dd->nt_prstatus_percpu[cpu] => points into relevant "notes_buf" location

Also, even though the s390x will not utilize the dd->nt_prstatus_percpu[]
array, your dump_nt_prstatus_offset() function should be usable by
the s390x since it uses the same Elf64_Nhdr as x86_64.

And lastly, two other minor things:

Move the user_regs_struct_eip to the bottom of the offset_table.
The offset_table is exported to extension modules, so changing
members in the middle of the structure could easily break a module
that was compiled against an earlier version. Note that it has
this at the top:

struct offset_table { /* stash of commonly-used offsets */
long list_head_next; /* add new entries to end of table */
long list_head_prev;
long task_struct_pid;
long task_struct_state;
...

Finally, add a display of the user_regs_struct_eip offset_table entry value
into dump_offset_table(), which is used by "help -o". In that function you
can put the user_regs_struct_eip display next to its other user_regs_struct
partners.

Thanks,
Dave

--
Crash-utility mailing list
Crash-utility@redhat.com
https://www.redhat.com/mailman/listinfo/crash-utility
 
Old 05-09-2011, 08:02 PM
Dave Anderson
 
Default Use register value in elf note NT_PRSTATUS to do backtrace

----- Original Message -----
> Hi Dave and all,
>
> Sent on 2011-5-7 0:11, Dave Anderson wrote:
>
> >> 2) Dump notes related information including total number of notes, their
> >> buffers' pointer value and finally offset in the dump file.
> >> 3) Move nt_prstatus_percpu and nt_prstatus_percpu to diskdump_data struct.
> >> However, bt command may need sp/ip from the notes later, so I guess
> >> the buffer should not be freed after diskdump_read_header().
> >
> > I wish I had understood the s390x implementation a bit better when I
> > reviewed your v2 patch. Currently read_dump_header() malloc's a
> > single "notes_buf" buffer and copies the complete ELF notes section
> > from the dumpfile into it. I believe (but am not completely sure) that
> > the s390x code references pieces of that buffer, and that's why they
> > originally kept the "notes_buf" buffer permanently allocated. That
> > being the case, the "notes_buf" pointer should be moved to the
> > diskdump header.
> >
> > Anyway, in the case of x86/x86_64, your patch:
> >
> > (1) malloc's dd->nt_prstatus_percpu as an array of pointers,
> > (2) malloc's a bunch of individual per-cpu ELF prstatus buffers, and
> > then copies the ELF data from the "notes_buf" buffer into
> > each per-cpu buffer.
> > (4) puts a pointer to each per-cpu buffer into each dd->nt_prstatus_percpu
> > pointer.
> >
> > But given that the original "notes_buf" buffer is permanent, your patch
> > should only have to store per-cpu pointers into the original note_buf
> > buffer -- rather than redundantly malloc'ing a bunch of new buffers that
> > contain copies of what's already permanently available. So I would suggest
> > setting it up like this:
> >
> > dd->nt_prstatus_percpu[cpu] => points into relevant "notes_buf"
> > location
> >
>
> Oh, sorry for my misunderstood.
>
> Since we will later refer to ip/sp value in the notes_buf, we should keep the
> notes_buf permanently and do not free them at the end of read_diskdump_header.
> s390x don't have such problem because the notes are copied to the s390x_cpu_vec
> when processing notes_buf.

The dd->notes_buf only needs to be kept permanently if the read_dump_header()
function succeeds (returns TRUE). If we do a "goto err" in read_dump_header(),
that means that dumpfile is not being recognized as a diskdump or compressed
kdump, and it returns FALSE. When that is the case, there is no possible way
that we could ever refer to the ip/sp values at a later point in time.

Accordingly, I have added this to your patch:

...
return TRUE;

err:
free(header);
if (sub_header)
free(sub_header);
if (sub_header_kdump)
free(sub_header_kdump);
if (dd->bitmap)
free(dd->bitmap);
if (dd->dumpable_bitmap)
free(dd->dumpable_bitmap);
+ if (dd->notes_buf)
+ free(dd->notes_buf);
+ if (dd->nt_prstatus_percpu)
+ free(dd->nt_prstatus_percpu);

dd->flags &= ~(DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL);
return FALSE;
}


> So just as what you suggested, notes_buf was moved to diskdump header in the
> attached v4 patch and nt_prstatus_percpu will store the address in notes_buf then.

I also slightly modified the patches to get_netdump_regs_x86() and
get_netdump_regs_x86_64() to avoid the goto's, but with no functional
changes.

Queued for crash version 5.1.5.

Thanks,
Dave


--
Crash-utility mailing list
Crash-utility@redhat.com
https://www.redhat.com/mailman/listinfo/crash-utility
 
Old 05-09-2011, 08:35 PM
Dave Anderson
 
Default Use register value in elf note NT_PRSTATUS to do backtrace

----- Original Message -----

> I also slightly modified the patches to get_netdump_regs_x86() and
> get_netdump_regs_x86_64() to avoid the goto's, but with no functional
> changes.
>
> Queued for crash version 5.1.5.
>
> Thanks,
> Dave

One other thing -- out of sheer paranoia, I've also added this to the
end of read_dump_header():

dd->flags &= ~(DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL);
+ pc->flags2 &= ~ELF_NOTES;
return FALSE;
}

That will prevent any potential problems with your patch to
x86_64_get_dumpfile_stack_frame():

} 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));
}

All of the other places that you use ELF_NOTES_VALID(), you
also check DISKDUMP_DUMPFILE().

Dave

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

Thread Tools




All times are GMT. The time now is 09:22 AM.

VBulletin, Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Content Relevant URLs by vBSEO ©2007, Crawlability, Inc.
Copyright 2007 - 2008, www.linux-archive.org