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