4 * cc -I/usr/src/sys vnodeinfo.c -o /usr/local/bin/vnodeinfo -lkvm
8 * Dump the mountlist and related vnodes.
11 * Copyright (c) 2004 The DragonFly Project. All rights reserved.
13 * This code is derived from software contributed to The DragonFly Project
14 * by Matthew Dillon <dillon@backplane.com>
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
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
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.
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
45 #define _KERNEL_STRUCTURES
46 #include <sys/param.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>
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>
62 #include <vfs/ufs/quota.h>
63 #include <vfs/ufs/inode.h>
64 #include <vfs/dirfs/dirfs.h>
76 { "_vnode_inactive_list" },
77 { "_vnode_active_list" },
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
);
91 static const struct dump_private_data
{
92 char vfc_name
[MFSNAMELEN
];
93 void (*dumpfn
)(kvm_t
*, void *);
95 { "dirfs", dump_dirfs_node
},
105 main(int ac
, char **av
)
111 const char *corefile
= NULL
;
112 const char *sysfile
= NULL
;
114 while ((ch
= getopt(ac
, av
, "alnbM:N:p")) != -1) {
141 fprintf(stderr
, "%s [-pbnla] [-M core] [-N system]\n", av
[0]);
146 if ((kd
= kvm_open(sysfile
, corefile
, NULL
, O_RDONLY
, "kvm:")) == NULL
) {
150 if (kvm_nlist(kd
, Nl
) != 0) {
154 kkread(kd
, Nl
[0].n_value
, &mp
, sizeof(mp
));
156 mp
= dumpmount(kd
, mp
);
157 printf("INACTIVELIST {\n");
158 kkread(kd
, Nl
[1].n_value
, &vp
, sizeof(vp
));
160 vp
= dumpvp(kd
, vp
, 0, NULL
);
162 printf("ACTIVELIST {\n");
163 kkread(kd
, Nl
[2].n_value
, &vp
, sizeof(vp
));
165 vp
= dumpvp(kd
, vp
, 0, NULL
);
170 static struct mount
*
171 dumpmount(kvm_t
*kd
, struct mount
*mp
)
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]);
189 /* Dump fs private node data */
190 kkread(kd
, (u_long
)mnt
.mnt_vfc
, &vfc
, sizeof(vfc
));
191 vp
= mnt
.mnt_nvnodelist
.tqh_first
;
193 vp
= dumpvp(kd
, vp
, 1, vfc
.vfc_name
);
197 return(mnt
.mnt_list
.tqe_next
);
201 vtype(enum vtype type
)
227 snprintf(buf
, sizeof(buf
), "%d", (int)type
);
232 void dump_dirfs_node(kvm_t
*kd
, void *arg
)
234 dirfs_node_t dnp
= (dirfs_node_t
)arg
;
235 struct dirfs_node dn
;
237 char strfd
[16] = {0};
239 /* No garbage allowed */
240 bzero(&dn
, sizeof(dn
));
243 * Attempt to read in the address of the dnp and also
244 * its name if there is any.
246 kkread(kd
, (u_long
)dnp
, &dn
, sizeof(dn
));
247 if (dn
.dn_name
!= NULL
) {
249 dn
.dn_name
= calloc(1, MAXPATHLEN
);
250 kkread(kd
, (u_long
)name
, dn
.dn_name
, dn
.dn_namelen
);
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
,
258 if (dn
.dn_state
& DIRFS_ROOT
)
259 printf("DIRFS_ROOT ");
262 sprintf(strfd
, "%s", "NOFD");
264 sprintf(strfd
, "%d", dn
.dn_fd
);
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
);
276 static struct vnode
*
277 dumpvp(kvm_t
*kd
, struct vnode
*vp
, int whichlist
, char *vfc_name
)
281 kkread(kd
, (u_long
)vp
, &vn
, sizeof(vn
));
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
);
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
);
293 if (vn
.v_flag
& VROOT
)
295 if (vn
.v_flag
& VTEXT
)
297 if (vn
.v_flag
& VSYSTEM
)
299 if (vn
.v_flag
& VISTTY
)
302 if (vn
.v_flag
& VXLOCK
)
304 if (vn
.v_flag
& VXWANT
)
308 if (vn
.v_flag
& VRECLAIMED
)
309 printf(" VRECLAIMED");
310 if (vn
.v_flag
& VINACTIVE
)
311 printf(" VINACTIVE");
313 if (vn
.v_flag
& VOBJBUF
)
316 if (vn
.v_flag
& VSWAPCACHE
)
317 printf(" VSWAPCACHE");
319 switch(vn
.v_flag
& (VAGE0
| VAGE1
)) {
334 if (vn
.v_flag
& VDOOMED
)
338 if (vn
.v_flag
& VINFREE
)
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");
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
);
356 if (withnames
&& TAILQ_FIRST(&vn
.v_namecache
)) {
357 struct namecache ncp
;
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;
367 kkread(kd
, (u_long
)ncp
.nc_name
, buf
, nlen
);
369 printf("\tfilename %s\n", buf
);
374 if (vn
.v_rbclean_tree
.rbh_root
) {
375 printf("\tCLEAN BUFFERS\n");
376 dumpbufs(kd
, vn
.v_rbclean_tree
.rbh_root
, "ROOT");
378 if (vn
.v_rbdirty_tree
.rbh_root
) {
379 printf("\tDIRTY BUFFERS\n");
380 dumpbufs(kd
, vn
.v_rbdirty_tree
.rbh_root
, "ROOT");
385 if (vn
.v_tag
== VT_UFS
&& vn
.v_data
) {
386 struct inode
*ip
= vn
.v_data
;
389 kkread(kd
, (u_long
)&ip
->i_lockf
, &lockf
, sizeof(lockf
));
390 dumplocks(kd
, &lockf
);
394 if (fsprivate
&& vfc_name
) {
396 * Actually find whether the filesystem can dump
397 * detailed inode information out of the vnode
399 const struct dump_private_data
*dpd
;
401 for (dpd
= dumplist
; dpd
->dumpfn
!= NULL
; dpd
++) {
402 if ((strcmp(dpd
->vfc_name
, vfc_name
) == 0) &&
404 dpd
->dumpfn(kd
, vn
.v_data
);
409 return(vn
.v_nmntvnodes
.tqe_next
);
411 return(vn
.v_list
.tqe_next
);
415 dumpbufs(kvm_t
*kd
, void *bufp
, const char *id
)
419 kkread(kd
, (u_long
)bufp
, &buf
, sizeof(buf
));
420 printf("\t %-8s %p loffset %012lx/%05x foffset %08lx",
422 buf
.b_bio1
.bio_offset
,
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
);
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");
437 dumplocks(kvm_t
*kd
, struct lockf
*lockf
)
439 struct lockf_range item
;
440 struct lockf_range
*scan
;
442 if ((scan
= TAILQ_FIRST(&lockf
->lf_range
)) != NULL
) {
445 kkread(kd
, (u_long
)scan
, &item
, sizeof(item
));
446 dumplockinfo(kd
, &item
);
447 } while ((scan
= TAILQ_NEXT(&item
, lf_link
)) != NULL
);
450 if ((scan
= TAILQ_FIRST(&lockf
->lf_blocked
)) != NULL
) {
453 kkread(kd
, (u_long
)scan
, &item
, sizeof(item
));
454 dumplockinfo(kd
, &item
);
455 } while ((scan
= TAILQ_NEXT(&item
, lf_link
)) != NULL
);
462 dumplockinfo(kvm_t
*kd
, struct lockf_range
*item
)
466 if (item
->lf_owner
&& (item
->lf_flags
& F_POSIX
)) {
467 kkread(kd
, (u_long
)&item
->lf_owner
->p_pid
,
468 &ownerpid
, sizeof(ownerpid
));
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
,
482 getobjpages(kvm_t
*kd
, struct vm_object
*obj
)
484 struct vm_object vmobj
;
486 kkread(kd
, (u_long
)obj
, &vmobj
, sizeof(vmobj
));
487 return(vmobj
.resident_page_count
);
492 getobjvnpsize(kvm_t
*kd
, struct vm_object
*obj
)
494 struct vm_object vmobj
;
496 kkread(kd
, (u_long
)obj
, &vmobj
, sizeof(vmobj
));
497 return ((int)vmobj
.size
);
501 kkread(kvm_t
*kd
, u_long addr
, void *buf
, size_t nbytes
)
503 if (kvm_read(kd
, addr
, buf
, nbytes
) != nbytes
) {