1 /* 2 * VMPAGEHASH.C 3 * 4 * cc -I/usr/src/sys vmpagehash.c -o ~/bin/vmpagehash -lkvm 5 * 6 * vmpageinfo 7 * 8 * Validate the vm_page_buckets[] hash array against the vm_page_array 9 * 10 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 11 * 12 * This code is derived from software contributed to The DragonFly Project 13 * by Matthew Dillon <dillon@backplane.com> 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 19 * 1. Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 2. Redistributions in binary form must reproduce the above copyright 22 * notice, this list of conditions and the following disclaimer in 23 * the documentation and/or other materials provided with the 24 * distribution. 25 * 3. Neither the name of The DragonFly Project nor the names of its 26 * contributors may be used to endorse or promote products derived 27 * from this software without specific, prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 30 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 31 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 32 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 33 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 34 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 35 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 36 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 37 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 38 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 39 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 * SUCH DAMAGE. 41 */ 42 43 #define _KERNEL_STRUCTURES_ 44 #include <sys/param.h> 45 #include <sys/user.h> 46 #include <sys/malloc.h> 47 #include <sys/signalvar.h> 48 #include <sys/vnode.h> 49 #include <sys/namecache.h> 50 #include <sys/slaballoc.h> 51 52 #include <vm/vm.h> 53 #include <vm/vm_page.h> 54 #include <vm/vm_kern.h> 55 #include <vm/vm_page.h> 56 #include <vm/vm_object.h> 57 #include <vm/swap_pager.h> 58 #include <vm/vnode_pager.h> 59 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <fcntl.h> 64 #include <kvm.h> 65 #include <nlist.h> 66 #include <getopt.h> 67 68 struct vm_page_hash_elm { 69 vm_page_t m; 70 int ticks; 71 int unused01; 72 }; 73 74 75 struct nlist Nl[] = { 76 { "_vm_page_hash" }, 77 { "_vm_page_hash_size" }, 78 { "_ticks" }, 79 { "_vm_page_array" }, 80 { "_vm_page_array_size" }, 81 { NULL } 82 }; 83 84 int debugopt; 85 int verboseopt; 86 struct vm_page *vm_page_array_ptr; 87 struct vm_page_hash_elm *vm_page_hash_ptr; 88 struct vm_page_hash_elm *vm_page_hash; 89 int vm_page_hash_size; 90 int vm_page_array_size; 91 int ticks; 92 93 void checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj); 94 static void kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m); 95 static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes); 96 static int kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes); 97 98 int 99 main(int ac, char **av) 100 { 101 const char *corefile = NULL; 102 const char *sysfile = NULL; 103 struct vm_page m; 104 struct vm_object obj; 105 kvm_t *kd; 106 int ch; 107 #if 0 108 vm_page_t mptr; 109 int hv; 110 #endif 111 int i; 112 const char *qstr; 113 const char *ostr; 114 115 while ((ch = getopt(ac, av, "M:N:dv")) != -1) { 116 switch(ch) { 117 case 'd': 118 ++debugopt; 119 break; 120 case 'v': 121 ++verboseopt; 122 break; 123 case 'M': 124 corefile = optarg; 125 break; 126 case 'N': 127 sysfile = optarg; 128 break; 129 default: 130 fprintf(stderr, "%s [-M core] [-N system]\n", av[0]); 131 exit(1); 132 } 133 } 134 ac -= optind; 135 av += optind; 136 137 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) { 138 perror("kvm_open"); 139 exit(1); 140 } 141 if (kvm_nlist(kd, Nl) != 0) { 142 perror("kvm_nlist"); 143 exit(1); 144 } 145 146 kkread(kd, Nl[0].n_value, &vm_page_hash_ptr, sizeof(vm_page_hash_ptr)); 147 kkread(kd, Nl[1].n_value, &vm_page_hash_size, sizeof(vm_page_hash_size)); 148 kkread(kd, Nl[2].n_value, &ticks, sizeof(ticks)); 149 kkread(kd, Nl[3].n_value, &vm_page_array_ptr, sizeof(vm_page_array_ptr)); 150 kkread(kd, Nl[4].n_value, &vm_page_array_size, sizeof(vm_page_array_size)); 151 152 vm_page_hash = malloc(vm_page_hash_size * sizeof(*vm_page_hash)); 153 kkread(kd, (intptr_t)vm_page_hash_ptr, vm_page_hash, 154 vm_page_hash_size * sizeof(*vm_page_hash)); 155 156 /* 157 * Scan the vm_page_hash validating all pages with associated objects 158 */ 159 printf("vm_page_hash[%d]\n", vm_page_hash_size); 160 for (i = 0; i < vm_page_hash_size; ++i) { 161 struct vm_page_hash_elm *elm; 162 163 elm = &vm_page_hash[i]; 164 if ((i & 3) == 0) { 165 printf(" group(%d-%d) ", i, i + 3); 166 if (elm[0].m && elm[1].m && elm[2].m && elm[3].m) 167 printf("FULL "); 168 printf("\n"); 169 } 170 printf(" %016jx %9d ", elm->m, elm->ticks); 171 172 if (elm->m) { 173 kkread_vmpage(kd, (u_long)elm->m, &m); 174 if (m.object) { 175 kkread(kd, (u_long)m.object, &obj, sizeof(obj)); 176 checkpage(kd, elm->m, &m, &obj); 177 } 178 if (m.queue >= PQ_HOLD) { 179 qstr = "HOLD"; 180 } else if (m.queue >= PQ_CACHE) { 181 qstr = "CACHE"; 182 } else if (m.queue >= PQ_ACTIVE) { 183 qstr = "ACTIVE"; 184 } else if (m.queue >= PQ_INACTIVE) { 185 qstr = "INACTIVE"; 186 } else if (m.queue >= PQ_FREE) { 187 qstr = "FREE"; 188 } else { 189 qstr = "NONE"; 190 } 191 printf("obj %p/%016jx\n\t\t\t\tval=%02x dty=%02x hold=%d " 192 "wire=%-2d act=%-3d busy=%d %8s", 193 m.object, 194 (intmax_t)m.pindex, 195 m.valid, 196 m.dirty, 197 m.hold_count, 198 m.wire_count, 199 m.act_count, 200 m.busy_count, 201 qstr 202 ); 203 204 if (m.object) { 205 switch(obj.type) { 206 case OBJT_DEFAULT: 207 ostr = "default"; 208 break; 209 case OBJT_SWAP: 210 ostr = "swap"; 211 break; 212 case OBJT_VNODE: 213 ostr = "vnode"; 214 break; 215 case OBJT_DEVICE: 216 ostr = "device"; 217 break; 218 case OBJT_PHYS: 219 ostr = "phys"; 220 break; 221 case OBJT_DEAD: 222 ostr = "dead"; 223 break; 224 default: 225 ostr = "unknown"; 226 break; 227 } 228 } else { 229 ostr = "-"; 230 } 231 printf(" %-7s", ostr); 232 if (m.busy_count & PBUSY_LOCKED) 233 printf(" BUSY"); 234 if (m.busy_count & PBUSY_WANTED) 235 printf(" WANTED"); 236 if (m.flags & PG_WINATCFLS) 237 printf(" WINATCFLS"); 238 if (m.flags & PG_FICTITIOUS) 239 printf(" FICTITIOUS"); 240 if (m.flags & PG_WRITEABLE) 241 printf(" WRITEABLE"); 242 if (m.flags & PG_MAPPED) 243 printf(" MAPPED"); 244 if (m.flags & PG_NEED_COMMIT) 245 printf(" NEED_COMMIT"); 246 if (m.flags & PG_REFERENCED) 247 printf(" REFERENCED"); 248 if (m.flags & PG_CLEANCHK) 249 printf(" CLEANCHK"); 250 if (m.busy_count & PBUSY_SWAPINPROG) 251 printf(" SWAPINPROG"); 252 if (m.flags & PG_NOSYNC) 253 printf(" NOSYNC"); 254 if (m.flags & PG_UNMANAGED) 255 printf(" UNMANAGED"); 256 if (m.flags & PG_MARKER) 257 printf(" MARKER"); 258 if (m.flags & PG_RAM) 259 printf(" RAM"); 260 if (m.flags & PG_SWAPPED) 261 printf(" SWAPPED"); 262 } 263 printf("\n"); 264 } 265 return(0); 266 } 267 268 /* 269 * A page with an object. 270 */ 271 void 272 checkpage(kvm_t *kd, vm_page_t mptr, vm_page_t m, struct vm_object *obj) 273 { 274 #if 0 275 struct vm_page scan; 276 vm_page_t scanptr; 277 int hv; 278 279 hv = ((uintptr_t)m->object + m->pindex) ^ obj->hash_rand; 280 hv &= vm_page_hash_mask; 281 kkread(kd, (u_long)&vm_page_buckets[hv], &scanptr, sizeof(scanptr)); 282 while (scanptr) { 283 if (scanptr == mptr) 284 break; 285 kkread(kd, (u_long)scanptr, &scan, sizeof(scan)); 286 scanptr = scan.hnext; 287 } 288 if (scanptr) { 289 if (debugopt > 1) 290 printf("good checkpage %p bucket %d\n", mptr, hv); 291 } else { 292 printf("vm_page_buckets[%d] ((struct vm_page *)%p)" 293 " page not found in bucket list\n", hv, mptr); 294 } 295 #endif 296 } 297 298 /* 299 * Acclerate the reading of VM pages 300 */ 301 static void 302 kkread_vmpage(kvm_t *kd, u_long addr, vm_page_t m) 303 { 304 static struct vm_page vpcache[1024]; 305 static u_long vpbeg; 306 static u_long vpend; 307 308 if (addr < vpbeg || addr >= vpend) { 309 vpbeg = addr; 310 vpend = addr + 1024 * sizeof(*m); 311 if (vpend > (u_long)(uintptr_t)vm_page_array_ptr + 312 vm_page_array_size * sizeof(*m)) { 313 vpend = (u_long)(uintptr_t)vm_page_array_ptr + 314 vm_page_array_size * sizeof(*m); 315 } 316 kkread(kd, vpbeg, vpcache, vpend - vpbeg); 317 } 318 *m = vpcache[(addr - vpbeg) / sizeof(*m)]; 319 } 320 321 static void 322 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 323 { 324 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 325 perror("kvm_read"); 326 exit(1); 327 } 328 } 329 330 static int 331 kkread_err(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 332 { 333 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 334 return 1; 335 } 336 return 0; 337 } 338 339 struct SLTrack { 340 struct SLTrack *next; 341 u_long addr; 342 }; 343 344 #define SLHSIZE 1024 345 #define SLHMASK (SLHSIZE - 1) 346 347 struct SLTrack *SLHash[SLHSIZE]; 348 349 #if 0 350 static 351 void 352 addsltrack(vm_page_t m) 353 { 354 struct SLTrack *slt; 355 u_long addr = (m->pindex * PAGE_SIZE) & ~131071L; 356 int i; 357 358 if (m->wire_count == 0 || (m->flags & PG_MAPPED) == 0 || 359 m->object == NULL) 360 return; 361 362 i = (addr / 131072) & SLHMASK; 363 for (slt = SLHash[i]; slt; slt = slt->next) { 364 if (slt->addr == addr) 365 break; 366 } 367 if (slt == NULL) { 368 slt = malloc(sizeof(*slt)); 369 slt->addr = addr; 370 slt->next = SLHash[i]; 371 SLHash[i] = slt; 372 } 373 } 374 #endif 375