4 * cc -I/usr/src/sys vmpageinfo.c -o ~/bin/vmpageinfo -lkvm
8 * Validate the vm_page_buckets[] hash array against the vm_page_array
11 * Copyright (c) 2004 The DragonFly Project. All rights reserved.
13 * This code is derived from software contributed to The DragonFly Project
14 * by Matthew Dillon <dillon@backplane.com>
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in
24 * the documentation and/or other materials provided with the
26 * 3. Neither the name of The DragonFly Project nor the names of its
27 * contributors may be used to endorse or promote products derived
28 * from this software without specific, prior written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
33 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
34 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
35 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
36 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
38 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
39 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
40 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 * $DragonFly: src/test/debug/vmpageinfo.c,v 1.2 2006/05/23 01:00:05 dillon Exp $
46 #define _KERNEL_STRUCTURES_
47 #include <sys/param.h>
49 #include <sys/malloc.h>
50 #include <sys/signalvar.h>
51 #include <sys/vnode.h>
52 #include <sys/namecache.h>
53 #include <sys/slaballoc.h>
56 #include <vm/vm_page.h>
57 #include <vm/vm_kern.h>
58 #include <vm/vm_page.h>
59 #include <vm/vm_object.h>
60 #include <vm/swap_pager.h>
61 #include <vm/vnode_pager.h>
73 { "_vm_page_array_size" },
87 struct vm_page **vm_page_buckets;
88 int vm_page_hash_mask;
90 struct vm_page *vm_page_array;
91 struct vm_object *kernel_object_ptr;
92 int vm_page_array_size;
99 void checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj);
100 static void kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m);
101 static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
102 static int kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
105 static void addsltrack(vm_page_t m);
106 static void dumpsltrack(kvm_t *kd);
108 static int unique_object(void *ptr);
111 long count_wired; /* total */
112 long count_wired_vnode;
113 long count_wired_anon;
114 long count_wired_in_pmap;
115 long count_wired_pgtable;
116 long count_wired_other;
117 long count_wired_kernel;
118 long count_wired_obj_other;
121 long count_anon_in_pmap;
127 long count_noobj_offqueue;
128 long count_noobj_onqueue;
131 main(int ac, char **av)
133 const char *corefile = NULL;
134 const char *sysfile = NULL;
136 struct vm_object obj;
147 while ((ch = getopt(ac, av, "M:N:dv")) != -1) {
162 fprintf(stderr, "%s [-M core] [-N system]\n", av[0]);
169 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) {
173 if (kvm_nlist(kd, Nl) != 0) {
178 kkread(kd, Nl[0].n_value, &vm_page_array, sizeof(vm_page_array));
179 kkread(kd, Nl[1].n_value, &vm_page_array_size, sizeof(vm_page_array_size));
180 kernel_object_ptr = (void *)Nl[2].n_value;
181 kkread(kd, Nl[3].n_value, &nbuf, sizeof(nbuf));
182 kkread(kd, Nl[4].n_value, &nswbuf_mem, sizeof(nswbuf_mem));
183 kkread(kd, Nl[5].n_value, &nswbuf_kva, sizeof(nswbuf_kva));
184 kkread(kd, Nl[6].n_value, &nswbuf_raw, sizeof(nswbuf_raw));
185 kern_size = Nl[8].n_value - Nl[7].n_value;
188 * Scan the vm_page_array validating all pages with associated objects
190 for (i = 0; i < vm_page_array_size; ++i) {
192 printf("page %d\r", i);
195 kkread_vmpage(kd, (u_long)&vm_page_array[i], &m);
197 kkread(kd, (u_long)m.object, &obj, sizeof(obj));
198 checkpage(kd, &vm_page_array[i], &m, &obj);
200 if (m.queue >= PQ_HOLD) {
202 } else if (m.queue >= PQ_CACHE) {
204 } else if (m.queue >= PQ_ACTIVE) {
206 } else if (m.queue >= PQ_INACTIVE) {
208 } else if (m.queue >= PQ_FREE) {
216 if (m.object == NULL) {
217 if ((m.flags & PG_MAPPED) &&
218 (m.flags & PG_WRITEABLE) &&
219 (m.flags & PG_UNQUEUED)) {
220 ++count_wired_pgtable;
224 } else if (m.object == kernel_object_ptr) {
225 ++count_wired_kernel;
234 ++count_wired_in_pmap;
239 ++count_wired_obj_other;
243 } else if (m.md.pmap_count) {
244 if (m.object && m.object != kernel_object_ptr) {
248 ++count_anon_in_pmap;
257 printf("page %p obj %p/%-8ju(%016jx) val=%02x dty=%02x hold=%d "
258 "wire=%-2d act=%-3d busy=%d w/pmapcnt=%d/%d %8s",
262 (intmax_t)m.pindex * PAGE_SIZE,
269 m.md.writeable_count,
275 if (m.object == kernel_object_ptr) {
277 if (unique_object(m.object))
278 count_kernel += obj.resident_page_count;
279 } else if (m.object) {
283 if (unique_object(m.object))
284 count_anon += obj.resident_page_count;
288 if (unique_object(m.object))
289 count_anon += obj.resident_page_count;
293 if (unique_object(m.object))
294 count_vnode += obj.resident_page_count;
298 if (unique_object(m.object))
299 count_device += obj.resident_page_count;
303 if (unique_object(m.object))
304 count_phys += obj.resident_page_count;
308 if (unique_object(m.object))
309 count_unknown += obj.resident_page_count;
312 if (unique_object(m.object))
313 count_unknown += obj.resident_page_count;
319 if (m.queue == PQ_NONE)
320 ++count_noobj_offqueue;
321 else if (m.queue - m.pc != PQ_FREE)
322 ++count_noobj_onqueue;
326 printf(" %-7s", ostr);
327 if (m.busy_count & PBUSY_LOCKED)
329 if (m.busy_count & PBUSY_WANTED)
331 if (m.flags & PG_WINATCFLS)
332 printf(" WINATCFLS");
333 if (m.flags & PG_FICTITIOUS)
334 printf(" FICTITIOUS");
335 if (m.flags & PG_WRITEABLE)
336 printf(" WRITEABLE");
337 if (m.flags & PG_MAPPED)
339 if (m.flags & PG_NEED_COMMIT)
340 printf(" NEED_COMMIT");
341 if (m.flags & PG_REFERENCED)
342 printf(" REFERENCED");
343 if (m.flags & PG_CLEANCHK)
345 if (m.busy_count & PBUSY_SWAPINPROG)
346 printf(" SWAPINPROG");
347 if (m.flags & PG_NOSYNC)
349 if (m.flags & PG_UNQUEUED)
351 if (m.flags & PG_MARKER)
353 if (m.flags & PG_RAM)
355 if (m.flags & PG_SWAPPED)
358 if (m.flags & PG_SLAB)
363 if (m.flags & PG_SLAB)
368 if (debugopt || verboseopt)
370 printf("%8.2fM free\n", count_free * 4096.0 / 1048576.0);
372 printf("%8.2fM wired vnode (in buffer cache)\n",
373 count_wired_vnode * 4096.0 / 1048576.0);
374 printf("%8.2fM wired in-pmap (probably vnode pages also in buffer cache)\n",
375 count_wired_in_pmap * 4096.0 / 1048576.0);
376 printf("%8.2fM wired pgtable\n",
377 count_wired_pgtable * 4096.0 / 1048576.0);
378 printf("%8.2fM wired anon\n",
379 count_wired_anon * 4096.0 / 1048576.0);
380 printf("%8.2fM wired kernel_object\n",
381 count_wired_kernel * 4096.0 / 1048576.0);
383 printf("\t%8.2fM vm_page_array\n",
384 vm_page_array_size * sizeof(struct vm_page) / 1048576.0);
385 printf("\t%8.2fM buf, swbuf_mem, swbuf_kva, swbuf_raw\n",
386 (nbuf + nswbuf_mem + nswbuf_kva + nswbuf_raw) *
387 sizeof(struct buf) / 1048576.0);
388 printf("\t%8.2fM kernel binary\n", kern_size / 1048576.0);
389 printf("\t(also add in KMALLOC id kmapinfo, or loosely, vmstat -m)\n");
391 printf("%8.2fM wired other (unknown object)\n",
392 count_wired_obj_other * 4096.0 / 1048576.0);
393 printf("%8.2fM wired other (no object, probably kernel)\n",
394 count_wired_other * 4096.0 / 1048576.0);
396 printf("%8.2fM WIRED TOTAL\n",
397 count_wired * 4096.0 / 1048576.0);
400 printf("%8.2fM anonymous (total, includes in-pmap)\n",
401 count_anon * 4096.0 / 1048576.0);
402 printf("%8.2fM anonymous memory in-pmap\n",
403 count_anon_in_pmap * 4096.0 / 1048576.0);
404 printf("%8.2fM vnode (includes wired)\n",
405 count_vnode * 4096.0 / 1048576.0);
406 printf("%8.2fM device\n", count_device * 4096.0 / 1048576.0);
407 printf("%8.2fM phys\n", count_phys * 4096.0 / 1048576.0);
408 printf("%8.2fM kernel (includes wired)\n",
409 count_kernel * 4096.0 / 1048576.0);
410 printf("%8.2fM unknown\n", count_unknown * 4096.0 / 1048576.0);
411 printf("%8.2fM no_object, off queue (includes wired w/o object)\n",
412 count_noobj_offqueue * 4096.0 / 1048576.0);
413 printf("%8.2fM no_object, on non-free queue (includes wired w/o object)\n",
414 count_noobj_onqueue * 4096.0 / 1048576.0);
418 * Scan the vm_page_buckets array validating all pages found
420 for (i = 0; i <= vm_page_hash_mask; ++i) {
422 printf("index %d\r", i);
425 kkread(kd, (u_long)&vm_page_buckets[i], &mptr, sizeof(mptr));
427 kkread(kd, (u_long)mptr, &m, sizeof(m));
429 kkread(kd, (u_long)m.object, &obj, sizeof(obj));
430 hv = ((uintptr_t)m.object + m.pindex) ^ obj.hash_rand;
431 hv &= vm_page_hash_mask;
433 printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
434 " should be in bucket %d\n", i, mptr, hv);
435 checkpage(kd, mptr, &m, &obj);
437 printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
438 " has no object\n", i, mptr);
453 * A page with an object.
456 checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj)
463 hv = ((uintptr_t)m->object + m->pindex) ^ obj->hash_rand;
464 hv &= vm_page_hash_mask;
465 kkread(kd, (u_long)&vm_page_buckets[hv], &scanptr, sizeof(scanptr));
469 kkread(kd, (u_long)scanptr, &scan, sizeof(scan));
470 scanptr = scan.hnext;
474 printf("good checkpage %p bucket %d\n", mptr, hv);
476 printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
477 " page not found in bucket list\n", hv, mptr);
483 * Acclerate the reading of VM pages
486 kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m)
488 static struct vm_page vpcache[1024];
492 if (addr < vpbeg || addr >= vpend) {
494 vpend = addr + 1024 * sizeof(*m);
495 if (vpend > (u_long)(uintptr_t)vm_page_array +
496 vm_page_array_size * sizeof(*m)) {
497 vpend = (u_long)(uintptr_t)vm_page_array +
498 vm_page_array_size * sizeof(*m);
500 kkread(kd, vpbeg, vpcache, vpend - vpbeg);
502 *m = vpcache[(addr - vpbeg) / sizeof(*m)];
506 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
508 if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
515 kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
517 if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
524 struct SLTrack *next;
529 #define SLHMASK (SLHSIZE - 1)
531 struct SLTrack *SLHash[SLHSIZE];
536 addsltrack(vm_page_t m)
539 u_long addr = (m->pindex * PAGE_SIZE) & ~131071L;
542 if (m->wire_count == 0 || (m->flags & PG_MAPPED) == 0 ||
546 i = (addr / 131072) & SLHMASK;
547 for (slt = SLHash[i]; slt; slt = slt->next) {
548 if (slt->addr == addr)
552 slt = malloc(sizeof(*slt));
554 slt->next = SLHash[i];
562 dumpsltrack(kvm_t *kd)
566 long total_zones = 0;
569 for (i = 0; i < SLHSIZE; ++i) {
570 for (slt = SLHash[i]; slt; slt = slt->next) {
573 if (kkread_err(kd, slt->addr, &z, sizeof(z))) {
574 printf("SLZone 0x%016lx not mapped\n",
578 printf("SLZone 0x%016lx { mag=%08x cpu=%-2d NFree=%-3d "
591 printf("FullZones/TotalZones: %ld/%ld\n", full_zones, total_zones);
594 #define HASH_SIZE (1024*1024)
595 #define HASH_MASK (HASH_SIZE - 1)
598 struct dup_entry *next;
602 struct dup_entry *dup_hash[HASH_SIZE];
605 unique_object(void *ptr)
607 struct dup_entry *hen;
610 hv = (intptr_t)ptr ^ ((intptr_t)ptr >> 20);
612 for (hen = dup_hash[hv]; hen; hen = hen->next) {
616 hen = malloc(sizeof(*hen));
617 hen->next = dup_hash[hv];