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 * $DragonFly: src/test/debug/vnodeinfo.c,v 1.13 2007/05/06 20:45:01 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/namecache.h> 52 #include <sys/mount.h> 53 #include <sys/vnode.h> 54 #include <sys/buf.h> 55 56 #include <vm/vm.h> 57 #include <vm/vm_page.h> 58 #include <vm/vm_kern.h> 59 #include <vm/vm_object.h> 60 #include <vm/swap_pager.h> 61 #include <vm/vnode_pager.h> 62 63 #include <vfs/ufs/quota.h> 64 #include <vfs/ufs/inode.h> 65 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <fcntl.h> 70 #include <kvm.h> 71 #include <nlist.h> 72 #include <getopt.h> 73 74 struct nlist Nl[] = { 75 { "_mountlist" }, 76 { "_vnode_free_list" }, 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); 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 int tracebufs = 0; 90 int tracelocks = 0; 91 int withnames = 0; 92 93 int 94 main(int ac, char **av) 95 { 96 struct mount *mp; 97 struct vnode *vp; 98 kvm_t *kd; 99 int i; 100 int ch; 101 const char *corefile = NULL; 102 const char *sysfile = NULL; 103 104 while ((ch = getopt(ac, av, "alnbM:N:")) != -1) { 105 switch(ch) { 106 case 'b': 107 tracebufs = 1; 108 break; 109 case 'n': 110 withnames = 1; 111 break; 112 case 'l': 113 tracelocks = 1; 114 break; 115 case 'a': 116 tracebufs = 1; 117 tracelocks = 1; 118 withnames = 1; 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 132 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) { 133 perror("kvm_open"); 134 exit(1); 135 } 136 if (kvm_nlist(kd, Nl) != 0) { 137 perror("kvm_nlist"); 138 exit(1); 139 } 140 kkread(kd, Nl[0].n_value, &mp, sizeof(mp)); 141 while (mp) 142 mp = dumpmount(kd, mp); 143 kkread(kd, Nl[1].n_value, &vp, sizeof(vp)); 144 printf("VNODEFREELIST {\n"); 145 while (vp) 146 vp = dumpvp(kd, vp, 0); 147 printf("}\n"); 148 return(0); 149 } 150 151 static struct mount * 152 dumpmount(kvm_t *kd, struct mount *mp) 153 { 154 struct mount mnt; 155 struct vnode *vp; 156 157 kkread(kd, (u_long)mp, &mnt, sizeof(mnt)); 158 printf("MOUNTPOINT %s on %s {\n", 159 mnt.mnt_stat.f_mntfromname, mnt.mnt_stat.f_mntonname); 160 printf(" lk_flags %08x share %d wait %d excl %d holder = %p\n", 161 mnt.mnt_lock.lk_flags, mnt.mnt_lock.lk_sharecount, 162 mnt.mnt_lock.lk_waitcount, mnt.mnt_lock.lk_exclusivecount, 163 mnt.mnt_lock.lk_lockholder); 164 printf(" mnt_flag %08x mnt_kern_flag %08x\n", 165 mnt.mnt_flag, mnt.mnt_kern_flag); 166 printf(" mnt_nvnodelistsize %d\n", mnt.mnt_nvnodelistsize); 167 printf(" mnt_stat.f_fsid %08x %08x\n", mnt.mnt_stat.f_fsid.val[0], 168 mnt.mnt_stat.f_fsid.val[1]); 169 vp = mnt.mnt_nvnodelist.tqh_first; 170 while (vp) 171 vp = dumpvp(kd, vp, 1); 172 173 printf("}\n"); 174 175 return(mnt.mnt_list.tqe_next); 176 } 177 178 static const char * 179 vtype(enum vtype type) 180 { 181 static char buf[32]; 182 183 switch(type) { 184 case VNON: 185 return("VNON"); 186 case VREG: 187 return("VREG"); 188 case VDIR: 189 return("VDIR"); 190 case VBLK: 191 return("VBLK"); 192 case VCHR: 193 return("VCHR"); 194 case VLNK: 195 return("VLNK"); 196 case VSOCK: 197 return("VSOCK"); 198 case VFIFO: 199 return("VFIFO"); 200 case VBAD: 201 return("VBAD"); 202 default: 203 break; 204 } 205 snprintf(buf, sizeof(buf), "%d", (int)type); 206 return(buf); 207 } 208 209 static struct vnode * 210 dumpvp(kvm_t *kd, struct vnode *vp, int whichlist) 211 { 212 struct vnode vn; 213 214 kkread(kd, (u_long)vp, &vn, sizeof(vn)); 215 216 printf(" vnode %p usecnt %08x holdcnt %d type=%s flags %08x", 217 vp, vn.v_sysref.refcnt, vn.v_auxrefs, vtype(vn.v_type), vn.v_flag); 218 219 if ((vn.v_flag & VOBJBUF) && vn.v_object) { 220 int npages = getobjpages(kd, vn.v_object); 221 int vnpsize = getobjvnpsize(kd, vn.v_object); 222 if (npages || vnpsize) 223 printf(" vmobjpgs=%d vnpsize=%d", npages, vnpsize); 224 } 225 226 if (vn.v_flag & VROOT) 227 printf(" VROOT"); 228 if (vn.v_flag & VTEXT) 229 printf(" VTEXT"); 230 if (vn.v_flag & VSYSTEM) 231 printf(" VSYSTEM"); 232 if (vn.v_flag & VISTTY) 233 printf(" VISTTY"); 234 #ifdef VXLOCK 235 if (vn.v_flag & VXLOCK) 236 printf(" VXLOCK"); 237 if (vn.v_flag & VXWANT) 238 printf(" VXWANT"); 239 #endif 240 #ifdef VRECLAIMED 241 if (vn.v_flag & VRECLAIMED) 242 printf(" VRECLAIMED"); 243 if (vn.v_flag & VINACTIVE) 244 printf(" VINACTIVE"); 245 #endif 246 if (vn.v_flag & VOBJBUF) 247 printf(" VOBJBUF"); 248 #ifdef VSWAPCACHE 249 if (vn.v_flag & VSWAPCACHE) 250 printf(" VSWAPCACHE"); 251 #endif 252 switch(vn.v_flag & (VAGE0 | VAGE1)) { 253 case 0: 254 printf(" VAGE0"); 255 break; 256 case VAGE0: 257 printf(" VAGE1"); 258 break; 259 case VAGE1: 260 printf(" VAGE2"); 261 break; 262 case VAGE0 | VAGE1: 263 printf(" VAGE3"); 264 break; 265 } 266 if (vn.v_flag & VOLOCK) 267 printf(" VOLOCK"); 268 if (vn.v_flag & VOWANT) 269 printf(" VOWANT"); 270 #ifdef VDOOMED 271 if (vn.v_flag & VDOOMED) 272 printf(" VDOOMED"); 273 #endif 274 if (vn.v_flag & VFREE) 275 printf(" VFREE"); 276 if (vn.v_flag & VCACHED) 277 printf(" VCACHED"); 278 #ifdef VINFREE 279 if (vn.v_flag & VINFREE) 280 printf(" VINFREE"); 281 #endif 282 if (vn.v_flag & VONWORKLST) 283 printf(" VONWORKLST"); 284 if (vn.v_flag & VOBJDIRTY) 285 printf(" VOBJDIRTY"); 286 if (vn.v_flag & VMAYHAVELOCKS) 287 printf(" VMAYHAVELOCKS"); 288 289 printf("\n"); 290 291 if (vn.v_lock.lk_sharecount || vn.v_lock.lk_waitcount || 292 vn.v_lock.lk_exclusivecount || vn.v_lock.lk_lockholder != LK_NOTHREAD) { 293 printf("\tlk_flags %08x share %d wait %d excl %d holder = %p\n", 294 vn.v_lock.lk_flags, vn.v_lock.lk_sharecount, 295 vn.v_lock.lk_waitcount, vn.v_lock.lk_exclusivecount, 296 vn.v_lock.lk_lockholder); 297 } 298 299 if (withnames && TAILQ_FIRST(&vn.v_namecache)) { 300 struct namecache ncp; 301 int nlen; 302 char buf[1024]; 303 304 kkread(kd, (u_long)TAILQ_FIRST(&vn.v_namecache), &ncp, sizeof(ncp)); 305 if ((nlen = ncp.nc_nlen) >= sizeof(buf)) 306 nlen = sizeof(buf) - 1; 307 if (nlen < 0) 308 nlen = 0; 309 if (nlen) { 310 kkread(kd, (u_long)ncp.nc_name, buf, nlen); 311 buf[nlen] = 0; 312 printf("\tfilename %s\n", buf); 313 } 314 } 315 316 if (tracebufs) { 317 if (vn.v_rbclean_tree.rbh_root) { 318 printf("\tCLEAN BUFFERS\n"); 319 dumpbufs(kd, vn.v_rbclean_tree.rbh_root, "ROOT"); 320 } 321 if (vn.v_rbdirty_tree.rbh_root) { 322 printf("\tDIRTY BUFFERS\n"); 323 dumpbufs(kd, vn.v_rbdirty_tree.rbh_root, "ROOT"); 324 } 325 } 326 327 if (tracelocks) { 328 if (vn.v_tag == VT_UFS && vn.v_data) { 329 struct inode *ip = vn.v_data; 330 struct lockf lockf; 331 332 kkread(kd, (u_long)&ip->i_lockf, &lockf, sizeof(lockf)); 333 dumplocks(kd, &lockf); 334 } 335 } 336 337 338 if (whichlist) 339 return(vn.v_nmntvnodes.tqe_next); 340 else 341 return(vn.v_freelist.tqe_next); 342 } 343 344 static void 345 dumpbufs(kvm_t *kd, void *bufp, const char *id) 346 { 347 struct buf buf; 348 349 kkread(kd, (u_long)bufp, &buf, sizeof(buf)); 350 printf("\t %-8s %p loffset %012llx/%05x foffset %08llx", 351 id, bufp, 352 buf.b_bio1.bio_offset, 353 buf.b_bufsize, 354 buf.b_bio2.bio_offset); 355 printf(" q=%d lck=%d/%d flags=%08x refs=%d dep=%p", 356 buf.b_qindex, buf.b_lock.lk_sharecount, 357 buf.b_lock.lk_exclusivecount, 358 buf.b_flags, buf.b_refs, buf.b_dep.lh_first); 359 printf("\n"); 360 361 if (buf.b_rbnode.rbe_left) 362 dumpbufs(kd, buf.b_rbnode.rbe_left, "LEFT"); 363 if (buf.b_rbnode.rbe_right) 364 dumpbufs(kd, buf.b_rbnode.rbe_right, "RIGHT"); 365 } 366 367 static void 368 dumplocks(kvm_t *kd, struct lockf *lockf) 369 { 370 struct lockf_range item; 371 struct lockf_range *scan; 372 373 if ((scan = TAILQ_FIRST(&lockf->lf_range)) != NULL) { 374 printf("\tLOCKS\n"); 375 do { 376 kkread(kd, (u_long)scan, &item, sizeof(item)); 377 dumplockinfo(kd, &item); 378 } while ((scan = TAILQ_NEXT(&item, lf_link)) != NULL); 379 printf("\n"); 380 } 381 if ((scan = TAILQ_FIRST(&lockf->lf_blocked)) != NULL) { 382 printf("\tBLKED\n"); 383 do { 384 kkread(kd, (u_long)scan, &item, sizeof(item)); 385 dumplockinfo(kd, &item); 386 } while ((scan = TAILQ_NEXT(&item, lf_link)) != NULL); 387 printf("\n"); 388 } 389 390 } 391 392 static void 393 dumplockinfo(kvm_t *kd, struct lockf_range *item) 394 { 395 int ownerpid; 396 397 if (item->lf_owner && (item->lf_flags & F_POSIX)) { 398 kkread(kd, (u_long)&item->lf_owner->p_pid, 399 &ownerpid, sizeof(ownerpid)); 400 } else { 401 ownerpid = -1; 402 } 403 404 printf("\t ty=%d flgs=%04x %lld-%lld owner=%d\n", 405 item->lf_type, item->lf_flags, 406 item->lf_start, item->lf_end, 407 ownerpid 408 ); 409 } 410 411 static 412 int 413 getobjpages(kvm_t *kd, struct vm_object *obj) 414 { 415 struct vm_object vmobj; 416 417 kkread(kd, (u_long)obj, &vmobj, sizeof(vmobj)); 418 return(vmobj.resident_page_count); 419 } 420 421 static 422 int 423 getobjvnpsize(kvm_t *kd, struct vm_object *obj) 424 { 425 struct vm_object vmobj; 426 427 kkread(kd, (u_long)obj, &vmobj, sizeof(vmobj)); 428 return ((int)vmobj.size); 429 } 430 431 static void 432 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 433 { 434 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 435 perror("kvm_read"); 436 exit(1); 437 } 438 } 439 440