1 /* 2 * VNODEINFO.C 3 * 4 * cc -I/usr/src/sys vnodeinfo.c -o /usr/local/bin/vnodeinfo -lkvm 5 * 6 * vnodeinfo 7 * 8 * Dump the mountlist and related vnodes. 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 */ 44 45 #define _KERNEL_STRUCTURES 46 #include <sys/param.h> 47 #include <sys/user.h> 48 #include <sys/malloc.h> 49 #include <sys/signalvar.h> 50 #include <sys/namecache.h> 51 #include <sys/mount.h> 52 #include <sys/vnode.h> 53 #include <sys/buf.h> 54 55 #include <vm/vm.h> 56 #include <vm/vm_page.h> 57 #include <vm/vm_kern.h> 58 #include <vm/vm_object.h> 59 #include <vm/swap_pager.h> 60 #include <vm/vnode_pager.h> 61 62 #include <vfs/ufs/quota.h> 63 #include <vfs/ufs/inode.h> 64 65 #include <stdio.h> 66 #include <stdlib.h> 67 #include <string.h> 68 #include <fcntl.h> 69 #include <kvm.h> 70 #include <nlist.h> 71 #include <getopt.h> 72 73 struct nlist Nl[] = { 74 { "_mountlist" }, 75 { "_vnode_list_hash" }, 76 { "_ncpus" }, 77 { NULL } 78 }; 79 80 static void kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes); 81 static struct mount *dumpmount(kvm_t *kd, struct mount *mp); 82 static struct vnode *dumpvp(kvm_t *kd, struct vnode *vp, int whichlist, char *vfc_name); 83 static void dumpbufs(kvm_t *kd, void *bufp, const char *id); 84 static void dumplocks(kvm_t *kd, struct lockf *lockf); 85 static void dumplockinfo(kvm_t *kd, struct lockf_range *item); 86 static int getobjpages(kvm_t *kd, struct vm_object *obj); 87 static int getobjvnpsize(kvm_t *kd, struct vm_object *obj); 88 89 static const struct dump_private_data { 90 char vfc_name[MFSNAMELEN]; 91 void (*dumpfn)(kvm_t *, void *); 92 } dumplist[] = { 93 { "", NULL } 94 }; 95 96 int tracebufs = 0; 97 int tracelocks = 0; 98 int withnames = 0; 99 int fsprivate = 0; 100 101 int 102 main(int ac, char **av) 103 { 104 struct mount *mp; 105 struct vnode_index *vib; 106 struct vnode_index vni; 107 kvm_t *kd; 108 int ch; 109 int ncpus = 0; 110 const char *corefile = NULL; 111 const char *sysfile = NULL; 112 113 while ((ch = getopt(ac, av, "alnbM:N:p")) != -1) { 114 switch(ch) { 115 case 'b': 116 tracebufs = 1; 117 break; 118 case 'n': 119 withnames = 1; 120 break; 121 case 'l': 122 tracelocks = 1; 123 break; 124 case 'a': 125 tracebufs = 1; 126 tracelocks = 1; 127 withnames = 1; 128 fsprivate = 1; 129 break; 130 case 'p': 131 fsprivate = 1; 132 break; 133 case 'M': 134 corefile = optarg; 135 break; 136 case 'N': 137 sysfile = optarg; 138 break; 139 default: 140 fprintf(stderr, "%s [-pbnla] [-M core] [-N system]\n", av[0]); 141 exit(1); 142 } 143 } 144 145 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) { 146 perror("kvm_open"); 147 exit(1); 148 } 149 if (kvm_nlist(kd, Nl) != 0) { 150 perror("kvm_nlist"); 151 exit(1); 152 } 153 154 /* Mount points and their private data */ 155 kkread(kd, Nl[0].n_value, &mp, sizeof(mp)); 156 while (mp) 157 mp = dumpmount(kd, mp); 158 159 /* 160 * Get ncpus for the vnode lists, we could get it with a sysctl 161 * but since we're reading kernel memory, take advantage of it. 162 * Also read the base address of vnode_list_hash. 163 */ 164 kkread(kd, Nl[1].n_value, &vib, sizeof(vib)); 165 kkread(kd, Nl[2].n_value, &ncpus, sizeof(ncpus)); 166 167 /* Per-CPU list of inactive vnodes */ 168 printf("INACTIVELIST {\n"); 169 170 for (int i = 0; i < ncpus; i++) { 171 struct vnode *vp; 172 173 kkread(kd, (u_long)(vib + i), &vni, sizeof(vni)); 174 vp = vni.inactive_list.tqh_first; 175 for (; vp != NULL;) 176 vp = dumpvp(kd, vp, 0, NULL); 177 } 178 printf("}\n"); 179 180 /* Per-CPU list of active vnodes */ 181 printf("ACTIVELIST {\n"); 182 for (int i = 0; i < ncpus; i++) { 183 struct vnode *vp; 184 185 kkread(kd, (u_long)(vib + i), &vni, sizeof(vni)); 186 vp = vni.active_list.tqh_first; 187 for (; vp;) 188 vp = dumpvp(kd, vp, 0, 189 NULL); 190 } 191 printf("}\n"); 192 return(0); 193 } 194 195 static struct mount * 196 dumpmount(kvm_t *kd, struct mount *mp) 197 { 198 struct mount mnt; 199 struct vnode *vp; 200 struct vfsconf vfc; 201 202 kkread(kd, (u_long)mp, &mnt, sizeof(mnt)); 203 printf("MOUNTPOINT %s on %s {\n", 204 mnt.mnt_stat.f_mntfromname, mnt.mnt_stat.f_mntonname); 205 printf(" lk_flags %08x count %016jx holder = %p\n", 206 mnt.mnt_lock.lk_flags, mnt.mnt_lock.lk_count, 207 mnt.mnt_lock.lk_lockholder); 208 printf(" mnt_flag %08x mnt_kern_flag %08x\n", 209 mnt.mnt_flag, mnt.mnt_kern_flag); 210 printf(" mnt_nvnodelistsize %d\n", mnt.mnt_nvnodelistsize); 211 printf(" mnt_stat.f_fsid %08x %08x\n", mnt.mnt_stat.f_fsid.val[0], 212 mnt.mnt_stat.f_fsid.val[1]); 213 214 /* Dump fs private node data */ 215 kkread(kd, (u_long)mnt.mnt_vfc, &vfc, sizeof(vfc)); 216 vp = mnt.mnt_nvnodelist.tqh_first; 217 while (vp) 218 vp = dumpvp(kd, vp, 1, vfc.vfc_name); 219 220 printf("}\n"); 221 222 return(mnt.mnt_list.tqe_next); 223 } 224 225 static const char * 226 vtype(enum vtype type) 227 { 228 static char buf[32]; 229 230 switch(type) { 231 case VNON: 232 return("VNON"); 233 case VREG: 234 return("VREG"); 235 case VDIR: 236 return("VDIR"); 237 case VBLK: 238 return("VBLK"); 239 case VCHR: 240 return("VCHR"); 241 case VLNK: 242 return("VLNK"); 243 case VSOCK: 244 return("VSOCK"); 245 case VFIFO: 246 return("VFIFO"); 247 case VBAD: 248 return("VBAD"); 249 default: 250 break; 251 } 252 snprintf(buf, sizeof(buf), "%d", (int)type); 253 return(buf); 254 } 255 256 static struct vnode * 257 dumpvp(kvm_t *kd, struct vnode *vp, int whichlist, char *vfc_name) 258 { 259 struct vnode vn; 260 261 kkread(kd, (u_long)vp, &vn, sizeof(vn)); 262 263 printf(" vnode %p.%d refcnt %08x auxcnt %d type=%s flags %08x", 264 vp, vn.v_state, vn.v_refcnt, vn.v_auxrefs, vtype(vn.v_type), vn.v_flag); 265 266 if ((vn.v_flag & VOBJBUF) && vn.v_object) { 267 int npages = getobjpages(kd, vn.v_object); 268 int vnpsize = getobjvnpsize(kd, vn.v_object); 269 if (npages || vnpsize) 270 printf(" vmobjpgs=%d vnpsize=%d", npages, vnpsize); 271 } 272 273 if (vn.v_flag & VROOT) 274 printf(" VROOT"); 275 if (vn.v_flag & VTEXT) 276 printf(" VTEXT"); 277 if (vn.v_flag & VSYSTEM) 278 printf(" VSYSTEM"); 279 if (vn.v_flag & VISTTY) 280 printf(" VISTTY"); 281 #ifdef VXLOCK 282 if (vn.v_flag & VXLOCK) 283 printf(" VXLOCK"); 284 if (vn.v_flag & VXWANT) 285 printf(" VXWANT"); 286 #endif 287 #ifdef VRECLAIMED 288 if (vn.v_flag & VRECLAIMED) 289 printf(" VRECLAIMED"); 290 if (vn.v_flag & VINACTIVE) 291 printf(" VINACTIVE"); 292 #endif 293 if (vn.v_flag & VOBJBUF) 294 printf(" VOBJBUF"); 295 #ifdef VSWAPCACHE 296 if (vn.v_flag & VSWAPCACHE) 297 printf(" VSWAPCACHE"); 298 #endif 299 switch(vn.v_flag & (VAGE0 | VAGE1)) { 300 case 0: 301 printf(" VAGE0"); 302 break; 303 case VAGE0: 304 printf(" VAGE1"); 305 break; 306 case VAGE1: 307 printf(" VAGE2"); 308 break; 309 case VAGE0 | VAGE1: 310 printf(" VAGE3"); 311 break; 312 } 313 #ifdef VDOOMED 314 if (vn.v_flag & VDOOMED) 315 printf(" VDOOMED"); 316 #endif 317 #ifdef VINFREE 318 if (vn.v_flag & VINFREE) 319 printf(" VINFREE"); 320 #endif 321 if (vn.v_flag & VONWORKLST) 322 printf(" VONWORKLST"); 323 if (vn.v_flag & VOBJDIRTY) 324 printf(" VOBJDIRTY"); 325 if (vn.v_flag & VMAYHAVELOCKS) 326 printf(" VMAYHAVELOCKS"); 327 328 printf("\n"); 329 330 if (vn.v_lock.lk_count || vn.v_lock.lk_lockholder != NULL) { 331 printf("\tlk_flags %08x count %016jx holder = %p\n", 332 vn.v_lock.lk_flags, vn.v_lock.lk_count, 333 vn.v_lock.lk_lockholder); 334 } 335 336 if (withnames && TAILQ_FIRST(&vn.v_namecache)) { 337 struct namecache ncp; 338 int nlen; 339 char buf[1024]; 340 341 kkread(kd, (u_long)TAILQ_FIRST(&vn.v_namecache), &ncp, sizeof(ncp)); 342 if ((nlen = ncp.nc_nlen) >= sizeof(buf)) 343 nlen = sizeof(buf) - 1; 344 if (nlen < 0) 345 nlen = 0; 346 if (nlen) { 347 kkread(kd, (u_long)ncp.nc_name, buf, nlen); 348 buf[nlen] = 0; 349 printf("\tfilename %s\n", buf); 350 } 351 } 352 353 if (tracebufs) { 354 if (vn.v_rbclean_tree.rbh_root) { 355 printf("\tCLEAN BUFFERS\n"); 356 dumpbufs(kd, vn.v_rbclean_tree.rbh_root, "ROOT"); 357 } 358 if (vn.v_rbdirty_tree.rbh_root) { 359 printf("\tDIRTY BUFFERS\n"); 360 dumpbufs(kd, vn.v_rbdirty_tree.rbh_root, "ROOT"); 361 } 362 } 363 364 if (tracelocks) { 365 if (vn.v_tag == VT_UFS && vn.v_data) { 366 struct inode *ip = vn.v_data; 367 struct lockf lockf; 368 369 kkread(kd, (u_long)&ip->i_lockf, &lockf, sizeof(lockf)); 370 dumplocks(kd, &lockf); 371 } 372 } 373 374 if (fsprivate && vfc_name) { 375 /* 376 * Actually find whether the filesystem can dump 377 * detailed inode information out of the vnode 378 */ 379 const struct dump_private_data *dpd; 380 381 for (dpd = dumplist; dpd->dumpfn != NULL; dpd++) { 382 if ((strcmp(dpd->vfc_name, vfc_name) == 0) && 383 vn.v_data != NULL) 384 dpd->dumpfn(kd, vn.v_data); 385 } 386 } 387 388 if (whichlist) 389 return(vn.v_nmntvnodes.tqe_next); 390 else 391 return(vn.v_list.tqe_next); 392 } 393 394 static void 395 dumpbufs(kvm_t *kd, void *bufp, const char *id) 396 { 397 struct buf buf; 398 399 kkread(kd, (u_long)bufp, &buf, sizeof(buf)); 400 printf("\t %-8s %p loffset %012lx/%05x foffset %08lx", 401 id, bufp, 402 buf.b_bio1.bio_offset, 403 buf.b_bufsize, 404 buf.b_bio2.bio_offset); 405 printf(" q=%d count=%016jx flags=%08x refs=%08x dep=%p", 406 buf.b_qindex, buf.b_lock.lk_count, 407 buf.b_flags, buf.b_refs, buf.b_dep.lh_first); 408 printf("\n"); 409 410 if (buf.b_rbnode.rbe_left) 411 dumpbufs(kd, buf.b_rbnode.rbe_left, "LEFT"); 412 if (buf.b_rbnode.rbe_right) 413 dumpbufs(kd, buf.b_rbnode.rbe_right, "RIGHT"); 414 } 415 416 static void 417 dumplocks(kvm_t *kd, struct lockf *lockf) 418 { 419 struct lockf_range item; 420 struct lockf_range *scan; 421 422 if ((scan = TAILQ_FIRST(&lockf->lf_range)) != NULL) { 423 printf("\tLOCKS\n"); 424 do { 425 kkread(kd, (u_long)scan, &item, sizeof(item)); 426 dumplockinfo(kd, &item); 427 } while ((scan = TAILQ_NEXT(&item, lf_link)) != NULL); 428 printf("\n"); 429 } 430 if ((scan = TAILQ_FIRST(&lockf->lf_blocked)) != NULL) { 431 printf("\tBLKED\n"); 432 do { 433 kkread(kd, (u_long)scan, &item, sizeof(item)); 434 dumplockinfo(kd, &item); 435 } while ((scan = TAILQ_NEXT(&item, lf_link)) != NULL); 436 printf("\n"); 437 } 438 439 } 440 441 static void 442 dumplockinfo(kvm_t *kd, struct lockf_range *item) 443 { 444 int ownerpid; 445 446 if (item->lf_owner && (item->lf_flags & F_POSIX)) { 447 kkread(kd, (u_long)&item->lf_owner->p_pid, 448 &ownerpid, sizeof(ownerpid)); 449 } else { 450 ownerpid = -1; 451 } 452 453 printf("\t ty=%d flgs=%04x %ld-%ld owner=%d\n", 454 item->lf_type, item->lf_flags, 455 item->lf_start, item->lf_end, 456 ownerpid 457 ); 458 } 459 460 static 461 int 462 getobjpages(kvm_t *kd, struct vm_object *obj) 463 { 464 struct vm_object vmobj; 465 466 kkread(kd, (u_long)obj, &vmobj, sizeof(vmobj)); 467 return(vmobj.resident_page_count); 468 } 469 470 static 471 int 472 getobjvnpsize(kvm_t *kd, struct vm_object *obj) 473 { 474 struct vm_object vmobj; 475 476 kkread(kd, (u_long)obj, &vmobj, sizeof(vmobj)); 477 return ((int)vmobj.size); 478 } 479 480 static void 481 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 482 { 483 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 484 perror("kvm_read"); 485 exit(1); 486 } 487 } 488