[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 8 of 8 [RFC]] tools/misc: introduce xen-mfndump



It's a little tool, useful when trying to figure out what goes
on in both the host and the guests memory, i.e., stuff like
MFN to PFN mappings, MFN/PFN mappings in a guest's PTEs, etc.

This is what it does as of now:

$ /usr/sbin/xen-mfndump
Usage: xen-mfndump <command> [args]
Commands:
  help                      show this help
  dump-m2p                  show M2P
  dump-p2m    <domid>       show P2M of <domid>
  dump-ptes   <domid> <mfn> show the PTEs in <mfn>
  lookup-pte  <domid> <mfn> find the PTE mapping <mfn>
  memcmp-mfns <domid1> <mfn1> <domid2> <mfn2>
                            (str)compare content of <mfn1> & <mfn2>

It's probably far from perfect, but it reveals quite useful when
debugging the kind of issues introduced by this series.

Signed-off-by: Dario Faggioli <dario.faggioli@xxxxxxxxxx>

diff --git a/tools/misc/Makefile b/tools/misc/Makefile
--- a/tools/misc/Makefile
+++ b/tools/misc/Makefile
@@ -10,7 +10,7 @@ CFLAGS += $(CFLAGS_libxenstore)
 HDRS     = $(wildcard *.h)
 
 TARGETS-y := xenperf xenpm xen-tmem-list-parse gtraceview gtracestat 
xenlockprof xenwatchdogd xencov
-TARGETS-$(CONFIG_X86) += xen-detect xen-hvmctx xen-hvmcrash xen-lowmemd
+TARGETS-$(CONFIG_X86) += xen-detect xen-hvmctx xen-hvmcrash xen-lowmemd 
xen-mfndump
 TARGETS-$(CONFIG_MIGRATE) += xen-hptool
 TARGETS := $(TARGETS-y)
 
@@ -24,7 +24,7 @@ INSTALL_BIN := $(INSTALL_BIN-y)
 
 INSTALL_SBIN-y := xm xen-bugtool xen-python-path xend xenperf xsview xenpm 
xen-tmem-list-parse gtraceview \
        gtracestat xenlockprof xenwatchdogd xen-ringwatch xencov
-INSTALL_SBIN-$(CONFIG_X86) += xen-hvmctx xen-hvmcrash xen-lowmemd
+INSTALL_SBIN-$(CONFIG_X86) += xen-hvmctx xen-hvmcrash xen-lowmemd xen-mfndump
 INSTALL_SBIN-$(CONFIG_MIGRATE) += xen-hptool
 INSTALL_SBIN := $(INSTALL_SBIN-y)
 
@@ -77,6 +77,9 @@ xenlockprof: xenlockprof.o
 xen-hptool: xen-hptool.o
        $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) 
$(LDLIBS_libxenstore) $(APPEND_LDFLAGS)
 
+xen-mfndump: xen-mfndump.o
+       $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest) 
$(LDLIBS_libxenstore) $(APPEND_LDFLAGS)
+
 xenwatchdogd: xenwatchdogd.o
        $(CC) $(LDFLAGS) -o $@ $< $(LDLIBS_libxenctrl) $(APPEND_LDFLAGS)
 
