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 */