/*	$OpenBSD$	*/

/*-
 * Copyright (c) 1996 Niklas Hallqvist.  All rights reserved.
 *
 * 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. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Niklas Hallqvist.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <fcntl.h>
#include <kvm.h>
#include <limits.h>
#include <paths.h>
#include <stdio.h>
#include <unistd.h>

extern char *optarg;

void usage(invocation)
{
	fprintf(stderr, "usage: %s [-p pid] \n", invocation);
}

static int indent = 0;

void
do_indent()
{
	int i;

	for (i = 0; i < indent; i++)
		putchar(' ');
}

void
vm_object_print(kd, kvm_object, full)
	kvm_t *kd;
	struct vm_object *kvm_object;
	int full;
{
	char *delim;
	struct vm_object obj, *object = &obj, *kvm_o, obj2, *o = &obj2;
	struct vm_page page, *p = &page, *kvm_p;
	int count;

	if (kvm_object == NULL)
		return;

	kvm_read(kd, (u_long)kvm_object, &obj, sizeof(struct vm_object));
	do_indent();
	printf("Object %p: size=0x%lx, res=%d, ref=%d, ",
	    kvm_object, (long)object->size, object->resident_page_count,
	    object->ref_count);
	printf("pager=%p+0x%lx, shadow=(%p)+0x%lx\n", object->pager,
	    (long)object->paging_offset, object->shadow,
	    (long)object->shadow_offset);
	indent += 2;
	do_indent();
	printf("shadowers=(");
	delim = "";
	for (kvm_o = object->shadowers.lh_first; kvm_o;
	    kvm_o = o->shadowers_list.le_next) {
		printf("%s%p", delim, kvm_o);
		delim = ", ";
		kvm_read(kd, (u_long)kvm_o, &obj2, sizeof(struct vm_object));        	
	};
	printf(")\n");
	do_indent();
	printf("cache: next=%p, prev=%p\n", object->cached_list.tqe_next,
	    object->cached_list.tqe_prev);
	indent -= 2;
	if (full) {
		indent += 2;
		count = 0;
		for (kvm_p = object->memq.tqh_first; kvm_p != NULL;
		    kvm_p = p->listq.tqe_next) {
			if (count == 0) {
				do_indent();
				printf("memory:=");
			} else if (count == 6) {
				putchar('\n');
				do_indent();
				printf(" ...");
				count = 0;
			} else
				putchar(',');
			count++;
			kvm_read(kd, (u_long)kvm_p, &page,
			    sizeof(struct vm_page));
			printf("(off=0x%x)", p->offset);
		}
		if (count != 0)
			putchar('\n');
		indent -= 2;
	}
	vm_object_print(kd, object->shadow, full);

}

void
vm_map_print(kd, map, full)
	kvm_t *kd;
	struct vm_map *map;
	int full;
{
	struct vm_map share_map;
	struct vm_map_entry ent, prev_ent;
	struct vm_map_entry *entry = &ent, *prev_entry = &prev_ent, *kvm_entry;
	int cnt;
     	static char *inheritance_name[4] =
	    { "share", "copy", "none", "donate_copy"};

	do_indent();
	printf("%s map %p: pmap=%p, ref=%d, nentries=%d, version=%d\n",
	    map->is_main_map ? "task" : "share", map, map->pmap,
	    map->ref_count, map->nentries, map->timestamp);

	if (!full && indent)
		return;

	kvm_entry = map->header.next;
	kvm_read(kd, (u_long)map->header.next, &ent,
	    sizeof(struct vm_map_entry));
	indent += 2;
	cnt = map->nentries;
	while (cnt--) {
		do_indent();
		printf("map entry %p: start=%p, end=%p, ", kvm_entry,
		    entry->start, entry->end);
		if (map->is_main_map) {
			printf("prot=%x/%x/%s, ", entry->protection,
                            entry->max_protection,
                            inheritance_name[entry->inheritance]);
			if (entry->wired_count != 0)
				printf("wired, ");
		}

		if (entry->is_a_map || entry->is_sub_map) {
		 	printf("share=%p, offset=%p\n",
			    entry->object.share_map, entry->offset);
			if (cnt == map->nentries - 1 ||
			    !prev_entry->is_a_map ||
			    prev_entry->object.share_map !=
			    entry->object.share_map) {
				indent += 2;
				kvm_read(kd, (u_long)entry->object.share_map,
				    &share_map, sizeof(struct vm_map));
				vm_map_print(kd, &share_map, full);
				indent -= 2;
			}
		} else {
			printf("object=%p, offset=%p", entry->object.vm_object,
			    entry->offset);
			if (entry->copy_on_write)
				printf(", copy (%s)",
                                    entry->needs_copy ? "needed" : "done");
			putchar('\n');

			if (cnt == map->nentries - 1 || prev_entry->is_a_map ||
			    prev_entry->object.vm_object !=
			    entry->object.vm_object) {
				indent += 2;
				vm_object_print(kd, entry->object.vm_object,
				    full);
				indent -= 2;
			}
		}
		kvm_entry = ent.next;
		prev_ent = ent;
		kvm_read(kd, (u_long)kvm_entry, &ent,
		    sizeof(struct vm_map_entry));
	}
	indent -= 2;
}

void
do_process(p, kd, full)
	struct kinfo_proc *p;
	kvm_t *kd;
	int full;
{
	struct vmspace vmspace;
	struct vm_map *map;

	printf("pid %d vmspace %p\n", p->kp_proc.p_pid, p->kp_proc.p_vmspace);

	if (kvm_read (kd, (u_long)p->kp_proc.p_vmspace, &vmspace,
	    sizeof(struct vmspace)) != sizeof(struct vmspace)) {
		warnx("kvm_read");
		return;
	}
	map = &vmspace.vm_map;
	vm_map_print(kd, &vmspace.vm_map, full);
}

int
main(argc, argv)
	int argc;
	char **argv;
{
	int op = KERN_PROC_ALL;
	int arg, cnt, c, full = 0;
	kvm_t *kd;
	struct kinfo_proc *p;
	char *core = NULL;
	char *kernel = NULL;
	char err[_POSIX2_LINE_MAX];

	while ((c = getopt(argc, argv, "fp:MN")) != - 1) {
		switch(c) {
			case 'f':
				full++;
				break;
			case 'p':
				op = KERN_PROC_PID;
				arg = atoi (optarg);
				break;

			case 'M':
				core = optarg;
				break;

			case 'N':
				kernel = optarg;
				break;

			default:
				usage(argv[0]);
				return 1;
		}
	}

	if ((kd = kvm_openfiles(kernel, core, NULL, O_RDONLY, err)) == 0)
		errx(1, "%s", err);

	if ((p = kvm_getprocs(kd, op, arg, &cnt)) == 0) {
		return 1;
	}

	while (cnt--)
		do_process(p++, kd, full);

	if (kvm_close(kd)) {
		return 1;
	}
	return 0;
}
