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
|