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