diff --git a/tools/misc/xen-mfndump.c b/tools/misc/xen-mfndump.c
new file mode 100644
--- /dev/null
+++ b/tools/misc/xen-mfndump.c
@@ -0,0 +1,425 @@
+#include <xenctrl.h>
+#include <xc_private.h>
+#include <xc_core.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "xg_save_restore.h"
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+static xc_interface *xch;
+
+int help_func(int argc, char *argv[])
+{
+    fprintf(stderr,
+            "Usage: xen-mfndump <command> [args]\n"
+            "Commands:\n"
+            "  help                      show this help\n"
+            "  dump-m2p                  show M2P\n"
+            "  dump-p2m    <domid>       show P2M of <domid>\n"
+            "  dump-ptes   <domid> <mfn> show the PTEs in <mfn>\n"
+            "  lookup-pte  <domid> <mfn> find the PTE mapping <mfn>\n"
+            "  memcmp-mfns <domid1> <mfn1> <domid2> <mfn2>\n"
+            "                            compare content of <mfn1> & <mfn2>\n"
+           );
+
+    return 0;
+}
+
+int dump_m2p_func(int argc, char *argv[])
+{
+    unsigned long i, max_mfn;
+    xen_pfn_t *m2p_table;
+
+    if ( argc > 0 )
+    {
+        help_func(0, NULL);
+        return 1;
+    }
+
+    /* Map M2P and obtain gpfn */
+    max_mfn = xc_maximum_ram_page(xch);
+    if ( !(m2p_table = xc_map_m2p(xch, max_mfn, PROT_READ, NULL)) )
+    {
+        ERROR("Failed to map live M2P table");
+        return -1;
+    }
+
+    printf(" --- Dumping M2P ---\n");
+    printf(" Max MFN: %lu\n", max_mfn);
+    for ( i = 0; i < max_mfn; i++ )
+    {
+        printf("  mfn=0x%lx ==> pfn=0x%lx\n", i, m2p_table[i]);
+    }
+    printf(" --- End of M2P ---\n");
+
+    munmap(m2p_table, M2P_SIZE(max_mfn));
+    return 0;
+}
+
+int dump_p2m_func(int argc, char *argv[])
+{
+    struct xc_domain_meminfo minfo;
+    xc_dominfo_t info;
+    unsigned long i;
+    int domid;
+
+    if ( argc < 1 )
+    {
+        help_func(0, NULL);
+        return 1;
+    }
+    domid = atoi(argv[0]);
+
+    if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ||
+         info.domid != domid )
+    {
+        ERROR("Failed to obtain info for domain %d\n", domid);
+        return -1;
+    }
+
+    /* Retrieve all the info about the domain's memory */
+    memset(&minfo, 0, sizeof(minfo));
+    if ( xc_map_domain_meminfo(xch, domid, &minfo) )
+    {
+        ERROR("Could not map domain %d memory information\n", domid);
+        return -1;
+    }
+
+    printf(" --- Dumping P2M for domain %d ---\n", domid);
+    printf(" Guest Width: %u, PT Levels: %u P2M size: = %lu\n",
+           minfo.guest_width, minfo.pt_levels, minfo.p2m_size);
+    for ( i = 0; i < minfo.p2m_size; i++ )
+    {
+        unsigned long pagetype = minfo.pfn_type[i] &
+                                     XEN_DOMCTL_PFINFO_LTAB_MASK;
+
+        printf("  pfn=0x%lx ==> mfn=0x%lx (type 0x%lx)", i, minfo.p2m_table[i],
+               pagetype >> XEN_DOMCTL_PFINFO_LTAB_SHIFT);
+
+        if ( is_mapped(minfo.p2m_table[i]) )
+            printf(" [mapped]");
+
+        if ( pagetype & XEN_DOMCTL_PFINFO_LPINTAB )
+            printf (" [pinned]");
+
+        if ( pagetype == XEN_DOMCTL_PFINFO_XTAB )
+            printf(" [xtab]");
+        if ( pagetype == XEN_DOMCTL_PFINFO_BROKEN )
+            printf(" [broken]");
+        if ( pagetype == XEN_DOMCTL_PFINFO_XALLOC )
+            printf( " [xalloc]");
+
+        switch ( pagetype & XEN_DOMCTL_PFINFO_LTABTYPE_MASK )
+        {
+            case XEN_DOMCTL_PFINFO_L1TAB:
+                printf(" L1 table");
+                break;
+
+            case XEN_DOMCTL_PFINFO_L2TAB:
+                printf(" L2 table");
+                break;
+
+            case XEN_DOMCTL_PFINFO_L3TAB:
+                printf(" L3 table");
+                break;
+
+            case XEN_DOMCTL_PFINFO_L4TAB:
+                printf(" L4 table");
+                break;
+        }
+
+        printf("\n");
+    }
+    printf(" --- End of P2M for domain %d ---\n", domid);
+
+    xc_unmap_domain_meminfo(xch, &minfo);
+    return 0;
+}
+
+int dump_ptes_func(int argc, char *argv[])
+{
+    struct xc_domain_meminfo minfo;
+    xc_dominfo_t info;
+    void *page = NULL;
+    unsigned long i, max_mfn;
+    int domid, pte_num, rc = 0;
+    xen_pfn_t pfn, mfn, *m2p_table;
+
+    if ( argc < 2 )
+    {
+        help_func(0, NULL);
+        return 1;
+    }
+    domid = atoi(argv[0]);
+    mfn = strtoul(argv[1], NULL, 16);
+
+    if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ||
+         info.domid != domid )
+    {
+        ERROR("Failed to obtain info for domain %d\n", domid);
+        return -1;
+    }
+
+    /* Retrieve all the info about the domain's memory */
+    memset(&minfo, 0, sizeof(minfo));
+    if ( xc_map_domain_meminfo(xch, domid, &minfo) )
+    {
+        ERROR("Could not map domain %d memory information\n", domid);
+        return -1;
+    }
+
+    /* Map M2P and obtain gpfn */
+    max_mfn = xc_maximum_ram_page(xch);
+    if ( (mfn > max_mfn) ||
+         !(m2p_table = xc_map_m2p(xch, max_mfn, PROT_READ, NULL)) )
+    {
+        xc_unmap_domain_meminfo(xch, &minfo);
+        ERROR("Failed to map live M2P table");
+        return -1;
+    }
+
+    pfn = m2p_table[mfn];
+    if ( pfn >= minfo.p2m_size )
+    {
+        ERROR("pfn 0x%lx out of range for domain %d\n", pfn, domid);
+        rc = -1;
+        goto out;
+    }
+
+    if ( !(minfo.pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) )
+    {
+        ERROR("pfn 0x%lx for domain %d is not a PT\n", pfn, domid);
+        rc = -1;
+        goto out;
+    }
+
+    page = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ,
+                                minfo.p2m_table[pfn]);
+    if ( !page )
+    {
+        ERROR("Failed to map 0x%lx\n", minfo.p2m_table[pfn]);
+        rc = -1;
+        goto out;
+    }
+
+    pte_num = PAGE_SIZE / 8;
+
+    printf(" --- Dumping %d PTEs for domain %d ---\n", pte_num, domid);
+    printf(" Guest Width: %u, PT Levels: %u P2M size: = %lu\n",
+           minfo.guest_width, minfo.pt_levels, minfo.p2m_size);
+    printf(" pfn: 0x%lx, mfn: 0x%lx",
+           pfn, minfo.p2m_table[pfn]);
+    switch ( minfo.pfn_type[pfn] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK )
+    {
+        case XEN_DOMCTL_PFINFO_L1TAB:
+            printf(", L1 table");
+            break;
+        case XEN_DOMCTL_PFINFO_L2TAB:
+            printf(", L2 table");
+            break;
+        case XEN_DOMCTL_PFINFO_L3TAB:
+            printf(", L3 table");
+            break;
+        case XEN_DOMCTL_PFINFO_L4TAB:
+            printf(", L4 table");
+            break;
+    }
+    if ( minfo.pfn_type[pfn] & XEN_DOMCTL_PFINFO_LPINTAB )
+        printf (" [pinned]");
+    if ( is_mapped(minfo.p2m_table[pfn]) )
+        printf(" [mapped]");
+    printf("\n");
+
+    for ( i = 0; i < pte_num; i++ )
+        printf("  pte[%lu]: 0x%lx\n", i, ((const uint64_t*)page)[i]);
+
+    printf(" --- End of PTEs for domain %d, pfn=0x%lx (mfn=0x%lx) ---\n",
+           domid, pfn, minfo.p2m_table[pfn]);
+
+ out:
+    munmap(page, PAGE_SIZE);
+    xc_unmap_domain_meminfo(xch, &minfo);
+    munmap(m2p_table, M2P_SIZE(max_mfn));
+    return rc;
+}
+
+int lookup_pte_func(int argc, char *argv[])
+{
+    struct xc_domain_meminfo minfo;
+    xc_dominfo_t info;
+    void *page = NULL;
+    unsigned long i, j;
+    int domid, pte_num;
+    xen_pfn_t mfn;
+
+    if ( argc < 2 )
+    {
+        help_func(0, NULL);
+        return 1;
+    }
+    domid = atoi(argv[0]);
+    mfn = strtoul(argv[1], NULL, 16);
+
+    if ( xc_domain_getinfo(xch, domid, 1, &info) != 1 ||
+         info.domid != domid )
+    {
+        ERROR("Failed to obtain info for domain %d\n", domid);
+        return -1;
+    }
+
+    /* Retrieve all the info about the domain's memory */
+    memset(&minfo, 0, sizeof(minfo));
+    if ( xc_map_domain_meminfo(xch, domid, &minfo) )
+    {
+        ERROR("Could not map domain %d memory information\n", domid);
+        return -1;
+    }
+
+    pte_num = PAGE_SIZE / 8;
+
+    printf(" --- Lookig for PTEs mapping mfn 0x%lx for domain %d ---\n",
+           mfn, domid);
+    printf(" Guest Width: %u, PT Levels: %u P2M size: = %lu\n",
+           minfo.guest_width, minfo.pt_levels, minfo.p2m_size);
+
+    for ( i = 0; i < minfo.p2m_size; i++ )
+    {
+        if ( !(minfo.pfn_type[i] & XEN_DOMCTL_PFINFO_LTABTYPE_MASK) )
+            continue;
+
+        page = xc_map_foreign_range(xch, domid, PAGE_SIZE, PROT_READ,
+                                    minfo.p2m_table[i]);
+        if ( !page )
+            continue;
+
+        for ( j = 0; j < pte_num; j++ )
+        {
+            uint64_t pte = ((const uint64_t*)page)[j];
+
+#define __MADDR_BITS_X86  ((minfo.guest_width == 8) ? 52 : 44)
+#define __MFN_MASK_X86    ((1ULL << (__MADDR_BITS_X86 - PAGE_SHIFT_X86)) - 1)
+            if ( ((pte >> PAGE_SHIFT_X86) & __MFN_MASK_X86) == mfn)
+                printf("  0x%lx <-- [0x%lx][%lu]: 0x%lx\n",
+                       mfn, minfo.p2m_table[i], j, pte);
+#undef __MADDR_BITS_X86
+#undef __MFN_MASK_X8
+        }
+
+        munmap(page, PAGE_SIZE);
+        page = NULL;
+    }
+
+    xc_unmap_domain_meminfo(xch, &minfo);
+
+    return 1;
+}
+
+int memcmp_mfns_func(int argc, char *argv[])
+{
+    xc_dominfo_t info1, info2;
+    void *page1 = NULL, *page2 = NULL;
+    int domid1, domid2;
+    xen_pfn_t mfn1, mfn2;
+    int rc = 0;
+
+    if ( argc < 4 )
+    {
+        help_func(0, NULL);
+        return 1;
+    }
+    domid1 = atoi(argv[0]);
+    domid2 = atoi(argv[2]);
+    mfn1 = strtoul(argv[1], NULL, 16);
+    mfn2 = strtoul(argv[3], NULL, 16);
+
+    if ( xc_domain_getinfo(xch, domid1, 1, &info1) != 1 ||
+         xc_domain_getinfo(xch, domid2, 1, &info2) != 1 ||
+         info1.domid != domid1 || info2.domid != domid2)
+    {
+        ERROR("Failed to obtain info for domains\n");
+        return -1;
+    }
+
+    page1 = xc_map_foreign_range(xch, domid1, PAGE_SIZE, PROT_READ, mfn1);
+    page2 = xc_map_foreign_range(xch, domid2, PAGE_SIZE, PROT_READ, mfn2);
+    if ( !page1 || !page2 )
+    {
+        ERROR("Failed to map either 0x%lx[dom %d] or 0x%lx[dom %d]\n",
+              mfn1, domid1, mfn2, domid2);
+        rc = -1;
+        goto out;
+    }
+
+    printf(" --- Comparing the content of 2 MFNs ---\n");
+    printf(" 1: 0x%lx[dom %d], 2: 0x%lx[dom %d]\n",
+           mfn1, domid1, mfn2, domid2);
+    printf("  memcpy(1, 2) = %d\n", memcmp(page1, page2, PAGE_SIZE));
+
+ out:
+    munmap(page1, PAGE_SIZE);
+    munmap(page2, PAGE_SIZE);
+    return rc;
+}
+
+
+
+struct {
+    const char *name;
+    int (*func)(int argc, char *argv[]);
+} opts[] = {
+    { "help", help_func },
+    { "dump-m2p", dump_m2p_func },
+    { "dump-p2m", dump_p2m_func },
+    { "dump-ptes", dump_ptes_func },
+    { "lookup-pte", lookup_pte_func },
+    { "memcmp-mfns", memcmp_mfns_func},
+};
+
+int main(int argc, char *argv[])
+{
+    int i, ret;
+
+    if (argc < 2)
+    {
+        help_func(0, NULL);
+        return 1;
+    }
+
+    xch = xc_interface_open(0, 0, 0);
+    if ( !xch )
+    {
+        ERROR("Failed to open an xc handler");
+        return 1;
+    }
+
+    for ( i = 0; i < ARRAY_SIZE(opts); i++ )
+    {
+        if ( !strncmp(opts[i].name, argv[1], strlen(argv[1])) )
+            break;
+    }
+
+    if ( i == ARRAY_SIZE(opts) )
+    {
+        fprintf(stderr, "Unknown option '%s'", argv[1]);
+        help_func(0, NULL);
+        return 1;
+    }
+
+    ret = opts[i].func(argc - 2, argv + 2);
+
+    xc_interface_close(xch);
+
+    return !!ret;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

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