[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] Add xentrace/xen_crash.
From: Don Slutz <Don@xxxxxxxxxxxxxxx> This allows crash to connect to a domU. Usage: usage: xen_crash <domid> [<optional port>] xen_crash 1& crash localhost:5001 /usr/lib/debug/lib/modules/3.8.11-100.fc17.x86_64/vmlinux The domU will be paused while crash is connected. Currently the code exits when crash disconnects. Signed-off-by: Don Slutz <dslutz@xxxxxxxxxxx> --- .gitignore | 1 + tools/xentrace/Makefile | 5 +- tools/xentrace/xen_crash.c | 697 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 702 insertions(+), 1 deletions(-) create mode 100644 tools/xentrace/xen_crash.c diff --git a/.gitignore b/.gitignore index 3253675..51226f5 100644 --- a/.gitignore +++ b/.gitignore @@ -278,6 +278,7 @@ tools/xenstore/xs_watch_stress tools/xentrace/xentrace_setsize tools/xentrace/tbctl tools/xentrace/xenctx +tools/xentrace/xen_crash tools/xentrace/xentrace tools/xm-test/ramdisk/buildroot tools/xm-test/aclocal.m4 diff --git a/tools/xentrace/Makefile b/tools/xentrace/Makefile index 63b09c0..a2313c6 100644 --- a/tools/xentrace/Makefile +++ b/tools/xentrace/Makefile @@ -7,7 +7,7 @@ CFLAGS += $(CFLAGS_libxenctrl) LDLIBS += $(LDLIBS_libxenctrl) BIN = xentrace xentrace_setsize -LIBBIN = xenctx +LIBBIN = xenctx xen_crash SCRIPTS = xentrace_format MAN1 = $(wildcard *.1) MAN8 = $(wildcard *.8) @@ -40,6 +40,9 @@ xentrace: xentrace.o xenctx: xenctx.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) $(APPEND_LDFLAGS) +xen_crash: xen_crash.o + $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) $(APPEND_LDFLAGS) + xentrace_setsize: setsize.o $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS) $(APPEND_LDFLAGS) diff --git a/tools/xentrace/xen_crash.c b/tools/xentrace/xen_crash.c new file mode 100644 index 0000000..6a4bb34 --- /dev/null +++ b/tools/xentrace/xen_crash.c @@ -0,0 +1,697 @@ +/****************************************************************************** + * tools/xentrace/xen_crash.c + * + * Connect crash to DOMu. + * + * Copyright (C) 2012 by Cloud Switch, Inc. + * + */ + +#include <ctype.h> +#include <time.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <netdb.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <inttypes.h> +#include <getopt.h> + +#include "xenctrl.h" +#include <xen/foreign/x86_32.h> +#include <xen/foreign/x86_64.h> +#include <xen/hvm/save.h> + +xc_interface *xc_handle = 0; +int domid = 0; +int debug = 0; + +typedef unsigned long long guest_word_t; +#define FMT_32B_WORD "%08llx" +#define FMT_64B_WORD "%016llx" + +/* Word-length of the guest's own data structures */ +int guest_word_size = sizeof (unsigned long); +/* Word-length of the context record we get from xen */ +int ctxt_word_size = sizeof (unsigned long); +int guest_protected_mode = 1; + +#define MACHINE_TYPE "X86_64" + +#define STRNEQ(A, B) (B && \ + (strncmp((char *)(A), (char *)(B), strlen((char *)(B))) == 0)) +#define FAILMSG "FAIL " +#define DONEMSG "DONE " +#define DATAMSG "DATA " + +#define DATA_HDRSIZE 13 /* strlen("XXXX ") + strlen("0131072") + NULL */ + +#define BUFSIZE 127 +#define READBUFSIZE DATA_HDRSIZE + XC_PAGE_SIZE + +#define MAX_REMOTE_FDS 10 + +void +print_now(void) +{ + struct timeval tp; + struct timezone tzp; + char *timeout; + int imil; + + gettimeofday(&tp, &tzp); + timeout = ctime(&tp.tv_sec); + imil = tp.tv_usec / 1000; + timeout += 4; /* Skip day of week */ + *(timeout + 3) = 0; /* Trim at space after month */ + *(timeout + 6) = 0; /* Trim at space after day */ + *(timeout + 15) = 0; /* Trim at seconds. */ + *(timeout + 20) = 0; /* Trim after year. */ + printf("%s %s %s %s.%.3d ", timeout + 4, timeout, timeout + 18, + timeout + 7, imil); +} + +int +RTTcpWrite(int Sock, const void *pvBuffer, size_t cbBuffer) +{ + if (debug & 4) { + print_now(); + printf("rtn: %s\n", (char*)pvBuffer); + } + do + { + size_t cbNow = cbBuffer; + ssize_t cbWritten = send(Sock, (const char *)pvBuffer, cbNow, MSG_NOSIGNAL); + + if (cbWritten < 0) + return 1; + cbBuffer -= cbWritten; + pvBuffer = (char *)pvBuffer + cbWritten; + } while (cbBuffer); + + return 0; +} + + +static void * +map_page(int vcpu, guest_word_t phys) +{ + static unsigned long previous_mfn = 0; + static void *mapped = NULL; + + unsigned long mfn = phys >> XC_PAGE_SHIFT; + unsigned long offset = phys & ~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) { + if (debug & 2) { + print_now(); + printf("failed to map page for %08llx.\n", phys); + } + return NULL; + } + + out: + return (void *)(mapped + offset); +} + +static int +copy_phys(int vcpu, char * pvDst, size_t cb, guest_word_t phys) +{ + void * localAddr; + size_t cbPage = XC_PAGE_SIZE - (phys & ~XC_PAGE_MASK); + + /* optimize for the case where access is completely within the first page. */ + localAddr = map_page(vcpu, phys); + if (!localAddr) { + return 2; + } + if (cb <= cbPage) { + memcpy(pvDst, localAddr, cb); + return 0; + } + memcpy(pvDst, localAddr, cbPage); + pvDst += cbPage; + phys += cbPage; + cb -= cbPage; + + /* Max transfer is XC_PAGE_SIZE... */ + if (cb > XC_PAGE_SIZE) + return 1; + localAddr = map_page(vcpu, phys); + if (!localAddr) { + return 3; + } + memcpy(pvDst, localAddr, cb); + return 0; +} + +static guest_word_t +convert_to_phys(int vcpu, guest_word_t virt) +{ + unsigned long mfn = xc_translate_foreign_address(xc_handle, domid, vcpu, virt); + unsigned long offset = virt & ~XC_PAGE_MASK; + + return (mfn << XC_PAGE_SHIFT) + offset; +} + +static int +copy_virt(int vcpu, char * pvDst, size_t cb, guest_word_t virt) +{ + void * localAddr; + unsigned long mfn = xc_translate_foreign_address(xc_handle, domid, vcpu, virt); + unsigned long offset = virt & ~XC_PAGE_MASK; + guest_word_t phys = (mfn << XC_PAGE_SHIFT) + offset; + size_t cbPage = XC_PAGE_SIZE - offset; + + /* optimize for the case where access is completely within the first page. */ + + localAddr = map_page(vcpu, phys); + if (!localAddr) { + return 2; + } + if (cb <= cbPage) { + memcpy(pvDst, localAddr, cb); + return 0; + } + memcpy(pvDst, localAddr, cbPage); + pvDst += cbPage; + phys += cbPage; + cb -= cbPage; + + /* Max transfer is XC_PAGE_SIZE... */ + if (cb > XC_PAGE_SIZE) + return 1; + localAddr = map_page(vcpu, phys); + if (!localAddr) { + return 3; + } + memcpy(pvDst, localAddr, cb); + return 0; +} + +int main(int argc, char **argv) +{ + int port=5001; + int sock; + unsigned int length; + struct sockaddr_in server; + int msgsock; + int nfds; + int reuseaddr; + int count; + int pass; + int i; + char recvbuf[BUFSIZE + 1]; + char sendbuf[READBUFSIZE + 1]; + int fds[MAX_REMOTE_FDS]; + size_t cbRead = 0; + + int ret; + int vcpu; + vcpu_guest_context_any_t ctx; + xc_dominfo_t dominfo; + struct hvm_hw_cpu cpuctx; + + if (argc < 2 || argc > 4) { + printf("usage: xen_crash <domid> [<optional port>]\n"); + exit(-1); + } + + domid = atoi(argv[1]); + if (domid==0) { + fprintf(stderr, "cannot trace dom0\n"); + exit(-1); + } + + if (argc > 2) + port = atoi(argv[2]); + if (argc > 3) + debug = atoi(argv[3]); + + signal(SIGPIPE, SIG_IGN); + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("socket()"); + exit(1); + } + reuseaddr = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuseaddr, + sizeof reuseaddr) < 0) { + perror("setsockopt()"); + exit(2); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(port); + count = -1; + errno = EADDRINUSE; + for (pass=0; (errno == EADDRINUSE) && (count < 0); pass++) { + if ((count = bind(sock, (struct sockaddr *) & server, sizeof server)) < + 0) { + if (errno != EADDRINUSE) { + /* printf("Errno is %d\n", errno); */ + perror("bind()"); + exit(3); + } + sleep(1); /* Waiting for kernel... */ + } + } + length = sizeof server; + if (getsockname(sock, (struct sockaddr *) & server, &length) < 0) { + perror("getsockname()"); + exit(4); + } + print_now(); + if (pass == 1) + printf("Socket ready on port %d after 1 bind call\n", port); + else + printf("Socket ready on port %d after %d bind calls\n", port, pass); + listen(sock, 1); + msgsock = accept(sock, NULL, NULL); + if (msgsock == -1) + perror("accept()"); + else { + print_now(); + printf("Accepted a connection.\n"); + close(sock); /* All done for now */ + errno = 0; /* Just in case */ + nfds = msgsock + 1; + } + + xc_handle = xc_interface_open(0,0,0); /* for accessing control interface */ + + ret = xc_domain_getinfo(xc_handle, domid, 1, &dominfo); + if (ret < 0) { + perror("xc_domain_getinfo"); + exit(-1); + } + + ret = xc_domain_pause(xc_handle, domid); + if (ret < 0) { + perror("xc_domain_pause"); + exit(-1); + } + + vcpu = 0; + ret = xc_vcpu_getcontext(xc_handle, domid, vcpu, &ctx); + if (ret < 0) { + if (!dominfo.paused) + xc_domain_unpause(xc_handle, domid); + perror("xc_vcpu_getcontext"); + exit(-1); + } + + if (dominfo.hvm) { + xen_capabilities_info_t xen_caps = ""; + if (xc_domain_hvm_getcontext_partial( + xc_handle, domid, HVM_SAVE_CODE(CPU), + vcpu, &cpuctx, sizeof cpuctx) != 0) { + perror("xc_domain_hvm_getcontext_partial"); + exit(-1); + } + guest_word_size = (cpuctx.msr_efer & 0x400) ? 8 : 4; + guest_protected_mode = (cpuctx.cr0 & 0x1); + /* HVM guest context records are always host-sized */ + if (xc_version(xc_handle, XENVER_capabilities, &xen_caps) != 0) { + perror("xc_version"); + exit(-1); + } + ctxt_word_size = (strstr(xen_caps, "xen-3.0-x86_64")) ? 8 : 4; + } else { + struct xen_domctl domctl; + memset(&domctl, 0, sizeof domctl); + domctl.domain = domid; + domctl.cmd = XEN_DOMCTL_get_address_size; + if (xc_domctl(xc_handle, &domctl) == 0) + ctxt_word_size = guest_word_size = domctl.u.address_size.size / 8; + } + + for (i = 0; i < MAX_REMOTE_FDS; i++) + fds[i] = -1; + + do { + cbRead = recv(msgsock, recvbuf, BUFSIZE, MSG_NOSIGNAL); + if (cbRead <= 0) { + close(msgsock); + msgsock = -1; + break; + } + recvbuf[cbRead] = 0; + + if (debug & 1) { + print_now(); + printf("req: %s\n", recvbuf); + } + + if (STRNEQ(recvbuf, "READ_LIVE")) + { + char *p1, *p2, *p3, *p4; + int rc2 = 0; + guest_word_t addr; + int fid; + int len; + + p1 = strtok(recvbuf, " "); /* READ_LIVE */ + p1 = strtok(NULL, " "); /* fid */ + p2 = strtok(NULL, " "); /* paddress or vaddress */ + p3 = strtok(NULL, " "); /* length */ + p4 = strtok(NULL, " "); /* vaddress or vcpu */ + + fid = atoi(p1); + addr = strtoull(p2, NULL, 16); + len = atoi(p3); + if (len < 0 || len > XC_PAGE_SIZE) + { + print_now(); + printf("bad len=%d page_size=%ld;%s %s %s %s %s\n", + len, XC_PAGE_SIZE, recvbuf, p1, p2, p3, p4); + len = 0; + rc2 = 4; + } + + if (len) + { + if (p4 && (fds[fid] == 3)) { + int myCpu = atoi(p4); + guest_word_t pAddr = convert_to_phys(myCpu, addr); + + if (debug & 2) { + print_now(); + printf("copy_virt(%d,,%d, 0x%llx)[%s %s %s %s] pAddr=0x%lx\n", + myCpu, len, addr, p1, p2, p3, p4, (long)pAddr); + } + rc2 = copy_virt(myCpu, &sendbuf[DATA_HDRSIZE], len, addr); + } else { + if (debug & 2) { + print_now(); + printf("copy_phys(%d,,%d, 0x%llx)[%s %s %s]\n", + vcpu, len, addr, p1, p2, p3); + } + rc2 = copy_phys(vcpu, &sendbuf[DATA_HDRSIZE], len, addr); + } + if (rc2) { + if (debug & 2) { + print_now(); + printf("Failed rc2=%d\n", rc2); + } + len = 0; + } + } + + if (!len) { + snprintf(sendbuf, sizeof(sendbuf), "%s%07ld", FAILMSG, (ulong)rc2); + } else { + snprintf(sendbuf, sizeof(sendbuf), "%s%07ld", DONEMSG, (ulong)len); + } + if (RTTcpWrite(msgsock, sendbuf, len + DATA_HDRSIZE)) + break; + } + else if (STRNEQ(recvbuf, "FETCH_LIVE_IP_SP_BP ")) + { + char *p1, *p2; + int cpu; + long g2ip = 0; + short g2cs = 0; + short g2ss = 0; + long g2sp = 0; + long g2bp = 0; + + p1 = strtok(recvbuf, " "); /* FETCH_LIVE_IP_SP_BP */ + p2 = strtok(NULL, " "); /* cpu */ + + cpu = atoi(p2); + if (cpu != vcpu) { + vcpu = cpu; + ret = xc_vcpu_getcontext(xc_handle, domid, vcpu, &ctx); + if (ret < 0) { + if (!dominfo.paused) + xc_domain_unpause(xc_handle, domid); + perror("xc_vcpu_getcontext"); + exit(-1); + } + if (dominfo.hvm) { + if (xc_domain_hvm_getcontext_partial( + xc_handle, domid, HVM_SAVE_CODE(CPU), + vcpu, &cpuctx, sizeof cpuctx) != 0) { + perror("xc_domain_hvm_getcontext_partial"); + exit(-1); + } + } + } + + if (ctxt_word_size == 4) { + struct cpu_user_regs_x86_32 * regs = &(ctx.x32.user_regs); + + g2ip = regs->eip; + g2sp = regs->esp; + g2bp = regs->ebp; + g2cs = regs->cs; + g2ss = regs->ss; + } else { + struct cpu_user_regs_x86_64 * regs = &(ctx.x64.user_regs); + + if (dominfo.hvm) { + g2ip = cpuctx.rip; + g2sp = cpuctx.rsp; + g2bp = cpuctx.rbp; + g2cs = cpuctx.cs_sel; + g2ss = cpuctx.ss_sel; + if (debug & 0x100) { + if (g2ip != regs->rip) { + printf("g2ip(%lx) != rip(%lx)\n", g2ip, regs->rip); + } + if (g2sp != regs->rsp) { + printf("g2sp(%lx) != rsp(%lx)\n", g2sp, regs->rsp); + } + if (g2bp != regs->rbp) { + printf("g2bp(%lx) != rbp(%lx)\n", g2bp, regs->rbp); + } + if (g2cs != regs->cs) { + printf("g2cs(%x) != cs(%x)\n", g2cs, regs->cs); + } + if (g2ss != regs->ss) { + printf("g2ss(%x) != ss(%x)\n", g2ss, regs->ss); + } + } + } else { + g2ip = regs->rip; + g2sp = regs->rsp; + g2bp = regs->rbp; + g2cs = regs->cs; + g2ss = regs->ss; + } + } + + snprintf(sendbuf, sizeof(sendbuf), "%s %d %04x:%lx %04x:%lx %lx", + p1, cpu, g2cs, g2ip, g2ss, g2sp, g2bp); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "FETCH_LIVE_CR3 ")) + { + char *p1, *p2; + int cpu; + long g2cr3 = 0; + + p1 = strtok(recvbuf, " "); /* FETCH_LIVE_CR3 */ + p2 = strtok(NULL, " "); /* cpu */ + + cpu = atoi(p2); + if (cpu != vcpu) { + vcpu = cpu; + ret = xc_vcpu_getcontext(xc_handle, domid, vcpu, &ctx); + if (ret < 0) { + if (!dominfo.paused) + xc_domain_unpause(xc_handle, domid); + perror("xc_vcpu_getcontext"); + exit(-1); + } + if (dominfo.hvm) { + if (xc_domain_hvm_getcontext_partial( + xc_handle, domid, HVM_SAVE_CODE(CPU), + vcpu, &cpuctx, sizeof cpuctx) != 0) { + perror("xc_domain_hvm_getcontext_partial"); + exit(-1); + } + } + } + + if (ctxt_word_size == 4) { + g2cr3 = ctx.x32.ctrlreg[3]; + } else { + if (dominfo.hvm) { + g2cr3 = cpuctx.cr3; + if (debug & 0x100) { + if (g2cr3 != ctx.x64.ctrlreg[3]) { + printf("g2cr3(%lx) != cr3(%lx)\n", g2cr3, ctx.x64.ctrlreg[3]); + } + } + } else { + g2cr3 = ctx.x64.ctrlreg[3]; + } + } + + snprintf(sendbuf, sizeof(sendbuf), "%s %d %lx", + p1, cpu, g2cr3); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "MACHINE_PID")) + { + snprintf(sendbuf, sizeof(sendbuf), "%s %s %d", + recvbuf, MACHINE_TYPE, 0); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "VTOP")) + { + char *p1, *p2, *p3; + int cpu; + guest_word_t vAddr, pAddr; + + p1 = strtok(recvbuf, " "); /* VTOP */ + p2 = strtok(NULL, " "); /* cpu */ + p3 = strtok(NULL, " "); /* vaddress */ + + cpu = atoi(p2); + vAddr = strtoull(p3, NULL, 16); + + pAddr = convert_to_phys(cpu, vAddr); + + snprintf(sendbuf, sizeof(sendbuf), "%s %d %lx %lx", + p1, cpu, (long)vAddr, (long)pAddr); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "OPEN ")) + { + char *p1; + char *file; + + p1 = strtok(recvbuf, " "); /* OPEN */ + file = strtok(NULL, " "); /* filename */ + + for (i = 0; i < MAX_REMOTE_FDS; i++) { + if (fds[i] == -1) + break; + } + + if (i < MAX_REMOTE_FDS) { + if (STRNEQ(file, "/dev/mem")) + { + fds[i] = 1; + snprintf(sendbuf, sizeof(sendbuf), "%s %s %d O_RDONLY %lld", p1, file, i, 1024LL * 1024LL * 1024LL * 1024LL); + } + else if (STRNEQ(file, "/dev/kmem")) + { + fds[i] = 2; + snprintf(sendbuf, sizeof(sendbuf), "%s %s %d O_RDONLY %lld", p1, file, i, 1024LL * 1024LL * 1024LL * 1024LL); + } + else if (STRNEQ(file, "/dev/vmem")) + { + fds[i] = 3; + snprintf(sendbuf, sizeof(sendbuf), "%s %s %d O_RDONLY %lld", p1, file, i, 1024LL * 1024LL * 1024LL * 1024LL); + } + else + { + snprintf(sendbuf, sizeof(sendbuf), "%s %s <FAIL>", p1, file); + } + } + else + { + snprintf(sendbuf, sizeof(sendbuf), "%s %s <FAIL>", p1, file); + } + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "CLOSE ")) + { + char *p1, *p2; + + p1 = strtok(recvbuf, " "); /* SIZE */ + p2 = strtok(NULL, " "); /* filename id */ + snprintf(sendbuf, sizeof(sendbuf), "%s %s <FAIL>", p1, p2); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "PROC_VERSION")) + { + /* + * Perform the detection. + */ + snprintf(sendbuf, sizeof(sendbuf), "<FAIL>"); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "PAGESIZE LIVE")) + { + snprintf(sendbuf, sizeof(sendbuf), "%s %ld XEN %d", + recvbuf, XC_PAGE_SIZE, dominfo.max_vcpu_id + 1); + if (RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + else if (STRNEQ(recvbuf, "EXIT")) + { + snprintf(sendbuf, sizeof(sendbuf), "%s OK", recvbuf); + RTTcpWrite(msgsock, sendbuf, strlen(sendbuf)); + break; + } + else + { + print_now(); + printf("unknown: %s\n", recvbuf); + snprintf(sendbuf, sizeof(sendbuf), "%s <FAIL>", recvbuf); + if(RTTcpWrite(msgsock, sendbuf, strlen(sendbuf))) + break; + } + } while (msgsock >= 0); + if (msgsock >= 0) + close(msgsock); + + if (!dominfo.paused) { + 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); + } + + return 0; +} + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- 1.7.1 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx http://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |