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