[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH] - xc_core.c/xenctrl.h - refactor slightly to allow user specified output routines
The existing xc_domain_dumpcore is very specific to disk/file based output. Refactor the code slightly to allow more user-specified control. This is done by adding a parallel xc_domain_dumpcore2 (naming isn't always my strong suit), which allows the specification of a callback routine and an opaque argument block. The existing dumpcore routine is modified to use the callback for all write operations and to turn the single seek into a small write (it's for page alignment). I've also included a small test routine, testdump.c, which drives both APIs and allows writing core to either a local disk file or across a network. And, I've included a sample network "catcher" program that receives a cross-network dump from testdump. And, good news, this probably ends my current hacking/interest in xc_domain_dumpcore. Signed-off-by: Ben Thomas (bthomas@xxxxxxxxxxxxxxx) -- ------------------------------------------------------------------------ Ben Thomas Virtual Iron Software bthomas@xxxxxxxxxxxxxxx Tower 1, Floor 2 978-849-1214 900 Chelmsford Street Lowell, MA 01851 diff -r 3ea1c6118fc2 tools/libxc/xc_core.c --- a/tools/libxc/xc_core.c Fri Mar 10 01:08:59 2006 +0100 +++ b/tools/libxc/xc_core.c Fri Mar 10 16:58:15 2006 -0500 @@ -8,6 +8,14 @@ /* number of pages to write at a time */ #define DUMP_INCREMENT (4 * 1024) #define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK) + +/* Define the local structure for local_fd_dump. + * A structure is overkill, but someone may use this as an example + */ + +typedef struct dump_args_s { + int fd; +} dump_args_t; static int copy_from_domain_page(int xc_handle, @@ -26,24 +34,21 @@ copy_from_domain_page(int xc_handle, } int -xc_domain_dumpcore(int xc_handle, - uint32_t domid, - const char *corename) +xc_domain_dumpcore2(int xc_handle, + uint32_t domid, + void *args, dumpcore_rtn_t dump_rtn) { unsigned long nr_pages; unsigned long *page_array; xc_dominfo_t info; - int i, nr_vcpus = 0, dump_fd; + int i, nr_vcpus = 0; char *dump_mem, *dump_mem_start = NULL; struct xc_core_header header; vcpu_guest_context_t ctxt[MAX_VIRT_CPUS]; + unsigned char dummy[PAGE_SIZE]; + int dummy_len; + int sts; - - if ((dump_fd = open(corename, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)) < 0) { - PERROR("Could not open corefile %s: %s", corename, strerror(errno)); - goto error_out; - } - if ((dump_mem_start = malloc(DUMP_INCREMENT*PAGE_SIZE)) == NULL) { PERROR("Could not allocate dump_mem"); goto error_out; @@ -61,27 +66,29 @@ xc_domain_dumpcore(int xc_handle, for (i = 0; i <= info.max_vcpu_id; i++) if (xc_vcpu_getcontext(xc_handle, domid, - i, &ctxt[nr_vcpus]) == 0) + i, &ctxt[nr_vcpus]) == 0) nr_vcpus++; nr_pages = info.nr_pages; - header.xch_magic = XC_CORE_MAGIC; + header.xch_magic = XC_CORE_MAGIC; header.xch_nr_vcpus = nr_vcpus; header.xch_nr_pages = nr_pages; header.xch_ctxt_offset = sizeof(struct xc_core_header); header.xch_index_offset = sizeof(struct xc_core_header) + sizeof(vcpu_guest_context_t)*nr_vcpus; - header.xch_pages_offset = round_pgup(sizeof(struct xc_core_header) + - (sizeof(vcpu_guest_context_t) * nr_vcpus) + - (nr_pages * sizeof(unsigned long))); + dummy_len = (sizeof(struct xc_core_header) + + (sizeof(vcpu_guest_context_t) * nr_vcpus) + + (nr_pages * sizeof(unsigned long))); + header.xch_pages_offset = round_pgup(dummy_len); + + sts = dump_rtn(args, &header, sizeof(struct xc_core_header)); + if (sts != 0) + return sts; - if (write(dump_fd, &header, sizeof(struct xc_core_header)) < 0 || - write(dump_fd, &ctxt, sizeof(ctxt[0]) * nr_vcpus) < 0) - { - PERROR("write failed"); - goto error_out; - } + sts = dump_rtn(args, &ctxt, sizeof(ctxt[0]) * nr_vcpus); + if (sts != 0) + return sts; if ((page_array = malloc(nr_pages * sizeof(unsigned long))) == NULL) { printf("Could not allocate memory\n"); @@ -91,18 +98,21 @@ xc_domain_dumpcore(int xc_handle, printf("Could not get the page frame list\n"); goto error_out; } - if (write(dump_fd, page_array, nr_pages * sizeof(unsigned long)) < 0) - { - PERROR("write failed"); - goto error_out; - } - lseek(dump_fd, header.xch_pages_offset, SEEK_SET); + sts = dump_rtn(args, page_array, nr_pages * sizeof(unsigned long)); + if (sts != 0) + return sts; + + memset(dummy, 0, PAGE_SIZE); + sts = dump_rtn(args, dummy, header.xch_pages_offset - dummy_len); + if (sts != 0) + return sts; + for (dump_mem = dump_mem_start, i = 0; i < nr_pages; i++) { copy_from_domain_page(xc_handle, domid, page_array, i, dump_mem); dump_mem += PAGE_SIZE; if (((i + 1) % DUMP_INCREMENT == 0) || (i + 1) == nr_pages) { - if (write(dump_fd, dump_mem_start, dump_mem - dump_mem_start) < - dump_mem - dump_mem_start) { + sts = dump_rtn(args, dump_mem_start, dump_mem - dump_mem_start); + if (sts != 0) { PERROR("Partial write, file system full?"); goto error_out; } @@ -110,14 +120,59 @@ xc_domain_dumpcore(int xc_handle, } } - close(dump_fd); free(dump_mem_start); return 0; error_out: - if (dump_fd != -1) - close(dump_fd); free(dump_mem_start); return -1; +} + +/* Callback routine for writing to a local dump file */ + +static int local_file_dump(void *args, void *buffer, int length) +{ + dump_args_t *da = args; + int bytes; + int offset; + unsigned char *buf = buffer; + + + if ( (args == NULL) || (buffer == NULL) || (length < 0) ) + return -EINVAL; + + /* This is to a local disk, so in practice a simple single "write" */ + /* would also work just as well as this loop which probably only */ + /* executes for 1 iteration anyhow */ + + offset = 0; + while (offset < length) { + bytes = write(da->fd, &buf[offset], length-offset); + if (bytes <= 0) { + PERROR("Failed to write buffer: %s", strerror(errno)); + return -errno; + } + offset += bytes; + } + return 0; +} + +int +xc_domain_dumpcore(int xc_handle, + uint32_t domid, + const char *corename) +{ + dump_args_t da; + int sts; + + if ((da.fd = open(corename, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)) < 0) { + PERROR("Could not open corefile %s: %s", corename, strerror(errno)); + return -errno; + } + + sts = xc_domain_dumpcore2(xc_handle, domid, &da, &local_file_dump); + close(da.fd); + + return sts; } /* diff -r 3ea1c6118fc2 tools/libxc/xenctrl.h --- a/tools/libxc/xenctrl.h Fri Mar 10 01:08:59 2006 +0100 +++ b/tools/libxc/xenctrl.h Fri Mar 10 16:58:15 2006 -0500 @@ -139,9 +139,28 @@ int xc_domain_create(int xc_handle, uint32_t *pdomid); +/* Functions to produce a dump of a given domain + * xc_domain_dumpcore - produces a dump to a specified file + * xc_domain_dumpcore2 - produces a dump, using a specified callback + */ + int xc_domain_dumpcore(int xc_handle, uint32_t domid, const char *corename); + +/* Define the callback function type for xc_domain_dumpcore2 + * + * This function is called by the coredump code for every "write", + * and passes an opaque object for the use of the function and + * created by the caller of xc_domain_dumpcore2 + */ + +typedef int (dumpcore_rtn_t)(void *arg, void *buffer, int length); + +int xc_domain_dumpcore2(int xc_handle, + uint32_t domid, + void *arg, + dumpcore_rtn_t dump_rtn); /* * This function sets the maximum number of vcpus that a domain may create. // Copyright (c) 2006, Virtual Iron Software, Inc. // All Rights Reserved // Sample program to catch network dumps #include <sys/types.h> #include <ctype.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <poll.h> #include <unistd.h> #include <fcntl.h> #include <xenctrl.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #define DUMP_PORT 778 // Definitions and typedefs typedef struct sockaddr SA_t; typedef struct sockaddr_in SIN_t; static int verbose = 0; /* create_listen_socket - create socket for new connections * * Input: * port port number desired * * Output: * none * * Returns: * socket number or -1 on error */ static int create_listen_socket(port) { SIN_t listenAddr; int listenSock; int sts; listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock < 0) { fprintf(stderr, "? %s: Unable to create listen socket, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return -errno; } bzero(&listenAddr, sizeof(listenAddr)); listenAddr.sin_family = AF_INET; listenAddr.sin_addr.s_addr = htonl(INADDR_ANY); listenAddr.sin_port = htons(port); if (verbose) fprintf(stdout, "[Listening on port %d]\n", port); sts = bind(listenSock, (SA_t *)&listenAddr, sizeof(listenAddr)); if (sts < 0) { fprintf(stderr, "? %s: Bind failed, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return -errno; } sts = listen(listenSock, SOMAXCONN); if (sts < 0) { fprintf(stderr, "? %s: Listen failed, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return -errno; } return listenSock; } /* process_dump - process a dump * * Input: * sockFD socket to read from * fileFD file to write to * * Output: * none * * Returns: * status */ static int process_dump(int sockFD, int fileFD) { int bytes; struct xc_core_header core; unsigned char *buffer; int bufferSize; int fileBytes; long totalSize; long last; int linepos; // Get the initial header totalSize = 0; bytes = read(sockFD, &core, sizeof(core)); if (bytes < 0) { fprintf(stderr, "? %s: failed to read from socket, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return errno; } if (bytes != sizeof(core)) { fprintf(stderr, "? %s: failed to read enough bytes from socket, got %d expected %lu\n", __FUNCTION__, bytes, sizeof(core)); return EINVAL; } if (verbose) { fprintf(stdout, "New header: Magic 0x%x, VCPUs %d, Pages %d\n", core.xch_magic,core.xch_nr_vcpus, core.xch_nr_pages); fprintf(stdout, " CTXT offset 0x%x Index offset 0x%x Pages Offset 0x%x\n", core.xch_ctxt_offset, core.xch_index_offset, core.xch_pages_offset); } // Now, start reading the rest of the dump info bufferSize = 32 * 1024; buffer = malloc(bufferSize); if (buffer == NULL) { fprintf(stderr, "? %s: failed to allocate input buffer\n", __FUNCTION__); return ENOMEM; } fileBytes = write(fileFD, &core, bytes); if (fileBytes < 0) { fprintf(stderr, "? %s: failed to write to file, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return errno; } totalSize += fileBytes; linepos = 0; last = totalSize; while (1) { bytes = read(sockFD, buffer, bufferSize); if (bytes < 0) { fprintf(stderr, "? %s: failed to read from socket, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return errno; } else if (bytes == 0) { break; } fileBytes = write(fileFD, buffer, bytes); if (fileBytes < 0) { fprintf(stderr, "? %s: failed to write to file, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return errno; } totalSize += fileBytes; if (verbose) { if (totalSize-last > (1024*1024)) { fprintf(stdout, "!"); linepos++; if ( (linepos % 64) == 0) fprintf(stdout, "\n"); fflush(stdout); last = totalSize; } } } if (verbose) fprintf(stdout, "\n"); fprintf(stdout, "[Wrote %ld bytes]\n", totalSize); return 0; } /* show_usage - output program usage infomration * * Input: * none * * Output: * none * * Returns: * status 0 for success */ static void show_usage(void) { fprintf(stdout, "Usage: dump [options]\n"); fprintf(stdout, " -p:<port> port number on which to listen (778 is the default)\n"); fprintf(stdout, " -f:<name> dump filename prefix\n"); fprintf(stdout, " -v be more verbose\n"); return; } /* main - main routine for config * * Input: * argc size of argv vector * argv ASCII arguments from command line * * Output: * none * * Returns: * status 0 for success */ int main(int argc, char *argv[]) { SIN_t connAddr; socklen_t connAddrLen; int listenSock; int sts; int newSock; struct pollfd pf; char ch; int port; int fileFD; char fileName[128]; char hostName[32]; const char *csts; char *prefix; port = 778; prefix = strdup("DUMP"); while ((ch = getopt(argc, argv, "f:hp:v")) != -1) { switch(ch) { case 'f': free(prefix); prefix = strdup(optarg); break; case 'h': show_usage(); return 0; case 'p': port = atol(optarg); break; case 'v': verbose++; break; default: fprintf(stderr, "? Unknown argument '%c'\n", ch); show_usage(); return 0; } } argc -= optind; if (argc != 0) { fprintf(stderr, "? Unknown arguments\n"); show_usage(); return 0; } listenSock = create_listen_socket(port); if (listenSock < 0) return -1; // Loop awaiting commands while (1) { newSock = accept(listenSock, (SA_t *)&connAddr, &connAddrLen); if (newSock < 0) { fprintf(stderr, "? %s: Accept failed, errno %d (%s), pf.revents 0x%x\n", __FUNCTION__, errno, strerror(errno), pf.revents); continue; } csts = inet_ntop(AF_INET, &connAddr.sin_addr, hostName, sizeof(hostName)); if (csts == NULL) { fprintf(stderr, "? %s: inet_ntop failed, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); strcpy(hostName, "Unknown"); } strcpy(fileName, prefix); strcat(fileName, hostName); fileFD = creat(fileName, 0x666); if (fileFD < 0) { fprintf(stderr, "? %s: failed to open dump file '%s', errno %d (%s)\n", __FUNCTION__, fileName, errno, strerror(errno)); close(newSock); return errno; } fprintf(stdout, "[Starting new dump from '%s' into '%s']\n", hostName, fileName); sts = process_dump(newSock, fileFD); free(prefix); close(newSock); close(fileFD); } return 0; } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 2 * tab-width: 8 * indent-tabs-mode: t * End: */ // Copyright (c) 2006, Virtual Iron Software, Inc. // All Rights Reserved // Sample program to generate dumps #include <stdio.h> #include <stdlib.h> #include <xenctrl.h> #include <netinet/in.h> #include <sys/poll.h> #include <string.h> #include <netdb.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> typedef struct sockaddr SA_t; typedef struct sockaddr_in SIN_t; // Opaque local structure for dumpcore typedef struct dump_args_s { int fd; long total; long last; int linepos; } dump_args_t; static int verbose = 0; /* net_dump - dumpcore callback routine for network connection * * Input: * args dump_args private data pointer * buffer pointer to buffer to be written * length length of buffer * * Output: * none * * Returns: * status */ static int net_dump(void *args, void *buffer, int length) { dump_args_t *da = args; int bytes; int offset; unsigned char *buf = buffer; int max; int linepos; if ( (args == NULL) || (buffer == NULL) || (length < 0) ) return -EINVAL; offset = 0; linepos = 0; while (offset < length) { max = (length-offset > 2048) ? 2048 : length-offset; bytes = write(da->fd, &buf[offset], max); if (bytes < 0) { fprintf(stderr, "? %s: Failed to write buffer, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); return -errno; } // Currently, ignore the 0 bytes case of a full buffer. It doesn't // appear to be happening in my testing. If needed, one other // approach might be: if (bytes==0) sleep(1); offset += bytes; da->total += bytes; if ( (da->total - da->last) > (1024*1024)) { if (verbose) { fprintf(stdout, "#"); da->linepos++; if ((da->linepos % 64) == 0) fprintf(stdout, "\n"); fflush(stdout); } da->last = da->total; } } return 0; } /* connect_net_dump - form connection to dump catcher on network * * Input: * host host name in addr:port format * * Output: * none * * Returns: * status */ static int connect_net_dump(char *host) { int cmdSock; SIN_t cmdAddr; int sts; char *portString; int port; char *destString; // Check input if (host == NULL) { fprintf(stderr, "? %s: host name required\n", __FUNCTION__); return -EINVAL; } // Get host and port destString = strdup(host); portString = strchr(destString, ':'); if (portString != NULL) { *portString = '\0'; portString++; port = strtol(portString, NULL, 10); } else port = 778; bzero(&cmdAddr, sizeof(cmdAddr)); cmdAddr.sin_family = AF_INET; cmdAddr.sin_port = htons(port); sts = inet_pton(AF_INET, destString, &cmdAddr.sin_addr.s_addr); if (sts <= 0) { fprintf(stderr, "? %s: inet_pton failed\n", __FUNCTION__); free(destString); return -errno; } if (verbose) fprintf(stdout, "%s: Connecting to %s:%d\n", __FUNCTION__, destString, port); free(destString); // Create socket and connect cmdSock = socket(AF_INET, SOCK_STREAM, 0); if (cmdSock < 0) { fprintf(stderr, "? %s: Unable to create listen socket, errno %d(%s)\n", __FUNCTION__, errno, strerror(errno)); return -errno; } sts = connect(cmdSock, (SA_t *)&cmdAddr, sizeof(cmdAddr)); if (sts == 0) // Return if immediate success return cmdSock; if (errno != EINPROGRESS) { fprintf(stderr, "? %s: Connect failed, errno %d (%s)\n", __FUNCTION__, errno, strerror(errno)); close(cmdSock); return -errno; } return -errno; } /* show_usage - output program usage infomration * * Input: * none * * Output: * none * * Returns: * status 0 for success */ static void show_usage(void) { fprintf(stdout, "Usage: testdump [options]\n"); fprintf(stdout, " -d:<domid> dump domain <domid>\n"); fprintf(stdout, " -f:<name> dump to with file <name>\n"); fprintf(stdout, " -h:<host:port> dump to network\n"); fprintf(stdout, " -v be more verbose\n"); return; } int main(int argc, char *argv[]) { int sts; dump_args_t da; int xc_handle; int domid = -1; int fileFlag; int netFlag; char *destName; int ch; destName = strdup(""); while ((ch = getopt(argc, argv, "d:f:h:v")) != -1) { switch(ch) { case 'd': domid = atoi(optarg); break; case 'f': free(destName); destName = strdup(optarg); fileFlag = 1; break; case 'h': free(destName); destName = strdup(optarg); netFlag = 1; break; case 'v': verbose = 1; break; default: fprintf(stderr, "? Unknown argument '%c'\n", ch); show_usage(); return 0; } } argc -= optind; if (argc != 0) { fprintf(stderr, "? Unknown arguments\n"); show_usage(); return 0; } if ( ( (fileFlag == 0) && (netFlag == 0) ) || ( (fileFlag != 0) && (netFlag != 0) ) ) { fprintf(stderr, "? Must specify one dump type of either -f<name> or -h<ipaddr:port>\n"); return EINVAL; } if (domid < 0) { fprintf(stderr, "? Must specify domain id with -d\n"); return EINVAL; } xc_handle = xc_interface_open(); if (netFlag) { da.fd = connect_net_dump(destName); if (da.fd < 0) { return -1; } da.total = 0; da.last = 0; da.linepos = 0; sts = xc_domain_dumpcore2(xc_handle, domid, &da, &net_dump); close(da.fd); } else { sts = xc_domain_dumpcore(xc_handle, domid, destName); } printf("%% %s: xc_dump_domain returned %d\n", __FUNCTION__, sts); return 0; } _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |