1 /* 2 * VMPAGEINFO.C 3 * 4 * cc -I/usr/src/sys vmpageinfo.c -o /usr/local/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 * $DragonFly: src/test/debug/vmpageinfo.c,v 1.2 2006/05/23 01:00:05 dillon Exp $ 44 */ 45 46 #define _KERNEL_STRUCTURES_ 47 #include <sys/param.h> 48 #include <sys/user.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> 54 55 #include <vm/vm.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> 62 63 #include <stdio.h> 64 #include <stdlib.h> 65 #include <string.h> 66 #include <fcntl.h> 67 #include <kvm.h> 68 #include <nlist.h> 69 #include <getopt.h> 70 71 struct nlist Nl[] = { 72 #if 0 73 { "_vm_page_buckets" }, 74 { "_vm_page_hash_mask" }, 75 #endif 76 { "_vm_page_array" }, 77 { "_vm_page_array_size" }, 78 { NULL } 79 }; 80 81 int debugopt; 82 int verboseopt; 83 #if 0 84 struct vm_page **vm_page_buckets; 85 int vm_page_hash_mask; 86 #endif 87 struct vm_page *vm_page_array; 88 int vm_page_array_size; 89 90 void checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj); 91 static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes); 92 static int kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes); 93 94 static void addsltrack(vm_page_t m); 95 static void dumpsltrack(kvm_t *kd); 96 97 int 98 main(int ac, char **av) 99 { 100 const char *corefile = NULL; 101 const char *sysfile = NULL; 102 vm_page_t mptr; 103 struct vm_page m; 104 struct vm_object obj; 105 kvm_t *kd; 106 int ch; 107 int hv; 108 int i; 109 const char *qstr; 110 const char *ostr; 111 112 while ((ch = getopt(ac, av, "M:N:dv")) != -1) { 113 switch(ch) { 114 case 'd': 115 ++debugopt; 116 break; 117 case 'v': 118 ++verboseopt; 119 break; 120 case 'M': 121 corefile = optarg; 122 break; 123 case 'N': 124 sysfile = optarg; 125 break; 126 default: 127 fprintf(stderr, "%s [-M core] [-N system]\n", av[0]); 128 exit(1); 129 } 130 } 131 ac -= optind; 132 av += optind; 133 134 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) { 135 perror("kvm_open"); 136 exit(1); 137 } 138 if (kvm_nlist(kd, Nl) != 0) { 139 perror("kvm_nlist"); 140 exit(1); 141 } 142 143 #if 0 144 kkread(kd, Nl[0].n_value, &vm_page_buckets, sizeof(vm_page_buckets)); 145 kkread(kd, Nl[1].n_value, &vm_page_hash_mask, sizeof(vm_page_hash_mask)); 146 #endif 147 kkread(kd, Nl[0].n_value, &vm_page_array, sizeof(vm_page_array)); 148 kkread(kd, Nl[1].n_value, &vm_page_array_size, sizeof(vm_page_array_size)); 149 150 /* 151 * Scan the vm_page_array validating all pages with associated objects 152 */ 153 for (i = 0; i < vm_page_array_size; ++i) { 154 if (debugopt) { 155 printf("page %d\r", i); 156 fflush(stdout); 157 } 158 kkread(kd, (u_long)&vm_page_array[i], &m, sizeof(m)); 159 if (m.object) { 160 kkread(kd, (u_long)m.object, &obj, sizeof(obj)); 161 checkpage(kd, &vm_page_array[i], &m, &obj); 162 } 163 if (verboseopt) { 164 if (m.queue >= PQ_HOLD) { 165 qstr = "HOLD"; 166 } else if (m.queue >= PQ_CACHE) { 167 qstr = "CACHE"; 168 } else if (m.queue >= PQ_ACTIVE) { 169 qstr = "ACTIVE"; 170 } else if (m.queue >= PQ_INACTIVE) { 171 qstr = "INACTIVE"; 172 } else if (m.queue >= PQ_FREE) { 173 qstr = "FREE"; 174 } else { 175 qstr = "NONE"; 176 } 177 printf("page %p obj %p/%-8ju(%016jx) val=%02x dty=%02x hold=%d " 178 "wire=%-2d act=%-3d busy=%d %8s", 179 &vm_page_array[i], 180 m.object, 181 (intmax_t)m.pindex, 182 (intmax_t)m.pindex * PAGE_SIZE, 183 m.valid, 184 m.dirty, 185 m.hold_count, 186 m.wire_count, 187 m.act_count, 188 m.busy, 189 qstr 190 ); 191 if (m.object) { 192 switch(obj.type) { 193 case OBJT_DEFAULT: 194 ostr = "default"; 195 break; 196 case OBJT_SWAP: 197 ostr = "swap"; 198 break; 199 case OBJT_VNODE: 200 ostr = "vnode"; 201 break; 202 case OBJT_DEVICE: 203 ostr = "device"; 204 break; 205 case OBJT_PHYS: 206 ostr = "phys"; 207 break; 208 case OBJT_DEAD: 209 ostr = "dead"; 210 break; 211 default: 212 ostr = "unknown"; 213 break; 214 } 215 } else { 216 ostr = "-"; 217 } 218 printf(" %-7s", ostr); 219 if (m.flags & PG_BUSY) 220 printf(" BUSY"); 221 if (m.flags & PG_WANTED) 222 printf(" WANTED"); 223 if (m.flags & PG_WINATCFLS) 224 printf(" WINATCFLS"); 225 if (m.flags & PG_FICTITIOUS) 226 printf(" FICTITIOUS"); 227 if (m.flags & PG_WRITEABLE) 228 printf(" WRITEABLE"); 229 if (m.flags & PG_MAPPED) 230 printf(" MAPPED"); 231 if (m.flags & PG_ZERO) 232 printf(" ZERO"); 233 if (m.flags & PG_REFERENCED) 234 printf(" REFERENCED"); 235 if (m.flags & PG_CLEANCHK) 236 printf(" CLEANCHK"); 237 if (m.flags & PG_SWAPINPROG) 238 printf(" SWAPINPROG"); 239 if (m.flags & PG_NOSYNC) 240 printf(" NOSYNC"); 241 if (m.flags & PG_UNMANAGED) 242 printf(" UNMANAGED"); 243 if (m.flags & PG_MARKER) 244 printf(" MARKER"); 245 if (m.flags & PG_RAM) 246 printf(" RAM"); 247 if (m.flags & PG_SWAPPED) 248 printf(" SWAPPED"); 249 if (m.flags & PG_SLAB) 250 printf(" SLAB"); 251 printf("\n"); 252 if (m.flags & PG_SLAB) 253 addsltrack(&m); 254 } 255 } 256 if (debugopt || verboseopt) 257 printf("\n"); 258 259 #if 0 260 /* 261 * Scan the vm_page_buckets array validating all pages found 262 */ 263 for (i = 0; i <= vm_page_hash_mask; ++i) { 264 if (debugopt) { 265 printf("index %d\r", i); 266 fflush(stdout); 267 } 268 kkread(kd, (u_long)&vm_page_buckets[i], &mptr, sizeof(mptr)); 269 while (mptr) { 270 kkread(kd, (u_long)mptr, &m, sizeof(m)); 271 if (m.object) { 272 kkread(kd, (u_long)m.object, &obj, sizeof(obj)); 273 hv = ((uintptr_t)m.object + m.pindex) ^ obj.hash_rand; 274 hv &= vm_page_hash_mask; 275 if (i != hv) 276 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 277 " should be in bucket %d\n", i, mptr, hv); 278 checkpage(kd, mptr, &m, &obj); 279 } else { 280 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 281 " has no object\n", i, mptr); 282 } 283 mptr = m.hnext; 284 } 285 } 286 #endif 287 if (debugopt) 288 printf("\n"); 289 dumpsltrack(kd); 290 return(0); 291 } 292 293 /* 294 * A page with an object. 295 */ 296 void 297 checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj) 298 { 299 #if 0 300 struct vm_page scan; 301 vm_page_t scanptr; 302 int hv; 303 304 hv = ((uintptr_t)m->object + m->pindex) ^ obj->hash_rand; 305 hv &= vm_page_hash_mask; 306 kkread(kd, (u_long)&vm_page_buckets[hv], &scanptr, sizeof(scanptr)); 307 while (scanptr) { 308 if (scanptr == mptr) 309 break; 310 kkread(kd, (u_long)scanptr, &scan, sizeof(scan)); 311 scanptr = scan.hnext; 312 } 313 if (scanptr) { 314 if (debugopt > 1) 315 printf("good checkpage %p bucket %d\n", mptr, hv); 316 } else { 317 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 318 " page not found in bucket list\n", hv, mptr); 319 } 320 #endif 321 } 322 323 static void 324 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 325 { 326 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 327 perror("kvm_read"); 328 exit(1); 329 } 330 } 331 332 static int 333 kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 334 { 335 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 336 return 1; 337 } 338 return 0; 339 } 340 341 struct SLTrack { 342 struct SLTrack *next; 343 u_long addr; 344 }; 345 346 #define SLHSIZE 1024 347 #define SLHMASK (SLHSIZE - 1) 348 349 struct SLTrack *SLHash[SLHSIZE]; 350 351 static 352 void 353 addsltrack(vm_page_t m) 354 { 355 struct SLTrack *slt; 356 u_long addr = (m->pindex * PAGE_SIZE) & ~131071L; 357 int i; 358 359 if (m->wire_count == 0 || (m->flags & PG_MAPPED) == 0 || 360 m->object == NULL) 361 return; 362 363 i = (addr / 131072) & SLHMASK; 364 for (slt = SLHash[i]; slt; slt = slt->next) { 365 if (slt->addr == addr) 366 break; 367 } 368 if (slt == NULL) { 369 slt = malloc(sizeof(*slt)); 370 slt->addr = addr; 371 slt->next = SLHash[i]; 372 SLHash[i] = slt; 373 } 374 } 375 376 static 377 void 378 dumpsltrack(kvm_t *kd) 379 { 380 struct SLTrack *slt; 381 int i; 382 long total_zones = 0; 383 long full_zones = 0; 384 385 for (i = 0; i < SLHSIZE; ++i) { 386 for (slt = SLHash[i]; slt; slt = slt->next) { 387 SLZone z; 388 389 if (kkread_err(kd, slt->addr, &z, sizeof(z))) { 390 printf("SLZone 0x%016lx not mapped\n", 391 slt->addr); 392 continue; 393 } 394 printf("SLZone 0x%016lx { mag=%08x cpu=%-2d NFree=%-3d " 395 "chunksz=%-5d }\n", 396 slt->addr, 397 z.z_Magic, 398 z.z_Cpu, 399 z.z_NFree, 400 z.z_ChunkSize 401 ); 402 ++total_zones; 403 if (z.z_NFree == 0) 404 ++full_zones; 405 } 406 } 407 printf("FullZones/TotalZones: %ld/%ld\n", full_zones, total_zones); 408 } 409