>From 92fc495b4cd67837e22eec5f4a4bf309d580f2d4 Mon Sep 17 00:00:00 2001 From: Wang Chao 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)\n", + 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\n"); + } + 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\n"); + } + 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\n"); + if ((dd->nt_prstatus_percpu = malloc(NR_CPUS * sizeof(void*))) == NULL) + error(FATAL, "compressed kdump: cannot malloc pointer" + " to NT_PRSTATUS notes\n"); + 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\n"); + + if (FLAT_FORMAT()) { + if (!read_flattened_format(dd->dfd, offset, notes_buf, size)) { + error(INFO, "compressed kdump: cannot read notes data" + "\n"); + goto err; + } + } else { + if (lseek(dd->dfd, offset, SEEK_SET) == (off_t)-1) { + error(INFO, "compressed kdump: cannot lseek notes data\n"); + goto err; + } + if (read(dd->dfd, notes_buf, size) < size) { + error(INFO, "compressed kdump: cannot read notes data" + "\n"); + 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\n", + (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\n", + (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\n", FLAT_FORMAT() ? "[FLAT]" : ""); fprintf(fp, " dfd: %d\n", dd->dfd); fprintf(fp, " ofp: %lx\n", (ulong)dd->ofp); @@ -1101,6 +1266,13 @@ __diskdump_memory_dump(FILE *fp) (ulong)dd->sub_header_kdump->offset_note); fprintf(fp, " size_note: %lu\n", dd->sub_header_kdump->size_note); + fprintf(fp, " number_of_notes: %d\n", + dd->num_prstatus_notes); + for (i = 0; i < dd->num_prstatus_notes; i++) { + fprintf(fp, " notes[%d]: %lx\n", + i, (ulong)dd->nt_prstatus_percpu[i]); + } + dump_nt_prstatus_offset(fp); } fprintf(fp, "\n"); } else @@ -1226,3 +1398,8 @@ show_split_dumpfiles(void) fprintf(fp, "\n"); } } + +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\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,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)\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.3