Subject: Re: -current memory leaks?
To: None <current-users@NetBSD.ORG>
From: Andrew Herbert <andrew@mira.net.au>
List: current-users
Date: 04/13/1995 19:15:18
Someone wrote:

>> Our -current system (about a week or two out of date, I guess, but
>> similar.) is exhibiting memory leakage; it runs out of memory and
>> panics after about 4 days.  We have 3-4 users average, 8-9 peak.
>> We have 8 megs of "real" memory and about 35 of swap.  As time goes
>> on, the swap space used goes from about 20% (fresh boot) to 50% or
>> so (a day or two) to 90% (3-4 days) and then crashes at 100%.  This
>> repeats.  We are using slip, but not PPP.

That certainly sounds like the vm_object leak mentioned in Luke's reply.
It's fixed in FreeBSD but I believe it is still broken in NetBSD.

Luke wrote:

>Andrew also mentioned: "And if anyone wants a copy of the abovementioned
>vm_object analyser, just drop me some mail."

That was actually a bit of a "whoops", as I hadn't changed the analyser
since NetBSD's VM structures were altered ages ago.  I did a bit of hacking
on it so it would at least compile again, but have not had time to get it
working properly.  It's reasonably small, so I have included it below.  If
anyone gets it running again, please drop me some mail!

Andrew
--
/* vmobj.c - analyse the vm objects in use by a process */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/user.h>
#include <sys/resource.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#define KERNEL
#include <ufs/ufs/quota.h>
#include <ufs/ufs/inode.h>
#undef KERNEL
#include <vm/swap_pager.h>
#include <vm/vnode_pager.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <nlist.h>
#include <fcntl.h>
#include <kvm.h>

kvm_t *kd;

#define kr(addr, buf) \
    if (kvm_read(kd, (u_long)addr, buf, sizeof(*buf)) != sizeof(*buf)) { \
	printf("kvm_read(%0x, %0x, %d) @ line %d failed\n", addr, buf, \
		sizeof(*buf), __LINE__); \
	exit(3); \
    }

void usage(void)
{
    puts("usage: vmobj <pid>");
    exit(1);
} /* end of usage */

void print_object(vm_object_t obj, int indent)
{
    char ind[128];

    memset(ind, ' ', sizeof ind);
    if (indent < (sizeof ind))
	ind[indent] = 0;	/* null terminate the indent string */

    if (indent > 1)
	printf("%s=== %d ===\n", ind, indent);
    printf("%sref count: %d\n", ind, obj->ref_count);
    printf("%ssize: %d\n", ind, obj->size);
    printf("%sresident pages: %d\n", ind, obj->resident_page_count);
    printf("%spager: ", ind);
    if (obj->pager) {
	struct pager_struct pager;

	kr(obj->pager, &pager);
	switch (pager.pg_type) {
	    case PG_DFLT:
		printf("default - handle=%d\n", pager.pg_handle);
		break;
	    case PG_SWAP: {
		struct swpager sp;

		kr(pager.pg_data, &sp);
		printf("swap %x - blocks=%d, block-size=%d (total %dK)\n",
		    obj->pager, sp.sw_nblocks, sp.sw_bsize,
		    sp.sw_nblocks*sp.sw_bsize*DEV_BSIZE/1024);
		if (sp.sw_osize != obj->size)
		    printf("%s             swap object size (%d) != vm_object size (%d)\n",
			ind, sp.sw_osize, obj->size);
		if (pager.pg_handle)
		    printf("%s             swapper not installed by pageout daemon - handle %d\n",
			ind, (unsigned)pager.pg_handle);
		break;
	    }
	    case PG_VNODE: {
		struct vnpager	vp;
		struct vnode	vn;

		kr(pager.pg_data, &vp);
		printf("vnode - ");
		if (vp.vnp_flags) {
		    int p=0;

		    putchar('(');
		    if (vp.vnp_flags & VNP_PAGING)
			printf("paging"), p=1;
		    if (vp.vnp_flags & VNP_CACHED)
			printf("%scached", p ? ", " : "");
		    putchar(')');
		}
		kr(vp.vnp_vp, &vn);
		if ((vn.v_tag == VT_UFS) || (vn.v_tag == VT_MFS)) {
		    printf("%s ", vn.v_tag == VT_UFS ? "ufs" : "mfs");
		    if (vn.v_type == VREG) {
			struct inode *ip = VTOI(&vn);

			printf("inode %u", ip->i_number);
		    }
		}
		else if (vn.v_tag == VT_NFS)
		    printf("nfs");
		putchar('\n');
		break;
	    }
	    case PG_DEVICE:
		printf("%s - handle=%d\n", "device", (int)pager.pg_handle);
		break;
	    default:
		printf("%s - handle=%d\n", "unknown", (int)pager.pg_handle);
		break;
	}
    }
    else
	puts("none");
    printf("%scopy object: %x\n", ind, obj->copy);
    printf("%sshadow object: %x\n", ind, obj->shadow);
    printf("%spaging_in_progress: %d\n", ind, obj->paging_in_progress);
    printf("%sflags: %d\n", ind, obj->flags);

    if (obj->shadow) {
	kr(obj->shadow, obj);	/* destroys existing obj,
				   but that doesn't matter */
	print_object(obj, indent+1);
    }
} /* end of print_object */

void do_vmspace(struct vmspace *vs)
{
    vm_map_t map = &(vs->vm_map);
    vm_map_entry_t entry = &(map->header);
    int i;

    /* print header summary information */
    printf("vm address range: %08x->%08x\n", entry->start, entry->end);

    for (i = 1; i <= map->nentries; i++) {
	int type;
	char *typedesc;

	/* advance to the next entry (initially @ header) */
	kr(entry->next, entry);	/* destroys vm_map, but doesn't matter */

	if (entry->is_a_map) type = 1, typedesc = "share map";
	else if (entry->is_sub_map) type = 2, typedesc = "submap";
	else type = 0, typedesc = "object";
	printf("entry %2d: %08x->%08x [%08x] %s\n", i, entry->start, entry->end,
	    entry->object, typedesc);
	switch (type) {
	    case 0: { /* object */
		    struct vm_object obj;

		    kr(entry->object.vm_object, &obj);
		    print_object(&obj, 1);
		}
		break;
	    case 1:
	    case 2:
		break;
	}
    }
}

void main(int argc, char *argv[])
{
    pid_t	pid;
    int		n;
    struct kinfo_proc	*proc;
    struct  vmspace vmspace;

    if (argc != 2)
	usage();

    pid = atoi(argv[1]);

    kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, NULL);
    if (kd == NULL) {
	printf("kvm_openfiles: failed\n");
	exit(2);
    }

    /* grab the proc structure */
    proc = kvm_getprocs(kd, KERN_PROC_PID, pid, &n);

    if (!n) {
	printf("%s: process %d doesn't exist\n", argv[0], pid);
	exit(2);
    }
    if (n > 1) {
	printf("%s: kvm_getprocs() returned %d!\n", argv[0], n);
	exit(2);
    }

    if (!proc) {
	printf("%s: null proc\n", argv[0]);
	exit(3);
    }

    do_vmspace(&proc->kp_eproc.e_vm);
} /* end of main */