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.
---
defs.h | 10 +++-
diskdump.c | 118 +++++++++++++++++++++++++++++++++++++++++---
netdump.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++-------------
task.c | 6 ++-
x86.c | 8 +++
x86_64.c | 6 ++
6 files changed, 263 insertions(+), 46 deletions(-)
diff --git a/defs.h b/defs.h
index af3c8ed..b926725 100644
--- a/defs.h
+++ b/defs.h
@@ -1426,6 +1426,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;
@@ -4567,13 +4568,16 @@ int xen_major_version(void);
int xen_minor_version(void);
int get_netdump_arch(void);
void *get_regs_from_elf_notes(struct task_context *);
-void map_cpus_to_prstatus(void);
+void map_cpus_to_prstatus(void **, size_t);
int arm_kdump_phys_base(ulong *);
int is_proc_kcore(char *, ulong);
int proc_kcore_init(FILE *);
int read_proc_kcore(int, void *, int, ulong, physaddr_t);
int write_proc_kcore(int, void *, int, ulong, physaddr_t);
int kcore_memory_dump(FILE *);
+void **netdump_get_prstatus(void);
+size_t netdump_get_num_prstatus(void);
+
/*
* diskdump.c
@@ -4595,6 +4599,10 @@ ulong *diskdump_flags;
int is_partial_diskdump(void);
int dumpfile_is_split(void);
void show_split_dumpfiles(void);
+int KDUMP_CMPRS_DUMPFILE(void);
+void **diskdump_get_prstatus(void);
+size_t diskdump_get_num_prstatus(void);
+void *diskdump_get_prstatus_percpu(int);
/*
* makedumpfile.c
diff --git a/diskdump.c b/diskdump.c
index 9a2f37f..074f1d7 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;
@@ -185,12 +188,86 @@ static int open_dump_file(char *file)
return TRUE;
}
+static size_t dump_elf_note(unsigned char *note_buf, size_t max_size, int machine)
+{
+ Elf64_Nhdr *note64;
+ Elf32_Nhdr *note32;
+ size_t len;
+ unsigned int type;
+ int i;
+
+ if (machine == EM_X86_64) {
+ note64 = (Elf64_Nhdr *)note_buf;
+ len = sizeof(Elf64_Nhdr);
+ len = roundup(len + note64->n_namesz, 4);
+ len = roundup(len + note64->n_descsz, 4);
+ type = note64->n_type;
+ } else {
+ note32 = (Elf32_Nhdr *)note_buf;
+ len = sizeof(Elf32_Nhdr);
+ len = roundup(len + note32->n_namesz, 4);
+ len = roundup(len + note32->n_descsz, 4);
+ type = note32->n_type;
+ }
+ if (len > max_size) {
+ /* this note is broken, do not restore it */
+ return max_size;
+ }
+
+ if (type != NT_PRSTATUS) {
+ /* This note segment does not contain copy of prstatus struct */
+ return len;
+ }
+
+ for (i = 0; i < NR_CPUS; i++) {
+ if (!nt_prstatus_percpu[i]) {
+ nt_prstatus_percpu[i] = (void *)note_buf;
+ num_prstatus_notes++;
+ break;
+ }
+ }
+ return len;
+}
+
+static void process_elf_notes(unsigned char *notes_buf, size_t size, int machine)
+{
+ size_t len = 0;
+ void **nt_ptr;
+ int online, i, j, nrcpus;
+ size_t buf_size;
+
+ while (len < size) {
+ len += dump_elf_note(notes_buf + len, size - len, machine);
+ }
+
+ /* copy from map_cpus_to_prstatus from netdump.c */
+ if (!(online = get_cpus_online()) || (online == kt->cpus))
+ return;
+
+ buf_size = NR_CPUS * sizeof(void *);
+
+ nt_ptr = (void **)GETBUF(buf_size);
+ BCOPY(nt_prstatus_percpu, nt_ptr, buf_size);
+ BZERO(nt_prstatus_percpu, buf_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 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);
@@ -394,16 +471,18 @@ restart:
/* process elf notes data */
if (KDUMP_CMPRS_VALID() && (dd->header->header_version >= 4)
&&
(sub_header_kdump->offset_note) &&
- (sub_header_kdump->size_note) && (machdep->process_elf_notes)) {
+ (sub_header_kdump->size_note) &&
+ (machdep->process_elf_notes || dd->machine_type == EM_X86_64 ||
+ dd->machine_type == EM_386)) {
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 +492,19 @@ 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);
+ if (machdep->process_elf_notes) {
+ /* s390 */
+ machdep->process_elf_notes(dd->notes_buf, size);
+ } else if (dd->machine_type == EM_X86_64 || dd->machine_type == EM_386) {
+ process_elf_notes(dd->notes_buf, size, dd->machine_type);
+ }
}
/* For split dumpfile */
@@ -468,8 +552,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 +1308,23 @@ show_split_dumpfiles(void)
fprintf(fp, "\n");
}
}
+
+int KDUMP_CMPRS_DUMPFILE(void)
+{
+ return KDUMP_CMPRS_VALID();
+}
+
+void **diskdump_get_prstatus(void)
+{
+ return (void **)nt_prstatus_percpu;
+}
+
+size_t diskdump_get_num_prstatus(void)
+{
+ return num_prstatus_notes;
+}
+
+void *diskdump_get_prstatus_percpu(int cpu)
+{
+ return nt_prstatus_percpu[cpu];
+}
diff --git a/netdump.c b/netdump.c
index 7916df1..6c1cf13 100644
--- a/netdump.c
+++ b/netdump.c
@@ -59,7 +59,7 @@ static int proc_kcore_init_64(FILE *fp);
* to remap the NT_PRSTATUS notes only to the online cpus.
*/
void
-map_cpus_to_prstatus(void)
+map_cpus_to_prstatus(void **nt_prstatus, size_t num_prstatus)
{
void **nt_ptr;
int online, i, j, nrcpus;
@@ -71,13 +71,13 @@ map_cpus_to_prstatus(void)
if (CRASHDEBUG(1))
error(INFO,
"cpus: %d online: %d NT_PRSTATUS notes: %d (remapping)\n",
- kt->cpus, online, nd->num_prstatus_notes);
+ kt->cpus, online, num_prstatus);
size = NR_CPUS * sizeof(void *);
nt_ptr = (void **)GETBUF(size);
- BCOPY(nd->nt_prstatus_percpu, nt_ptr, size);
- BZERO(nd->nt_prstatus_percpu, size);
+ BCOPY(nt_prstatus, nt_ptr, size);
+ BZERO(nt_prstatus, size);
/*
* Re-populate the array with the notes mapping to online cpus
@@ -86,12 +86,22 @@ map_cpus_to_prstatus(void)
for (i = 0, j = 0; i < nrcpus; i++) {
if (in_cpu_map(ONLINE, i))
- nd->nt_prstatus_percpu[i] = nt_ptr[j++];
+ nt_prstatus[i] = nt_ptr[j++];
}
FREEBUF(nt_ptr);
}
+void **netdump_get_prstatus(void)
+{
+ return nd->nt_prstatus_percpu;
+}
+
+size_t netdump_get_num_prstatus(void)
+{
+ return nd->num_prstatus_notes;
+}
+
/*
* Determine whether a file is a netdump/diskdump/kdump creation,
* and if TRUE, initialize the vmcore_data structure.
@@ -2170,60 +2180,71 @@ get_netdump_regs(struct bt_info *bt, ulong *eip, ulong *esp)
}
}
-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;
- unsigned long rip,cs,eflags;
- unsigned long rsp,ss;
- unsigned long fs_base, gs_base;
- unsigned long ds,es,fs,gs;
-};
+/* get regs from elf note, and return the address of user_regs. */
+static char * get_x86_regs_from_note(char *note, ulong *eip, ulong *esp)
+{
+ Elf32_Nhdr *note32;
+ size_t len;
+ char *user_regs;
+
+ note32 = (Elf32_Nhdr *)note;
+ len = sizeof(Elf32_Nhdr);
+ len = roundup(len + note32->n_namesz, 4);
+ len = roundup(len + note32->n_descsz, 4);
+
+ user_regs = note + len - SIZE(user_regs_struct) - sizeof(long);
+ *esp = ULONG(user_regs + OFFSET(user_regs_struct_esp));
+ *eip = ULONG(user_regs + OFFSET(user_regs_struct_eip));
+
+ return user_regs;
+}
+
+static char * get_x86_64_regs_from_note(char *note, ulong *rip, ulong *rsp)
+{
+ Elf64_Nhdr *note64;
+ size_t len;
+ char *user_regs;
+
+ note64 = (Elf64_Nhdr *)note;
+ len = sizeof(Elf64_Nhdr);
+ len = roundup(len + note64->n_namesz, 4);
+ len = roundup(len + note64->n_descsz, 4);
+
+ user_regs = note + len - SIZE(user_regs_struct) - sizeof(long);
+ *rsp = ULONG(user_regs + OFFSET(user_regs_struct_rsp));
+ *rip = ULONG(user_regs + OFFSET(user_regs_struct_rip));
+
+ return user_regs;
+}
void
get_netdump_regs_x86_64(struct bt_info *bt, ulong *ripp, ulong *rspp)
{
Elf64_Nhdr *note;
- 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;
if (((NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) &&
VALID_STRUCT(user_regs_struct) && (bt->task == tt->panic_task))
||
- (KDUMP_DUMPFILE() && (kt->flags & DWARF_UNWIND) &&
- (bt->flags & BT_DUMPFILE_SEARCH))) {
+ (KDUMP_DUMPFILE() && (bt->flags & BT_DUMPFILE_SEARCH))) {
if (nd->num_prstatus_notes > 1)
note = (Elf64_Nhdr *)
nd->nt_prstatus_percpu[bt->tc->processor];
else
note = (Elf64_Nhdr *)nd->nt_prstatus;
- len = sizeof(Elf64_Nhdr);
- len = roundup(len + note->n_namesz, 4);
- len = roundup(len + note->n_descsz, 4);
-
- regs_size = VALID_STRUCT(user_regs_struct) ?
- SIZE(user_regs_struct) :
- sizeof(struct x86_64_user_regs_struct);
- rsp_offset = VALID_MEMBER(user_regs_struct_rsp) ?
- OFFSET(user_regs_struct_rsp) :
- offsetof(struct x86_64_user_regs_struct, rsp);
- rip_offset = VALID_MEMBER(user_regs_struct_rip) ?
- OFFSET(user_regs_struct_rip) :
- offsetof(struct x86_64_user_regs_struct, rip);
-
- user_regs = ((char *)note + len) - regs_size - sizeof(long);
+ user_regs = get_x86_64_regs_from_note((char *)note, &rip, &rsp);
if (CRASHDEBUG(1))
netdump_print("ELF prstatus rsp: %lx rip: %lx\n",
- ULONG(user_regs + rsp_offset),
- ULONG(user_regs + rip_offset));
+ rsp, rip);
if (KDUMP_DUMPFILE()) {
- *rspp = ULONG(user_regs + rsp_offset);
- *ripp = ULONG(user_regs + rip_offset);
+ *rspp = rsp;
+ *ripp = rip;
if (*ripp && *rspp)
bt->flags |= BT_KDUMP_ELF_REGS;
@@ -2232,6 +2253,28 @@ get_netdump_regs_x86_64(struct bt_info *bt, ulong *ripp, ulong
*rspp)
bt->machdep = (void *)user_regs;
}
+ if (DISKDUMP_DUMPFILE() && (bt->flags & BT_DUMPFILE_SEARCH)) {
+ note = (Elf64_Nhdr *)diskdump_get_prstatus_percpu(bt->tc->processor);
+ if (note == NULL)
+ /* The version of makedumpfile may be less than 4 */
+ goto skip_notes;
+
+ user_regs = get_x86_64_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 +2466,52 @@ next_sysrq:
goto retry;
}
+ if (KDUMP_DUMPFILE()) {
+ Elf32_Nhdr *note;
+ char *user_regs;
+ ulong ip, sp;
+
+ if (nd->num_prstatus_notes > 1)
+ note = (Elf32_Nhdr *)
+ nd->nt_prstatus_percpu[bt->tc->processor];
+ else
+ note = (Elf32_Nhdr *)nd->nt_prstatus;
+
+ user_regs = get_x86_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;
+ }
+ }
+ if (DISKDUMP_DUMPFILE()) {
+ Elf32_Nhdr *note = (Elf32_Nhdr
*)diskdump_get_prstatus_percpu(bt->tc->processor);
+ char *user_regs;
+ ulong ip, sp;
+
+ if (note == NULL)
+ /* The version of makedumpfile may be less than 4 */
+ goto skip_notes;
+
+ user_regs = get_x86_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 290463f..de77033 100644
--- a/task.c
+++ b/task.c
@@ -457,7 +457,11 @@ task_init(void)
}
else {
if (KDUMP_DUMPFILE())
- map_cpus_to_prstatus();
+ map_cpus_to_prstatus(netdump_get_prstatus(),
+ netdump_get_num_prstatus());
+ if (KDUMP_CMPRS_DUMPFILE())
+ map_cpus_to_prstatus(diskdump_get_prstatus(),
+ diskdump_get_num_prstatus());
please_wait("determining panic task");
set_context(get_panic_context(), NO_PID);
please_wait_done();
diff --git a/x86.c b/x86.c
index ab2e7f3..e6fa2e2 100644
--- a/x86.c
+++ b/x86.c
@@ -1798,6 +1798,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
@@ -1818,6 +1824,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 853a1aa..48864f9 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -4009,6 +4009,12 @@ x86_64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *rip,
ulong *rsp)
goto skip_stage;
}
}
+ } else if (bt->machdep) {
+ 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