Add a pmap(1) command that is effectively the same in function as the

Solaris command by the same name.  pmap(1) prints out the given
process's address space, each vm_map_entry at a time, with several
different output formats.

I wrote this tool as an autodidactic means of learning something about
UVM.
This commit is contained in:
atatat 2002-08-31 19:09:30 +00:00
parent 1b6aaa02a2
commit 0df8113ba6
4 changed files with 1304 additions and 0 deletions

9
usr.sbin/pmap/Makefile Normal file
View File

@ -0,0 +1,9 @@
# $NetBSD: Makefile,v 1.1 2002/08/31 19:09:30 atatat Exp $
PROG= pmap
BINGRP= kmem
BINMODE= 2555
LDADD= -lkvm
DPADD= ${LIBKVM}
.include <bsd.prog.mk>

14
usr.sbin/pmap/TODO Normal file
View File

@ -0,0 +1,14 @@
$NetBSD: TODO,v 1.1 2002/08/31 19:09:30 atatat Exp $
1) The Solaris pmap command also has -r, -x, -l, and -F. One or
more of those may not apply to the way vm is managed under NetBSD,
but the -x option (print resident/shared/private mapping details)
would certainly be interesting to adapt.
2 The rest of the Solaris p* commands (eg, pflags, pcred, pldd,
psig, pstack, pfiles and pwdx (or perhaps not, since they're pretty
much covered by our fstat), pwait, ptree, and ptime) would be nice
to have, too. Some of them ought to be doable now, but others
(like pwait) will probably be easier once we have kqueue on the
main trunk, and pstack will make much more sense after the nathanw_sa
branch is brought to the head.

352
usr.sbin/pmap/pmap.1 Normal file
View File

