xref: /dragonfly/test/debug/vmpageinfo.c (revision e8341a9a215228844cfca357354c8d66aff1336e)
1 /*
2  * VMPAGEINFO.C
3  *
4  * cc -I/usr/src/sys vmpageinfo.c -o ~/bin/vmpageinfo -lkvm
5  *
6  * vmpageinfo
7  *
8  * Validate the vm_page_buckets[] hash array against the vm_page_array
9  *
10  *
11  * Copyright (c) 2004 The DragonFly Project.  All rights reserved.
12  *
13  * This code is derived from software contributed to The DragonFly Project
14  * by Matthew Dillon <dillon@backplane.com>
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  *
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
25  *    distribution.
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.
29  *
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
41  * SUCH DAMAGE.
42  */
43 
44 #define _KERNEL_STRUCTURES_
45 #include <sys/param.h>
46 #include <sys/user.h>
47 #include <sys/buf.h>
48 #include <sys/malloc.h>
49 #include <sys/signalvar.h>
50 #include <sys/vnode.h>
51 #include <sys/namecache.h>
52 #include <sys/slaballoc.h>
53 
54 #include <vm/vm.h>
55 #include <vm/vm_page.h>
56 #include <vm/vm_kern.h>
57 #include <vm/vm_object.h>
58 #include <vm/swap_pager.h>
59 #include <vm/vnode_pager.h>
60 
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <fcntl.h>
65 #include <kvm.h>
66 #include <nlist.h>
67 #include <getopt.h>
68 
69 struct nlist Nl[] = {
70     { "_vm_page_array" },
71     { "_vm_page_array_size" },
72     { "_kernel_object" },
73     { "_nbuf" },
74     { "_nswbuf_mem" },
75     { "_nswbuf_kva" },
76     { "_nswbuf_raw" },
77     { "_kernbase" },
78     { "__end" },
79     { NULL }
80 };
81 
82 int debugopt;
83 int verboseopt;
84 #if 0
85 struct vm_page **vm_page_buckets;
86 int vm_page_hash_mask;
87 #endif
88 struct vm_page *vm_page_array;
89 struct vm_object *kernel_object_ptr;
90 int vm_page_array_size;
91 long nbuf;
92 long nswbuf_mem;
93 long nswbuf_kva;
94 long nswbuf_raw;
95 long kern_size;
96 
97 void checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj);
98 static void kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m);
99 static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
100 static int kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes);
101 
102 #if 0
103 static void addsltrack(vm_page_t m);
104 static void dumpsltrack(kvm_t *kd);
105 #endif
106 static int unique_object(void *ptr);
107 
108 long count_free;
109 long count_wired;             /* total */
110 long count_wired_vnode;
111 long count_wired_anon;
112 long count_wired_in_pmap;
113 long count_wired_pgtable;
114 long count_wired_other;
115 long count_wired_kernel;
116 long count_wired_obj_other;
117 
118 long count_anon;
119 long count_anon_in_pmap;
120 long count_vnode;
121 long count_device;
122 long count_phys;
123 long count_kernel;
124 long count_unknown;
125 long count_noobj_offqueue;
126 long count_noobj_onqueue;
127 
128 int
main(int ac,char ** av)129 main(int ac, char **av)
130 {
131     const char *corefile = NULL;
132     const char *sysfile = NULL;
133     struct vm_page m;
134     struct vm_object obj;
135     kvm_t *kd;
136     int ch;
137 #if 0
138     vm_page_t mptr;
139     int hv;
140 #endif
141     int i;
142     const char *qstr;
143     const char *ostr;
144 
145     while ((ch = getopt(ac, av, "M:N:dv")) != -1) {
146           switch(ch) {
147           case 'd':
148               ++debugopt;
149               break;
150           case 'v':
151               ++verboseopt;
152               break;
153           case 'M':
154               corefile = optarg;
155               break;
156           case 'N':
157               sysfile = optarg;
158               break;
159           default:
160               fprintf(stderr, "%s [-M core] [-N system]\n", av[0]);
161               exit(1);
162           }
163     }
164     ac -= optind;
165     av += optind;
166 
167     if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) {
168           perror("kvm_open");
169           exit(1);
170     }
171     if (kvm_nlist(kd, Nl) != 0) {
172           perror("kvm_nlist");
173           exit(1);
174     }
175 
176     kkread(kd, Nl[0].n_value, &vm_page_array, sizeof(vm_page_array));
177     kkread(kd, Nl[1].n_value, &vm_page_array_size, sizeof(vm_page_array_size));
178     kernel_object_ptr = (void *)Nl[2].n_value;
179     kkread(kd, Nl[3].n_value, &nbuf, sizeof(nbuf));
180     kkread(kd, Nl[4].n_value, &nswbuf_mem, sizeof(nswbuf_mem));
181     kkread(kd, Nl[5].n_value, &nswbuf_kva, sizeof(nswbuf_kva));
182     kkread(kd, Nl[6].n_value, &nswbuf_raw, sizeof(nswbuf_raw));
183     kern_size = Nl[8].n_value - Nl[7].n_value;
184 
185     /*
186      * Scan the vm_page_array validating all pages with associated objects
187      */
188     for (i = 0; i < vm_page_array_size; ++i) {
189           if (debugopt) {
190               printf("page %d\r", i);
191               fflush(stdout);
192           }
193           kkread_vmpage(kd, (u_long)&vm_page_array[i], &m);
194           if (m.object) {
195               kkread(kd, (u_long)m.object, &obj, sizeof(obj));
196               checkpage(kd, &vm_page_array[i], &m, &obj);
197           }
198           if (m.queue >= PQ_HOLD) {
199               qstr = "HOLD";
200           } else if (m.queue >= PQ_CACHE) {
201               qstr = "CACHE";
202           } else if (m.queue >= PQ_ACTIVE) {
203               qstr = "ACTIVE";
204           } else if (m.queue >= PQ_INACTIVE) {
205               qstr = "INACTIVE";
206           } else if (m.queue >= PQ_FREE) {
207               qstr = "FREE";
208               ++count_free;
209           } else {
210               qstr = "NONE";
211           }
212           if (m.wire_count) {
213                     ++count_wired;
214                     if (m.object == NULL) {
215                               if ((m.flags & PG_MAPPED) &&
216                                   (m.flags & PG_WRITEABLE) &&
217                                   (m.flags & PG_UNQUEUED)) {
218                                         ++count_wired_pgtable;
219                               } else {
220                                         ++count_wired_other;
221                               }
222                     } else if (m.object == kernel_object_ptr) {
223                               ++count_wired_kernel;
224                     } else {
225                               switch(obj.type) {
226                               case OBJT_VNODE:
227                                         ++count_wired_vnode;
228                                         break;
229                               case OBJT_DEFAULT:
230                               case OBJT_SWAP:
231                                         /*
232                                         if (m.md.pmap_count)
233                                                   ++count_wired_in_pmap;
234                                         else
235                                         */
236                                                   ++count_wired_anon;
237                                         break;
238                               default:
239                                         ++count_wired_obj_other;
240                                         break;
241                               }
242                     }
243           } else
244 #if 0
245           if (m.md.pmap_count) {
246                     if (m.object && m.object != kernel_object_ptr) {
247                               switch(obj.type) {
248                               case OBJT_DEFAULT:
249                               case OBJT_SWAP:
250                                         ++count_anon_in_pmap;
251                                         break;
252                               default:
253                                         break;
254                               }
255                     }
256           }
257 #endif
258 
259           if (verboseopt) {
260               printf("page %p obj %p/%-8ju(%016jx) val=%02x dty=%02x hold=%d "
261                        "wire=%-2d act=%-3d busy=%d w/pmapcnt=%d/%d %8s",
262                     &vm_page_array[i],
263                     m.object,
264                     (intmax_t)m.pindex,
265                     (intmax_t)m.pindex * PAGE_SIZE,
266                     m.valid,
267                     m.dirty,
268                     m.hold_count,
269                     m.wire_count,
270                     m.act_count,
271                     m.busy_count,
272                     0 /* m.md.writeable_count */,
273                     0 /*m.md.pmap_count*/,
274                     qstr
275               );
276           }
277 
278           if (m.object == kernel_object_ptr) {
279                     ostr = "kernel";
280                     if (unique_object(m.object))
281                               count_kernel += obj.resident_page_count;
282           } else if (m.object) {
283               switch(obj.type) {
284               case OBJT_DEFAULT:
285                     ostr = "default";
286                     if (unique_object(m.object))
287                               count_anon += obj.resident_page_count;
288                     break;
289               case OBJT_SWAP:
290                     ostr = "swap";
291                     if (unique_object(m.object))
292                               count_anon += obj.resident_page_count;
293                     break;
294               case OBJT_VNODE:
295                     ostr = "vnode";
296                     if (unique_object(m.object))
297                               count_vnode += obj.resident_page_count;
298                     break;
299               case OBJT_DEVICE:
300                     ostr = "device";
301                     if (unique_object(m.object))
302                               count_device += obj.resident_page_count;
303                     break;
304               case OBJT_PHYS:
305                     ostr = "phys";
306                     if (unique_object(m.object))
307                               count_phys += obj.resident_page_count;
308                     break;
309               case OBJT_DEAD:
310                     ostr = "dead";
311                     if (unique_object(m.object))
312                               count_unknown += obj.resident_page_count;
313                     break;
314               default:
315                     if (unique_object(m.object))
316                               count_unknown += obj.resident_page_count;
317                     ostr = "unknown";
318                     break;
319               }
320           } else {
321               ostr = "-";
322               if (m.queue == PQ_NONE)
323                         ++count_noobj_offqueue;
324               else if (m.queue - m.pc != PQ_FREE)
325                         ++count_noobj_onqueue;
326           }
327 
328           if (verboseopt) {
329               printf(" %-7s", ostr);
330               if (m.busy_count & PBUSY_LOCKED)
331                     printf(" BUSY");
332               if (m.busy_count & PBUSY_WANTED)
333                     printf(" WANTED");
334               if (m.flags & PG_WINATCFLS)
335                     printf(" WINATCFLS");
336               if (m.flags & PG_FICTITIOUS)
337                     printf(" FICTITIOUS");
338               if (m.flags & PG_WRITEABLE)
339                     printf(" WRITEABLE");
340               if (m.flags & PG_MAPPED)
341                     printf(" MAPPED");
342               if (m.flags & PG_NEED_COMMIT)
343                     printf(" NEED_COMMIT");
344               if (m.flags & PG_REFERENCED)
345                     printf(" REFERENCED");
346               if (m.flags & PG_CLEANCHK)
347                     printf(" CLEANCHK");
348               if (m.busy_count & PBUSY_SWAPINPROG)
349                     printf(" SWAPINPROG");
350               if (m.flags & PG_NOSYNC)
351                     printf(" NOSYNC");
352               if (m.flags & PG_UNQUEUED)
353                     printf(" UNQUEUED");
354               if (m.flags & PG_MARKER)
355                     printf(" MARKER");
356               if (m.flags & PG_RAM)
357                     printf(" RAM");
358               if (m.flags & PG_SWAPPED)
359                     printf(" SWAPPED");
360 #if 0
361               if (m.flags & PG_SLAB)
362                     printf(" SLAB");
363 #endif
364               printf("\n");
365 #if 0
366               if (m.flags & PG_SLAB)
367                     addsltrack(&m);
368 #endif
369           }
370     }
371     if (debugopt || verboseopt)
372           printf("\n");
373     printf("%8.2fM free\n", count_free * 4096.0 / 1048576.0);
374 
375     printf("%8.2fM wired vnode (in buffer cache)\n",
376           count_wired_vnode * 4096.0 / 1048576.0);
377     printf("%8.2fM wired in-pmap (probably vnode pages also in buffer cache)\n",
378           count_wired_in_pmap * 4096.0 / 1048576.0);
379     printf("%8.2fM wired pgtable\n",
380           count_wired_pgtable * 4096.0 / 1048576.0);
381     printf("%8.2fM wired anon\n",
382           count_wired_anon * 4096.0 / 1048576.0);
383     printf("%8.2fM wired kernel_object\n",
384           count_wired_kernel * 4096.0 / 1048576.0);
385 
386           printf("\t%8.2fM vm_page_array\n",
387               vm_page_array_size * sizeof(struct vm_page) / 1048576.0);
388           printf("\t%8.2fM buf, swbuf_mem, swbuf_kva, swbuf_raw\n",
389               (nbuf + nswbuf_mem + nswbuf_kva + nswbuf_raw) *
390               sizeof(struct buf) / 1048576.0);
391           printf("\t%8.2fM kernel binary\n", kern_size / 1048576.0);
392           printf("\t(also add in KMALLOC id kmapinfo, or loosely, vmstat -m)\n");
393 
394     printf("%8.2fM wired other (unknown object)\n",
395           count_wired_obj_other * 4096.0 / 1048576.0);
396     printf("%8.2fM wired other (no object, probably kernel)\n",
397           count_wired_other * 4096.0 / 1048576.0);
398 
399     printf("%8.2fM WIRED TOTAL\n",
400           count_wired * 4096.0 / 1048576.0);
401 
402     printf("\n");
403     printf("%8.2fM anonymous (total, includes in-pmap)\n",
404           count_anon * 4096.0 / 1048576.0);
405     printf("%8.2fM anonymous memory in-pmap\n",
406           count_anon_in_pmap * 4096.0 / 1048576.0);
407     printf("%8.2fM vnode (includes wired)\n",
408           count_vnode * 4096.0 / 1048576.0);
409     printf("%8.2fM device\n", count_device * 4096.0 / 1048576.0);
410     printf("%8.2fM phys\n", count_phys * 4096.0 / 1048576.0);
411     printf("%8.2fM kernel (includes wired)\n",
412           count_kernel * 4096.0 / 1048576.0);
413     printf("%8.2fM unknown\n", count_unknown * 4096.0 / 1048576.0);
414     printf("%8.2fM no_object, off queue (includes wired w/o object)\n",
415           count_noobj_offqueue * 4096.0 / 1048576.0);
416     printf("%8.2fM no_object, on non-free queue (includes wired w/o object)\n",
417           count_noobj_onqueue * 4096.0 / 1048576.0);
418 
419 #if 0
420     /*
421      * Scan the vm_page_buckets array validating all pages found
422      */
423     for (i = 0; i <= vm_page_hash_mask; ++i) {
424           if (debugopt) {
425               printf("index %d\r", i);
426               fflush(stdout);
427           }
428           kkread(kd, (u_long)&vm_page_buckets[i], &mptr, sizeof(mptr));
429           while (mptr) {
430               kkread(kd, (u_long)mptr, &m, sizeof(m));
431               if (m.object) {
432                     kkread(kd, (u_long)m.object, &obj, sizeof(obj));
433                     hv = ((uintptr_t)m.object + m.pindex) ^ obj.hash_rand;
434                     hv &= vm_page_hash_mask;
435                     if (i != hv)
436                         printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
437                               " should be in bucket %d\n", i, mptr, hv);
438                     checkpage(kd, mptr, &m, &obj);
439               } else {
440                     printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
441                               " has no object\n", i, mptr);
442               }
443               mptr = m.hnext;
444           }
445     }
446 #endif
447     if (debugopt)
448           printf("\n");
449 #if 0
450     dumpsltrack(kd);
451 #endif
452     return(0);
453 }
454 
455 /*
456  * A page with an object.
457  */
458 void
checkpage(kvm_t * kd,vm_page_t mptr,vm_page_t m,struct vm_object * obj)459 checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj)
460 {
461 #if 0
462     struct vm_page scan;
463     vm_page_t scanptr;
464     int hv;
465 
466     hv = ((uintptr_t)m->object + m->pindex) ^ obj->hash_rand;
467     hv &= vm_page_hash_mask;
468     kkread(kd, (u_long)&vm_page_buckets[hv], &scanptr, sizeof(scanptr));
469     while (scanptr) {
470           if (scanptr == mptr)
471               break;
472           kkread(kd, (u_long)scanptr, &scan, sizeof(scan));
473           scanptr = scan.hnext;
474     }
475     if (scanptr) {
476           if (debugopt > 1)
477               printf("good checkpage %p bucket %d\n", mptr, hv);
478     } else {
479           printf("vm_page_buckets[%d] ((struct vm_page *)%p)"
480                     " page not found in bucket list\n", hv, mptr);
481     }
482 #endif
483 }
484 
485 /*
486  * Acclerate the reading of VM pages
487  */
488 static void
kkread_vmpage(kvm_t * kd,u_long addr,vm_page_t m)489 kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m)
490 {
491     static struct vm_page vpcache[1024];
492     static u_long vpbeg;
493     static u_long vpend;
494 
495     if (addr < vpbeg || addr >= vpend) {
496           vpbeg = addr;
497           vpend = addr + 1024 * sizeof(*m);
498           if (vpend > (u_long)(uintptr_t)vm_page_array +
499                         vm_page_array_size * sizeof(*m)) {
500               vpend = (u_long)(uintptr_t)vm_page_array +
501                         vm_page_array_size * sizeof(*m);
502           }
503           kkread(kd, vpbeg, vpcache, vpend - vpbeg);
504     }
505     *m = vpcache[(addr - vpbeg) / sizeof(*m)];
506 }
507 
508 static void
kkread(kvm_t * kd,u_long addr,void * buf,size_t nbytes)509 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
510 {
511     if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
512         perror("kvm_read");
513         exit(1);
514     }
515 }
516 
517 static int
kkread_err(kvm_t * kd,u_long addr,void * buf,size_t nbytes)518 kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes)
519 {
520     if (kvm_read(kd, addr, buf, nbytes) != nbytes) {
521           return 1;
522     }
523     return 0;
524 }
525 
526 struct SLTrack {
527         struct SLTrack *next;
528         u_long addr;
529 };
530 
531 #define SLHSIZE 1024
532 #define SLHMASK (SLHSIZE - 1)
533 
534 struct SLTrack *SLHash[SLHSIZE];
535 
536 #if 0
537 static
538 void
539 addsltrack(vm_page_t m)
540 {
541           struct SLTrack *slt;
542           u_long addr = (m->pindex * PAGE_SIZE) & ~131071L;
543           int i;
544 
545           if (m->wire_count == 0 || (m->flags & PG_MAPPED) == 0 ||
546               m->object == NULL)
547                     return;
548 
549           i = (addr / 131072) & SLHMASK;
550           for (slt = SLHash[i]; slt; slt = slt->next) {
551                     if (slt->addr == addr)
552                               break;
553           }
554           if (slt == NULL) {
555                     slt = malloc(sizeof(*slt));
556                     slt->addr = addr;
557                     slt->next = SLHash[i];
558                     SLHash[i] = slt;
559           }
560 }
561 #endif
562 
563 static
564 void
dumpsltrack(kvm_t * kd)565 dumpsltrack(kvm_t *kd)
566 {
567           struct SLTrack *slt;
568           int i;
569           long total_zones = 0;
570           long full_zones = 0;
571 
572           for (i = 0; i < SLHSIZE; ++i) {
573                     for (slt = SLHash[i]; slt; slt = slt->next) {
574                               SLZone z;
575 
576                               if (kkread_err(kd, slt->addr, &z, sizeof(z))) {
577                                         printf("SLZone 0x%016lx not mapped\n",
578                                                   slt->addr);
579                                         continue;
580                               }
581                               printf("SLZone 0x%016lx { mag=%08x cpu=%-2d NFree=%-3d "
582                                      "chunksz=%-5d }\n",
583                                      slt->addr,
584                                      z.z_Magic,
585                                      z.z_Cpu,
586                                      z.z_NFree,
587                                      z.z_ChunkSize
588                               );
589                               ++total_zones;
590                               if (z.z_NFree == 0)
591                                         ++full_zones;
592                     }
593           }
594           printf("FullZones/TotalZones: %ld/%ld\n", full_zones, total_zones);
595 }
596 
597 #define HASH_SIZE   (1024*1024)
598 #define HASH_MASK   (HASH_SIZE - 1)
599 
600 struct dup_entry {
601           struct dup_entry *next;
602           void      *ptr;
603 };
604 
605 struct dup_entry *dup_hash[HASH_SIZE];
606 
607 static int
unique_object(void * ptr)608 unique_object(void *ptr)
609 {
610           struct dup_entry *hen;
611           int hv;
612 
613           hv = (intptr_t)ptr ^ ((intptr_t)ptr >> 20);
614           hv &= HASH_MASK;
615           for (hen = dup_hash[hv]; hen; hen = hen->next) {
616                     if (hen->ptr == ptr)
617                               return 0;
618           }
619           hen = malloc(sizeof(*hen));
620           hen->next = dup_hash[hv];
621           hen->ptr = ptr;
622           dup_hash[hv] = hen;
623 
624           return 1;
625 }
626