[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

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.