@ -0,0 +1,352 @@
.\" $NetBSD: pmap.1,v 1.1 2002/08/31 19:09:30 atatat Exp $
.\"
.\" Copyright (c) 2002 The NetBSD Foundation, Inc.
.\" All rights reserved.
.\"
.\" This code is derived from software contributed to The NetBSD Foundation
.\" by Andrew Brown.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the NetBSD
.\" Foundation, Inc. and its contributors.
.\" 4. Neither the name of The NetBSD Foundation nor the names of its
.\" contributors may be used to endorse or promote products derived
.\" from this software without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.Dd August 29, 2002
.Dt PMAP 1
.Os
.Sh NAME
.Nm pmap
.Nd display process memory map
.Sh SYNOPSIS
.Nm
.Op Fl adlmPsv
.Op Fl D Ar number
.Op Fl M Ar core
.Op Fl N Ar system
.Op Fl p Ar pid
.Op Ar pid ...
.Sh DESCRIPTION
The
.Nm
utility lists the virtual memory mappings underlying the given
process. The start address of each entry is always given, and,
depending on the options given, other information such as the end
address, the underlying file's device and inode numbers, and various
protection information will be displayed, along with the path to the
file, if such data is available.
.Pp
By default,
.Nm
displays information for it's parent process, so that when run from a
shell prompt, the shell's memory information is displayed. If other
pids are given as arguments on the command line, information for those
processes will be printed also. If the special pid of 0 is given,
then information for the kernel's memory map is printed.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl D Ar number
Enable various debug facilities. The
.Ar number
is a bitmask of the values:
.Pp
.Bl -tag -width flag -compact
.It Cm 1
dump the process's vmspace structure
.It Cm 2
dump the process's vm_map structure
.It Cm 4
dump the vm_map.header structure
.It Cm 8
dump each vm_map_entry in it's entirety
.It Cm 16
dump the namei cache as it is traversed
.El
.It Fl M Ar core
Extract values associated with the name list from the specified core
instead of the default
.Pa /dev/kmem .
.It Fl N Ar system
Extract the name list from the specified system instead of the default
.Pa /netbsd .
.It Fl P
Causes
.Nm
to print information about itself.
.It Fl a
Display "all" information from the process's memory map. This output
mode is an amalgam of the contents of the Solaris, Linux, and NetBSD
style output modes.
.It Fl d
Dumps the vm_map and vm_map_entry structures in a style similar to
that of
.Xr ddb 4 .
When combined with the
.Fl v
option, the device number, inode number, name, vnode addresses or
other identifying information from the vm_map_entries will be printed.
.It Fl l
Dumps information in a format like the contents of the maps
pseudo-file under the /proc filesystem which was, in turn, modeled
after the simiarly named entry in the Linux /proc filesystem.
.It Fl m
Dumps information the same format as the map pseudo-file of the /proc
filesystem. When the
.Fl v
option is also given, device number, inode number, and filename
or other identifying information is printed.
.It Fl p Ar pid
Tells
.Nm
to print information about the given process. If
.Fl p Ar pid
occurs last on the command line, the
.Fl p
is optional.
.It Fl s
The Solaris style output format, modelled after the Solaris command of
the same name. This is the default output style.
.It Fl v
Verbose output. When used with
.Fl m
or
.Fl d ,
more information is printed, possibly including device and inode
numbers, file path names, or other identifying information.
.El
.Pp
The
.Fl P
and
.Fl p
options override each other, so the last one to appear on the command
line takes effect. If you do wish to see information about
.Nm
and another process as the same time, simply omit the
.Fl p
and place the extra pid at the end of the command line.
.Pp
.Sh EXIT STATUS
.Nm
exits 0 on success, and \*[Gt]0 if an error occurred.
.Sh EXAMPLES
While the meaning most of the output is self evident, some pieces of
it may appear to be a little instrutable.
.Pp
Here a portion of the default output from pmap being run at an
.Xr sh 1
prompt shows the starting address of the map entry, the size of the
map entry, the current protection level of the map entry, and either
the name of the file backing the entry or some other descriptive text.
.Bd -literal -offset indent
$ pmap
08048000 420K read/exec /bin/sh
080B1000 8K read/write /bin/sh
080B3000 28K read/write [ anon ]
080BA000 16K read/write/exec [ heap ]
\&...
.Ed
.Pp
When the
.Xr ddb 4
output style is selected, the first thing printed is the contents of
the vm_map structure, followed by the individual map entries.
.Bd -literal -offset indent
$ pmap -d
MAP 0xcf7cac84: [0x0->0xbfbfe000]
#ent=8, sz=34041856, ref=1, version=20, flags=0x21
pmap=0xcf44cee0(resident=<unknown>)
- 0xcfa3a358: 0x8048000->0x80b1000: obj=0xcf45a8e8/0x0, amap=0x0/0
submap=F, cow=T, nc=T, prot(max)=5/7, inh=1, wc=0, adv=0
\&...
.Ed
.Pp
The value of the flags field (in hexadecimal) is taken from
the include file
.Aq Pa uvm/uvm_map.h :
.Bl -column VM_MAP_WIREFUTURE VM_MAP_WIREFUTURE -offset indent
.It Dv "VM_MAP_PAGEABLE" Ta No "0x01 entries are pageable"
.It Dv "VM_MAP_INTRSAFE" Ta No "0x02 interrupt safe map"
.It Dv "VM_MAP_WIREFUTURE" Ta No "0x04 future mappings are wired
.It Dv "VM_MAP_BUSY" Ta No "0x08 map is busy
.It Dv "VM_MAP_WANTLOCK" Ta No "0x10 want to write-lock
.El
.Pp
The
.Dq Tn submap ,
.Dq Tn cow ,
and
.Dq Tn nc
fields are true or false, and indicate whether the map is a submap,
whether it is marked for copy on write, and whether it needs a copy.
The
.Dq Tn prot
\&(or protection) field, along with
.Dq Tn max
\&(maximum protection allowed) are made up of the following flags from
.Aq Pa uvm/uvm_extern.h :
.\" this column width specifically chosen so that all the header file
.\" excerpts appear to line up cleanly
.Bl -column VM_MAP_WIREFUTURE VM_MAP_WIREFUTURE -offset indent
.It Dv "UVM_PROT_READ" Ta No "0x01 read allowed"
.It Dv "UVM_PROT_WRITE" Ta No "0x02 write allowed"
.It Dv "UVM_PROT_EXEC" Ta No "0x04 execute allowed"
.El
.Pp
The
.Dq Tn obj
and
.Dq Tn amap
fields are pointers to, and offsets into, the underlying uvm_object or
amap. The value for resident is always unknown because digging suck
information out of the kernel is beyond the scope of this application.
.Pp
The two output styles that mirror the contents of the /proc filesystem
appear as follows:
.Bd -literal -offset indent
$ pmap -m
0x8048000 0x80b1000 r-x rwx COW NC 1 0 0
0x80b1000 0x80b3000 rw- rwx COW NC 1 0 0
0x80b3000 0x80ba000 rw- rwx COW NNC 1 0 0
0x80ba000 0x80be000 rwx rwx COW NNC 1 0 0
\&...
$ pmap -l
08048000-080b1000 r-xp 00000000 00:00 70173 /bin/sh
080b1000-080b3000 rw-p 00068000 00:00 70173 /bin/sh
080b3000-080ba000 rw-p 00000000 00:00 0
080ba000-080be000 rwxp 00000000 00:00 0
\&...
.Ed
.Pp
Here the protection and maximum protection values are indicate with
.Dq Tn r ,
.Dq Tn w ,
and
.Dq Tn x
characters, indicating read permission, write permission, and execute
permission, respectively. The
.Dq Tn COW ,
.Dq Tn NC ,
and
.Dq Tn NNC
values that follow indicate, again, that the map is marked for copy on
write and either needs or does not need a copy. It is also possible
to see the value
.Dq Tn NCOW
here, which indicates that an entry will not be copied. The three
following numbers indicate the inheritance type of the map, the wired
count of the map, and any advice value assigned via
.Xr madvise 2 .
.Pp
In the second form, the permissions indicated are followed by a
.Dq Tn p
or
.Dq Tn s
character indicated whether the map entry is private or shared (copy
on write or not), and the numbers are the offset into the underlying
object, the device and numbers of the object if it is a file, and the
path to the file (if available).
.Pp
As noted above (see section DESCRIPTION), the
.Dq Tn all
output format is an amalgam of the previous output formats.
.Bd -literal -offset indent
$ pmap -a
Start End Size Offset rwxpc RWX I/W/A ...
08048000-080b0fff 420k 00000000 r-xp+ (rwx) 1/0/0 ...
\&...
.Ed
.Pp
In this format, the column labeled
.Dq Tn rwxpc
contains the permissions for the mapping along with the shared/private
flag, and a character indicating whether the mapping needs to be
copied on write
.Po
.Dq +
.Pc
or has already been copied
.Po
.Dq -
.Pc
and is followed by a column that indicates the maximum permissions for
the map entry. The column labeled
.Dq Tn I/W/A
indicates the inheritance, wired, and advice values for the map entry,
as previously described.
.Sh SEE ALSO
.Xr ls 1 ,
.Xr stat 1 ,
.Xr madvise 2 ,
.Xr mmap 2 ,
.Xr kvm 3 ,
.Xr ddb 4 ,
.Xr mount_procfs 8
.Sh HISTORY
The
.Nm
utility appeared in
.Nx 1.7 .
.Sh AUTHORS
The
.Nm
utility and documentation was written by Andrew Brown
.Aq atatat@netbsd.org .
.Sh BUGS
Since processes can change state while
.Nm
is running, some of the information printed may be inaccurate.
.Pp
The pathnames to files backing certain vnodes (such as the text and
data sections of programs and shared libraries) are extracted from the
kernel's namei cache which is considerably volatile. If a path is not
found there in its entirety, as much information as was available
will be printed. In most cases, simply running
.Xr ls 1
or
.Xr stat 1
with the expected path to the file will cause the information to be
reentered into the cache.
.Pp
The Solaris command by the same name has some interesting command line
flags that would be nice to emulate here. In particular, the
.Fl r
option that lists a process's reserved addressed, and the
.Fl x
option that prints resident/shared/private mapping details for each
entry.
.Pp
Some of the output modes can be or are wider than the standard 80
columns of a terminal. Some sort of formatting might be nice.
.Sh SECURITY CONSIDERATIONS
The Solaris command controls access to processes the user does not own
via the permissions its /proc filesystem. Since
.Nm
uses
.Xr kvm 3
to read the requested data directly from kernel memory, no such
limitation exists.

