[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] Add support to xenctx for printing stack traces on x86_32 and x86_64.
# HG changeset patch # User Ian.Campbell@xxxxxxxxxxxxx # Node ID 8d5d4d58407f7071ee1dc0cc2418ffce508d8c1c # Parent 934470721c46e85651a364ab5d8369e8fde8798d Add support to xenctx for printing stack traces on x86_32 and x86_64. To support this add xc_translate_foreign_address to libxc. This function walks page tables and translates virtual addresses using a given domain and vcpu page table. Signed-off-by: Ian Campbell <Ian.Campbell@xxxxxxxxxxxxx> diff -r 934470721c46 -r 8d5d4d58407f tools/libxc/Makefile --- a/tools/libxc/Makefile Wed Dec 21 19:18:19 2005 +++ b/tools/libxc/Makefile Thu Dec 22 14:33:19 2005 @@ -27,6 +27,11 @@ ifeq ($(XEN_TARGET_ARCH),x86_32) SRCS += xc_ptrace.c SRCS += xc_ptrace_core.c +SRCS += xc_pagetab.c +endif + +ifeq ($(XEN_TARGET_ARCH),x86_64) +SRCS += xc_pagetab.c endif BUILD_SRCS := diff -r 934470721c46 -r 8d5d4d58407f tools/libxc/xenctrl.h --- a/tools/libxc/xenctrl.h Wed Dec 21 19:18:19 2005 +++ b/tools/libxc/xenctrl.h Thu Dec 22 14:33:19 2005 @@ -416,6 +416,19 @@ void *xc_map_foreign_batch(int xc_handle, uint32_t dom, int prot, unsigned long *arr, int num ); +/** + * Translates a virtual address in the context of a given domain and + * vcpu returning the machine page frame number of the associated + * page. + * + * @parm xc_handle a handle on an open hypervisor interface + * @parm dom the domain to perform the translation in + * @parm vcpu the vcpu to perform the translation on + * @parm virt the virtual address to translate + */ +unsigned long xc_translate_foreign_address(int xc_handle, uint32_t dom, + int vcpu, unsigned long long virt); + int xc_get_pfn_list(int xc_handle, uint32_t domid, unsigned long *pfn_buf, unsigned long max_pfns); diff -r 934470721c46 -r 8d5d4d58407f tools/xentrace/Makefile --- a/tools/xentrace/Makefile Wed Dec 21 19:18:19 2005 +++ b/tools/xentrace/Makefile Thu Dec 22 14:33:19 2005 @@ -15,25 +15,29 @@ OBJS = $(patsubst %.c,%.o,$(wildcard *.c)) BIN = xentrace tbctl setsize +LIBBIN = xenctx SCRIPTS = xentrace_format MAN1 = $(wildcard *.1) MAN8 = $(wildcard *.8) all: build -build: $(BIN) +build: $(BIN) $(LIBBIN) install: build [ -d $(DESTDIR)/usr/bin ] || $(INSTALL_DIR) $(DESTDIR)/usr/bin + [ -d $(DESTDIR)/usr/$(LIBDIR)/xen/bin ] || \ + $(INSTALL_DIR) $(DESTDIR)/usr/$(LIBDIR)/xen/bin [ -d $(DESTDIR)/usr/share/man/man1 ] || \ $(INSTALL_DIR) $(DESTDIR)/usr/share/man/man1 [ -d $(DESTDIR)/usr/share/man/man8 ] || \ $(INSTALL_DIR) $(DESTDIR)/usr/share/man/man8 $(INSTALL_PROG) $(BIN) $(SCRIPTS) $(DESTDIR)/usr/bin + $(INSTALL_PROG) $(LIBBIN) $(DESTDIR)/usr/$(LIBDIR)/xen/bin $(INSTALL_DATA) $(MAN1) $(DESTDIR)/usr/share/man/man1 $(INSTALL_DATA) $(MAN8) $(DESTDIR)/usr/share/man/man8 clean: - $(RM) *.a *.so *.o *.rpm $(BIN) + $(RM) *.a *.so *.o *.rpm $(BIN) $(LIBBIN) %: %.c $(HDRS) Makefile $(CC) $(CFLAGS) -o $@ $< -L$(XEN_LIBXC) -lxenctrl diff -r 934470721c46 -r 8d5d4d58407f tools/xentrace/xenctx.c --- a/tools/xentrace/xenctx.c Wed Dec 21 19:18:19 2005 +++ b/tools/xentrace/xenctx.c Thu Dec 22 14:33:19 2005 @@ -20,15 +20,184 @@ #include <errno.h> #include <argp.h> #include <signal.h> +#include <string.h> +#include <getopt.h> #include "xenctrl.h" + +int xc_handle = 0; +int domid = 0; +int frame_ptrs = 0; +int stack_trace = 0; + +#if defined (__i386__) +#define FMT_SIZE_T "%08x" +#define STACK_POINTER(regs) (regs->esp) +#define FRAME_POINTER(regs) (regs->ebp) +#define INSTR_POINTER(regs) (regs->eip) +#define STACK_ROWS 4 +#define STACK_COLS 8 +#elif defined (__x86_64__) +#define FMT_SIZE_T "%016lx" +#define STACK_POINTER(regs) (regs->rsp) +#define FRAME_POINTER(regs) (regs->rbp) +#define INSTR_POINTER(regs) (regs->rip) +#define STACK_ROWS 4 +#define STACK_COLS 4 +#endif + +struct symbol { + size_t address; + char type; + char *name; + struct symbol *next; +} *symbol_table = NULL; + +size_t kernel_stext, kernel_etext, kernel_sinittext, kernel_einittext; + +int is_kernel_text(size_t addr) +{ +#if defined (__i386__) + if (symbol_table == NULL) + return (addr > 0xc000000); +#elif defined (__x86_64__) + if (symbol_table == NULL) + return (addr > 0xffffffff80000000UL); +#endif + + if (addr >= kernel_stext && + addr <= kernel_etext) + return 1; + if (addr >= kernel_sinittext && + addr <= kernel_einittext) + return 1; + return 0; +} + +void free_symbol(struct symbol *symbol) +{ + if (symbol == NULL) + return; + if (symbol->name) + free(symbol->name); + free(symbol); +} + +void insert_symbol(struct symbol *symbol) +{ + static struct symbol *prev = NULL; + struct symbol *s = symbol_table; + + if (s == NULL) { + symbol_table = symbol; + symbol->next = NULL; + return; + } + + /* The System.map is usually already sorted... */ + if (prev + && prev->address < symbol->address + && (!prev->next || prev->next->address > symbol->address)) { + s = prev; + } else { + /* ... otherwise do crappy/slow search for the correct place */ + while(s && s->next && s->next->address < symbol->address) + s = s->next; + } + + symbol->next = s->next; + s->next = symbol; + prev = symbol; +} + +struct symbol *lookup_symbol(size_t address) +{ + struct symbol *s = symbol_table; + + while(s && s->next && s->next->address < address) + s = s->next; + + if (s && s->address < address) + return s; + + return NULL; +} + +void print_symbol(size_t addr) +{ + struct symbol *s; + + if (!is_kernel_text(addr)) + return; + + s = lookup_symbol(addr); + + if (s==NULL) + return; + + if (addr==s->address) + printf("%s", s->name); + else + printf("%s+%#x", s->name, (unsigned int)(addr - s->address)); +} + +void read_symbol_table(const char *symtab) +{ + char line[256]; + char *p; + struct symbol *symbol; + FILE *f; + + f = fopen(symtab, "r"); + if(f == NULL) { + fprintf(stderr, "failed to open symbol table %s\n", symtab); + exit(-1); + } + + while(!feof(f)) { + if(fgets(line,256,f)==NULL) + break; + + symbol = malloc(sizeof(*symbol)); + + /* need more checks for syntax here... */ + symbol->address = strtoull(line, &p, 16); + p++; + symbol->type = *p++; + p++; + + /* in the future we should handle the module name + * being appended here, this would allow us to use + * /proc/kallsyms as our symbol table + */ + if (p[strlen(p)-1] == '\n') + p[strlen(p)-1] = '\0'; + symbol->name = strdup(p); + + insert_symbol(symbol); + + if (strcmp(symbol->name, "_stext") == 0) + kernel_stext = symbol->address; + else if (strcmp(symbol->name, "_etext") == 0) + kernel_etext = symbol->address; + else if (strcmp(symbol->name, "_sinittext") == 0) + kernel_sinittext = symbol->address; + else if (strcmp(symbol->name, "_einittext") == 0) + kernel_einittext = symbol->address; + } + + fclose(f); +} #ifdef __i386__ void print_ctx(vcpu_guest_context_t *ctx1) { struct cpu_user_regs *regs = &ctx1->user_regs; - printf("eip: %08x\t", regs->eip); + printf("eip: %08x ", regs->eip); + print_symbol(regs->eip); + printf("\n"); + printf("esp: %08x\n", regs->esp); printf("eax: %08x\t", regs->eax); @@ -51,7 +220,9 @@ { struct cpu_user_regs *regs = &ctx1->user_regs; - printf("rip: %08lx\t", regs->rip); + printf("rip: %08lx ", regs->rip); + print_symbol(regs->rip); + printf("\n"); printf("rsp: %08lx\n", regs->rsp); printf("rax: %08lx\t", regs->rax); @@ -63,8 +234,8 @@ printf("rdi: %08lx\t", regs->rdi); printf("rbp: %08lx\n", regs->rbp); - printf("r8: %08lx\t", regs->r8); - printf("r9: %08lx\t", regs->r9); + printf(" r8: %08lx\t", regs->r8); + printf(" r9: %08lx\t", regs->r9); printf("r10: %08lx\t", regs->r10); printf("r11: %08lx\n", regs->r11); @@ -81,35 +252,238 @@ } #endif -void dump_ctx(uint32_t domid, uint32_t vcpu) +void *map_page(vcpu_guest_context_t *ctx, int vcpu, size_t virt) +{ + static unsigned long previous_mfn = 0; + static void *mapped = NULL; + + unsigned long mfn = xc_translate_foreign_address(xc_handle, domid, vcpu, virt); + unsigned long offset = virt & ~XC_PAGE_MASK; + + if (mapped && mfn == previous_mfn) + goto out; + + if (mapped) + munmap(mapped, XC_PAGE_SIZE); + + previous_mfn = mfn; + + mapped = xc_map_foreign_range(xc_handle, domid, XC_PAGE_SIZE, PROT_READ, mfn); + + if (mapped == NULL) { + fprintf(stderr, "failed to map page.\n"); + exit(-1); + } + + out: + return (void *)(mapped + offset); +} + +void print_stack(vcpu_guest_context_t *ctx, int vcpu) +{ + struct cpu_user_regs *regs = &ctx->user_regs; + size_t stack = STACK_POINTER(regs); + size_t stack_limit = (STACK_POINTER(regs) & XC_PAGE_MASK) + XC_PAGE_SIZE; + size_t frame; + size_t instr; + size_t *p; + int i; + + printf("\n"); + printf("Stack:\n"); + for (i=1; i<STACK_ROWS+1 && stack < stack_limit; i++) { + while(stack < stack_limit && stack < STACK_POINTER(regs) + i*STACK_COLS*sizeof(stack)) { + p = map_page(ctx, vcpu, stack); + printf(" " FMT_SIZE_T, *p); + stack += sizeof(stack); + } + printf("\n"); + } + printf("\n"); + + printf("Code:\n"); + instr = INSTR_POINTER(regs) - 21; + for(i=0; i<32; i++) { + unsigned char *c = map_page(ctx, vcpu, instr+i); + if (instr+i == INSTR_POINTER(regs)) + printf("<%02x> ", *c); + else + printf("%02x ", *c); + } + printf("\n"); + + printf("\n"); + + if(stack_trace) + printf("Stack Trace:\n"); + else + printf("Call Trace:\n"); + printf("%c [<" FMT_SIZE_T ">] ", stack_trace ? '*' : ' ', INSTR_POINTER(regs)); + + print_symbol(INSTR_POINTER(regs)); + printf(" <--\n"); + if (frame_ptrs) { + stack = STACK_POINTER(regs); + frame = FRAME_POINTER(regs); + while(frame && stack < stack_limit) { + if (stack_trace) { + while (stack < frame) { + p = map_page(ctx, vcpu, stack); + printf("| " FMT_SIZE_T " ", *p); + printf("\n"); + stack += sizeof(*p); + } + } else { + stack = frame; + } + + p = map_page(ctx, vcpu, stack); + frame = *p; + if (stack_trace) + printf("|-- " FMT_SIZE_T "\n", *p); + stack += sizeof(*p); + + if (frame) { + p = map_page(ctx, vcpu, stack); + printf("%c [<" FMT_SIZE_T ">] ", stack_trace ? '|' : ' ', *p); + print_symbol(*p); + printf("\n"); + stack += sizeof(*p); + } + } + } else { + stack = STACK_POINTER(regs); + while(stack < stack_limit) { + p = map_page(ctx, vcpu, stack); + if (is_kernel_text(*p)) { + printf(" [<" FMT_SIZE_T ">] ", *p); + print_symbol(*p); + printf("\n"); + } else if (stack_trace) { + printf(" " FMT_SIZE_T "\n", *p); + } + stack += sizeof(*p); + } + } +} + +void dump_ctx(int vcpu) { int ret; vcpu_guest_context_t ctx; - int xc_handle = xc_interface_open(); /* for accessing control interface */ + xc_handle = xc_interface_open(); /* for accessing control interface */ + + ret = xc_domain_pause(xc_handle, domid); + if (ret < 0) { + perror("xc_domain_pause"); + exit(-1); + } ret = xc_domain_get_vcpu_context(xc_handle, domid, vcpu, &ctx); - if (ret != 0) { + if (ret < 0) { + xc_domain_unpause(xc_handle, domid); perror("xc_domain_get_vcpu_context"); exit(-1); } + print_ctx(&ctx); + if (is_kernel_text(ctx.user_regs.eip)) + print_stack(&ctx, vcpu); + + ret = xc_domain_unpause(xc_handle, domid); + if (ret < 0) { + perror("xc_domain_unpause"); + exit(-1); + } + xc_interface_close(xc_handle); + if (ret < 0) { + perror("xc_interface_close"); + exit(-1); + } +} + +void usage(void) +{ + printf("usage:\n\n"); + + printf(" xenctx [options] <DOMAIN> [VCPU]\n\n"); + + printf("options:\n"); + printf(" -f, --frame-pointers\n"); + printf(" assume the kernel was compiled with\n"); + printf(" frame pointers.\n"); + printf(" -s SYMTAB, --symbol-table=SYMTAB\n"); + printf(" read symbol table from SYMTAB.\n"); + printf(" --stack-trace print a complete stack trace.\n"); } int main(int argc, char **argv) { + int ch; + const char *sopts = "fs:h"; + const struct option lopts[] = { + {"stack-trace", 0, NULL, 'S'}, + {"symbol-table", 1, NULL, 's'}, + {"frame-pointers", 0, NULL, 'f'}, + {"help", 0, NULL, 'h'}, + {0, 0, 0, 0} + }; + const char *symbol_table = NULL; + int vcpu = 0; - if (argc < 2) { - printf("usage: xenctx <domid> <optional vcpu>\n"); - exit(-1); - } - - if (argc == 3) - vcpu = atoi(argv[2]); - - dump_ctx(atoi(argv[1]), vcpu); + while ((ch = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { + switch(ch) { + case 'f': + frame_ptrs = 1; + break; + case 's': + symbol_table = optarg; + break; + case 'S': + stack_trace = 1; + break; + case 'h': + usage(); + exit(-1); + case '?': + fprintf(stderr, "%s --help for more options\n", argv[0]); + exit(-1); + } + } + + argv += optind; argc -= optind; + + if (argc < 1 || argc > 2) { + printf("usage: xenctx [options] <domid> <optional vcpu>\n"); + exit(-1); + } + + domid = atoi(argv[0]); + if (domid==0) { + fprintf(stderr, "cannot trace dom0\n"); + exit(-1); + } + + if (argc == 2) + vcpu = atoi(argv[1]); + + if (symbol_table) + read_symbol_table(symbol_table); + + dump_ctx(vcpu); return 0; } + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff -r 934470721c46 -r 8d5d4d58407f tools/libxc/xc_pagetab.c --- /dev/null Wed Dec 21 19:18:19 2005 +++ b/tools/libxc/xc_pagetab.c Thu Dec 22 14:33:19 2005 @@ -0,0 +1,192 @@ +/****************************************************************************** + * xc_pagetab.c + * + * Function to translate virtual to physical addresses. + */ +#include "xc_private.h" + +#if defined(__i386__) + +#define L1_PAGETABLE_SHIFT_PAE 12 +#define L2_PAGETABLE_SHIFT_PAE 21 +#define L3_PAGETABLE_SHIFT_PAE 30 + +#define L1_PAGETABLE_SHIFT 12 +#define L2_PAGETABLE_SHIFT 22 + +#define L0_PAGETABLE_MASK_PAE 0x0000000ffffff000ULL +#define L1_PAGETABLE_MASK_PAE 0x1ffULL +#define L2_PAGETABLE_MASK_PAE 0x1ffULL +#define L3_PAGETABLE_MASK_PAE 0x3ULL + +#define L0_PAGETABLE_MASK 0xfffff000ULL +#define L1_PAGETABLE_MASK 0x3ffULL +#define L2_PAGETABLE_MASK 0x3ffULL + +#elif defined(__x86_64__) + +#define L1_PAGETABLE_SHIFT_PAE 12 +#define L2_PAGETABLE_SHIFT_PAE 21 +#define L3_PAGETABLE_SHIFT_PAE 30 +#define L4_PAGETABLE_SHIFT_PAE 39 + +#define L1_PAGETABLE_SHIFT L1_PAGETABLE_SHIFT_PAE +#define L2_PAGETABLE_SHIFT L2_PAGETABLE_SHIFT_PAE + +#define L0_PAGETABLE_MASK_PAE 0x000000fffffff000ULL +#define L1_PAGETABLE_MASK_PAE 0x1ffULL +#define L2_PAGETABLE_MASK_PAE 0x1ffULL +#define L3_PAGETABLE_MASK_PAE 0x1ffULL +#define L4_PAGETABLE_MASK_PAE 0x1ffULL + +#define L0_PAGETABLE_MASK L0_PAGETABLE_MASK_PAE +#define L1_PAGETABLE_MASK L1_PAGETABLE_MASK_PAE +#define L2_PAGETABLE_MASK L2_PAGETABLE_MASK_PAE + +#endif + +unsigned long xc_translate_foreign_address(int xc_handle, uint32_t dom, + int vcpu, unsigned long long virt ) +{ + vcpu_guest_context_t ctx; + unsigned long long cr3; + void *pd, *pt, *pdppage = NULL, *pdp, *pml = NULL; + unsigned long long pde, pte, pdpe, pmle; + unsigned long mfn = 0; +#if defined (__i386__) + static int pt_levels = 0; + + if (pt_levels == 0) { + xen_capabilities_info_t xen_caps = ""; + + if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) + goto out; + if (strstr(xen_caps, "xen-3.0-x86_64")) + pt_levels = 4; + else if (strstr(xen_caps, "xen-3.0-x86_32p")) + pt_levels = 3; + else if (strstr(xen_caps, "xen-3.0-x86_32")) + pt_levels = 2; + else + goto out; + } +#elif defined (__x86_64__) +#define pt_levels 4 +#endif + + if (xc_domain_get_vcpu_context(xc_handle, dom, vcpu, &ctx) != 0) { + fprintf(stderr, "failed to retreive vcpu context\n"); + goto out; + } + cr3 = ctx.ctrlreg[3]; + + /* Page Map Level 4 */ + +#if defined(__i386__) + pmle = cr3; +#elif defined(__x86_64__) + pml = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, cr3 >> PAGE_SHIFT); + if (pml == NULL) { + fprintf(stderr, "failed to map PML4\n"); + goto out; + } + pmle = *(unsigned long long *)(pml + 8 * ((virt >> L4_PAGETABLE_SHIFT_PAE) & L4_PAGETABLE_MASK_PAE)); + if((pmle & 1) == 0) { + fprintf(stderr, "page entry not present in PML4\n"); + goto out_unmap_pml; + } +#endif + + /* Page Directory Pointer Table */ + + if (pt_levels >= 3) { + pdppage = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pmle >> PAGE_SHIFT); + if (pdppage == NULL) { + fprintf(stderr, "failed to map PDP\n"); + goto out_unmap_pml; + } + if (pt_levels >= 4) + pdp = pdppage; + else + /* PDP is only 32 bit aligned with 3 level pts */ + pdp = pdppage + (pmle & ~(XC_PAGE_MASK | 0x1f)); + + pdpe = *(unsigned long long *)(pdp + 8 * ((virt >> L3_PAGETABLE_SHIFT_PAE) & L3_PAGETABLE_MASK_PAE)); + + if((pdpe & 1) == 0) { + fprintf(stderr, "page entry not present in PDP\n"); + goto out_unmap_pdp; + } + } else { + pdpe = pmle; + } + + /* Page Directory */ + + pd = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pdpe >> PAGE_SHIFT); + if (pd == NULL) { + fprintf(stderr, "failed to map PD\n"); + goto out_unmap_pdp; + } + + if (pt_levels >= 3) + pde = *(unsigned long long *)(pd + 8 * ((virt >> L2_PAGETABLE_SHIFT_PAE) & L2_PAGETABLE_MASK_PAE)); + else + pde = *(unsigned long long *)(pd + 4 * ((virt >> L2_PAGETABLE_SHIFT) & L2_PAGETABLE_MASK)); + + if ((pde & 1) == 0) { + fprintf(stderr, "page entry not present in PD\n"); + goto out_unmap_pd; + } + + /* Page Table */ + + if (pde & 0x00000008) { /* 4M page (or 2M in PAE mode) */ + fprintf(stderr, "Cannot currently cope with 2/4M pages\n"); + exit(-1); + } else { /* 4k page */ + pt = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, + pde >> PAGE_SHIFT); + + if (pt == NULL) { + fprintf(stderr, "failed to map PT\n"); + goto out_unmap_pd; + } + + if (pt_levels >= 3) + pte = *(unsigned long long *)(pt + 8 * ((virt >> L1_PAGETABLE_SHIFT_PAE) & L1_PAGETABLE_MASK_PAE)); + else + pte = *(unsigned long long *)(pt + 4 * ((virt >> L1_PAGETABLE_SHIFT) & L1_PAGETABLE_MASK)); + + if ((pte & 0x00000001) == 0) { + fprintf(stderr, "page entry not present in PT\n"); + goto out_unmap_pt; + } + + if (pt_levels >= 3) + mfn = (pte & L0_PAGETABLE_MASK_PAE) >> PAGE_SHIFT; + else + mfn = (pte & L0_PAGETABLE_MASK) >> PAGE_SHIFT; + } + + out_unmap_pt: + munmap(pt, PAGE_SIZE); + out_unmap_pd: + munmap(pd, PAGE_SIZE); + out_unmap_pdp: + munmap(pdppage, PAGE_SIZE); + out_unmap_pml: + munmap(pml, PAGE_SIZE); + out: + return mfn; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |