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_NEED_COMMIT) 232 printf(" NEED_COMMIT"); 233 if (m.flags & PG_ZERO) 234 printf(" ZERO"); 235 if (m.flags & PG_REFERENCED) 236 printf(" REFERENCED"); 237 if (m.flags & PG_CLEANCHK) 238 printf(" CLEANCHK"); 239 if (m.flags & PG_SWAPINPROG) 240 printf(" SWAPINPROG"); 241 if (m.flags & PG_NOSYNC) 242 printf(" NOSYNC"); 243 if (m.flags & PG_UNMANAGED) 244 printf(" UNMANAGED"); 245 if (m.flags & PG_MARKER) 246 printf(" MARKER"); 247 if (m.flags & PG_RAM) 248 printf(" RAM"); 249 if (m.flags & PG_SWAPPED) 250 printf(" SWAPPED"); 251 #if 0 252 if (m.flags & PG_SLAB) 253 printf(" SLAB"); 254 #endif 255 printf("\n"); 256 #if 0 257 if (m.flags & PG_SLAB) 258 addsltrack(&m); 259 #endif 260 } 261 } 262 if (debugopt || verboseopt) 263 printf("\n"); 264 265 #if 0 266 /* 267 * Scan the vm_page_buckets array validating all pages found 268 */ 269 for (i = 0; i <= vm_page_hash_mask; ++i) { 270 if (debugopt) { 271 printf("index %d\r", i); 272 fflush(stdout); 273 } 274 kkread(kd, (u_long)&vm_page_buckets[i], &mptr, sizeof(mptr)); 275 while (mptr) { 276 kkread(kd, (u_long)mptr, &m, sizeof(m)); 277 if (m.object) { 278 kkread(kd, (u_long)m.object, &obj, sizeof(obj)); 279 hv = ((uintptr_t)m.object + m.pindex) ^ obj.hash_rand; 280 hv &= vm_page_hash_mask; 281 if (i != hv) 282 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 283 " should be in bucket %d\n", i, mptr, hv); 284 checkpage(kd, mptr, &m, &obj); 285 } else { 286 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 287 " has no object\n", i, mptr); 288 } 289 mptr = m.hnext; 290 } 291 } 292 #endif 293 if (debugopt) 294 printf("\n"); 295 dumpsltrack(kd); 296 return(0); 297 } 298 299 /* 300 * A page with an object. 301 */ 302 void 303 checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj) 304 { 305 #if 0 306 struct vm_page scan; 307 vm_page_t scanptr; 308 int hv; 309 310 hv = ((uintptr_t)m->object + m->pindex) ^ obj->hash_rand; 311 hv &= vm_page_hash_mask; 312 kkread(kd, (u_long)&vm_page_buckets[hv], &scanptr, sizeof(scanptr)); 313 while (scanptr) { 314 if (scanptr == mptr) 315 break; 316 kkread(kd, (u_long)scanptr, &scan, sizeof(scan)); 317 scanptr = scan.hnext; 318 } 319 if (scanptr) { 320 if (debugopt > 1) 321 printf("good checkpage %p bucket %d\n", mptr, hv); 322 } else { 323 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 324 " page not found in bucket list\n", hv, mptr); 325 } 326 #endif 327 } 328 329 static void 330 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 331 { 332 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 333 perror("kvm_read"); 334 exit(1); 335 } 336 } 337 338 static int 339 kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 340 { 341 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 342 return 1; 343 } 344 return 0; 345 } 346 347 struct SLTrack { 348 struct SLTrack *next; 349 u_long addr; 350 }; 351 352 #define SLHSIZE 1024 353 #define SLHMASK (SLHSIZE - 1) 354 355 struct SLTrack *SLHash[SLHSIZE]; 356 357 static 358 void 359 addsltrack(vm_page_t m) 360 { 361 struct SLTrack *slt; 362 u_long addr = (m->pindex * PAGE_SIZE) & ~131071L; 363 int i; 364 365 if (m->wire_count == 0 || (m->flags & PG_MAPPED) == 0 || 366 m->object == NULL) 367 return; 368 369 i = (addr / 131072) & SLHMASK; 370 for (slt = SLHash[i]; slt; slt = slt->next) { 371 if (slt->addr == addr) 372 break; 373 } 374 if (slt == NULL) { 375 slt = malloc(sizeof(*slt)); 376 slt->addr = addr; 377 slt->next = SLHash[i]; 378 SLHash[i] = slt; 379 } 380 } 381 382 static 383 void 384 dumpsltrack(kvm_t *kd) 385 { 386 struct SLTrack *slt; 387 int i; 388 long total_zones = 0; 389 long full_zones = 0; 390 391 for (i = 0; i < SLHSIZE; ++i) { 392 for (slt = SLHash[i]; slt; slt = slt->next) { 393 SLZone z; 394 395 if (kkread_err(kd, slt->addr, &z, sizeof(z))) { 396 printf("SLZone 0x%016lx not mapped\n", 397 slt->addr); 398 continue; 399 } 400 printf("SLZone 0x%016lx { mag=%08x cpu=%-2d NFree=%-3d " 401 "chunksz=%-5d }\n", 402 slt->addr, 403 z.z_Magic, 404 z.z_Cpu, 405 z.z_NFree, 406 z.z_ChunkSize 407 ); 408 ++total_zones; 409 if (z.z_NFree == 0) 410 ++full_zones; 411 } 412 } 413 printf("FullZones/TotalZones: %ld/%ld\n", full_zones, total_zones); 414 } 415