root/test/debug/vmobjinfo.c
/*
 * VMOBJINFO.C
 *
 * cc -I/usr/src/sys vmobjinfo.c -o ~/bin/vmobjinfo -lkvm
 *
 * Dump all vm_object's in the system
 *
 * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
 *
 * This code is derived from software contributed to The DragonFly Project
 * by Matthew Dillon <dillon@backplane.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name of The DragonFly Project nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific, prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#define _KERNEL_STRUCTURES
#include <sys/param.h>
#include <sys/user.h>
#include <sys/malloc.h>
#include <sys/signalvar.h>
#include <sys/namecache.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/buf.h>

#include <vm/vm.h>
#include <vm/vm_page.h>
#include <vm/vm_kern.h>
#include <vm/vm_object.h>
#include <vm/swap_pager.h>
#include <vm/vnode_pager.h>

#include <vfs/ufs/quota.h>
#include <vfs/ufs/inode.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <kvm.h>
#include <nlist.h>
#include <getopt.h>
#include <ctype.h>

TAILQ_HEAD(object_q, vm_object);

struct nlist Nl[] = {
    { "_vm_object_lists" },
    { "_nswdev" },
    { "_dmmax" },
    { NULL }
};

int VerboseOpt;
int nswdev;
int dmmax;
int memfds = -1;
int *swapfds;
char pgbuf[PAGE_SIZE];

static void scan_vmobjs(kvm_t *kd, struct object_q *obj_list);
static void dump_swap(kvm_t *kd, struct swblock *swbp);
static void dump_memq(kvm_t *kd, struct vm_page *pgp);
static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
static off_t devoffset(long blkno, int *whichp);

int
main(int ac, char **av)
{
    struct object_q obj_list[VMOBJ_HSIZE];
    kvm_t *kd;
    int i;
    int nswap;
    int ch;
    const char *corefile = NULL;
    const char *sysfile = NULL;

    while ((ch = getopt(ac, av, "M:N:v")) != -1) {
        switch(ch) {
        case 'M':
            corefile = optarg;
            break;
        case 'N':
            sysfile = optarg;
            break;
        case 'v':
            ++VerboseOpt;
            break;
        default:
            fprintf(stderr, "%s [-M core] [-N system]\n", av[0]);
            exit(1);
        }
    }
    if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) {
        perror("kvm_open");
        exit(1);
    }
    if (kvm_nlist(kd, Nl) != 0) {
        perror("kvm_nlist");
        exit(1);
    }
    kkread(kd, Nl[1].n_value, &nswdev, sizeof(nswdev));
    kkread(kd, Nl[2].n_value, &dmmax, sizeof(dmmax));

    if (VerboseOpt) {
        swapfds = calloc(sizeof(int), nswdev);
        for (i = 0; i < nswdev && i < ac - optind; ++i) {
                printf("open %s\n", av[optind + i]);
                swapfds[i] = open(av[optind + i], O_RDONLY);
        }
        while (i < nswdev) {
                swapfds[i] = -1;
                ++i;
        }
        memfds = open("/dev/mem", O_RDONLY);
    }

    kkread(kd, Nl[0].n_value, obj_list, sizeof(obj_list));
    for (i = 0; i < VMOBJ_HSIZE; ++i)
            scan_vmobjs(kd, &obj_list[i]);
    return(0);
}

static void
scan_vmobjs(kvm_t *kd, struct object_q *obj_list)
{
    struct vm_object *op;
    struct vm_object obj;

    op = TAILQ_FIRST(obj_list);
    while (op) {
        kkread(kd, (long)op, &obj, sizeof(obj));

        printf("%p type=%d size=%016jx handle=%p swblocks=%d\n",
                op, obj.type, (intmax_t)obj.size, obj.handle,
                obj.swblock_count);
        printf("\t\t   ref_count=%d backing_obj=%p\n",
                obj.ref_count, obj.backing_object);

        if (VerboseOpt) {
                dump_swap(kd, obj.swblock_root.rbh_root);
                if (obj.type == OBJT_DEFAULT || obj.type == OBJT_SWAP)
                        dump_memq(kd, obj.rb_memq.rbh_root);
        }

        op = TAILQ_NEXT(&obj, object_list);
    }
}

static void
dump_swap(kvm_t *kd, struct swblock *swbp)
{
        struct swblock swb;
        int which;
        int i;
        int j;
        int k;
        int fd;
        off_t off;

        if (swbp == NULL)
                return;
        kkread(kd, (long)swbp, &swb, sizeof(swb));
        dump_swap(kd, swb.swb_entry.rbe_left);

        for (i = 0; i < SWAP_META_PAGES; ++i) {
                printf("    %016lx: ", (swb.swb_index + i) * 4096L);
                if (swb.swb_pages[i] == SWAPBLK_NONE) {
                        printf(" (unassigned)\n");
                        continue;
                }
                printf(" %ld\n", swb.swb_pages[i]);
                off = devoffset(swb.swb_pages[i], &which);
                if (swapfds[which] >= 0) {
                        lseek(swapfds[which], off, 0);
                        if (read(swapfds[which], pgbuf, sizeof(pgbuf)) <= 0)
                                printf("\t(read failed)\n");
                        else
                        for (j = 0; j < PAGE_SIZE; j += 16) {
                                printf("\t%04x ", j);
                                for (k = 0; k < 16; ++k) {
                                        printf(" %02x", (uint8_t)pgbuf[j+k]);
                                        if (k == 7)
                                                printf(" ");
                                }
                                printf("  ");
                                for (k = 0; k < 16; ++k) {
                                        if (isprint((uint8_t)pgbuf[j+k]))
                                                printf("%c", pgbuf[j+k]);
                                        else
                                                printf(".");
                                }
                                printf("\n");
                        }
                }
        }

        dump_swap(kd, swb.swb_entry.rbe_right);
}

static void
dump_memq(kvm_t *kd, struct vm_page *pgp)
{
        struct vm_page pg;
        int j;
        int k;

        if (pgp == NULL)
                return;
        kkread(kd, (long)pgp, &pg, sizeof(pg));
        dump_memq(kd, pg.rb_entry.rbe_left);
        printf("    %016lx: %016jx (physical)\n",
               pg.pindex * 4096L, (intmax_t)pg.phys_addr);
        lseek(memfds, pg.phys_addr, 0);
        if (read(memfds, pgbuf, sizeof(pgbuf)) <= 0) {
                printf("\t(read failed)\n");
        } else {
                for (j = 0; j < PAGE_SIZE; j += 16) {
                        printf("\t%04x ", j);
                        for (k = 0; k < 16; ++k) {
                                printf(" %02x", (uint8_t)pgbuf[j+k]);
                                if (k == 7)
                                        printf(" ");
                        }
                        printf("  ");
                        for (k = 0; k < 16; ++k) {
                                if (isprint((uint8_t)pgbuf[j+k]))
                                        printf("%c", pgbuf[j+k]);
                                else
                                        printf(".");
                        }
                        printf("\n");
                }
        }

        dump_memq(kd, pg.rb_entry.rbe_right);
}

static void
kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
{
    if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
        perror("kvm_read");
        exit(1);
    }
}

static off_t
devoffset(long blkno, int *whichp)
{
        off_t off;
        long seg;

        if (nswdev > 1) {
                off = blkno % dmmax;
                seg = blkno / dmmax;
                *whichp = seg % nswdev;
                seg /= nswdev;
                off = (off_t)(seg * dmmax + off) << PAGE_SHIFT;
        } else {
                *whichp = 0;
                off = blkno * PAGE_SIZE;
        }
}