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@XenSource.com>
This commit is contained in:
Ian.Campbell@xensource.com committed 2005-12-22 14:33:19 +00:00
1 parent 4f27f31a47
commit 40924689a9
5 files changed
+602 -14

No files matched your search

+5
View File
@@ -27,6 +27,11 @@ SRCS += xc_tbuf.c
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 :=
+192
View File
@@ -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:
*/
+13
View File
@@ -416,6 +416,19 @@ void *xc_map_foreign_range(int xc_handle, uint32_t dom,
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);
+6 -2
View File
@@ -15,25 +15,29 @@ HDRS = $(wildcard *.h)
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
+386 -12
View File
@@ -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 @@ void print_ctx(vcpu_guest_context_t *ctx1)
{
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 @@ void print_ctx(vcpu_guest_context_t *ctx1)
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 @@ void print_ctx(vcpu_guest_context_t *ctx1)
}
#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");
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);
}
if (argc == 3)
vcpu = atoi(argv[2]);
domid = atoi(argv[0]);
if (domid==0) {
fprintf(stderr, "cannot trace dom0\n");
exit(-1);
}
dump_ctx(atoi(argv[1]), vcpu);
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:
*/
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.