* Copyright (c) 1989 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 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 University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)kvm.c 5.18 (Berkeley) 5/7/91";
#endif /* LIBC_SCCS and not lint */
#include <machine/vmparam.h>
#define btop(x) (((unsigned)(x)) >> PGSHIFT) /* XXX */
#define ptob(x) ((caddr_t)((x) << PGSHIFT)) /* XXX */
#include <vm/vm.h> /* ??? kinfo_proc currently includes this*/
#include <vm/swap_pager.h>
#include <sys/kinfo_proc.h>
static const char *unixf
, *memf
, *kmemf
, *swapf
;
static int unixx
, mem
, kmem
, swap
;
static int kvmfilesopen
= 0;
static struct kinfo_proc
*kvmprocbase
, *kvmprocptr
;
char upages
[UPAGES
][NBPG
];
long offset
; /* offset in swap device */
long size
; /* remaining size of block in swap device */
static int argaddr0
; /* XXX */
#define basename(cp) ((tmp=rindex((cp), '/')) ? tmp+1 : (cp))
#define iskva(v) ((u_long)(v) & KERNBASE)
static struct nlist nl
[] = {
#define X_VM_PAGE_BUCKETS 5
{ "_vm_page_hash_mask" },
#define X_VM_PAGE_HASH_MASK 6
#define X_KERNEL_OBJECT 9
#define X_KERNEL_BTEXT 10
* everything here and down, only if a dead kernel
#define X_DEADKERNEL X_SYSMAP
#define X_IdlePTD (X_LAST+1)
static void klseek(), seterr(), setsyserr(), vstodb();
static int getkvars(), kvm_doprocs(), kvm_init();
* returns 0 if files were opened now,
* 1 if files were already opened,
* -1 if files could not be opened.
kvm_openfiles(uf
, mf
, sf
)
const char *uf
, *mf
, *sf
;
unixx
= mem
= kmem
= swap
= -1;
unixf
= (uf
== NULL
) ? _PATH_UNIX
: uf
;
memf
= (mf
== NULL
) ? _PATH_MEM
: mf
;
if ((unixx
= open(unixf
, O_RDONLY
, 0)) == -1) {
setsyserr("can't open %s", unixf
);
if ((mem
= open(memf
, O_RDONLY
, 0)) == -1) {
setsyserr("can't open %s", memf
);
if ((kmem
= open(kmemf
, O_RDONLY
, 0)) == -1) {
setsyserr("can't open %s", kmemf
);
swapf
= (sf
== NULL
) ? _PATH_DRUM
: sf
;
* live kernel - avoid looking up nlist entries
nl
[X_DEADKERNEL
].n_name
= "";
if (swapf
!= NULL
&& ((swap
= open(swapf
, O_RDONLY
, 0)) == -1)) {
seterr("can't open %s", swapf
);
if (kvminit
== 0 && kvm_init(NULL
, NULL
, NULL
, 0) == -1) /*XXX*/
if (kvmfilesopen
== 0 && kvm_openfiles(NULL
, NULL
, NULL
) == -1)
/* otherwise kmem is a copy of mem, and will be closed below */
char dbversion
[_POSIX2_LINE_MAX
];
char kversion
[_POSIX2_LINE_MAX
];
if (kvmfilesopen
== 0 && kvm_openfiles(NULL
, NULL
, NULL
) == -1)
goto win
; /* off to the races */
sprintf(dbname
, "%s/kvm_%s", _PATH_VARRUN
, basename(unixf
));
if ((db
= dbm_open(dbname
, O_RDONLY
, 0)) == NULL
)
* read version out of database
bcopy("VERSION", symbuf
, sizeof ("VERSION")-1);
key
.dsize
= (sizeof ("VERSION") - 1);
data
= dbm_fetch(db
, key
);
bcopy(data
.dptr
, dbversion
, data
.dsize
);
dbversionlen
= data
.dsize
;
* read version string from kernel memory
bcopy("_version", symbuf
, sizeof ("_version")-1);
key
.dsize
= (sizeof ("_version")-1);
data
= dbm_fetch(db
, key
);
if (data
.dsize
!= sizeof (struct nlist
))
bcopy(data
.dptr
, &nbuf
, sizeof (struct nlist
));
lseek(kmem
, nbuf
.n_value
, 0);
if (read(kmem
, kversion
, dbversionlen
) != dbversionlen
)
* if they match, we win - otherwise do it the hard way
if (bcmp(dbversion
, kversion
, dbversionlen
) != 0)
* getem from the database.
for (n
= nl
; n
->n_name
&& n
->n_name
[0]; n
++, num
++) {
* clear out fields from users buffer
if ((len
= strlen(n
->n_name
)) > MAXSYMSIZE
) {
seterr("symbol too large");
(void)strcpy(symbuf
, n
->n_name
);
data
= dbm_fetch(db
, key
);
if (data
.dptr
== NULL
|| data
.dsize
!= sizeof (struct nlist
))
bcopy(data
.dptr
, &nbuf
, sizeof (struct nlist
));
n
->n_value
= nbuf
.n_value
;
n
->n_other
= nbuf
.n_other
;
seterr("nlist (hard way) failed");
static int ocopysize
= -1;
if (kvminit
== 0 && kvm_init(NULL
, NULL
, NULL
, 0) == -1)
if ((ret
= getkerninfo(what
, NULL
, NULL
, arg
)) == -1) {
setsyserr("can't get estimate for kerninfo");
if (copysize
> ocopysize
&&
(kvmprocbase
= (struct kinfo_proc
*)
realloc(kvmprocbase
, copysize
)) == NULL
) {
if ((ret
= getkerninfo(what
, kvmprocbase
, ©size
,
setsyserr("can't get proc list");
if (copysize
% sizeof (struct kinfo_proc
)) {
seterr("proc size mismatch (got %d total, kinfo_proc: %d)",
copysize
, sizeof (struct kinfo_proc
));
kvmnprocs
= copysize
/ sizeof (struct kinfo_proc
);
if (kvm_read((void *) nl
[X_NPROC
].n_value
, &nproc
,
sizeof (int)) != sizeof (int)) {
seterr("can't read nproc");
if ((kvmprocbase
= (struct kinfo_proc
*)
malloc(nproc
* sizeof (struct kinfo_proc
))) == NULL
) {
seterr("out of memory (addr: %x nproc = %d)",
nl
[X_NPROC
].n_value
, nproc
);
kvmnprocs
= kvm_doprocs(what
, arg
, kvmprocbase
);
realloc(kvmprocbase
, kvmnprocs
* sizeof (struct kinfo_proc
));
kvmprocptr
= kvmprocbase
;
* XXX - should NOT give up so easily - especially since the kernel
* may be corrupt (it died). Should gather as much information as possible.
* Follows proc ptrs instead of reading table since table may go
kvm_doprocs(what
, arg
, buff
)
register char *bp
= buff
;
if (kvm_read((void *) nl
[X_ALLPROC
].n_value
, &p
,
sizeof (struct proc
*)) != sizeof (struct proc
*)) {
seterr("can't read allproc");
for (; p
; p
= proc
.p_nxt
) {
if (kvm_read(p
, &proc
, sizeof (struct proc
)) !=
seterr("can't read proc at %x", p
);
if (kvm_read(proc
.p_cred
, &eproc
.e_pcred
,
sizeof (struct pcred
)) == sizeof (struct pcred
))
(void) kvm_read(eproc
.e_pcred
.pc_ucred
, &eproc
.e_ucred
,
if (proc
.p_pid
!= (pid_t
)arg
)
if (eproc
.e_ucred
.cr_uid
!= (uid_t
)arg
)
if (eproc
.e_pcred
.p_ruid
!= (uid_t
)arg
)
if (kvm_read(proc
.p_pgrp
, &pgrp
, sizeof (struct pgrp
)) !=
seterr("can't read pgrp at %x", proc
.p_pgrp
);
eproc
.e_sess
= pgrp
.pg_session
;
eproc
.e_pgid
= pgrp
.pg_id
;
eproc
.e_jobc
= pgrp
.pg_jobc
;
if (kvm_read(pgrp
.pg_session
, &sess
, sizeof (struct session
))
!= sizeof (struct session
)) {
seterr("can't read session at %x", pgrp
.pg_session
);
if ((proc
.p_flag
&SCTTY
) && sess
.s_ttyp
!= NULL
) {
if (kvm_read(sess
.s_ttyp
, &tty
, sizeof (struct tty
))
!= sizeof (struct tty
)) {
seterr("can't read tty at %x", sess
.s_ttyp
);
eproc
.e_tdev
= tty
.t_dev
;
eproc
.e_tsess
= tty
.t_session
;
if (tty
.t_pgrp
!= NULL
) {
if (kvm_read(tty
.t_pgrp
, &pgrp
, sizeof (struct
pgrp
)) != sizeof (struct pgrp
)) {
seterr("can't read tpgrp at &x",
eproc
.e_tpgid
= pgrp
.pg_id
;
kvm_read(proc
.p_wmesg
, eproc
.e_wmesg
, WMESGLEN
);
(void) kvm_read(proc
.p_vmspace
, &eproc
.e_vm
,
sizeof (struct vmspace
));
eproc
.e_xsize
= eproc
.e_xrssize
=
eproc
.e_xccount
= eproc
.e_xswrss
= 0;
if (eproc
.e_pgid
!= (pid_t
)arg
)
if ((proc
.p_flag
&SCTTY
) == 0 ||
eproc
.e_tdev
!= (dev_t
)arg
)
bcopy(&proc
, bp
, sizeof (struct proc
));
bp
+= sizeof (struct proc
);
bcopy(&eproc
, bp
, sizeof (struct eproc
));
bp
+= sizeof (struct eproc
);
if (kvm_read((void *) nl
[X_ZOMBPROC
].n_value
, &p
,
sizeof (struct proc
*)) != sizeof (struct proc
*)) {
seterr("can't read zombproc");
if (!kvmprocbase
&& kvm_getprocs(0, 0) == -1)
if (kvmprocptr
>= (kvmprocbase
+ kvmnprocs
)) {
seterr("end of proc list");
return((struct proc
*)(kvmprocptr
++));
return ((struct eproc
*)(((char *)p
) + sizeof (struct proc
)));
kvmprocptr
= kvmprocbase
;
proc_getmem(const struct proc
*p
, void *buffer
, vm_offset_t size
, vm_offset_t offset
) {
sprintf(fn
,"/proc/%d",p
->p_pid
);
if (lseek(fd
, offset
, 0) == -1) {
if (read(fd
, buffer
, size
) <= 0) {
register struct kinfo_proc
*kp
= (struct kinfo_proc
*)p
;
if (kvminit
== 0 && kvm_init(NULL
, NULL
, NULL
, 0) == -1)
if (p
->p_stat
== SZOMB
) {
seterr("zombie process");
if (proc_getmem(p
, user
.upages
, sizeof user
.upages
, USRSTACK
)) {
kp
->kp_eproc
.e_vm
.vm_rssize
=
kp
->kp_eproc
.e_vm
.vm_pmap
.pm_stats
.resident_count
; /* XXX */
* Read u-area one page at a time for the benefit of post-mortems
for (i
= 0; i
< UPAGES
; i
++) {
klseek(kmem
, (long)up
, 0);
if (read(kmem
, user
.upages
[i
], CLBYTES
) != CLBYTES
) {
seterr("cant read page %x of u of pid %d from %s",
pcbpf
= (int) btop(p
->p_addr
); /* what should this be really? */
* Conjure up a physical address for the arguments.
kp
->kp_eproc
.e_vm
.vm_rssize
=
kp
->kp_eproc
.e_vm
.vm_pmap
.pm_stats
.resident_count
; /* XXX */
vaddr
= (u_int
)kp
->kp_eproc
.e_vm
.vm_minsaddr
;
arg_size
= USRSTACK
- vaddr
;
if (kp
->kp_eproc
.e_vm
.vm_pmap
.pm_pdir
) {
(long)(&kp
->kp_eproc
.e_vm
.vm_pmap
.pm_pdir
[pdei(vaddr
)]), 0);
if (read(kmem
, (char *)&pde
, sizeof pde
) == sizeof pde
if (lseek(mem
, (long)ctob(pde
.pd_pfnum
) +
(ptei(vaddr
) * sizeof pte
), 0) == -1)
seterr("kvm_getu: lseek");
if (read(mem
, (char *)&pte
, sizeof pte
) == sizeof pte
) {
argaddr1
= (pte
.pg_pfnum
<< PGSHIFT
) |
((u_long
)vaddr
& (NBPG
-1));
seterr("kvm_getu: read");
int arg_size
, arg_offset
;
static char cmdbuf
[ARG_MAX
];
vaddr
= (u_int
)((struct kinfo_proc
*)p
)->kp_eproc
.e_vm
.vm_minsaddr
;
arg_size
= USRSTACK
- vaddr
;
if (arg_size
>= 3*ARG_MAX
)
sprintf(procfile
, "/proc/%d", p
->p_pid
);
if ((mmfd
= open(procfile
, O_RDONLY
, 0)) == -1) {
printf("failed to open %s\n",procfile
);
if ((argc
= mmap(0, arg_size
, PROT_READ
, MAP_FILE
, mmfd
, vaddr
))
printf("failed to mmap %s error=%s\n", procfile
, strerror(errno
));
if (!proc_getmem(p
, argc
, arg_size
, vaddr
)) {
if ((p
->p_flag
& SLOAD
) == 0 || argaddr1
== 0) {
lseek(mem
, (long)argaddr1
, 0);
if (read(mem
, argc
, arg_size
) != arg_size
)
arg_offset
= argv
[0] - vaddr
;
if (arg_offset
>= 3*ARG_MAX
)
for (cp
= &argc
[arg_offset
]; cp
< &argc
[arg_size
]; cp
++, cmdbufp
++) {
if (c
== 0) { /* convert null between arguments to space */
if (*(cp
+1) == 0) break; /* if null argument follows then no more args */
else if (c
< ' ' || c
> 0176) {
if (++nbad
>= 5*(0+1)) { /* eflg -> 0 XXX */ /* limit number of bad chars to 5 */
else if (0 == 0 && c
== '=') { /* eflg -> 0 XXX */
while (*--cmdbufp
!= ' ')
while (*--cmdbufp
== ' ')
if (cmdbuf
[0] == '-' || cmdbuf
[0] == '?' || cmdbuf
[0] <= ' ') {
(void) strcat(cmdbuf
, " (");
(void) strncat(cmdbuf
, p
->p_comm
, sizeof(p
->p_comm
));
(void) strcat(cmdbuf
, ")");
if (argc
&& argc
!= (char *)-1)
/* We must do the sys map first because klseek uses it */
PTD
= (struct pde
*) malloc(NBPG
);
seterr("out of space for PTD");
addr
= (long) nl
[X_IdlePTD
].n_value
;
(void) lseek(kmem
, addr
, 0);
read(kmem
, (char *)&addr
, sizeof(addr
));
(void) lseek(kmem
, (long)addr
, 0);
if (read(kmem
, (char *) PTD
, NBPG
) != NBPG
) {
seterr("can't read PTD");
if (kvm_read((void *) nl
[X_NSWAP
].n_value
, &nswap
, sizeof (long)) !=
seterr("can't read nswap");
if (kvm_read((void *) nl
[X_DMMIN
].n_value
, &dmmin
, sizeof (long)) !=
seterr("can't read dmmin");
if (kvm_read((void *) nl
[X_DMMAX
].n_value
, &dmmax
, sizeof (long)) !=
seterr("can't read dmmax");
if (kvmfilesopen
== 0 && kvm_openfiles(NULL
, NULL
, NULL
) == -1)
klseek(kmem
, (off_t
) loc
, 0);
if (read(kmem
, buf
, len
) != len
) {
seterr("error reading kmem at %x", loc
);
lseek(mem
, (off_t
) loc
, 0);
if (read(mem
, buf
, len
) != len
) {
seterr("error reading mem at %x", loc
);
if ((loc
= Vtophys(loc
)) == -1)
(void) lseek(fd
, (off_t
)loc
, off
);
off_t newloc
= (off_t
) -1;
pde
= PTD
[loc
>> PD_SHIFT
];
seterr("vtophys: page directory entry not valid");
newloc
= pde
.pd_pfnum
+ (p
* sizeof(struct pte
));
(void) lseek(kmem
, (long)newloc
, 0);
if (read(kmem
, (char *)&pte
, sizeof pte
) != sizeof pte
) {
seterr("vtophys: cannot obtain desired pte");
seterr("vtophys: page table entry not valid");
newloc
+= (loc
& PGOFSET
);
* locate address of unwired or swapped page
#define KREAD(off, addr, len) \
(kvm_read((void *)(off), (char *)(addr), (len)) == (len))
#define atop(x) (((unsigned)(x)) >> page_shift)
#define vm_page_hash(object, offset) \
(((unsigned)object+(unsigned)atop(offset))&vm_page_hash_mask)
findpage(object
, offset
, maddr
)
static long vm_page_hash_mask
;
static long vm_page_buckets
;
if (vm_page_hash_mask
== 0 && !KREAD(nl
[X_VM_PAGE_HASH_MASK
].n_value
,
&vm_page_hash_mask
, sizeof (long))) {
seterr("can't read vm_page_hash_mask");
if (page_shift
== 0 && !KREAD(nl
[X_PAGE_SHIFT
].n_value
,
&page_shift
, sizeof (long))) {
seterr("can't read page_shift");
if (vm_page_buckets
== 0 && !KREAD(nl
[X_VM_PAGE_BUCKETS
].n_value
,
&vm_page_buckets
, sizeof (long))) {
seterr("can't read vm_page_buckets");
baddr
= vm_page_buckets
+ vm_page_hash(object
,offset
) * sizeof(queue_head_t
);
if (!KREAD(baddr
, &bucket
, sizeof (bucket
))) {
seterr("can't read vm_page_bucket");
addr
= (long)bucket
.next
;
if (!KREAD(addr
, &mem
, sizeof (mem
))) {
seterr("can't read vm_page");
if ((long)mem
.object
== object
&& mem
.offset
== offset
) {
*maddr
= (long)mem
.phys_addr
;
addr
= (long)mem
.hashq
.next
;
static char errbuf
[_POSIX2_LINE_MAX
];
fmt
= va_arg(ap
, char *);
(void) vsnprintf(errbuf
, _POSIX2_LINE_MAX
, fmt
, ap
);
(void) vfprintf(stderr
, fmt
, ap
);
fmt
= va_arg(ap
, char *);
(void) vsnprintf(errbuf
, _POSIX2_LINE_MAX
, fmt
, ap
);
for (cp
=errbuf
; *cp
; cp
++)
snprintf(cp
, _POSIX2_LINE_MAX
- (cp
- errbuf
), ": %s", strerror(errno
));
(void) fprintf(stderr
, "%s\n", errbuf
);