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