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 ch; 111 const char *corefile = NULL; 112 const char *sysfile = NULL; 113 114 while ((ch = getopt(ac, av, "alnbM:N:p")) != -1) { 115 switch(ch) { 116 case 'b': 117 tracebufs = 1; 118 break; 119 case 'n': 120 withnames = 1; 121 break; 122 case 'l': 123 tracelocks = 1; 124 break; 125 case 'a': 126 tracebufs = 1; 127 tracelocks = 1; 128 withnames = 1; 129 fsprivate = 1; 130 break; 131 case 'p': 132 fsprivate = 1; 133 break; 134 case 'M': 135 corefile = optarg; 136 break; 137 case 'N': 138 sysfile = optarg; 139 break; 140 default: 141 fprintf(stderr, "%s [-pbnla] [-M core] [-N system]\n", av[0]); 142 exit(1); 143 } 144 } 145 146 if ((kd = kvm_open(sysfile, corefile, NULL, O_RDONLY, "kvm:")) == NULL) { 147 perror("kvm_open"); 148 exit(1); 149 } 150 if (kvm_nlist(kd, Nl) != 0) { 151 perror("kvm_nlist"); 152 exit(1); 153 } 154 kkread(kd, Nl[0].n_value, &mp, sizeof(mp)); 155 while (mp) 156 mp = dumpmount(kd, mp); 157 printf("INACTIVELIST {\n"); 158 kkread(kd, Nl[1].n_value, &vp, sizeof(vp)); 159 while (vp) 160 vp = dumpvp(kd, vp, 0, NULL); 161 printf("}\n"); 162 printf("ACTIVELIST {\n"); 163 kkread(kd, Nl[2].n_value, &vp, sizeof(vp)); 164 while (vp) 165 vp = dumpvp(kd, vp, 0, NULL); 166 printf("}\n"); 167 return(0); 168 } 169 170 static struct mount * 171 dumpmount(kvm_t *kd, struct mount *mp) 172 { 173 struct mount mnt; 174 struct vnode *vp; 175 struct vfsconf vfc; 176 177 kkread(kd, (u_long)mp, &mnt, sizeof(mnt)); 178 printf("MOUNTPOINT %s on %s {\n", 179 mnt.mnt_stat.f_mntfromname, mnt.mnt_stat.f_mntonname); 180 printf(" lk_flags %08x count %08x holder = %p\n", 181 mnt.mnt_lock.lk_flags, mnt.mnt_lock.lk_count, 182 mnt.mnt_lock.lk_lockholder); 183 printf(" mnt_flag %08x mnt_kern_flag %08x\n", 184 mnt.mnt_flag, mnt.mnt_kern_flag); 185 printf(" mnt_nvnodelistsize %d\n", mnt.mnt_nvnodelistsize); 186 printf(" mnt_stat.f_fsid %08x %08x\n", mnt.mnt_stat.f_fsid.val[0], 187 mnt.mnt_stat.f_fsid.val[1]); 188 189 /* Dump fs private node data */ 190 kkread(kd, (u_long)mnt.mnt_vfc, &vfc, sizeof(vfc)); 191 vp = mnt.mnt_nvnodelist.tqh_first; 192 while (vp) 193 vp = dumpvp(kd, vp, 1, vfc.vfc_name); 194 195 printf("}\n"); 196 197 return(mnt.mnt_list.tqe_next); 198 } 199 200 static const char * 201 vtype(enum vtype type) 202 { 203 static char buf[32]; 204 205 switch(type) { 206 case VNON: 207 return("VNON"); 208 case VREG: 209 return("VREG"); 210 case VDIR: 211 return("VDIR"); 212 case VBLK: 213 return("VBLK"); 214 case VCHR: 215 return("VCHR"); 216 case VLNK: 217 return("VLNK"); 218 case VSOCK: 219 return("VSOCK"); 220 case VFIFO: 221 return("VFIFO"); 222 case VBAD: 223 return("VBAD"); 224 default: 225 break; 226 } 227 snprintf(buf, sizeof(buf), "%d", (int)type); 228 return(buf); 229 } 230 231 static 232 void dump_dirfs_node(kvm_t *kd, void *arg) 233 { 234 dirfs_node_t dnp = (dirfs_node_t)arg; 235 struct dirfs_node dn; 236 char *name = NULL; 237 char strfd[16] = {0}; 238 239 /* No garbage allowed */ 240 bzero(&dn, sizeof(dn)); 241 242 /* 243 * Attempt to read in the address of the dnp and also 244 * its name if there is any. 245 */ 246 kkread(kd, (u_long)dnp, &dn, sizeof(dn)); 247 if (dn.dn_name != NULL) { 248 name = dn.dn_name; 249 dn.dn_name = calloc(1, MAXPATHLEN); 250 kkread(kd, (u_long)name, dn.dn_name, dn.dn_namelen); 251 } 252 printf("\tDIRFS\n"); 253 printf("\t\tdn_name=%s mode=%u flags=%08x refs=%d ", 254 (dn.dn_name ? dn.dn_name : "NULL"), dn.dn_mode, dn.dn_flags, 255 dn.dn_refcnt); 256 257 printf("state="); 258 if (dn.dn_state & DIRFS_ROOT) 259 printf("DIRFS_ROOT "); 260 261 if (dn.dn_fd == -1) 262 sprintf(strfd, "%s", "NOFD"); 263 else 264 sprintf(strfd, "%d", dn.dn_fd); 265 266 printf("\n\t\tuid=%u gid=%u objtype=%s nlinks=%d dn_fd=%s\n", dn.dn_uid, 267 dn.dn_gid, vtype(dn.dn_type), dn.dn_links, strfd); 268 printf("\t\tsize=%jd ctime=%ju atime=%ju mtime=%ju\n\n", 269 dn.dn_size, (intmax_t)dn.dn_ctime, 270 (uintmax_t)dn.dn_atime, (uintmax_t)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 %012lx/%05x foffset %08lx", 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 %ld-%ld 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