main.c global_data.c help.c task.c kernel.c dev.c s390.c s390x.c ppc64.c x86_64.c cmdline.c lkcd_x86_trace.c netdump.c xendump.c unwind.c kvmdump.c qemu.c qemu-load.c defs.h netdump.h xendump.h kvmdump.h lkcd_x86_trace.h extensions/trace.c --- crash-5.0.3/main.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/main.c 2010-05-17 16:16:54.000000000 -0400 @@ -1,8 +1,8 @@ /* main.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -420,6 +420,14 @@ main(int argc, char **argv) pc->readmem = read_kvmdump; pc->writemem = write_kvmdump; + } else if (is_kvmdump_mapfile(argv[optind])) { + if (pc->kvmdump_mapfile) { + error(INFO, + "too many KVM map file arguments\n"); + program_usage(SHORT_FORM); + } + pc->kvmdump_mapfile = argv[optind]; + } else if (is_xendump(argv[optind])) { if (pc->flags & MEMORY_SOURCES) { error(INFO, @@ -1170,6 +1178,7 @@ dump_program_context(void) fprintf(fp, " system_map: %s\n", pc->system_map); fprintf(fp, " namelist_debug: %s\n", pc->namelist_debug); fprintf(fp, " debuginfo_file: %s\n", pc->debuginfo_file); + fprintf(fp, " kvmdump_mapfile: %s\n", pc->kvmdump_mapfile); fprintf(fp, " memory_module: %s\n", pc->memory_module); fprintf(fp, " memory_device: %s\n", pc->memory_device); fprintf(fp, " machine_type: %s\n", pc->machine_type); --- crash-5.0.3/global_data.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/global_data.c 2010-05-17 16:16:59.000000000 -0400 @@ -1,8 +1,8 @@ /* global_data.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -89,6 +89,7 @@ struct command_table_entry linux_command {"list", cmd_list, help__list, REFRESH_TASK_TABLE}, {"log", cmd_log, help_log, 0}, {"mach", cmd_mach, help_mach, 0}, + {"map", cmd_map, help_map, HIDDEN_COMMAND}, {"mod", cmd_mod, help_mod, 0}, {"mount", cmd_mount, help_mount, 0}, {"net", cmd_net, help_net, REFRESH_TASK_TABLE}, --- crash-5.0.3/help.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/help.c 2010-05-17 16:17:05.000000000 -0400 @@ -1,8 +1,8 @@ /* help.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -1729,6 +1729,40 @@ char *help_mach[] = { NULL }; +char *help_map[] = { +"map", +"store KVM dumpfile memory map data", +"[-a][-f [filename]]", +" The layout of KVM guest dumpfiles created with \"virsh dump\" does not allow", +" the crash utility to access the system's memory in a random access manner.", +" Therefore, during crash session initialization, a potentially time-consuming", +" dumpfile scan procedure is required to create a physical-memory-to-file-offset", +" map for use during the session.", +" ", +" This command may be used to append the memory map data to the dumpfile or", +" to store it in a permanent file. After this has been done, subsequent crash", +" sessions using the dumpfile will no longer require the scan procedure:", +" ", +" -a Append the memory map to the end of the KVM dumpfile.", +" -f Create a memory map file. If no filename argument is entered, the", +" filename will consist of the dumpfile name with \".map\" appended,", +" and will be located in the same directory as the dumpfile; it will", +" be recognized and used automatically during subsequent %s sessions.", +" However, if a \"filename\" argument is entered, and the default location", +" and naming convention are not used, then the new memory map file will", +" have to be added to the %s command line during invocation.", +"\nEXAMPLES", +" %s> map", +" MAP FILE IN USE: vmcore.map", +" %s> map -a", +" MAP FILE APPENDED TO: vmcore", +" %s> map -f", +" MAP FILE CREATED: vmcore.map", +" %s> map -f /tmp/vmcore.map", +" MAP FILE CREATED: /tmp/vmcore.map", +NULL +}; + char *help_timer[] = { "timer", "timer queue data", --- crash-5.0.3/task.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/task.c 2010-05-06 10:53:11.000000000 -0400 @@ -1,8 +1,8 @@ /* task.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/kernel.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/kernel.c 2010-05-17 16:17:11.000000000 -0400 @@ -3872,6 +3872,10 @@ display_sys_stats(void) fprintf(fp, " [PARTIAL DUMP]"); fprintf(fp, "\n"); + + if (KVMDUMP_DUMPFILE() && pc->kvmdump_mapfile) + fprintf(fp, " MAPFILE: %s\n", + pc->kvmdump_mapfile); } fprintf(fp, " CPUS: %d\n", --- crash-5.0.3/dev.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/dev.c 2010-05-06 10:54:31.000000000 -0400 @@ -1,8 +1,8 @@ /* dev.c - core analysis suite * * Copyright (C) 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -100,7 +100,8 @@ cmd_dev(void) return; case 'p': - if (machine_type("S390X")) + if (machine_type("S390X") || + (THIS_KERNEL_VERSION >= LINUX(2,6,26))) option_not_supported(c); do_pci(); return; --- crash-5.0.3/s390.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/s390.c 2010-04-26 13:57:43.000000000 -0400 @@ -603,11 +603,16 @@ s390_back_trace_cmd(struct bt_info *bt) unsigned long async_start = 0, async_end = 0; unsigned long panic_start = 0, panic_end = 0; unsigned long stack_end, stack_start, stack_base; + int cpu = bt->tc->processor; if (bt->hp && bt->hp->eip) { error(WARNING, "instruction pointer argument ignored on this architecture!\n"); } + if (is_task_active(bt->task) && !(kt->cpu_flags[cpu] & ONLINE)) { + fprintf(fp, " CPU offline\n"); + return; + } ksp = bt->stkptr; /* print lowcore and get async stack when task has cpu */ --- crash-5.0.3/s390x.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/s390x.c 2010-04-26 14:00:22.000000000 -0400 @@ -836,11 +836,16 @@ s390x_back_trace_cmd(struct bt_info *bt) unsigned long panic_start = 0, panic_end = 0; unsigned long stack_end, stack_start, stack_base; unsigned long r14; + int cpu = bt->tc->processor; if (bt->hp && bt->hp->eip) { error(WARNING, "instruction pointer argument ignored on this architecture!\n"); } + if (is_task_active(bt->task) && !(kt->cpu_flags[cpu] & ONLINE)) { + fprintf(fp, " CPU offline\n"); + return; + } ksp = bt->stkptr; /* print lowcore and get async stack when task has cpu */ --- crash-5.0.3/ppc64.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/ppc64.c 2010-04-22 15:30:41.000000000 -0400 @@ -751,8 +751,8 @@ void ppc64_vmemmap_init(void) ld = &list_data; BZERO(ld, sizeof(struct list_data)); - if (!readmem(symbol_value("vmemmap_list")+OFFSET(list_head_next), - KVADDR, &ld->start, sizeof(void *), "vmemmap_list.next", + if (!readmem(symbol_value("vmemmap_list"), + KVADDR, &ld->start, sizeof(void *), "vmemmap_list", RETURN_ON_ERROR)) return; ld->end = symbol_value("vmemmap_list"); @@ -771,27 +771,26 @@ void ppc64_vmemmap_init(void) vmemmap_buf = GETBUF(backing_size); for (i = 0; i < cnt; i++) { if (!readmem(vmemmap_list[i], KVADDR, vmemmap_buf, - backing_size, "vmemmap_backing", RETURN_ON_ERROR)) + backing_size, "vmemmap_backing", RETURN_ON_ERROR)) { + free(ms->vmemmap_list); goto out; + } ms->vmemmap_list[i].phys = ULONG(vmemmap_buf + phys_offset); ms->vmemmap_list[i].virt = ULONG(vmemmap_buf + virt_addr_offset); - } - if (ms->vmemmap_base != ms->vmemmap_list[0].virt) { - ms->vmemmap_base = ms->vmemmap_list[0].virt; - if (CRASHDEBUG(1)) - fprintf(fp, - "ppc64_vmemmap_init: vmemmap base: %lx\n", - ms->vmemmap_base); + if (ms->vmemmap_list[i].virt < ms->vmemmap_base) + ms->vmemmap_base = ms->vmemmap_list[i].virt; } - ms->vmemmap_cnt = cnt; + ms->vmemmap_cnt = cnt; machdep->flags |= VMEMMAP_AWARE; + if (CRASHDEBUG(1)) + fprintf(fp, "ppc64_vmemmap_init: vmemmap base: %lx\n", + ms->vmemmap_base); out: FREEBUF(vmemmap_buf); FREEBUF(vmemmap_list); - machdep->flags |= VMEMMAP_AWARE; } /* --- crash-5.0.3/x86_64.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/x86_64.c 2010-05-20 14:15:06.000000000 -0400 @@ -80,7 +80,9 @@ static void x86_64_post_init(void); static void parse_cmdline_args(void); static void x86_64_clear_machdep_cache(void); static void x86_64_irq_eframe_link_init(void); +static void x86_64_thread_return_init(void); static void x86_64_framepointer_init(void); +static void x86_64_xendump_phys_base(void); static int x86_64_xendump_p2m_create(struct xendump_data *); static char *x86_64_xendump_load_page(ulong, struct xendump_data *); static int x86_64_xendump_page_index(ulong, struct xendump_data *); @@ -309,10 +311,19 @@ x86_64_init(int when) } MEMBER_OFFSET_INIT(user_regs_struct_rip, "user_regs_struct", "rip"); + if (INVALID_MEMBER(user_regs_struct_rip)) + MEMBER_OFFSET_INIT(user_regs_struct_rip, + "user_regs_struct", "ip"); MEMBER_OFFSET_INIT(user_regs_struct_rsp, "user_regs_struct", "rsp"); + if (INVALID_MEMBER(user_regs_struct_rsp)) + MEMBER_OFFSET_INIT(user_regs_struct_rsp, + "user_regs_struct", "sp"); MEMBER_OFFSET_INIT(user_regs_struct_eflags, "user_regs_struct", "eflags"); + if (INVALID_MEMBER(user_regs_struct_eflags)) + MEMBER_OFFSET_INIT(user_regs_struct_eflags, + "user_regs_struct", "flags"); MEMBER_OFFSET_INIT(user_regs_struct_cs, "user_regs_struct", "cs"); MEMBER_OFFSET_INIT(user_regs_struct_ss, @@ -391,6 +402,7 @@ x86_64_init(int when) } x86_64_irq_eframe_link_init(); x86_64_framepointer_init(); + x86_64_thread_return_init(); break; case POST_VM: @@ -602,6 +614,7 @@ x86_64_dump_machdep_table(ulong arg) if (ms->crash_nmi_rsp) fprintf(fp, "\n"); fprintf(fp, " vsyscall_page: %lx\n", ms->vsyscall_page); + fprintf(fp, " thread_return: %lx\n", ms->thread_return); fprintf(fp, " stkinfo: isize: %d\n", ms->stkinfo.isize); @@ -1974,7 +1987,7 @@ x86_64_verify_symbol(const char *name, u if (STRNEQ(name, "per_cpu") || STREQ(name, "__per_cpu_end")) return TRUE; - if (type == 'V') + if ((type == 'V') || (type == 'd') || (type == 'D')) return TRUE; } @@ -2309,6 +2322,56 @@ text_lock_function(char *name, struct bt } +/* + * As of 2.6.29, the handy check for the "error_exit:" label + * no longer applies; it became an entry point that was jmp'd to + * after the exception handler was called. Therefore, if the + * return address is an offset from any of these functions, + * then the exception frame should be checked for: + * + * .macro errorentry sym do_sym + * errorentry invalid_TSS do_invalid_TSS + * errorentry segment_not_present do_segment_not_present + * errorentry alignment_check do_alignment_check + * errorentry xen_stack_segment do_stack_segment + * errorentry general_protection do_general_protection + * errorentry page_fault do_page_fault + * + * .macro zeroentry sym do_sym + * zeroentry divide_error do_divide_error + * zeroentry overflow do_overflow + * zeroentry bounds do_bounds + * zeroentry invalid_op do_invalid_op + * zeroentry device_not_available do_device_not_available + * zeroentry coprocessor_segment_overrun do_coprocessor_segment_overrun + * zeroentry spurious_interrupt_bug do_spurious_interrupt_bug + * zeroentry coprocessor_error do_coprocessor_error + * zeroentry simd_coprocessor_error do_simd_coprocessor_error + * zeroentry xen_hypervisor_callback xen_do_hypervisor_callback + * zeroentry xen_debug do_debug + * zeroentry xen_int3 do_int3 +*/ +static const char *exception_functions[] = { + "invalid_TSS", + "segment_not_present", + "alignment_check", + "xen_stack_segment", + "general_protection", + "page_fault", + "divide_error", + "overflow", + "bounds", + "invalid_op", + "device_not_available", + "coprocessor_segment_overrun", + "spurious_interrupt_bug", + "coprocessor_error", + "simd_coprocessor_error", + "xen_hypervisor_callback", + "xen_debug", + "xen_int3", + NULL, +}; /* * print one entry of a stack trace @@ -2325,7 +2388,7 @@ x86_64_print_stack_entry(struct bt_info ulong rsp, offset, locking_func; struct syment *sp, *spl; char *name; - int result; + int i, result; long eframe_check; char buf[BUFSIZE]; @@ -2373,8 +2436,16 @@ x86_64_print_stack_entry(struct bt_info } } - if (STREQ(name, "invalid_op")) - eframe_check = 8; + if ((THIS_KERNEL_VERSION >= LINUX(2,6,29)) && + (eframe_check == -1) && offset && + !(bt->flags & (BT_EXCEPTION_FRAME|BT_START|BT_SCHEDULE))) { + for (i = 0; exception_functions[i]; i++) { + if (STREQ(name, exception_functions[i])) { + eframe_check = 8; + break; + } + } + } if (bt->flags & BT_SCHEDULE) name = "schedule"; @@ -2587,6 +2658,7 @@ x86_64_low_budget_back_trace_cmd(struct struct machine_specific *ms; ulong last_process_stack_eframe; ulong user_mode_eframe; + char *rip_symbol; /* * User may have made a run-time switch. @@ -2873,7 +2945,9 @@ in_exception_stack: */ if (!done && !(bt->flags & (BT_TEXT_SYMBOLS|BT_EXCEPTION_STACK|BT_IRQSTACK)) && - STREQ(closest_symbol(bt->instptr), "thread_return")) { + (rip_symbol = closest_symbol(bt->instptr)) && + (STREQ(rip_symbol, "thread_return") || + STREQ(rip_symbol, "schedule"))) { bt->flags |= BT_SCHEDULE; i = (rsp - bt->stackbase)/sizeof(ulong); x86_64_print_stack_entry(bt, ofp, level, @@ -4076,7 +4150,7 @@ x86_64_get_sp(struct bt_info *bt) /* * Get the saved PC from the task's thread_struct if it exists; - * otherwise just use the "thread_return" label value. + * otherwise just use the pre-determined thread_return value. */ static ulong x86_64_get_pc(struct bt_info *bt) @@ -4084,7 +4158,7 @@ x86_64_get_pc(struct bt_info *bt) ulong offset, rip; if (INVALID_MEMBER(thread_struct_rip)) - return symbol_value("thread_return"); + return machdep->machspec->thread_return; if (tt->flags & THREAD_INFO) { readmem(bt->task + OFFSET(task_struct_thread) + @@ -4094,7 +4168,7 @@ x86_64_get_pc(struct bt_info *bt) if (rip) return rip; else - return symbol_value("thread_return"); + return machdep->machspec->thread_return; } offset = OFFSET(task_struct_thread) + OFFSET(thread_struct_rip); @@ -4809,6 +4883,52 @@ x86_64_framepointer_init(void) machdep->flags |= FRAMEPOINTER; } +static void +x86_64_thread_return_init(void) +{ + int found; + struct syment *sp, *spn; + ulong max_instructions, address; + char buf[BUFSIZE]; + + if ((sp = symbol_search("thread_return"))) { + machdep->machspec->thread_return = sp->value; + return; + } + + if (!(sp = symbol_search("schedule")) || + !(spn = next_symbol(NULL, sp))) { + error(WARNING, "schedule: symbol does not exist\n"); + return; + } + max_instructions = spn->value - sp->value; + found = FALSE; + + open_tmpfile(); + + sprintf(buf, "x/%ldi 0x%lx", + max_instructions, sp->value); + + if (!gdb_pass_through(buf, pc->tmpfile, GNU_RETURN_ON_ERROR)) + return; + + rewind(pc->tmpfile); + while (fgets(buf, BUFSIZE, pc->tmpfile)) { + if (found) + break; + if (strstr(buf, "__switch_to")) + found = TRUE; + } + close_tmpfile(); + + if (found && extract_hex(buf, &address, NULLCHAR, TRUE)) + machdep->machspec->thread_return = address; + else { + machdep->machspec->thread_return = symbol_value("schedule"); + error(INFO, "cannot determing thread return address\n"); + } +} + static void x86_64_irq_eframe_link_init(void) { @@ -5281,9 +5401,49 @@ x86_64_calc_phys_base(void) break; } } + + x86_64_xendump_phys_base(); } } +/* + * Because the xendump phys_base calculation is so speculative, + * first verify and then possibly override it by trying to read + * linux_banner from a range of typical physical offsets. + */ +static void +x86_64_xendump_phys_base(void) +{ + char buf[BUFSIZE]; + struct syment *sp; + ulong phys, linux_banner_phys; + + if (!(sp = symbol_search("linux_banner")) || + !((sp->type == 'R') || (sp->type == 'r'))) + return; + + linux_banner_phys = sp->value - __START_KERNEL_map; + + if (readmem(linux_banner_phys + machdep->machspec->phys_base, + PHYSADDR, buf, strlen("Linux version"), "xendump linux_banner", + QUIET|RETURN_ON_ERROR) && STRNEQ(buf, "Linux version")) + return; + + for (phys = (ulong)(-MEGABYTES(16)); phys != MEGABYTES(16+1); + phys += MEGABYTES(1)) { + if (readmem(linux_banner_phys + phys, PHYSADDR, buf, + strlen("Linux version"), "xendump linux_banner", + QUIET|RETURN_ON_ERROR) && STRNEQ(buf, "Linux version")) { + if (CRASHDEBUG(1)) + fprintf(fp, + "xendump phys_base: %lx %s\n", phys, + machdep->machspec->phys_base != phys ? + "override" : ""); + machdep->machspec->phys_base = phys; + return; + } + } +} /* * Create an index of mfns for each page that makes up the @@ -5569,6 +5729,7 @@ x86_64_get_xendump_regs(struct xendump_d ulong task, xrip, xrsp; off_t offset; struct syment *sp; + char *rip_symbol; int cpu; if (INVALID_MEMBER(vcpu_guest_context_user_regs) || @@ -5616,7 +5777,8 @@ generic: * then the thread_struct rsp is stale. It has to be coming * from a callback via the interrupt stack. */ - if (is_task_active(bt->task) && (symbol_value("thread_return") == *rip)) { + if (is_task_active(bt->task) && (rip_symbol = closest_symbol(*rip)) && + (STREQ(rip_symbol, "thread_return") || STREQ(rip_symbol, "schedule"))) { cpu = bt->tc->processor; xrsp = machdep->machspec->stkinfo.ibase[cpu] + machdep->machspec->stkinfo.isize - sizeof(ulong); @@ -6454,12 +6616,14 @@ x86_64_get_active_set(void) if (ACTIVE()) return; + ms = machdep->machspec; + if (!ms->current) + return; + if (CRASHDEBUG(1)) fprintf(fp, "x86_64_get_active_set: runqueue vs. %s\n", VALID_STRUCT(x8664_pda) ? "x8664_pda" : "current_task"); - ms = machdep->machspec; - for (c = 0; c < kt->cpus; c++) { if (!tt->active_set[c]) --- crash-5.0.3/cmdline.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/cmdline.c 2010-05-06 10:57:59.000000000 -0400 @@ -1,8 +1,8 @@ /* cmdline.c - core analysis suite * * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc. - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/lkcd_x86_trace.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/lkcd_x86_trace.c 2010-05-21 10:28:13.000000000 -0400 @@ -5,8 +5,8 @@ /* * lkcd_x86_trace.c * - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -1535,10 +1535,19 @@ find_trace( } else { if (!XEN_HYPER_MODE() && !is_kernel_thread(bt->task) && - (bt->stacktop == machdep->get_stacktop(bt->task)) && - (((ulong)(bp+4) + SIZE(pt_regs)) > bt->stacktop)) - flag = INCOMPLETE_EX_FRAME; - else { + (bt->stacktop == machdep->get_stacktop(bt->task))) { + if (((ulong)(bp+4) + SIZE(pt_regs)) > bt->stacktop) + flag = INCOMPLETE_EX_FRAME; + else if ((sp1 = eframe_label(NULL, pc)) && + STREQ(sp1->name, "system_call")) + flag = EX_FRAME|SET_EX_FRAME_ADDR; + else if (STREQ(closest_symbol(pc), "ret_from_fork")) + flag = EX_FRAME|SET_EX_FRAME_ADDR; + else { + curframe->error = KLE_BAD_RA; + flag = 0; + } + } else { curframe->error = KLE_BAD_RA; flag = 0; } @@ -1828,6 +1837,32 @@ dump_stack_frame(trace_t *trace, sframe_ } /* + * eframe_address() + */ +static uaddr_t * +eframe_address(sframe_t *frmp, struct bt_info *bt) +{ + ulong esp0, pt; + + if (!(frmp->flag & SET_EX_FRAME_ADDR) || + INVALID_MEMBER(task_struct_thread) || + (((esp0 = MEMBER_OFFSET("thread_struct", "esp0")) < 0) && + ((esp0 = MEMBER_OFFSET("thread_struct", "sp0")) < 0))) + return frmp->asp; + /* + * Work required in rarely-seen SET_EX_FRAME_ADDR circumstances. + */ + pt = ULONG(tt->task_struct + OFFSET(task_struct_thread) + esp0) + - SIZE(pt_regs); + + if (!INSTACK(pt, bt)) + return frmp->asp; + + return ((uint32_t *)(bt->stackbuf + (pt - bt->stackbase))); +} + + +/* * print_trace() */ void @@ -1837,7 +1872,6 @@ print_trace(trace_t *trace, int flags, F #ifdef REDHAT kaddr_t fp = 0; kaddr_t last_fp, last_pc, next_fp, next_pc; - uaddr_t *pt; struct bt_info *bt; bt = trace->bt; @@ -1898,15 +1932,14 @@ print_trace(trace_t *trace, int flags, F fprintf(ofp, " [0x%x]\n", frmp->pc); #endif if (frmp->flag & EX_FRAME) { - pt = frmp->asp; if (CRASHDEBUG(1)) fprintf(ofp, " EXCEPTION FRAME: %lx\n", (unsigned long)frmp->sp); - print_eframe(ofp, pt); + print_eframe(ofp, eframe_address(frmp, bt)); } #ifdef REDHAT - if (CRASHDEBUG(1) && (frmp->flag == INCOMPLETE_EX_FRAME)) { + if (CRASHDEBUG(1) && (frmp->flag & INCOMPLETE_EX_FRAME)) { fprintf(ofp, " INCOMPLETE EXCEPTION FRAME:\n"); fprintf(ofp, " user stacktop: %lx frame #%d: %lx (+pt_regs: %lx)\n", @@ -2586,7 +2619,15 @@ eframe_label(char *funcname, ulong eip) (efp->sysenter = symbol_search("ia32_sysenter_target"))) { if ((sp = symbol_search("sysexit_ret_end_marker"))) efp->sysenter_end = sp; - else if ((sp = symbol_search("system_call"))) + else if (THIS_KERNEL_VERSION >= LINUX(2,6,33)) { + if ((sp = symbol_search("sysexit_audit")) || + (sp = symbol_search("sysenter_exit"))) + efp->sysenter_end = + next_symbol(NULL, sp); + else error(WARNING, + "cannot determine end of %s function\n", + efp->sysenter->name); + } else if ((sp = symbol_search("system_call"))) efp->sysenter_end = sp; else error(WARNING, --- crash-5.0.3/netdump.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/netdump.c 2010-05-06 10:59:02.000000000 -0400 @@ -1,7 +1,7 @@ /* netdump.c * - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/xendump.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/xendump.c 2010-05-06 11:00:40.000000000 -0400 @@ -1,8 +1,8 @@ /* * xendump.c * - * Copyright (C) 2006, 2007, 2008 David Anderson - * Copyright (C) 2006, 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009, 2010 David Anderson + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/unwind.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/unwind.c 2010-05-06 11:03:21.000000000 -0400 @@ -6,8 +6,8 @@ /* * unwind.c * - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2009 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/kvmdump.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/kvmdump.c 2010-05-17 16:17:17.000000000 -0400 @@ -1,8 +1,8 @@ /* * kvmdump.c * - * Copyright (C) 2009 David Anderson - * Copyright (C) 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009, 2010 David Anderson + * Copyright (C) 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -18,9 +18,16 @@ #include "defs.h" #include "kvmdump.h" -static struct kvmdump_data kvmdump_data = { 0 }; +static struct kvmdump_data kvmdump_data = { 0 }; struct kvmdump_data *kvm = &kvmdump_data; static int cache_page(physaddr_t); +static int kvmdump_mapfile_exists(void); +static off_t mapfile_offset(uint64_t); +static void kvmdump_mapfile_create(char *); +static void kvmdump_mapfile_append(void); +static char *mapfile_in_use(void); +static void write_mapfile_trailer(void); +static void read_mapfile_trailer(void); #define RAM_OFFSET_COMPRESSED (~(off_t)255) #define QEMU_COMPRESSED ((WRITE_ERROR)-1) @@ -29,14 +36,55 @@ static int cache_page(physaddr_t); int is_kvmdump(char *filename) { - return (is_qemu_vm_file(filename)); + int i; + ulong *ptr; + off_t eof; + ulonglong csum; + struct mapinfo_trailer trailer; + char buf[CHKSUM_SIZE]; + + if (!is_qemu_vm_file(filename)) + return FALSE; + + if (lseek(kvm->vmfd, 0, SEEK_SET) < 0) { + error(INFO, "%s: read: %s\n", filename, strerror(errno)); + return FALSE; + } + if (read(kvm->vmfd, buf, CHKSUM_SIZE) != CHKSUM_SIZE) { + error(INFO, "%s: read: %s\n", filename, strerror(errno)); + return FALSE; + } + + ptr = (ulong *)&buf[0]; + for (i = csum = 0; i < (CHKSUM_SIZE/sizeof(ulong)); i++, ptr++) + csum += *ptr; + + eof = lseek(kvm->vmfd, 0, SEEK_END); + if (lseek(kvm->vmfd, eof - sizeof(trailer), SEEK_SET) < 0) { + error(INFO, "%s: lseek: %s\n", filename, strerror(errno)); + return FALSE; + } + if (read(kvm->vmfd, &trailer, sizeof(trailer)) != sizeof(trailer)) { + error(INFO, "%s: read: %s\n", filename, strerror(errno)); + return FALSE; + } + if (trailer.magic == MAPFILE_MAGIC) { + kvm->mapinfo.map_start_offset = trailer.map_start_offset; + kvm->flags |= MAPFILE_APPENDED; + } + + kvm->mapinfo.checksum = csum; + + return TRUE; } int kvmdump_init(char *filename, FILE *fptr) { int i, page_size; - char *buf; + struct command_table_entry *cp; + char *cachebuf; + FILE *tmpfp; if (!machine_type("X86") && !machine_type("X86_64")) { error(FATAL, "invalid host architecture for KVM: %s\n", @@ -48,20 +96,63 @@ kvmdump_init(char *filename, FILE *fptr) kvm->debug = &pc->debug; page_size = memory_page_size(); - if ((kvm->mem = tmpfile()) == NULL) - error(FATAL, - "cannot create tmpfile for KVM file offsets: %s\n", - strerror(errno)); +#ifdef X86_64 + kvm->kvbase = __START_KERNEL_map; +#endif + + switch (kvm->flags & (TMPFILE|MAPFILE|MAPFILE_APPENDED)) + { + case MAPFILE_APPENDED: + kvm->mapfd = kvm->vmfd; + break; - if ((buf = calloc(1, KVMDUMP_CACHED_PAGES * page_size)) == NULL) + case MAPFILE|MAPFILE_APPENDED: + case MAPFILE: + break; + + default: + if (kvmdump_mapfile_exists()) + break; + + if ((tmpfp = tmpfile()) == NULL) + error(FATAL, + "cannot create tmpfile for KVM file offsets: %s\n", + strerror(errno)); + + kvm->mapfd = fileno(tmpfp); + kvm->flags |= TMPFILE; + break; + } + + if ((cachebuf = calloc(1, KVMDUMP_CACHED_PAGES * page_size)) == NULL) error(FATAL, "%s: cannot malloc KVM page_cache_buf\n"); for (i = 0; i < KVMDUMP_CACHED_PAGES; i++) { kvm->page_cache[i].paddr = CACHE_UNUSED; - kvm->page_cache[i].bufptr = buf + (i * page_size); + kvm->page_cache[i].bufptr = cachebuf + (i * page_size); } if (qemu_init(filename)) { + switch (kvm->flags & (TMPFILE|MAPFILE|MAPFILE_APPENDED)) + { + case TMPFILE: + write_mapfile_trailer(); + break; + + case MAPFILE: + case MAPFILE_APPENDED: + case MAPFILE|MAPFILE_APPENDED: + read_mapfile_trailer(); + break; + } + + for (cp = pc->cmd_table; cp->name; cp++) { + if (STREQ(cp->name, "map")) { + cp->flags &= ~HIDDEN_COMMAND; + break; + } + } + kvm->flags |= KVMDUMP_LOCAL; return TRUE; } else @@ -122,29 +213,53 @@ int kvmdump_memory_dump(FILE *ofp) { int i, others; + struct mapinfo_trailer trailer; + off_t eof; - fprintf(ofp, " flags: %lx (", kvm->flags); + fprintf(ofp, " flags: %lx (", kvm->flags); others = 0; if (kvm->flags & KVMDUMP_LOCAL) fprintf(ofp, "%sKVMDUMP_LOCAL", others++ ? "|" : ""); + if (kvm->flags & TMPFILE) + fprintf(ofp, "%sTMPFILE", others++ ? "|" : ""); + if (kvm->flags & MAPFILE) + fprintf(ofp, "%sMAPFILE", others++ ? "|" : ""); + if (kvm->flags & MAPFILE_FOUND) + fprintf(ofp, "%sMAPFILE_FOUND", others++ ? "|" : ""); + if (kvm->flags & MAPFILE_APPENDED) + fprintf(ofp, "%sMAPFILE_APPENDED", others++ ? "|" : ""); fprintf(ofp, ")\n"); - fprintf(ofp, " ofp: %lx\n", (ulong)kvm->ofp); - fprintf(ofp, " mem: %lx\n", (ulong)kvm->mem); - fprintf(ofp, " vmp: %lx\n", (ulong)kvm->vmp); - fprintf(ofp, " debug: %lx\n", (ulong)kvm->debug); - fprintf(ofp, " phys_base: %lx\n", kvm->phys_base); - fprintf(ofp, "last_ram_offset: %llx\n", (ulonglong)kvm->last_ram_offset); - - fprintf(ofp, " curbufptr: %lx\n", (ulong)kvm->un.curbufptr); - fprintf(ofp, " evict_index: %d\n", kvm->evict_index); - fprintf(ofp, " accesses: %ld\n", kvm->accesses); - fprintf(ofp, " hit_count: %ld ", kvm->hit_count); + + fprintf(ofp, " mapfd: %d\n", kvm->mapfd); + fprintf(ofp, " vmfd: %d\n", kvm->vmfd); + fprintf(ofp, " vmp: %lx (fd: %d)\n", (ulong)kvm->vmp, + fileno(kvm->vmp)); + fprintf(ofp, " ofp: %lx\n", (ulong)kvm->ofp); + fprintf(ofp, " debug: %lx\n", (ulong)kvm->debug); + if (machine_type("X86_64")) + fprintf(ofp, " kvbase: %llx\n", (ulonglong)kvm->kvbase); + else + fprintf(ofp, " kvbase: (unused)\n"); + fprintf(ofp, " mapinfo:\n"); + fprintf(ofp, " magic: %llx %s\n", (ulonglong)kvm->mapinfo.magic, + kvm->mapinfo.magic == MAPFILE_MAGIC ? "(MAPFILE_MAGIC)" : ""); + fprintf(ofp, " phys_base: %llx %s\n", (ulonglong)kvm->mapinfo.phys_base, + machine_type("X86") ? "(unused)" : ""); + fprintf(ofp, " cpu_version_id: %ld\n", (ulong)kvm->mapinfo.cpu_version_id); + fprintf(ofp, " ram_version_id: %ld\n", (ulong)kvm->mapinfo.ram_version_id); + fprintf(ofp, " map_start_offset: %llx\n", (ulonglong)kvm->mapinfo.map_start_offset); + fprintf(ofp, " checksum: %llx\n", (ulonglong)kvm->mapinfo.checksum); + + fprintf(ofp, " curbufptr: %lx\n", (ulong)kvm->un.curbufptr); + fprintf(ofp, " evict_index: %d\n", kvm->evict_index); + fprintf(ofp, " accesses: %ld\n", kvm->accesses); + fprintf(ofp, " hit_count: %ld ", kvm->hit_count); if (kvm->accesses) fprintf(ofp, "(%ld%%)\n", kvm->hit_count * 100 / kvm->accesses); else fprintf(ofp, "\n"); - fprintf(ofp, " compresses: %ld ", kvm->compresses); + fprintf(ofp, " compresses: %ld ", kvm->compresses); if (kvm->accesses) fprintf(ofp, "(%ld%%)\n", kvm->compresses * 100 / kvm->accesses); @@ -153,11 +268,11 @@ kvmdump_memory_dump(FILE *ofp) for (i = 0; i < KVMDUMP_CACHED_PAGES; i++) { if (kvm->page_cache[i].paddr == CACHE_UNUSED) - fprintf(ofp, " %spage_cache[%d]: CACHE_UNUSED\n", + fprintf(ofp, " %spage_cache[%d]: CACHE_UNUSED\n", i < 10 ? " " : "", i); else fprintf(ofp, - " %spage_cache[%d]: bufptr: %lx addr: %llx\n", + " %spage_cache[%d]: bufptr: %lx addr: %llx\n", i < 10 ? " " : "", i, (ulong)kvm->page_cache[i].bufptr, (ulonglong)kvm->page_cache[i].paddr); @@ -168,6 +283,25 @@ kvmdump_memory_dump(FILE *ofp) dump_qemu_header(ofp); + fprintf(ofp, "\n%s: mapinfo trailer:\n\n", mapfile_in_use()); + + eof = lseek(kvm->mapfd, 0, SEEK_END); + if (lseek(kvm->mapfd, eof - sizeof(trailer), SEEK_SET) < 0) + error(FATAL, "%s: lseek: %s\n", mapfile_in_use(), + strerror(errno)); + if (read(kvm->mapfd, &trailer, sizeof(trailer)) != sizeof(trailer)) + error(FATAL, "%s: read: %s\n", mapfile_in_use(), + strerror(errno)); + + fprintf(ofp, " magic: %llx %s\n", (ulonglong)trailer.magic, + trailer.magic == MAPFILE_MAGIC ? "(MAPFILE_MAGIC)" : ""); + fprintf(ofp, " phys_base: %llx %s\n", (ulonglong)trailer.phys_base, + machine_type("X86") ? "(unused)" : ""); + fprintf(ofp, " cpu_version_id: %ld\n", (ulong)trailer.cpu_version_id); + fprintf(ofp, " ram_version_id: %ld\n", (ulong)trailer.ram_version_id); + fprintf(ofp, " map_start_offset: %llx\n", (ulonglong)trailer.map_start_offset); + fprintf(ofp, " checksum: %llx\n\n", (ulonglong)trailer.checksum); + return TRUE; } @@ -187,7 +321,13 @@ int kvmdump_phys_base(unsigned long *phys_base) { if (KVMDUMP_VALID()) { - *phys_base = kvm->phys_base; + if (CRASHDEBUG(1) && (kvm->mapinfo.cpu_version_id > 9)) + error(NOTE, + "KVM/QEMU CPU_SAVE_VERSION %d is greater than" + " supported version 9\n\n", + kvm->mapinfo.cpu_version_id); + + *phys_base = kvm->mapinfo.phys_base; return TRUE; } @@ -217,7 +357,7 @@ cache_page(physaddr_t paddr) } } - if ((err = load_memfile_offset(paddr, &offset)) < 0) + if ((err = load_mapfile_offset(paddr, &offset)) < 0) return err; if ((offset & RAM_OFFSET_COMPRESSED) == RAM_OFFSET_COMPRESSED) { @@ -230,11 +370,11 @@ cache_page(physaddr_t paddr) pgc = &kvm->page_cache[idx]; page_size = memory_page_size(); - if (fseek(kvm->vmp, offset, SEEK_SET) < 0) { + if (lseek(kvm->vmfd, offset, SEEK_SET) < 0) { pgc->paddr = CACHE_UNUSED; return SEEK_ERROR; } - if (fread(pgc->bufptr, page_size, 1, kvm->vmp) != 1) { + if (read(kvm->vmfd, pgc->bufptr, page_size) != page_size) { pgc->paddr = CACHE_UNUSED; return READ_ERROR; } @@ -247,16 +387,42 @@ cache_page(physaddr_t paddr) return idx; } +static off_t +mapfile_offset(uint64_t physaddr) +{ + off_t offset = 0; + + switch (kvm->flags & (TMPFILE|MAPFILE|MAPFILE_APPENDED)) + { + case TMPFILE: + case TMPFILE|MAPFILE_APPENDED: + case MAPFILE: + case MAPFILE|MAPFILE_APPENDED: + offset = (off_t)(((((uint64_t)physaddr/(uint64_t)4096)) + * sizeof(off_t))); + break; + + case MAPFILE_APPENDED: + offset = (off_t)(((((uint64_t)physaddr/(uint64_t)4096)) + * sizeof(off_t)) + kvm->mapinfo.map_start_offset); + break; + } + + return offset; +} + int -store_memfile_offset(uint64_t physaddr, off_t *entry_ptr) +store_mapfile_offset(uint64_t physaddr, off_t *entry_ptr) { - if (fseek(kvm->mem, MEMFILE_OFFSET(physaddr), SEEK_SET) < 0) { - error(INFO, "%s: fseek: %s\n", pc->dumpfile, strerror(errno)); + if (lseek(kvm->mapfd, mapfile_offset(physaddr), SEEK_SET) < 0) { + error(INFO, "%s: lseek: %s\n", + mapfile_in_use(), strerror(errno)); return SEEK_ERROR; } - if (fwrite((entry_ptr), sizeof(off_t), 1, kvm->mem) != 1) { - error(INFO, "%s: fwrite: %s\n", pc->dumpfile, strerror(errno)); + if (write(kvm->mapfd, entry_ptr, sizeof(off_t)) != sizeof(off_t)) { + error(INFO, "%s: write: %s\n", + mapfile_in_use(), strerror(errno)); return WRITE_ERROR; } @@ -264,19 +430,335 @@ store_memfile_offset(uint64_t physaddr, } int -load_memfile_offset(uint64_t physaddr, off_t *entry_ptr) +load_mapfile_offset(uint64_t physaddr, off_t *entry_ptr) { - if (fseek(kvm->mem, MEMFILE_OFFSET(physaddr), SEEK_SET) < 0) { - error(INFO, "%s: fseek: %s\n", pc->dumpfile, strerror(errno)); + if (lseek(kvm->mapfd, mapfile_offset(physaddr), SEEK_SET) < 0) { + error(INFO, "load_memfile_offset: lseek: %s\n", + strerror(errno)); return SEEK_ERROR; } - if (fread((entry_ptr), sizeof(off_t), 1, kvm->mem) != 1) { - error(INFO, "%s: fwrite: %s\n", pc->dumpfile, strerror(errno)); + if (read(kvm->mapfd, entry_ptr, sizeof(off_t)) != sizeof(off_t)) { + error(INFO, "load_memfile_offset: read: %s\n", strerror(errno)); return READ_ERROR; } return 0; } +static void +kvmdump_mapfile_create(char *filename) +{ + int fdmem, n; + off_t offset; + char buf[4096]; + + if (kvm->flags & MAPFILE) { + error(INFO, "%s: mapfile in use\n", pc->kvmdump_mapfile); + return; + } + + if (file_exists(filename, NULL)) { + error(INFO, + "%s: file already exists!\n", filename); + return; + } + + if ((fdmem = open(filename, O_CREAT|O_RDWR, 0644)) < 0) { + error(INFO, "%s: open: %s\n", filename, strerror(errno)); + return; + } + + offset = kvm->mapinfo.map_start_offset; + + if (lseek(kvm->mapfd, offset, SEEK_SET) < 0) { + error(INFO, "%s: leek: %s\n", + mapfile_in_use(), strerror(errno)); + return; + } + + while ((n = read(kvm->mapfd, buf, 4096)) > 0) { + if (write(fdmem, buf, n) != n) { + error(INFO, "%s: write: %s\n", filename, + strerror(errno)); + break; + } + } + + close(fdmem); + + fprintf(fp, "MAP FILE CREATED: %s\n", filename); +} + +static void +kvmdump_mapfile_append(void) +{ + int n, fdcore; + ulong round_bytes; + struct stat statbuf; + uint64_t map_start_offset; + off_t eof, orig_dumpfile_size; + char buf[4096]; + + if (kvm->flags & MAPFILE_APPENDED) + error(FATAL, "mapfile already appended to %s\n", + pc->dumpfile); + + if (access(pc->dumpfile, W_OK) != 0) + error(FATAL, + "%s: cannot append map information to this file\n", + pc->dumpfile); + + if (stat(pc->dumpfile, &statbuf) < 0) + error(FATAL, "%s: stat: %s\n", + pc->dumpfile, strerror(errno)); + + round_bytes = (sizeof(uint64_t) - (statbuf.st_size % sizeof(uint64_t))) + % sizeof(uint64_t); + + if ((fdcore = open(pc->dumpfile, O_WRONLY)) < 0) + error(FATAL, "%s: open: %s\n", + pc->dumpfile, strerror(errno)); + + if ((orig_dumpfile_size = lseek(fdcore, 0, SEEK_END)) < 0) { + error(INFO, "%s: lseek: %s\n", pc->dumpfile, strerror(errno)); + goto bailout1; + } + + if (round_bytes) { + BZERO(buf, round_bytes); + if (write(fdcore, buf, round_bytes) != round_bytes) { + error(INFO, "%s: write: %s\n", + pc->dumpfile, strerror(errno)); + goto bailout2; + } + + } + + map_start_offset = orig_dumpfile_size + round_bytes; + + if (lseek(kvm->mapfd, 0, SEEK_SET) != 0) { + error(INFO, "%s: lseek: %s\n", + mapfile_in_use(), strerror(errno)); + goto bailout2; + } + + while ((n = read(kvm->mapfd, buf, 4096)) > 0) { + if (write(fdcore, buf, n) != n) { + error(INFO, "%s: write: %s\n", pc->dumpfile, + strerror(errno)); + goto bailout2; + } + } + + /* + * Overwrite the map_start_offset value in the trailer to reflect + * its location in the appended-to dumpfile. + */ + eof = lseek(fdcore, 0, SEEK_END); + if (lseek(fdcore, eof - sizeof(struct mapinfo_trailer), SEEK_SET) < 0) { + error(INFO, "%s: write: %s\n", pc->dumpfile, strerror(errno)); + goto bailout2; + } + if (write(fdcore, &map_start_offset, sizeof(uint64_t)) != sizeof(uint64_t)) { + error(INFO, "%s: write: %s\n", pc->dumpfile, strerror(errno)); + goto bailout2; + } + + close(fdcore); + + kvm->flags |= MAPFILE_APPENDED; + fprintf(fp, "MAP FILE APPENDED TO: %s\n", pc->dumpfile); + + return; + +bailout2: + if (ftruncate(fdcore, (off_t)orig_dumpfile_size) < 0) + error(INFO, "%s: ftruncate: %s\n", + pc->dumpfile, strerror(errno)); +bailout1: + close(fdcore); + error(INFO, "failed to append map to %s\n", pc->dumpfile); +} + +int +is_kvmdump_mapfile(char *filename) +{ + int fd; + struct mapinfo_trailer trailer; + off_t eof; + + if ((fd = open(filename, O_RDONLY)) < 0) { + error(INFO, "%s: open: %s\n", filename, strerror(errno)); + return FALSE; + } + + eof = lseek(fd, 0, SEEK_END); + if (lseek(fd, eof - sizeof(trailer), SEEK_SET) < 0) { + error(INFO, "%s: lseek: %s\n", filename, strerror(errno)); + goto bailout; + } + + if (read(fd, &trailer, sizeof(trailer)) != sizeof(trailer)) { + error(INFO, "%s: read: %s\n", filename, strerror(errno)); + goto bailout; + } + + if (trailer.magic == MAPFILE_MAGIC) { + if (pc->dumpfile && (trailer.checksum != kvm->mapinfo.checksum)) { + error(kvm->flags & MAPFILE_FOUND ? INFO : FATAL, + "checksum mismatch between %s and %s\n\n", + pc->dumpfile, filename); + goto bailout; + } + kvm->mapfd = fd; + kvm->flags |= MAPFILE; + return TRUE; + } + +bailout: + close(fd); + return FALSE; +} + +static int +kvmdump_mapfile_exists(void) +{ + char *filename; + struct stat stat; + + if (!(filename = malloc(strlen(pc->dumpfile) + strlen(".map") + 10))) + return FALSE; + + sprintf(filename, "%s.map", pc->dumpfile); + + if (!file_exists(filename, &stat) || !S_ISREG(stat.st_mode)) + return FALSE; + + if (is_kvmdump_mapfile(filename)) { + pc->kvmdump_mapfile = filename; + kvm->flags |= MAPFILE_FOUND; + return TRUE; + } + + free(filename); + return FALSE; +} + +void +cmd_map(void) +{ + int c; + int append, file, specified; + char *mapfile; + + append = file = specified = 0; + mapfile = NULL; + + while ((c = getopt(argcnt, args, "af")) != EOF) { + switch(c) + { + case 'a': + append++; + break; + case 'f': + file++; + break; + default: + argerrs++; + break; + } + } + + if (argerrs) + cmd_usage(pc->curcmd, SYNOPSIS); + + while (args[optind]) { + if (!mapfile) { + mapfile = args[optind]; + specified++; + } else + cmd_usage(pc->curcmd, SYNOPSIS); + optind++; + } + + if (file && !specified) { + mapfile = GETBUF(strlen(pc->dumpfile)+10); + sprintf(mapfile, "%s.map", pc->dumpfile); + } + + if (append) + kvmdump_mapfile_append(); + + if (file) { + kvmdump_mapfile_create(mapfile); + if (!specified) + FREEBUF(mapfile); + } + + if (!file && !append) + fprintf(fp, "MAP FILE IN USE: %s\n", mapfile_in_use()); +} + +static char * +mapfile_in_use(void) +{ + char *name; + + switch (kvm->flags & (TMPFILE|MAPFILE|MAPFILE_APPENDED)) + { + default: + case TMPFILE: + case TMPFILE|MAPFILE_APPENDED: + name = "(tmpfile)"; + break; + case MAPFILE: + case MAPFILE|MAPFILE_APPENDED: + name = pc->kvmdump_mapfile; + break; + case MAPFILE_APPENDED: + name = pc->dumpfile; + break; + } + + return name; +} + +static void +write_mapfile_trailer(void) +{ + kvm->mapinfo.magic = MAPFILE_MAGIC; + + if (lseek(kvm->mapfd, 0, SEEK_END) < 0) + error(FATAL, "%s: lseek: %s\n", mapfile_in_use(), strerror(errno)); + + if (write(kvm->mapfd, &kvm->mapinfo, sizeof(struct mapinfo_trailer)) + != sizeof(struct mapinfo_trailer)) + error(FATAL, "%s: write: %s\n", mapfile_in_use(), strerror(errno)); +} + +static void +read_mapfile_trailer(void) +{ + off_t eof; + struct mapinfo_trailer trailer; + + if ((eof = lseek(kvm->mapfd, 0, SEEK_END)) < 0) + error(FATAL, "%s: lseek: %s\n", + mapfile_in_use(), strerror(errno)); + + if (lseek(kvm->mapfd, eof - sizeof(trailer), SEEK_SET) < 0) + error(FATAL, "%s: lseek: %s\n", + mapfile_in_use(), strerror(errno)); + + if (read(kvm->mapfd, &trailer, sizeof(trailer)) != sizeof(trailer)) + error(FATAL, "%s: read: %s\n", + mapfile_in_use(), strerror(errno)); + + if (kvm->mapinfo.checksum != trailer.checksum) + error(FATAL, "checksum mismatch between %s and %s\n", + pc->dumpfile, mapfile_in_use()); + + kvm->mapinfo = trailer; +} --- crash-5.0.3/qemu.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/qemu.c 2010-05-17 16:17:29.000000000 -0400 @@ -1,7 +1,7 @@ /* * Derive kernel base from a QEMU saved VM file * - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009, 2010 Red Hat, Inc. * Written by Paolo Bonzini. * * Portions Copyright (C) 2009 David Anderson @@ -196,14 +196,50 @@ get_idt_base(struct qemu_device_list *dl static uint64_t get_kernel_base(struct qemu_device_list *dl) { + int i; + uint64_t kernel_base = -1; + uint64_t base_vaddr, last, mask; struct qemu_device_x86 *dx86 = (struct qemu_device_x86 *) device_find_instance (dl, "cpu", 0); struct qemu_device_ram *dram = (struct qemu_device_ram *) device_find_instance (dl, "ram", 0); - uint64_t base_vaddr = dx86->idt.base & ~((1LL << 30) - 1); - dprintf ("Virtual base address guessed at %llx\n", (unsigned long long)base_vaddr); - return get_phys_page(dx86, dram, base_vaddr); + for (i = 30, last = -1; (kernel_base == -1) && (i >= 20); i--) + { + mask = ~((1LL << i) - 1); + base_vaddr = dx86->idt.base & mask; + if (base_vaddr == last) + continue; + if (base_vaddr < kvm->kvbase) { + fprintf(stderr, + "WARNING: IDT base contains: %llx\n " + "cannot determine physical base address: defaulting to 0\n\n", + (unsigned long long)base_vaddr); + return 0; + } + dprintf("get_kernel_base: %llx\n", (unsigned long long)base_vaddr); + kernel_base = get_phys_page(dx86, dram, base_vaddr); + last = base_vaddr; + } + + if (kernel_base != -1) { + dprintf("kvbase: %llx vaddr used: %llx physical: %llx\n", + (unsigned long long)kvm->kvbase, + (unsigned long long)base_vaddr, + (unsigned long long)kernel_base); + /* + * Subtract the offset between the virtual address used + * and the kernel's base virtual address. + */ + kernel_base -= (base_vaddr - kvm->kvbase); + } else { + fprintf(stderr, + "WARNING: cannot determine physical base address:" + " defaulting to 0\n\n"); + kernel_base = 0; + } + + return kernel_base; } @@ -249,13 +285,16 @@ qemu_init(char *filename) { struct qemu_device_list *dl; struct qemu_device_ram *dram; - uint64_t idt; + uint64_t idt = 0; if (CRASHDEBUG(1)) dump_qemu_header(kvm->ofp); rewind(kvm->vmp); + if (kvm->flags & (MAPFILE|MAPFILE_APPENDED)) + return TRUE; + please_wait("scanning KVM dumpfile"); if (machine_type("X86")) @@ -270,22 +309,23 @@ qemu_init(char *filename) please_wait_done(); if (dl) { - /* - * TBD: the IDT base is a kernel virtual address -- verify here - * that it makes sense for the host machine type. - */ - idt = get_idt_base(dl); - kvm->phys_base = get_kernel_base(dl); + if (machine_type("X86_64")) { + idt = get_idt_base(dl); + kvm->mapinfo.phys_base = get_kernel_base(dl); + } + dram = (struct qemu_device_ram *) device_find_instance (dl, "ram", 0); - kvm->last_ram_offset = dram->last_ram_offset; if (CRASHDEBUG(1)) { - fprintf(kvm->ofp, "IDT: %llx\n", (ulonglong)idt); - fprintf(kvm->ofp, "physical kernel base: %lx\n", - kvm->phys_base); + if (machine_type("X86_64")) { + fprintf(kvm->ofp, "IDT: %llx\n", + (ulonglong)idt); + fprintf(kvm->ofp, "physical kernel base: %llx\n", + (ulonglong)kvm->mapinfo.phys_base); + } fprintf(kvm->ofp, "last RAM offset: %llx\n", - (ulonglong)kvm->last_ram_offset); + (ulonglong)dram->last_ram_offset); } device_list_free (dl); --- crash-5.0.3/qemu-load.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/qemu-load.c 2010-05-17 16:17:37.000000000 -0400 @@ -1,7 +1,7 @@ /* * Qemu save VM loader * - * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2009, 2010 Red Hat, Inc. * Written by Paolo Bonzini. * * Portions Copyright (C) 2009 David Anderson @@ -209,12 +209,12 @@ ram_load (struct qemu_device *d, FILE *f else if (header & RAM_SAVE_FLAG_COMPRESS) { // dram->offsets[addr / 4096] = entry = RAM_OFFSET_COMPRESSED | getc(fp); - store_memfile_offset(addr, &entry); + store_mapfile_offset(addr, &entry); } else if (header & RAM_SAVE_FLAG_PAGE) { // dram->offsets[addr / 4096] = ftell (fp); entry = ftell(fp); - store_memfile_offset(addr, &entry); + store_mapfile_offset(addr, &entry); fseek (fp, 4096, SEEK_CUR); } @@ -241,7 +241,7 @@ ram_read_phys_page (struct qemu_device_r return false; assert ((addr & 0xfff) == 0); // ofs = dram->offsets[addr / 4096]; - if (load_memfile_offset(addr, &ofs) < 0) + if (load_mapfile_offset(addr, &ofs) < 0) return 0; if ((ofs & RAM_OFFSET_COMPRESSED) == RAM_OFFSET_COMPRESSED) memset (buf, ofs & 255, 4096); @@ -262,6 +262,7 @@ ram_init_load (struct qemu_device_list * }; assert (version_id == 3); + kvm->mapinfo.ram_version_id = version_id; return device_alloc (dl, sizeof (struct qemu_device_ram), &ram, section_id, instance_id); } @@ -481,7 +482,9 @@ cpu_init_load_32 (struct qemu_device_lis }; assert (!live); - assert (version_id >= 4 && version_id <= 9); +// assert (version_id >= 4 && version_id <= 9); + assert (version_id >= 4 && version_id <= 12); + kvm->mapinfo.cpu_version_id = version_id; dx86 = (struct qemu_device_x86 *) device_alloc (dl, sizeof (struct qemu_device_x86), &cpu, section_id, instance_id); @@ -508,7 +511,9 @@ cpu_init_load_64 (struct qemu_device_lis }; assert (!live); - assert (version_id >= 4 && version_id <= 9); +// assert (version_id >= 4 && version_id <= 9); + assert (version_id >= 4 && version_id <= 12); + kvm->mapinfo.cpu_version_id = version_id; dx86 = (struct qemu_device_x86 *) device_alloc (dl, sizeof (struct qemu_device_x86), &cpu, section_id, instance_id); @@ -638,6 +643,7 @@ qemu_load (const struct qemu_device_load FILE *fp) { struct qemu_device_list *result = NULL; + struct qemu_device *last = NULL;; size_t items; switch (get_be32 (fp)) { @@ -664,6 +670,8 @@ qemu_load (const struct qemu_device_load if (get_be32 (fp) != 3) return NULL; + dprintf("\n"); + result = calloc (1, sizeof (struct qemu_device_list)); for (;;) { struct qemu_device *d; @@ -679,7 +687,10 @@ qemu_load (const struct qemu_device_load if (!d) break; - dprintf("qemu_load: \"%s\"\n", d->vtbl->name); + if (d != last) { + dprintf("qemu_load: \"%s\"\n", d->vtbl->name); + last = d; + } features = d->vtbl->load (d, fp, sec); if (feof (fp) || ferror (fp)) @@ -711,11 +722,12 @@ int is_qemu_vm_file(char *filename) { struct libvirt_header header; + FILE *vmp; int retval; size_t items; char *xml; - if ((kvm->vmp = fopen(filename, "r")) == NULL) { + if ((vmp = fopen(filename, "r")) == NULL) { error(INFO, "%s: %s\n", filename, strerror(errno)); return FALSE; } @@ -723,25 +735,25 @@ is_qemu_vm_file(char *filename) retval = FALSE; xml = NULL; - switch (get_be32(kvm->vmp)) + switch (get_be32(vmp)) { case QEMU_VM_FILE_MAGIC: retval = TRUE; break; case LIBVIRT_QEMU_VM_FILE_MAGIC: - rewind(kvm->vmp); - items = fread(&header.magic[0], sizeof(header), 1, kvm->vmp); + rewind(vmp); + items = fread(&header.magic[0], sizeof(header), 1, vmp); if (STRNEQ(header.magic, "LibvirtQemudSave")) { if ((xml = (char *)malloc(header.xml_length))) { - items = fread(xml, header.xml_length, 1, kvm->vmp); + items = fread(xml, header.xml_length, 1, vmp); /* * Parse here if necessary or desirable. */ } else - fseek(kvm->vmp, header.xml_length, SEEK_CUR); + fseek(vmp, header.xml_length, SEEK_CUR); - if (get_be32(kvm->vmp) == QEMU_VM_FILE_MAGIC) + if (get_be32(vmp) == QEMU_VM_FILE_MAGIC) retval = TRUE; } break; @@ -752,8 +764,17 @@ is_qemu_vm_file(char *filename) if (xml) free(xml); - if (retval == FALSE) - fclose(kvm->vmp); + + switch (retval) + { + case TRUE: + kvm->vmp = vmp; + kvm->vmfd = fileno(vmp); + break; + case FALSE: + fclose(vmp); + break; + } return retval; } --- crash-5.0.3/defs.h 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/defs.h 2010-05-21 11:15:52.000000000 -0400 @@ -411,6 +411,7 @@ struct program_context { ulong ifile_in_progress; /* original xxx_IFILE flags */ off_t ifile_offset; /* current offset into input file */ char *runtime_ifile_cmd; /* runtime command using input file */ + char *kvmdump_mapfile; /* storage of physical to file offsets */ }; #define READMEM pc->readmem @@ -3183,6 +3184,7 @@ void cmd_extend(void); /* extensio #if defined(S390) || defined(S390X) void cmd_s390dbf(void); #endif +void cmd_map(void); /* kvmdump.c */ /* * main.c @@ -3624,6 +3626,7 @@ extern char *help_wr[]; #if defined(S390) || defined(S390X) extern char *help_s390dbf[]; #endif +extern char *help_map[]; /* * task.c @@ -3936,6 +3939,7 @@ struct machine_specific { ulong *current; ulong *crash_nmi_rsp; ulong vsyscall_page; + ulong thread_return; }; #define KSYMS_START (0x1) @@ -4286,6 +4290,7 @@ struct xendump_data *get_xendump_data(vo * kvmdump.c */ int is_kvmdump(char *); +int is_kvmdump_mapfile(char *); int kvmdump_init(char *, FILE *); int read_kvmdump(int, void *, int, ulong, physaddr_t); int write_kvmdump(int, void *, int, ulong, physaddr_t); --- crash-5.0.3/netdump.h 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/netdump.h 2010-05-06 11:08:55.000000000 -0400 @@ -1,7 +1,7 @@ /* netdump.h * - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/xendump.h 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/xendump.h 2010-05-06 11:01:16.000000000 -0400 @@ -1,8 +1,8 @@ /* * xendump.h * - * Copyright (C) 2006, 2007 David Anderson - * Copyright (C) 2006, 2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2009, 2010 David Anderson + * Copyright (C) 2006, 2007, 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 --- crash-5.0.3/kvmdump.h 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/kvmdump.h 2010-05-17 16:17:49.000000000 -0400 @@ -1,8 +1,8 @@ /* * kvmdump.h * - * Copyright (C) 2009 David Anderson - * Copyright (C) 2009 Red Hat, Inc. All rights reserved. + * Copyright (C) 2009, 2010 David Anderson + * Copyright (C) 2009, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -15,16 +15,27 @@ * GNU General Public License for more details. */ +struct mapinfo_trailer { + uint64_t map_start_offset; + uint64_t phys_base; + uint32_t cpu_version_id; + uint32_t ram_version_id; + uint64_t checksum; + uint64_t magic; +}; + +#define MAPFILE_MAGIC (0xfeedbabedeadbeefULL) +#define CHKSUM_SIZE (4096) + #define KVMDUMP_CACHED_PAGES 32 struct kvmdump_data { ulong flags; FILE *ofp; - FILE *mem; FILE *vmp; - ulong *debug; - ulong phys_base; - uint64_t last_ram_offset; + int mapfd; + int vmfd; + struct mapinfo_trailer mapinfo; /* page cache */ struct kvm_page_cache_hdr { uint64_t paddr; @@ -38,14 +49,19 @@ struct kvmdump_data { ulong accesses; ulong hit_count; ulong compresses; + uint64_t kvbase; + ulong *debug; }; +#define TMPFILE (0x2) +#define MAPFILE (0x4) +#define MAPFILE_FOUND (0x8) +#define MAPFILE_APPENDED (0x10) + extern struct kvmdump_data *kvm; #undef dprintf #define dprintf(x...) do { if (*(kvm->debug)) fprintf(kvm->ofp, x); } while (0) -#define MEMFILE_OFFSET(addr) ((off_t)((((uint64_t)addr/(uint64_t)4096)) * sizeof(off_t))) - -int store_memfile_offset(uint64_t, off_t *); -int load_memfile_offset(uint64_t, off_t *); +int store_mapfile_offset(uint64_t, off_t *); +int load_mapfile_offset(uint64_t, off_t *); --- crash-5.0.3/lkcd_x86_trace.h 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/lkcd_x86_trace.h 2010-05-06 10:55:57.000000000 -0400 @@ -5,8 +5,8 @@ /* * lkcd_x86_trace.h * - * Copyright (C) 2002, 2003, 2004, 2005 David Anderson - * Copyright (C) 2002, 2003, 2004, 2005 Red Hat, Inc. All rights reserved. + * Copyright (C) 2002, 2003, 2004, 2005, 2010 David Anderson + * Copyright (C) 2002, 2003, 2004, 2005, 2010 Red Hat, Inc. All rights reserved. * * 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 @@ -467,7 +467,8 @@ typedef struct sframe_rec { #define EX_FRAME 0x1 /* this frame is an interrupt or exception frame, pt_regs field of sframe_t is valid in this case */ -#define INCOMPLETE_EX_FRAME 0x2 +#define INCOMPLETE_EX_FRAME 0x2 +#define SET_EX_FRAME_ADDR 0x4 /* Stack segment structure */ --- crash-5.0.3/extensions/trace.c 2010-05-21 11:33:25.000000000 -0400 +++ crash-5.0.4/extensions/trace.c 2010-05-20 10:41:43.000000000 -0400 @@ -1,7 +1,7 @@ /* * trace extension module for crash * - * Copyright (C) 2009 FUJITSU LIMITED + * Copyright (C) 2009, 2010 FUJITSU LIMITED * Author: Lai Jiangshan * * This program is free software; you can redistribute it and/or modify @@ -53,6 +53,7 @@ static int koffset(list_head, next); static int koffset(ftrace_event_call, list); static int koffset(ftrace_event_call, name); static int koffset(ftrace_event_call, system); +static int koffset(ftrace_event_call, print_fmt); static int koffset(ftrace_event_call, id); static int koffset(ftrace_event_call, fields); @@ -74,8 +75,11 @@ struct ring_buffer_per_cpu { ulong reader_page; ulong real_head_page; - ulong *pages; int head_page_index; + ulong *pages; + + ulong *linear_pages; + int nr_linear_pages; ulong overrun; ulong entries; @@ -112,6 +116,22 @@ static void ftrace_show_destroy(void); /* Remove the "const" qualifiers for ptr */ #define free(ptr) free((void *)(ptr)) +static int write_and_check(int fd, void *data, size_t size) +{ + size_t tot = 0; + size_t w; + + do { + w = write(fd, data, size - tot); + tot += w; + + if (w <= 0) + return -1; + } while (tot != size); + + return 0; +} + #ifndef PATH_MAX #define PATH_MAX 4096 #endif @@ -153,6 +173,7 @@ static void init_offsets(void) init_offset(ftrace_event_call, list); init_offset(ftrace_event_call, name); init_offset(ftrace_event_call, system); + init_offset(ftrace_event_call, print_fmt); init_offset(ftrace_event_call, id); init_offset(ftrace_event_call, fields); @@ -199,6 +220,7 @@ static void print_offsets(void) print_offset(ftrace_event_call, list); print_offset(ftrace_event_call, name); print_offset(ftrace_event_call, system); + print_offset(ftrace_event_call, print_fmt); print_offset(ftrace_event_call, id); print_offset(ftrace_event_call, fields); @@ -214,7 +236,7 @@ static void print_offsets(void) static int ftrace_init_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned nr_pages) { - unsigned j = 0; + unsigned j = 0, count = 0; ulong head, page; ulong real_head_page = cpu_buffer->head_page; @@ -222,6 +244,12 @@ static int ftrace_init_pages(struct ring if (cpu_buffer->pages == NULL) return -1; + cpu_buffer->linear_pages = calloc(sizeof(ulong), nr_pages + 1); + if (cpu_buffer->linear_pages == NULL) { + free(cpu_buffer->pages); + return -1; + } + if (lockless_ring_buffer) { read_value(head, cpu_buffer->kaddr, ring_buffer_per_cpu, pages); cpu_buffer->pages[j++] = head - koffset(buffer_page, list); @@ -253,6 +281,8 @@ static int ftrace_init_pages(struct ring goto out_fail; } + /* find head page and head_page_index */ + cpu_buffer->real_head_page = real_head_page; cpu_buffer->head_page_index = -1; @@ -268,10 +298,38 @@ static int ftrace_init_pages(struct ring goto out_fail; } + /* Setup linear pages */ + + cpu_buffer->linear_pages[count++] = cpu_buffer->reader_page; + + if (cpu_buffer->reader_page == cpu_buffer->commit_page) + goto done; + + j = cpu_buffer->head_page_index; + for (;;) { + cpu_buffer->linear_pages[count++] = cpu_buffer->pages[j]; + + if (cpu_buffer->pages[j] == cpu_buffer->commit_page) + break; + + j++; + if (j == nr_pages) + j = 0; + + if (j == cpu_buffer->head_page_index) { + /* cpu_buffer->commit_page may be corrupted */ + break; + } + } + +done: + cpu_buffer->nr_linear_pages = count; + return 0; out_fail: free(cpu_buffer->pages); + free(cpu_buffer->linear_pages); return -1; } @@ -279,8 +337,13 @@ static void ftrace_destroy_buffers(struc { int i; - for (i = 0; i < nr_cpu_ids; i++) + for (i = 0; i < nr_cpu_ids; i++) { + if (!buffers[i].kaddr) + continue; + free(buffers[i].pages); + free(buffers[i].linear_pages); + } } static int ftrace_init_buffers(struct ring_buffer_per_cpu *buffers, @@ -466,7 +529,7 @@ static void ftrace_destroy(void) free(global_buffers); } -static int ftrace_dump_page(FILE *out, ulong page, void *page_tmp) +static int ftrace_dump_page(int fd, ulong page, void *page_tmp) { ulong raw_page; @@ -476,7 +539,8 @@ static int ftrace_dump_page(FILE *out, u RETURN_ON_ERROR)) goto out_fail; - fwrite(page_tmp, 1, PAGESIZE(), out); + if (write_and_check(fd, page_tmp, PAGESIZE())) + return -1; return 0; @@ -485,33 +549,15 @@ out_fail: } static -void ftrace_dump_buffer(FILE *out, struct ring_buffer_per_cpu *cpu_buffer, +void ftrace_dump_buffer(int fd, struct ring_buffer_per_cpu *cpu_buffer, unsigned pages, void *page_tmp) { - unsigned i; - - if (ftrace_dump_page(out, cpu_buffer->reader_page, page_tmp) < 0) - return; - - if (cpu_buffer->reader_page == cpu_buffer->commit_page) - return; - - i = cpu_buffer->head_page_index; - for (;;) { - if (ftrace_dump_page(out, cpu_buffer->pages[i], page_tmp) < 0) - break; - - if (cpu_buffer->pages[i] == cpu_buffer->commit_page) - break; - - i++; - if (i == pages) - i = 0; + int i; - if (i == cpu_buffer->head_page_index) { - /* cpu_buffer->commit_page may be corrupted */ + for (i = 0; i < cpu_buffer->nr_linear_pages; i++) { + if (ftrace_dump_page(fd, cpu_buffer->linear_pages[i], + page_tmp) < 0) break; - } } } @@ -536,7 +582,7 @@ static int ftrace_dump_buffers(const cha int i; void *page_tmp; char path[PATH_MAX]; - FILE *out; + int fd; page_tmp = malloc(PAGESIZE()); if (page_tmp == NULL) @@ -554,12 +600,12 @@ static int ftrace_dump_buffers(const cha snprintf(path, sizeof(path), "%s/cpu%d/trace_pipe_raw", per_cpu_path, i); - out = fopen(path, "wb"); - if (out == NULL) + fd = open(path, O_WRONLY | O_CREAT, 0644); + if (fd < 0) goto out_fail; - ftrace_dump_buffer(out, cpu_buffer, global_pages, page_tmp); - fclose(out); + ftrace_dump_buffer(fd, cpu_buffer, global_pages, page_tmp); + close(fd); } free(page_tmp); @@ -724,6 +770,7 @@ static void ftrace_destroy_event_types(v free(event_types[i]->fields); free(event_types[i]->system); free(event_types[i]->name); + free(event_types[i]->print_fmt); free(event_types[i]); } @@ -743,8 +790,8 @@ static int ftrace_init_event_types(void) read_value(event, ftrace_events, list_head, next); while (event != ftrace_events) { ulong call; - ulong name_addr, system_addr; - char name[128], system[128]; + ulong name_addr, system_addr, print_fmt_addr; + char name[128], system[128], print_fmt[4096]; int id; call = event - koffset(ftrace_event_call, list); @@ -753,11 +800,14 @@ static int ftrace_init_event_types(void) read_value(id, call, ftrace_event_call, id); read_value(name_addr, call, ftrace_event_call, name); read_value(system_addr, call, ftrace_event_call, system); + read_value(print_fmt_addr, call, ftrace_event_call, print_fmt); if (!read_string(name_addr, name, 128)) goto out_fail; if (!read_string(system_addr, system, 128)) goto out_fail; + if (!read_string(print_fmt_addr, print_fmt, 4096)) + goto out_fail; /* Enlarge event types array when need */ if (nr_event_types >= max_types) { @@ -779,6 +829,7 @@ static int ftrace_init_event_types(void) aevent_type->system = strdup(system); aevent_type->name = strdup(name); + aevent_type->print_fmt = strdup(print_fmt); aevent_type->id = id; aevent_type->nfields = 0; aevent_type->fields = NULL; @@ -809,6 +860,7 @@ static int ftrace_init_event_types(void) out_fail_free_aevent_type: free(aevent_type->system); free(aevent_type->name); + free(aevent_type->print_fmt); free(aevent_type); out_fail: ftrace_destroy_event_types(); @@ -865,6 +917,7 @@ static int ftrace_dump_event_type(struct char format_path[PATH_MAX]; FILE *out; int i; + int common_field_count = 5; snprintf(format_path, sizeof(format_path), "%s/format", path); out = fopen(format_path, "w"); @@ -875,15 +928,40 @@ static int ftrace_dump_event_type(struct fprintf(out, "ID: %d\n", t->id); fprintf(out, "format:\n"); - for (i = 0; i < t->nfields; i++) { - struct ftrace_field *f = &t->fields[i]; + for (i = t->nfields - 1; i >= 0; i--) { + /* + * Smartly shows the array type(except dynamic array). + * Normal: + * field:TYPE VAR + * If TYPE := TYPE[LEN], it is shown: + * field:TYPE VAR[LEN] + */ + struct ftrace_field *field = &t->fields[i]; + const char *array_descriptor = strchr(field->type, '['); + + if (!strncmp(field->type, "__data_loc", 10)) + array_descriptor = NULL; + + if (!array_descriptor) { + fprintf(out, "\tfield:%s %s;\toffset:%u;" + "\tsize:%u;\tsigned:%d;\n", + field->type, field->name, field->offset, + field->size, !!field->is_signed); + } else { + fprintf(out, "\tfield:%.*s %s%s;\toffset:%u;" + "\tsize:%u;\tsigned:%d;\n", + (int)(array_descriptor - field->type), + field->type, field->name, + array_descriptor, field->offset, + field->size, !!field->is_signed); + } - fprintf(out, "\tfield:%s %s;\toffset:%d;\tsize:%d;\n", - f->type, f->name, f->offset, f->size); + if (--common_field_count == 0) + fprintf(out, "\n"); } - /* TODO */ - fprintf(out, "\nprint fmt: \"unknow fmt from dump\"\n"); + fprintf(out, "\nprint fmt: %s\n", t->print_fmt); + fclose(out); return 0; @@ -914,9 +992,8 @@ static int ftrace_dump_event_types(const } struct ring_buffer_per_cpu_stream { - ulong *pages; + struct ring_buffer_per_cpu *cpu_buffer; void *curr_page; - int available_pages; int curr_page_indx; uint64_t ts; @@ -928,42 +1005,11 @@ static int ring_buffer_per_cpu_stream_init(struct ring_buffer_per_cpu *cpu_buffer, unsigned pages, struct ring_buffer_per_cpu_stream *s) { - unsigned i, count = 0; - + s->cpu_buffer = cpu_buffer; s->curr_page = malloc(PAGESIZE()); if (s->curr_page == NULL) return -1; - s->pages = malloc(sizeof(ulong) * (pages + 1)); - if (s->pages == NULL) { - free(s->curr_page); - return -1; - } - - s->pages[count++] = cpu_buffer->reader_page; - - if (cpu_buffer->reader_page == cpu_buffer->commit_page) - goto pages_done; - - i = cpu_buffer->head_page_index; - for (;;) { - s->pages[count++] = cpu_buffer->pages[i]; - - if (cpu_buffer->pages[i] == cpu_buffer->commit_page) - break; - - i++; - if (i == pages) - i = 0; - - if (i == cpu_buffer->head_page_index) { - /* cpu_buffer->commit_page may be corrupted */ - break; - } - } - -pages_done: - s->available_pages = count; s->curr_page_indx = -1; return 0; } @@ -972,7 +1018,6 @@ static void ring_buffer_per_cpu_stream_destroy(struct ring_buffer_per_cpu_stream *s) { free(s->curr_page); - free(s->pages); } struct ftrace_event { @@ -998,7 +1043,8 @@ int ring_buffer_per_cpu_stream_get_page( { ulong raw_page; - read_value(raw_page, s->pages[s->curr_page_indx], buffer_page, page); + read_value(raw_page, s->cpu_buffer->linear_pages[s->curr_page_indx], + buffer_page, page); if (!readmem(raw_page, KVADDR, s->curr_page, PAGESIZE(), "get page context", RETURN_ON_ERROR)) @@ -1022,18 +1068,18 @@ int ring_buffer_per_cpu_stream_pop_event res->data = NULL; - if (s->curr_page_indx >= s->available_pages) + if (s->curr_page_indx >= s->cpu_buffer->nr_linear_pages) return -1; again: if ((s->curr_page_indx == -1) || (s->offset >= s->commit)) { s->curr_page_indx++; - if (s->curr_page_indx == s->available_pages) + if (s->curr_page_indx == s->cpu_buffer->nr_linear_pages) return -1; if (ring_buffer_per_cpu_stream_get_page(s) < 0) { - s->curr_page_indx = s->available_pages; + s->curr_page_indx = s->cpu_buffer->nr_linear_pages; return -1; } @@ -1105,9 +1151,7 @@ static void __rbs_destroy(struct ring_bu int cpu; for (cpu = 0; cpu < nr; cpu++) { - if (!global_buffers[cpu].kaddr) - continue; - if (cpulist && !cpulist[cpu]) + if (!s->ss[cpu].cpu_buffer) continue; ring_buffer_per_cpu_stream_destroy(s->ss + cpu); @@ -1133,6 +1177,7 @@ int ring_buffer_stream_init(struct ring_ } for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + s->ss[cpu].cpu_buffer = NULL; s->es[cpu].data = NULL; if (!global_buffers[cpu].kaddr) @@ -1184,7 +1229,7 @@ static int ring_buffer_stream_pop_event( if (s->popped_cpu == nr_cpu_ids) { for (cpu = 0; cpu < nr_cpu_ids; cpu++) { - if (!global_buffers[cpu].kaddr) + if (!s->ss[cpu].cpu_buffer) continue; ring_buffer_per_cpu_stream_pop_event(s->ss + cpu, @@ -1346,6 +1391,39 @@ static int dump_saved_cmdlines(const cha return 0; } +static int dump_kallsyms(const char *dump_tracing_dir) +{ + char path[PATH_MAX]; + FILE *out; + int i; + struct syment *sp; + + snprintf(path, sizeof(path), "%s/kallsyms", dump_tracing_dir); + out = fopen(path, "w"); + if (out == NULL) + return -1; + + for (sp = st->symtable; sp < st->symend; sp++) + fprintf(out, "%lx %c %s\n", sp->value, sp->type, sp->name); + + for (i = 0; i < st->mods_installed; i++) { + struct load_module *lm = &st->load_modules[i]; + + for (sp = lm->mod_symtable; sp <= lm->mod_symend; sp++) { + if (!strncmp(sp->name, "_MODULE_", strlen("_MODULE_"))) + continue; + + fprintf(out, "%lx %c %s\t[%s]\n", sp->value, sp->type, + sp->name, lm->mod_name); + } + } + + fclose(out); + return 0; +} + +static int trace_cmd_data_output(int fd); + static void ftrace_dump(int argc, char *argv[]) { int c; @@ -1355,7 +1433,7 @@ static void ftrace_dump(int argc, char * char path[PATH_MAX]; int ret; - while ((c = getopt(argc, argv, "sm")) != EOF) { + while ((c = getopt(argc, argv, "smt")) != EOF) { switch(c) { case 's': @@ -1364,6 +1442,23 @@ static void ftrace_dump(int argc, char * case 'm': dump_meta_data = 1; break; + case 't': + if (dump_symbols || dump_meta_data || argc - optind > 1) + cmd_usage(pc->curcmd, SYNOPSIS); + else { + char *trace_dat; + int fd; + + if (argc - optind == 0) + trace_dat = "trace.dat"; + else if (argc - optind == 1) + trace_dat = argv[optind]; + fd = open(trace_dat, O_WRONLY | O_CREAT + | O_TRUNC, 0644); + trace_cmd_data_output(fd); + close(fd); + } + return; default: cmd_usage(pc->curcmd, SYNOPSIS); return; @@ -1409,10 +1504,7 @@ static void ftrace_dump(int argc, char * if (dump_symbols) { /* Dump all symbols of the kernel */ - fprintf(fp, "\n-s option hasn't been implemented.\n"); - fprintf(fp, "symbols is not dumpped.\n"); - fprintf(fp, "You can use `sym -l > %s/kallsyms`\n\n", - dump_tracing_dir); + dump_kallsyms(dump_tracing_dir); } } @@ -2504,6 +2596,9 @@ static char *help_ftrace[] = { " the same as debugfs/tracing.", " -m: also dump metadata of ftrace.", " -s: also dump symbols of the kernel .", +"trace dump -t [output-file-name]", +" dump ring_buffers and all meta data to a file that can", +" be parsed by trace-cmd. Default output file name is \"trace.dat\".", NULL }; @@ -2532,3 +2627,521 @@ int _fini(void) return 1; } + +#define TRACE_CMD_FILE_VERSION_STRING "6" + +static inline int host_bigendian(void) +{ + unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 }; + unsigned int *ptr; + + ptr = (unsigned int *)str; + return *ptr == 0x01020304; +} + +static char *tmp_file_buf; +static unsigned long long tmp_file_pos; +static unsigned long long tmp_file_size; +static int tmp_file_error; + +static int init_tmp_file(void) +{ + tmp_file_buf = malloc(4096); + if (tmp_file_buf == NULL) + return -1; + + tmp_file_pos = 0; + tmp_file_size = 4096; + tmp_file_error = 0; + + return 0; +} + +static void destory_tmp_file(void) +{ + free(tmp_file_buf); +} + +#define tmp_fprintf(fmt...) \ +do { \ + char *__buf = tmp_file_buf; \ + unsigned long long __pos; \ + \ + if (tmp_file_error) \ + break; \ + __pos = tmp_file_pos; \ + __pos += snprintf(__buf + __pos, tmp_file_size - __pos, fmt); \ + if (__pos > tmp_file_size) { \ + tmp_file_size = __pos + tmp_file_size; \ + __buf = realloc(__buf, tmp_file_size); \ + if (!__buf) { \ + tmp_file_error = 1; \ + break; \ + } \ + tmp_file_buf = __buf; \ + __pos = tmp_file_pos; \ + __pos += snprintf(__buf + __pos, tmp_file_size - __pos, fmt);\ + } \ + tmp_file_pos = __pos; \ +} while (0) + +static int tmp_file_record_size4(int fd) +{ + unsigned int size = tmp_file_pos; + + if (tmp_file_error) + return -1; + if (write_and_check(fd, &size, 4)) + return -1; + return 0; +} + +static int tmp_file_record_size8(int fd) +{ + if (tmp_file_error) + return -1; + if (write_and_check(fd, &tmp_file_pos, 8)) + return -1; + return 0; +} + +static int tmp_file_flush(int fd) +{ + if (tmp_file_error) + return -1; + if (write_and_check(fd, tmp_file_buf, tmp_file_pos)) + return -1; + tmp_file_pos = 0; + return 0; +} + +static int save_initial_data(int fd) +{ + int page_size; + char buf[20]; + + if (write_and_check(fd, "\027\010\104tracing", 10)) + return -1; + + if (write_and_check(fd, TRACE_CMD_FILE_VERSION_STRING, + strlen(TRACE_CMD_FILE_VERSION_STRING) + 1)) + return -1; + + /* Crash ensure core file endian and the host endian are the same */ + if (host_bigendian()) + buf[0] = 1; + else + buf[0] = 0; + + if (write_and_check(fd, buf, 1)) + return -1; + + /* save size of long (this may not be what the kernel is) */ + buf[0] = sizeof(long); + if (write_and_check(fd, buf, 1)) + return -1; + + page_size = PAGESIZE(); + if (write_and_check(fd, &page_size, 4)) + return -1; + + return 0; +} + +static int save_header_files(int fd) +{ + /* save header_page */ + if (write_and_check(fd, "header_page", 12)) + return -1; + + tmp_fprintf("\tfield: u64 timestamp;\toffset:0;\tsize:8;\tsigned:0;\n"); + + tmp_fprintf("\tfield: local_t commit;\toffset:8;\tsize:%u;\t" + "signed:1;\n", (unsigned int)sizeof(long)); + + tmp_fprintf("\tfield: int overwrite;\toffset:8;\tsize:%u;\tsigned:1;\n", + (unsigned int)sizeof(long)); + + tmp_fprintf("\tfield: char data;\toffset:%u;\tsize:%u;\tsigned:1;\n", + (unsigned int)(8 + sizeof(long)), + (unsigned int)(PAGESIZE() - 8 - sizeof(long))); + + if (tmp_file_record_size8(fd)) + return -1; + if (tmp_file_flush(fd)) + return -1; + + /* save header_event */ + if (write_and_check(fd, "header_event", 13)) + return -1; + + tmp_fprintf( + "# compressed entry header\n" + "\ttype_len : 5 bits\n" + "\ttime_delta : 27 bits\n" + "\tarray : 32 bits\n" + "\n" + "\tpadding : type == 29\n" + "\ttime_extend : type == 30\n" + "\tdata max type_len == 28\n" + ); + + if (tmp_file_record_size8(fd)) + return -1; + if (tmp_file_flush(fd)) + return -1; + + return 0; +} + +static int save_event_file(int fd, struct event_type *t) +{ + int i; + int common_field_count = 5; + + tmp_fprintf("name: %s\n", t->name); + tmp_fprintf("ID: %d\n", t->id); + tmp_fprintf("format:\n"); + + for (i = t->nfields - 1; i >= 0; i--) { + /* + * Smartly shows the array type(except dynamic array). + * Normal: + * field:TYPE VAR + * If TYPE := TYPE[LEN], it is shown: + * field:TYPE VAR[LEN] + */ + struct ftrace_field *field = &t->fields[i]; + const char *array_descriptor = strchr(field->type, '['); + + if (!strncmp(field->type, "__data_loc", 10)) + array_descriptor = NULL; + + if (!array_descriptor) { + tmp_fprintf("\tfield:%s %s;\toffset:%u;" + "\tsize:%u;\tsigned:%d;\n", + field->type, field->name, field->offset, + field->size, !!field->is_signed); + } else { + tmp_fprintf("\tfield:%.*s %s%s;\toffset:%u;" + "\tsize:%u;\tsigned:%d;\n", + (int)(array_descriptor - field->type), + field->type, field->name, + array_descriptor, field->offset, + field->size, !!field->is_signed); + } + + if (--common_field_count == 0) + tmp_fprintf("\n"); + } + + tmp_fprintf("\nprint fmt: %s\n", t->print_fmt); + + if (tmp_file_record_size8(fd)) + return -1; + return tmp_file_flush(fd); +} + +static int save_system_files(int fd, int *system_ids, int system_id) +{ + int i, total = 0; + + for (i = 0; i < nr_event_types; i++) { + if (system_ids[i] == system_id) + total++; + } + + if (write_and_check(fd, &total, 4)) + return -1; + + for (i = 0; i < nr_event_types; i++) { + if (system_ids[i] != system_id) + continue; + + if (save_event_file(fd, event_types[i])) + return -1; + } + + return 0; +} + +static int save_events_files(int fd) +{ + int system_id = 1, *system_ids; + const char *system = "ftrace"; + int i; + int nr_systems; + + system_ids = calloc(sizeof(*system_ids), nr_event_types); + if (system_ids == NULL) + return -1; + + for (;;) { + for (i = 0; i < nr_event_types; i++) { + if (system_ids[i]) + continue; + if (!system) { + system = event_types[i]->system; + system_ids[i] = system_id; + continue; + } + if (!strcmp(event_types[i]->system, system)) + system_ids[i] = system_id; + } + if (!system) + break; + system_id++; + system = NULL; + } + + /* ftrace events */ + if (save_system_files(fd, system_ids, 1)) + goto fail; + + /* other systems events */ + nr_systems = system_id - 2; + if (write_and_check(fd, &nr_systems, 4)) + goto fail; + for (system_id = 2; system_id < nr_systems + 2; system_id++) { + for (i = 0; i < nr_event_types; i++) { + if (system_ids[i] == system_id) + break; + } + if (write_and_check(fd, (void *)event_types[i]->system, + strlen(event_types[i]->system) + 1)) + goto fail; + if (save_system_files(fd, system_ids, system_id)) + goto fail; + } + + free(system_ids); + return 0; + +fail: + free(system_ids); + return -1; +} + +static int save_proc_kallsyms(int fd) +{ + int i; + struct syment *sp; + + for (sp = st->symtable; sp < st->symend; sp++) + tmp_fprintf("%lx %c %s\n", sp->value, sp->type, sp->name); + + for (i = 0; i < st->mods_installed; i++) { + struct load_module *lm = &st->load_modules[i]; + + for (sp = lm->mod_symtable; sp <= lm->mod_symend; sp++) { + if (!strncmp(sp->name, "_MODULE_", strlen("_MODULE_"))) + continue; + + tmp_fprintf("%lx %c %s\t[%s]\n", sp->value, sp->type, + sp->name, lm->mod_name); + } + } + + if (tmp_file_record_size4(fd)) + return -1; + return tmp_file_flush(fd); +} + +static int save_ftrace_printk(int fd) +{ + struct syment *s, *e; + long bprintk_fmt_s, bprintk_fmt_e; + char string[4096]; + long *address; + size_t i, count; + + s = symbol_search("__start___trace_bprintk_fmt"); + e = symbol_search("__stop___trace_bprintk_fmt"); + if (s == NULL || e == NULL) + return -1; + + bprintk_fmt_s = s->value; + bprintk_fmt_e = e->value; + count = (bprintk_fmt_e - bprintk_fmt_s) / sizeof(long); + + if (count == 0) { + unsigned int size = 0; + return write_and_check(fd, &size, 4); + } + + address = malloc(count * sizeof(long)); + if (address == NULL) + return -1; + + if (!readmem(bprintk_fmt_s, KVADDR, address, count * sizeof(long), + "get printk address", RETURN_ON_ERROR)) { + free(address); + return -1; + } + + for (i = 0; i < count; i++) { + size_t len = read_string(address[i], string, sizeof(string)); + if (!len) { + free(address); + return -1; + } + + tmp_fprintf("0x%lx : \"", address[i]); + + for (i = 0; string[i]; i++) { + switch (string[i]) { + case '\n': + tmp_fprintf("\\n"); + break; + case '\t': + tmp_fprintf("\\t"); + break; + case '\\': + tmp_fprintf("\\\\"); + break; + case '"': + tmp_fprintf("\\\""); + break; + default: + tmp_fprintf("%c", string[i]); + } + } + tmp_fprintf("\"\n"); + } + + free(address); + + if (tmp_file_record_size4(fd)) + return -1; + return tmp_file_flush(fd); +} + +static int save_ftrace_cmdlines(int fd) +{ + int i; + struct task_context *tc = FIRST_CONTEXT(); + + for (i = 0; i < RUNNING_TASKS(); i++) + tmp_fprintf("%d %s\n", (int)tc[i].pid, tc[i].comm); + + if (tmp_file_record_size8(fd)) + return -1; + return tmp_file_flush(fd); +} + +static int save_res_data(int fd, int nr_cpu_buffers) +{ + unsigned short option = 0; + + if (write_and_check(fd, &nr_cpu_buffers, 4)) + return -1; + + if (write_and_check(fd, "options ", 10)) + return -1; + + if (write_and_check(fd, &option, 2)) + return -1; + + if (write_and_check(fd, "flyrecord", 10)) + return -1; + + return 0; +} + +static int save_record_data(int fd, int nr_cpu_buffers) +{ + int i, j; + unsigned long long offset, buffer_offset; + void *page_tmp; + + offset = lseek(fd, 0, SEEK_CUR); + offset += nr_cpu_buffers * 16; + offset = (offset + (PAGESIZE() - 1)) & ~(PAGESIZE() - 1); + buffer_offset = offset; + + for (i = 0; i < nr_cpu_ids; i++) { + struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + unsigned long long buffer_size; + + if (!cpu_buffer->kaddr) + continue; + + buffer_size = PAGESIZE() * cpu_buffer->nr_linear_pages; + if (write_and_check(fd, &buffer_offset, 8)) + return -1; + if (write_and_check(fd, &buffer_size, 8)) + return -1; + buffer_offset += buffer_size; + } + + page_tmp = malloc(PAGESIZE()); + if (page_tmp == NULL) + return -1; + + lseek(fd, offset, SEEK_SET); + for (i = 0; i < nr_cpu_ids; i++) { + struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + + if (!cpu_buffer->kaddr) + continue; + + for (j = 0; j < cpu_buffer->nr_linear_pages; j++) { + if (ftrace_dump_page(fd, cpu_buffer->linear_pages[i], + page_tmp) < 0) { + free(page_tmp); + return -1; + } + } + } + + free(page_tmp); + + return 0; +} + +static int __trace_cmd_data_output(int fd) +{ + int i; + int nr_cpu_buffers = 0; + + for (i = 0; i < nr_cpu_ids; i++) { + struct ring_buffer_per_cpu *cpu_buffer = &global_buffers[i]; + + if (!cpu_buffer->kaddr) + continue; + + nr_cpu_buffers++; + } + + if (save_initial_data(fd)) + return -1; + if (save_header_files(fd)) + return -1; + if (save_events_files(fd)) /* ftrace events and other systems events */ + return -1; + if (save_proc_kallsyms(fd)) + return -1; + if (save_ftrace_printk(fd)) + return -1; + if (save_ftrace_cmdlines(fd)) + return -1; + if (save_res_data(fd, nr_cpu_buffers)) + return -1; + if (save_record_data(fd, nr_cpu_buffers)) + return -1; + + return 0; +} + +static int trace_cmd_data_output(int fd) +{ + int ret; + + if (init_tmp_file()) + return -1; + + ret = __trace_cmd_data_output(fd); + destory_tmp_file(); + + return ret; +}