From: Ben Thomas <bthomas@virtualiron.com>
To: xen-devel@lists.xensource.com
Subject: [PATCH] - xc_core.c/xenctrl.h - refactor slightly to allow user specified output routines
Date: Fri, 10 Mar 2006 17:06:24 -0500 [thread overview]
Message-ID: <4411F860.8090506@virtualiron.com> (raw)
[-- Attachment #1: Type: text/plain, Size: 1237 bytes --]
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@virtualiron.com)
--
------------------------------------------------------------------------
Ben Thomas Virtual Iron Software
bthomas@virtualiron.com Tower 1, Floor 2
978-849-1214 900 Chelmsford Street
Lowell, MA 01851
[-- Attachment #2: core.patch --]
[-- Type: text/x-patch, Size: 7048 bytes --]
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.
[-- Attachment #3: dump.c --]
[-- Type: text/x-csrc, Size: 6810 bytes --]
// 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:
*/
[-- Attachment #4: testdump.c --]
[-- Type: text/x-csrc, Size: 5562 bytes --]
// 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;
}
[-- Attachment #5: Type: text/plain, Size: 138 bytes --]
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
next reply other threads:[~2006-03-10 22:06 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-03-10 22:06 Ben Thomas [this message]
2006-03-14 8:36 ` [PATCH] - xc_core.c/xenctrl.h - refactor slightly to allow user specified output routines NAHieu
2006-03-14 8:59 ` NAHieu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4411F860.8090506@virtualiron.com \
--to=bthomas@virtualiron.com \
--cc=xen-devel@lists.xensource.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.