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_inactive_list" }, 76 { "_vnode_active_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, 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 *vp; 106 kvm_t *kd; 107 int ch; 108 const char *corefile = NULL; 109 const char *sysfile = NULL; 110 111 while ((ch = getopt(ac, av, "alnbM:N:p")) != -1) { 112 switch(ch) { 113 case 'b': 114 tracebufs = 1; 115 break; 116 case 'n': 117 withnames = 1; 118 break; 119 case 'l': 120 tracelocks = 1; 121 break; 122 case 'a': 123 tracebufs = 1; 124 tracelocks = 1; 125 withnames = 1; 126 fsprivate = 1; 127 break; 128 case 'p': 129 fsprivate = 1; 130 break; 131 case 'M': 132 corefile = optarg; 133 break; 134 case 'N': 135 sysfile = optarg; 136 break; 137 default: 138 fprintf(stderr, "%s [-pbnla] [-M core] [-N system]\n", av[0]); 139 exit(1); 140 } 141 } 142 143 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) { 144 perror("kvm_open"); 145 exit(1); 146 } 147 if (kvm_nlist(kd, Nl) != 0) { 148 perror("kvm_nlist"); 149 exit(1); 150 } 151 kkread(kd, Nl[0].n_value, &mp, sizeof(mp)); 152 while (mp) 153 mp = dumpmount(kd, mp); 154 printf("INACTIVELIST {\n"); 155 kkread(kd, Nl[1].n_value, &vp, sizeof(vp)); 156 while (vp) 157 vp = dumpvp(kd, vp, 0, NULL); 158 printf("}\n"); 159 printf("ACTIVELIST {\n"); 160 kkread(kd, Nl[2].n_value, &vp, sizeof(vp)); 161 while (vp) 162 vp = dumpvp(kd, vp, 0, NULL); 163 printf("}\n"); 164 return(0); 165 } 166 167 static struct mount * 168 dumpmount(kvm_t *kd, struct mount *mp) 169 { 170 struct mount mnt; 171 struct vnode *vp; 172 struct vfsconf vfc; 173 174 kkread(kd, (u_long)mp, &mnt, sizeof(mnt)); 175 printf("MOUNTPOINT %s on %s {\n", 176 mnt.mnt_stat.f_mntfromname, mnt.mnt_stat.f_mntonname); 177 printf(" lk_flags %08x count %08x holder = %p\n", 178 mnt.mnt_lock.lk_flags, mnt.mnt_lock.lk_count, 179 mnt.mnt_lock.lk_lockholder); 180 printf(" mnt_flag %08x mnt_kern_flag %08x\n", 181 mnt.mnt_flag, mnt.mnt_kern_flag); 182 printf(" mnt_nvnodelistsize %d\n", mnt.mnt_nvnodelistsize); 183 printf(" mnt_stat.f_fsid %08x %08x\n", mnt.mnt_stat.f_fsid.val[0], 184 mnt.mnt_stat.f_fsid.val[1]); 185 186 /* Dump fs private node data */ 187 kkread(kd, (u_long)mnt.mnt_vfc, &vfc, sizeof(vfc)); 188 vp = mnt.mnt_nvnodelist.tqh_first; 189 while (vp) 190 vp = dumpvp(kd, vp, 1, vfc.vfc_name); 191 192 printf("}\n"); 193 194 return(mnt.mnt_list.tqe_next); 195 } 196 197 static const char * 198 vtype(enum vtype type) 199 { 200 static char buf[32]; 201 202 switch(type) { 203 case VNON: 204 return("VNON"); 205 case VREG: 206 return("VREG"); 207 case VDIR: 208 return("VDIR"); 209 case VBLK: 210 return("VBLK"); 211 case VCHR: 212 return("VCHR"); 213 case VLNK: 214 return("VLNK"); 215 case VSOCK: 216 return("VSOCK"); 217 case VFIFO: 218 return("VFIFO"); 219 case VBAD: 220 return("VBAD"); 221 default: 222 break; 223 } 224 snprintf(buf, sizeof(buf), "%d", (int)type); 225 return(buf); 226 } 227 228 static struct vnode * 229 dumpvp(kvm_t *kd, struct vnode *vp, int whichlist, char *vfc_name) 230 { 231 struct vnode vn; 232 233 kkread(kd, (u_long)vp, &vn, sizeof(vn)); 234 235 printf(" vnode %p.%d refcnt %08x auxcnt %d type=%s flags %08x", 236 vp, vn.v_state, vn.v_refcnt, vn.v_auxrefs, vtype(vn.v_type), vn.v_flag); 237 238 if ((vn.v_flag & VOBJBUF) && vn.v_object) { 239 int npages = getobjpages(kd, vn.v_object); 240 int vnpsize = getobjvnpsize(kd, vn.v_object); 241 if (npages || vnpsize) 242 printf(" vmobjpgs=%d vnpsize=%d", npages, vnpsize); 243 } 244 245 if (vn.v_flag & VROOT) 246 printf(" VROOT"); 247 if (vn.v_flag & VTEXT) 248 printf(" VTEXT"); 249 if (vn.v_flag & VSYSTEM) 250 printf(" VSYSTEM"); 251 if (vn.v_flag & VISTTY) 252 printf(" VISTTY"); 253 #ifdef VXLOCK 254 if (vn.v_flag & VXLOCK) 255 printf(" VXLOCK"); 256 if (vn.v_flag & VXWANT) 257 printf(" VXWANT"); 258 #endif 259 #ifdef VRECLAIMED 260 if (vn.v_flag & VRECLAIMED) 261 printf(" VRECLAIMED"); 262 if (vn.v_flag & VINACTIVE) 263 printf(" VINACTIVE"); 264 #endif 265 if (vn.v_flag & VOBJBUF) 266 printf(" VOBJBUF"); 267 #ifdef VSWAPCACHE 268 if (vn.v_flag & VSWAPCACHE) 269 printf(" VSWAPCACHE"); 270 #endif 271 switch(vn.v_flag & (VAGE0 | VAGE1)) { 272 case 0: 273 printf(" VAGE0"); 274 break; 275 case VAGE0: 276 printf(" VAGE1"); 277 break; 278 case VAGE1: 279 printf(" VAGE2"); 280 break; 281 case VAGE0 | VAGE1: 282 printf(" VAGE3"); 283 break; 284 } 285 #ifdef VDOOMED 286 if (vn.v_flag & VDOOMED) 287 printf(" VDOOMED"); 288 #endif 289 #ifdef VINFREE 290 if (vn.v_flag & VINFREE) 291 printf(" VINFREE"); 292 #endif 293 if (vn.v_flag & VONWORKLST) 294 printf(" VONWORKLST"); 295 if (vn.v_flag & VOBJDIRTY) 296 printf(" VOBJDIRTY"); 297 if (vn.v_flag & VMAYHAVELOCKS) 298 printf(" VMAYHAVELOCKS"); 299 300 printf("\n"); 301 302 if (vn.v_lock.lk_count || vn.v_lock.lk_lockholder != LK_NOTHREAD) { 303 printf("\tlk_flags %08x count %08x holder = %p\n", 304 vn.v_lock.lk_flags, vn.v_lock.lk_count, 305 vn.v_lock.lk_lockholder); 306 } 307 308 if (withnames && TAILQ_FIRST(&vn.v_namecache)) { 309 struct namecache ncp; 310 int nlen; 311 char buf[1024]; 312 313 kkread(kd, (u_long)TAILQ_FIRST(&vn.v_namecache), &ncp, sizeof(ncp)); 314 if ((nlen = ncp.nc_nlen) >= sizeof(buf)) 315 nlen = sizeof(buf) - 1; 316 if (nlen < 0) 317 nlen = 0; 318 if (nlen) { 319 kkread(kd, (u_long)ncp.nc_name, buf, nlen); 320 buf[nlen] = 0; 321 printf("\tfilename %s\n", buf); 322 } 323 } 324 325 if (tracebufs) { 326 if (vn.v_rbclean_tree.rbh_root) { 327 printf("\tCLEAN BUFFERS\n"); 328 dumpbufs(kd, vn.v_rbclean_tree.rbh_root, "ROOT"); 329 } 330 if (vn.v_rbdirty_tree.rbh_root) { 331 printf("\tDIRTY BUFFERS\n"); 332 dumpbufs(kd, vn.v_rbdirty_tree.rbh_root, "ROOT"); 333 } 334 } 335 336 if (tracelocks) { 337 if (vn.v_tag == VT_UFS && vn.v_data) { 338 struct inode *ip = vn.v_data; 339 struct lockf lockf; 340 341 kkread(kd, (u_long)&ip->i_lockf, &lockf, sizeof(lockf)); 342 dumplocks(kd, &lockf); 343 } 344 } 345 346 if (fsprivate && vfc_name) { 347 /* 348 * Actually find whether the filesystem can dump 349 * detailed inode information out of the vnode 350 */ 351 const struct dump_private_data *dpd; 352 353 for (dpd = dumplist; dpd->dumpfn != NULL; dpd++) { 354 if ((strcmp(dpd->vfc_name, vfc_name) == 0) && 355 vn.v_data != NULL) 356 dpd->dumpfn(kd, vn.v_data); 357 } 358 } 359 360 if (whichlist) 361 return(vn.v_nmntvnodes.tqe_next); 362 else 363 return(vn.v_list.tqe_next); 364 } 365 366 static void 367 dumpbufs(kvm_t *kd, void *bufp, const char *id) 368 { 369 struct buf buf; 370 371 kkread(kd, (u_long)bufp, &buf, sizeof(buf)); 372 printf("\t %-8s %p loffset %012lx/%05x foffset %08lx", 373 id, bufp, 374 buf.b_bio1.bio_offset, 375 buf.b_bufsize, 376 buf.b_bio2.bio_offset); 377 printf(" q=%d count=%08x flags=%08x refs=%08x dep=%p", 378 buf.b_qindex, buf.b_lock.lk_count, 379 buf.b_flags, buf.b_refs, buf.b_dep.lh_first); 380 printf("\n"); 381 382 if (buf.b_rbnode.rbe_left) 383 dumpbufs(kd, buf.b_rbnode.rbe_left, "LEFT"); 384 if (buf.b_rbnode.rbe_right) 385 dumpbufs(kd, buf.b_rbnode.rbe_right, "RIGHT"); 386 } 387 388 static void 389 dumplocks(kvm_t *kd, struct lockf *lockf) 390 { 391 struct lockf_range item; 392 struct lockf_range *scan; 393 394 if ((scan = TAILQ_FIRST(&lockf->lf_range)) != NULL) { 395 printf("\tLOCKS\n"); 396 do { 397 kkread(kd, (u_long)scan, &item, sizeof(item)); 398 dumplockinfo(kd, &item); 399 } while ((scan = TAILQ_NEXT(&item, lf_link)) != NULL); 400 printf("\n"); 401 } 402 if ((scan = TAILQ_FIRST(&lockf->lf_blocked)) != NULL) { 403 printf("\tBLKED\n"); 404 do { 405 kkread(kd, (u_long)scan, &item, sizeof(item)); 406 dumplockinfo(kd, &item); 407 } while ((scan = TAILQ_NEXT(&item, lf_link)) != NULL); 408 printf("\n"); 409 } 410 411 } 412 413 static void 414 dumplockinfo(kvm_t *kd, struct lockf_range *item) 415 { 416 int ownerpid; 417 418 if (item->lf_owner && (item->lf_flags & F_POSIX)) { 419 kkread(kd, (u_long)&item->lf_owner->p_pid, 420 &ownerpid, sizeof(ownerpid)); 421 } else { 422 ownerpid = -1; 423 } 424 425 printf("\t ty=%d flgs=%04x %ld-%ld owner=%d\n", 426 item->lf_type, item->lf_flags, 427 item->lf_start, item->lf_end, 428 ownerpid 429 ); 430 } 431 432 static 433 int 434 getobjpages(kvm_t *kd, struct vm_object *obj) 435 { 436 struct vm_object vmobj; 437 438 kkread(kd, (u_long)obj, &vmobj, sizeof(vmobj)); 439 return(vmobj.resident_page_count); 440 } 441 442 static 443 int 444 getobjvnpsize(kvm_t *kd, struct vm_object *obj) 445 { 446 struct vm_object vmobj; 447 448 kkread(kd, (u_long)obj, &vmobj, sizeof(vmobj)); 449 return ((int)vmobj.size); 450 } 451 452 static void 453 kkread(kvm_t *kd, u_long addr, void *buf, size_t nbytes) 454 { 455 if (kvm_read(kd, addr, buf, nbytes) != nbytes) { 456 perror("kvm_read"); 457 exit(1); 458 } 459 } 460