929
usr.sbin/pmap/pmap.c Normal file
View File

@ -0,0 +1,929 @@
/* $NetBSD: pmap.c,v 1.1 2002/08/31 19:09:30 atatat Exp $ */
/*
* Copyright (c) 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Andrew Brown.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: pmap.c,v 1.1 2002/08/31 19:09:30 atatat Exp $");
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/exec.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/uio.h>
#include <sys/namei.h>
#include <sys/sysctl.h>
#include <uvm/uvm.h>
#include <uvm/uvm_device.h>
#include <ufs/ufs/inode.h>
#undef doff_t
#undef IN_ACCESS
#include <isofs/cd9660/iso.h>
#include <isofs/cd9660/cd9660_node.h>
#include <kvm.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#ifndef __NetBSD_Version__
#error go away, you fool
#elif (__NetBSD_Version__ < 105000000)
#error only works with uvm
#endif
/*
* stolen (and munged) from #include <uvm/uvm_object.h>
*/
#define UVM_OBJ_IS_VNODE(uobj) ((uobj)->pgops == uvm_vnodeops)
#define UVM_OBJ_IS_AOBJ(uobj) ((uobj)->pgops == aobj_pager)
#define UVM_OBJ_IS_DEVICE(uobj) ((uobj)->pgops == uvm_deviceops)
#define UVM_OBJ_IS_UBCPAGER(uobj) ((uobj)->pgops == ubc_pager)
#define PRINT_VMSPACE 0x00000001
#define PRINT_VM_MAP 0x00000002
#define PRINT_VM_MAP_HEADER 0x00000004
#define PRINT_VM_MAP_ENTRY 0x00000008
#define DUMP_NAMEI_CACHE 0x00000010
struct cache_entry {
LIST_ENTRY(cache_entry) ce_next;
struct vnode *ce_vp, *ce_pvp;
u_long ce_cid, ce_pcid;
int ce_nlen;
char ce_name[256];
};
LIST_HEAD(cache_head, cache_entry) lcache;
LIST_HEAD(nchashhead, namecache) *nchashtbl = NULL;
void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
void *kernel_floor;
u_long nchash_addr, nchashtbl_addr, kernel_map_addr;
int debug, verbose;
int print_all, print_map, print_maps, print_solaris, print_ddb;
int rwx = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE, heapfound;
rlim_t maxssiz;
struct kbit {
/*
* size of data chunk
*/
size_t k_size;
/*
* something for printf() and something for kvm_read()
*/
union {
void *k_addr_p;
u_long k_addr_ul;
} k_addr;
/*
* where we actually put the "stuff"
*/
union {
char data[1];
struct vmspace vmspace;
struct vm_map vm_map;
struct vm_map_entry vm_map_entry;
struct vnode vnode;
struct uvm_object uvm_object;
struct mount mount;
struct namecache namecache;
struct inode inode;
struct iso_node iso_node;
struct uvm_device uvm_device;
} k_data;
};
/* the size of the object in the kernel */
#define S(x) ((x)->k_size)
/* the address of the object in kernel, two forms */
#define A(x) ((x)->k_addr.k_addr_ul)
#define P(x) ((x)->k_addr.k_addr_p)
/* the data from the kernel */
#define D(x,d) (&((x)->k_data.d))
/* suck the data from the kernel */
#define _KDEREF(kd, addr, dst, sz) do { \
ssize_t len; \
len = kvm_read((kd), (addr), (dst), (sz)); \
if (len != (sz)) \
errx(1, "%s == %ld vs. %lu @ %lx", \
kvm_geterr(kd), (long)len, (unsigned long)(sz), (addr)); \
} while (0/*CONSTCOND*/)
/* suck the data using the structure */
#define KDEREF(kd, item) _KDEREF((kd), A(item), D(item, data), S(item))
struct nlist nl[] = {
{ "_maxsmap" },
#define NL_MAXSSIZ 0
{ "_uvm_vnodeops" },
#define NL_UVM_VNODEOPS 1
{ "_uvm_deviceops" },
#define NL_UVM_DEVICEOPS 2
{ "_aobj_pager" },
#define NL_AOBJ_PAGER 3
{ "_ubc_pager" },
#define NL_UBC_PAGER 4
{ "_kernel_map" },
#define NL_KERNEL_MAP 5
{ "_nchashtbl" },
#define NL_NCHASHTBL 6
{ "_nchash" },
#define NL_NCHASH 7
{ "_kernel_text" },
#define NL_KENTER 8
{ NULL }
};
void load_symbols(kvm_t *);
void process_map(kvm_t *, pid_t, struct kinfo_proc2 *);
size_t dump_vm_map_entry(kvm_t *, struct kbit *, struct kbit *, int);
char *findname(kvm_t *, struct kbit *, struct kbit *, struct kbit *,
struct kbit *, struct kbit *);
int search_cache(kvm_t *, struct kbit *, char **, char *, size_t);
void load_name_cache(kvm_t *);
void cache_enter(struct namecache *);
int
main(int argc, char *argv[])
{
kvm_t *kd;
pid_t pid;
int many, ch, rc;
char errbuf[_POSIX2_LINE_MAX + 1];
/* u_long addr, next; */
struct kinfo_proc2 *kproc;
/* struct proc proc; */
char *kmem, *kernel;
pid = -1;
verbose = debug = 0;
print_all = print_map = print_maps = print_solaris = print_ddb = 0;
kmem = kernel = NULL;
while ((ch = getopt(argc, argv, "aD:dlmM:N:p:Prsvx")) != -1) {
switch (ch) {
case 'a':
print_all = 1;
break;
case 'd':
print_ddb = 1;
break;
case 'D':
debug = atoi(optarg);
break;
case 'l':
print_maps = 1;
break;
case 'm':
print_map = 1;
break;
case 'M':
kmem = optarg;
break;
case 'N':
kernel = optarg;
break;
case 'p':
pid = atoi(optarg);
break;
case 'P':
pid = getpid();
break;
case 's':
print_solaris = 1;
break;
case 'v':
verbose = 1;
break;
case 'r':
case 'x':
errx(1, "-%c option not implemented, sorry", optopt);
/*NOTREACHED*/
case '?':
default:
fprintf(stderr, "usage: %s [-adlmPsv] [-D number] "
"[-M core] [-N system] [-p pid] [pid ...]\n",
getprogname());
exit(1);
}
}
argc -= optind;
argv += optind;
/* more than one "process" to dump? */
many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0;
/* apply default */
if (print_all + print_map + print_maps + print_solaris +
print_ddb == 0)
print_solaris = 1;
/* start by opening libkvm */
kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
errbuf[_POSIX2_LINE_MAX] = '\0';
if (kd == NULL)
errx(1, "%s", errbuf);
/* get "bootstrap" addresses from kernel */
load_symbols(kd);
do {
if (pid == -1) {
if (argc == 0)
pid = getppid();
else {
pid = atoi(argv[0]);
argv++;
argc--;
}
}
/* find the process id */
if (pid == 0)
kproc = NULL;
else {
kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
sizeof(struct kinfo_proc2), &rc);
if (kproc == NULL || rc == 0) {
errno = ESRCH;
warn("%d", pid);
pid = -1;
continue;
}
}
/* dump it */
if (many) {
if (kproc)
printf("process %d:\n", kproc->p_pid);
else
printf("kernel:\n");
}
process_map(kd, pid, kproc);
pid = -1;
} while (argc > 0);
/* done. go away. */
rc = kvm_close(kd);
if (rc == -1)
err(1, "kvm_close");
return (0);
}
void
process_map(kvm_t *kd, pid_t pid, struct kinfo_proc2 *proc)
{
struct kbit kbit[4];
struct kbit *vmspace, *vm_map, *header, *vm_map_entry;
struct vm_map_entry *last;
size_t total;
u_long addr, next;
char *thing;
vmspace = &kbit[0];
vm_map = &kbit[1];
header = &kbit[2];
vm_map_entry = &kbit[3];
A(vmspace) = 0;
A(vm_map) = 0;
A(header) = 0;
A(vm_map_entry) = 0;
if (pid > 0) {
heapfound = 0;
A(vmspace) = (u_long)proc->p_vmspace;
S(vmspace) = sizeof(struct vmspace);
KDEREF(kd, vmspace);
thing = "proc->p_vmspace.vm_map";
} else {
heapfound = 1; /* but really, do kernels have a heap? */
A(vmspace) = 0;
S(vmspace) = 0;
thing = "kernel_map";
}
if (pid > 0 && (debug & PRINT_VMSPACE)) {
printf("proc->p_vmspace %p = {", P(vmspace));
printf(" vm_refcnt = %d,", D(vmspace, vmspace)->vm_refcnt);
printf(" vm_shm = %p,\n", D(vmspace, vmspace)->vm_shm);
printf(" vm_rssize = %d,", D(vmspace, vmspace)->vm_rssize);
printf(" vm_swrss = %d,", D(vmspace, vmspace)->vm_swrss);
printf(" vm_tsize = %d,", D(vmspace, vmspace)->vm_tsize);
printf(" vm_dsize = %d,\n", D(vmspace, vmspace)->vm_dsize);
printf(" vm_ssize = %d,", D(vmspace, vmspace)->vm_ssize);
printf(" vm_taddr = %p,", D(vmspace, vmspace)->vm_taddr);
printf(" vm_daddr = %p,\n", D(vmspace, vmspace)->vm_daddr);
printf(" vm_maxsaddr = %p,",
D(vmspace, vmspace)->vm_maxsaddr);
printf(" vm_minsaddr = %p }\n",
D(vmspace, vmspace)->vm_minsaddr);
}
S(vm_map) = sizeof(struct vm_map);
if (pid > 0) {
A(vm_map) = A(vmspace);
memcpy(D(vm_map, vm_map), &D(vmspace, vmspace)->vm_map,
S(vm_map));
} else {
A(vm_map) = kernel_map_addr;
KDEREF(kd, vm_map);
}
if (debug & PRINT_VM_MAP) {
printf("%s %p = {", thing, P(vm_map));
printf(" pmap = %p,\n", D(vm_map, vm_map)->pmap);
printf(" lock = <struct lock>,");
printf(" header = <struct vm_map_entry>,");
printf(" nentries = %d,\n", D(vm_map, vm_map)->nentries);
printf(" size = %lx,", D(vm_map, vm_map)->size);
printf(" ref_count = %d,", D(vm_map, vm_map)->ref_count);
printf(" ref_lock = <struct simplelock>,\n");
printf(" hint = %p,", D(vm_map, vm_map)->hint);
printf(" hint_lock = <struct simplelock>,\n");
printf(" first_free = %p,", D(vm_map, vm_map)->first_free);
printf(" flags = %x <%s%s%s%s%s%s >,\n", D(vm_map, vm_map)->flags,
D(vm_map, vm_map)->flags & VM_MAP_PAGEABLE ? " PAGEABLE" : "",
D(vm_map, vm_map)->flags & VM_MAP_INTRSAFE ? " INTRSAFE" : "",
D(vm_map, vm_map)->flags & VM_MAP_WIREFUTURE ? " WIREFUTURE" : "",
D(vm_map, vm_map)->flags & VM_MAP_BUSY ? " BUSY" : "",
D(vm_map, vm_map)->flags & VM_MAP_WANTLOCK ? " WANTLOCK" : "",
#if VM_MAP_TOPDOWN > 0
D(vm_map, vm_map)->flags & VM_MAP_TOPDOWN ? " TOPDOWN" :
#endif
"");
printf(" flags_lock = <struct simplelock>,");
printf(" timestamp = %u }\n", D(vm_map, vm_map)->timestamp);
}
if (print_ddb) {
printf("MAP %p: [0x%lx->0x%lx]\n", P(vm_map),
D(vm_map, vm_map)->min_offset, D(vm_map, vm_map)->max_offset);
printf("\t#ent=%d, sz=%ld, ref=%d, version=%d, flags=0x%x\n",
D(vm_map, vm_map)->nentries, D(vm_map, vm_map)->size,
D(vm_map, vm_map)->ref_count, D(vm_map, vm_map)->timestamp,
D(vm_map, vm_map)->flags);
printf("\tpmap=%p(resident=<unknown>)\n", D(vm_map, vm_map)->pmap);
}
A(header) = A(vm_map) + offsetof(struct vm_map, header);
S(header) = sizeof(struct vm_map_entry);
memcpy(D(header, vm_map_entry), &D(vm_map, vm_map)->header, S(header));
dump_vm_map_entry(kd, vmspace, header, 1);
/* headers */
#ifdef DISABLED_HEADERS
if (print_map)
printf("%-*s %-*s rwx RWX CPY NCP I W A\n",
(int)sizeof(long) * 2 + 2, "Start",
(int)sizeof(long) * 2 + 2, "End");
if (print_maps)
printf("%-*s %-*s rwxp %-*s Dev Inode File\n",
(int)sizeof(long) * 2 + 0, "Start",
(int)sizeof(long) * 2 + 0, "End",
(int)sizeof(long) * 2 + 0, "Offset");
if (print_solaris)
printf("%-*s %*s Protection File\n",
(int)sizeof(long) * 2 + 0, "Start",
(int)sizeof(int) * 2 - 1, "Size ");
#endif
if (print_all)
printf("%-*s %-*s %*s %-*s rwxpc RWX I/W/A Dev %*s - File\n",
(int)sizeof(long) * 2, "Start",
(int)sizeof(long) * 2, "End",
(int)sizeof(int) * 2, "Size ",
(int)sizeof(long) * 2, "Offset",
(int)sizeof(int) * 2, "Inode");
/* these are the "sub entries" */
total = 0;
next = (u_long)D(header, vm_map_entry)->next;
D(vm_map_entry, vm_map_entry)->next =
D(header, vm_map_entry)->next + 1;
last = P(header);
while (next != 0 && D(vm_map_entry, vm_map_entry)->next != last) {
addr = next;
A(vm_map_entry) = addr;
S(vm_map_entry) = sizeof(struct vm_map_entry);
KDEREF(kd, vm_map_entry);
total += dump_vm_map_entry(kd, vmspace, vm_map_entry, 0);
next = (u_long)D(vm_map_entry, vm_map_entry)->next;
}
if (print_solaris)
printf("%-*s %8luK\n",
(int)sizeof(void *) * 2 - 2, " total",
(unsigned long)total);
if (print_all)
printf("%-*s %9luk\n",
(int)sizeof(void *) * 4 - 1, " total",
(unsigned long)total);
}
void
load_symbols(kvm_t *kd)
{
int rc;
rc = kvm_nlist(kd, &nl[0]);
if (rc != 0)
errx(1, "%s == %d", kvm_geterr(kd), rc);
uvm_vnodeops = (void*)nl[NL_UVM_VNODEOPS].n_value;
uvm_deviceops = (void*)nl[NL_UVM_DEVICEOPS].n_value;
aobj_pager = (void*)nl[NL_AOBJ_PAGER].n_value;
ubc_pager = (void*)nl[NL_UBC_PAGER].n_value;
kernel_floor = (void*)nl[NL_KENTER].n_value;
nchash_addr = nl[NL_NCHASH].n_value;
_KDEREF(kd, nl[NL_MAXSSIZ].n_value, &maxssiz,
sizeof(maxssiz));
_KDEREF(kd, nl[NL_NCHASHTBL].n_value, &nchashtbl_addr,
sizeof(nchashtbl_addr));
_KDEREF(kd, nl[NL_KERNEL_MAP].n_value, &kernel_map_addr,
sizeof(kernel_map_addr));
}
size_t
dump_vm_map_entry(kvm_t *kd, struct kbit *vmspace,
struct kbit *vm_map_entry,
int ishead)
{
struct kbit kbit[3];
struct kbit *uvm_obj, *vp, *vfs;
struct vm_map_entry *vme;
size_t sz;
char *name;
dev_t dev;
ino_t inode;
uvm_obj = &kbit[0];
vp = &kbit[1];
vfs = &kbit[2];
A(uvm_obj) = 0;
A(vp) = 0;
A(vfs) = 0;
vme = D(vm_map_entry, vm_map_entry);
if ((ishead && (debug & PRINT_VM_MAP_HEADER)) ||
(!ishead && (debug & PRINT_VM_MAP_ENTRY))) {
printf("%s %p = {", ishead ? "vm_map.header" : "vm_map_entry",
P(vm_map_entry));
printf(" prev = %p,", vme->prev);
printf(" next = %p,\n", vme->next);
printf(" start = %lx,", vme->start);
printf(" end = %lx,", vme->end);
printf(" object.uvm_obj/sub_map = %p,\n", vme->object.uvm_obj);
printf(" offset = %lx,", (unsigned long)vme->offset);
printf(" etype = %x <%s%s%s%s >,", vme->etype,
vme->etype & UVM_ET_OBJ ? " OBJ" : "",
vme->etype & UVM_ET_SUBMAP ? " SUBMAP" : "",
vme->etype & UVM_ET_COPYONWRITE ? " COW" : "",
vme->etype & UVM_ET_NEEDSCOPY ? " NEEDSCOPY" : "");
printf(" protection = %x,\n", vme->protection);
printf(" max_protection = %x,", vme->max_protection);
printf(" inheritance = %d,", vme->inheritance);
printf(" wired_count = %d,\n", vme->wired_count);
printf(" aref = <struct vm_aref>,");
printf(" advice = %d,", vme->advice);
printf(" flags = %x <%s%s > }\n", vme->flags,
vme->flags & UVM_MAP_STATIC ? " STATIC" : "",
vme->flags & UVM_MAP_KMEM ? " KMEM" : "");
}
if (ishead)
return (0);
A(vp) = 0;
A(uvm_obj) = 0;
if (vme->object.uvm_obj != NULL) {
P(uvm_obj) = vme->object.uvm_obj;
S(uvm_obj) = sizeof(struct uvm_object);
KDEREF(kd, uvm_obj);
if (UVM_ET_ISOBJ(vme) &&
UVM_OBJ_IS_VNODE(D(uvm_obj, uvm_object))) {
P(vp) = P(uvm_obj);
S(vp) = sizeof(struct vnode);
KDEREF(kd, vp);
}
}
A(vfs) = NULL;
if (P(vp) != NULL && D(vp, vnode)->v_mount != NULL) {
P(vfs) = D(vp, vnode)->v_mount;
S(vfs) = sizeof(struct mount);
KDEREF(kd, vfs);
D(vp, vnode)->v_mount = D(vfs, mount);
}
/*
* dig out the device number and inode number from certain
* file system types.
*/
#define V_DATA_IS(vp, type, d, i) do { \
struct kbit data; \
P(&data) = D(vp, vnode)->v_data; \
S(&data) = sizeof(*D(&data, type)); \
KDEREF(kd, &data); \
dev = D(&data, type)->d; \
inode = D(&data, type)->i; \
} while (0/*CONSTCOND*/)
dev = 0;
inode = 0;
if (A(vp) &&
D(vp, vnode)->v_type == VREG &&
D(vp, vnode)->v_data != NULL) {
switch (D(vp, vnode)->v_tag) {
case VT_UFS:
case VT_LFS:
case VT_EXT2FS:
V_DATA_IS(vp, inode, i_dev, i_number);
break;
case VT_ISOFS:
V_DATA_IS(vp, iso_node, i_dev, i_number);
break;
case VT_NON:
case VT_NFS:
case VT_MFS:
case VT_MSDOSFS:
case VT_LOFS:
case VT_FDESC:
case VT_PORTAL:
case VT_NULL:
case VT_UMAP:
case VT_KERNFS:
case VT_PROCFS:
case VT_AFS:
case VT_UNION:
case VT_ADOSFS:
case VT_CODA:
case VT_FILECORE:
case VT_NTFS:
case VT_VFS:
case VT_OVERLAY:
case VT_SMBFS:
break;
}
}
name = findname(kd, vmspace, vm_map_entry, vp, vfs, uvm_obj);
if (print_map) {
printf("0x%lx 0x%lx %c%c%c %c%c%c %s %s %d %d %d",
vme->start, vme->end,
(vme->protection & VM_PROT_READ) ? 'r' : '-',
(vme->protection & VM_PROT_WRITE) ? 'w' : '-',
(vme->protection & VM_PROT_EXECUTE) ? 'x' : '-',
(vme->max_protection & VM_PROT_READ) ? 'r' : '-',
(vme->max_protection & VM_PROT_WRITE) ? 'w' : '-',
(vme->max_protection & VM_PROT_EXECUTE) ? 'x' : '-',
(vme->etype & UVM_ET_COPYONWRITE) ? "COW" : "NCOW",
(vme->etype & UVM_ET_NEEDSCOPY) ? "NC" : "NNC",
vme->inheritance, vme->wired_count,
vme->advice);
if (verbose) {
if (inode)
printf(" %d,%d %d",
major(dev), minor(dev), inode);
if (name[0])
printf(" %s", name);
}
printf("\n");
}
if (print_maps)
printf("%0*lx-%0*lx %c%c%c%c %0*lx %02x:%02x %d %s\n",
(int)sizeof(void *) * 2, vme->start,
(int)sizeof(void *) * 2, vme->end,
(vme->protection & VM_PROT_READ) ? 'r' : '-',
(vme->protection & VM_PROT_WRITE) ? 'w' : '-',
(vme->protection & VM_PROT_EXECUTE) ? 'x' : '-',
(vme->etype & UVM_ET_COPYONWRITE) ? 'p' : 's',
(int)sizeof(void *) * 2,
(unsigned long)vme->offset,
major(dev), minor(dev), inode, inode ? name : "");
if (print_ddb) {
printf(" - %p: 0x%lx->0x%lx: obj=%p/0x%lx, amap=%p/%d\n",
P(vm_map_entry), vme->start, vme->end,
vme->object.uvm_obj, (unsigned long)vme->offset,
vme->aref.ar_amap, vme->aref.ar_pageoff);
printf("\tsubmap=%c, cow=%c, nc=%c, prot(max)=%d/%d, inh=%d, "
"wc=%d, adv=%d\n",
(vme->etype & UVM_ET_SUBMAP) ? 'T' : 'F',
(vme->etype & UVM_ET_COPYONWRITE) ? 'T' : 'F',
(vme->etype & UVM_ET_NEEDSCOPY) ? 'T' : 'F',
vme->protection, vme->max_protection,
vme->inheritance, vme->wired_count, vme->advice);
if (inode && verbose)
printf("\t(dev=%d,%d ino=%d [%s] [%p])\n",
major(dev), minor(dev), inode,
inode ? name : "", P(vp));
else if (name[0] == ' ' && verbose)
printf("\t(%s)\n", &name[2]);
}
sz = 0;
if (print_solaris) {
char prot[30];
prot[0] = '\0';
prot[1] = '\0';
if (vme->protection & VM_PROT_READ)
strcat(prot, "/read");
if (vme->protection & VM_PROT_WRITE)
strcat(prot, "/write");
if (vme->protection & VM_PROT_EXECUTE)
strcat(prot, "/exec");
sz = (size_t)((vme->end - vme->start) / 1024);
printf("%0*lX %6luK %-15s %s\n",
(int)sizeof(void *) * 2,
(unsigned long)vme->start,
(unsigned long)sz,
&prot[1],
name);
}
if (print_all) {
sz = (size_t)((vme->end - vme->start) / 1024);
printf(A(vp) ?
"%0*lx-%0*lx %7luk %0*lx %c%c%c%c%c (%c%c%c) %d/%d/%d %02d:%02d %7d - %s [%p]\n" :
"%0*lx-%0*lx %7luk %0*lx %c%c%c%c%c (%c%c%c) %d/%d/%d %02d:%02d %7d - %s\n",
(int)sizeof(void *) * 2,
vme->start,
(int)sizeof(void *) * 2,
vme->end - (vme->start != vme->end ? 1 : 0),
(unsigned long)sz,
(int)sizeof(void *) * 2,
(unsigned long)vme->offset,
(vme->protection & VM_PROT_READ) ? 'r' : '-',
(vme->protection & VM_PROT_WRITE) ? 'w' : '-',
(vme->protection & VM_PROT_EXECUTE) ? 'x' : '-',
(vme->etype & UVM_ET_COPYONWRITE) ? 'p' : 's',
(vme->etype & UVM_ET_NEEDSCOPY) ? '+' : '-',
(vme->max_protection & VM_PROT_READ) ? 'r' : '-',
(vme->max_protection & VM_PROT_WRITE) ? 'w' : '-',
(vme->max_protection & VM_PROT_EXECUTE) ? 'x' : '-',
vme->inheritance,
vme->wired_count,
vme->advice,
major(dev), minor(dev), inode,
name, P(vp));
}
/* no access allowed, don't count space */
if ((vme->protection & rwx) == 0)
sz = 0;
return (sz);
}
char*
findname(kvm_t *kd, struct kbit *vmspace,
struct kbit *vm_map_entry, struct kbit *vp,
struct kbit *vfs, struct kbit *uvm_obj)
{
static char buf[1024], *name;
struct vm_map_entry *vme;
size_t l;
vme = D(vm_map_entry, vm_map_entry);
if (UVM_ET_ISOBJ(vme)) {
if (A(vfs)) {
l = (unsigned)strlen(D(vfs, mount)->mnt_stat.f_mntonname);
switch (search_cache(kd, vp, &name, buf, sizeof(buf))) {
case 0: /* found something */
name--;
*name = '/';
/*FALLTHROUGH*/
case 2: /* found nothing */
name -= 6;
memcpy(name, " -??- ", (size_t)6);
name -= l;
memcpy(name,
D(vfs, mount)->mnt_stat.f_mntonname, l);
break;
case 1: /* all is well */
name--;
*name = '/';
if (l != 1) {
name -= l;
memcpy(name,
D(vfs, mount)->mnt_stat.f_mntonname, l);
}
break;
}
}
else if (UVM_OBJ_IS_DEVICE(D(uvm_obj, uvm_object))) {
struct kbit kdev;
dev_t dev;
P(&kdev) = P(uvm_obj);
S(&kdev) = sizeof(struct uvm_device);
KDEREF(kd, &kdev);
dev = D(&kdev, uvm_device)->u_device;
name = devname(dev, S_IFCHR);
if (name != NULL)
snprintf(buf, sizeof(buf), "/dev/%s", name);
else
snprintf(buf, sizeof(buf), " [ device %d,%d ]",
major(dev), minor(dev));
name = buf;
}
else if (UVM_OBJ_IS_AOBJ(D(uvm_obj, uvm_object)))
name = " [ uvm_aobj ]";
else if (UVM_OBJ_IS_UBCPAGER(D(uvm_obj, uvm_object)))
name = " [ ubc_pager ]";
else if (UVM_OBJ_IS_VNODE(D(uvm_obj, uvm_object)))
name = " [ ?VNODE? ]";
else {
snprintf(buf, sizeof(buf), " [ ?? %p ?? ]",
D(uvm_obj, uvm_object)->pgops);
name = buf;
}
}
else if (D(vmspace, vmspace)->vm_maxsaddr <=
(caddr_t)vme->start &&
(D(vmspace, vmspace)->vm_maxsaddr + (size_t)maxssiz) >=
(caddr_t)vme->end)
name = " [ stack ]";
else if ((vme->protection & rwx) == rwx && !heapfound) {
/* XXX this could probably be done better */
heapfound = 1;
name = " [ heap ]";
}
else
name = " [ anon ]";
return (name);
}
int
search_cache(kvm_t *kd, struct kbit *vp, char **name, char *buf, size_t blen)
{
char *o, *e;
struct cache_entry *ce;
struct kbit svp;
u_long cid;
if (nchashtbl == NULL)
load_name_cache(kd);
P(&svp) = P(vp);
S(&svp) = sizeof(struct vnode);
cid = D(vp, vnode)->v_id;
e = &buf[blen - 1];
o = e;
do {
LIST_FOREACH(ce, &lcache, ce_next)
if (ce->ce_vp == P(&svp) && ce->ce_cid == cid)
break;
if (ce && ce->ce_vp == P(&svp) && ce->ce_cid == cid) {
if (o != e)
*(--o) = '/';
o -= ce->ce_nlen;
memcpy(o, ce->ce_name, (unsigned)ce->ce_nlen);
P(&svp) = ce->ce_pvp;
cid = ce->ce_pcid;
}
else
break;
} while (1/*CONSTCOND*/);
*e = '\0';
*name = o;
if (e == o)
return (2);
KDEREF(kd, &svp);
return (D(&svp, vnode)->v_flag & VROOT);
}
void
load_name_cache(kvm_t *kd)
{
struct namecache _ncp, *ncp, *oncp;
struct nchashhead _ncpp, *ncpp;
u_long nchash;
int i;
LIST_INIT(&lcache);
_KDEREF(kd, nchash_addr, &nchash, sizeof(nchash));
nchashtbl = malloc(sizeof(nchashtbl) * (int)nchash);
_KDEREF(kd, nchashtbl_addr, nchashtbl,
sizeof(nchashtbl) * (int)nchash);
ncpp = &_ncpp;
for (i = 0; i <= nchash; i++) {
ncpp = &nchashtbl[i];
oncp = NULL;
LIST_FOREACH(ncp, ncpp, nc_hash) {
if (ncp == oncp ||
(void*)ncp < kernel_floor ||
ncp == (void*)0xdeadbeef)
break;
oncp = ncp;
_KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp));
ncp = &_ncp;
if ((void*)ncp->nc_vp > kernel_floor &&
ncp->nc_nlen > 0) {
if (ncp->nc_nlen > 2 ||
ncp->nc_name[0] != '.' ||
(ncp->nc_name[1] != '.' &&
ncp->nc_nlen != 1))
cache_enter(ncp);
}
}
}
}
void
cache_enter(struct namecache *ncp)
{
struct cache_entry *ce;
if (debug & DUMP_NAMEI_CACHE)
printf("ncp->nc_vp %10p, ncp->nc_dvp %10p, ncp->nc_nlen "
"%3d [%.*s] (nc_dvpid=%lu, nc_vpid=%lu)\n",
ncp->nc_vp, ncp->nc_dvp,
ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name,
ncp->nc_dvpid, ncp->nc_vpid);
ce = malloc(sizeof(struct cache_entry));
ce->ce_vp = ncp->nc_vp;
ce->ce_pvp = ncp->nc_dvp;
ce->ce_cid = ncp->nc_vpid;
ce->ce_pcid = ncp->nc_dvpid;
ce->ce_nlen = ncp->nc_nlen;
strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name));
ce->ce_name[MIN(ce->ce_nlen, sizeof(ce->ce_name) - 1)] = '\0';
LIST_INSERT_HEAD(&lcache, ce, ce_next);
}