Home | History | Annotate | Line # | Download | only in pmap
main.c revision 1.7
      1 /*	$NetBSD: main.c,v 1.7 2003/04/04 03:49:20 atatat Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2002, 2003 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Andrew Brown.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *      This product includes software developed by the NetBSD
     21  *      Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <sys/cdefs.h>
     40 #ifndef lint
     41 __RCSID("$NetBSD: main.c,v 1.7 2003/04/04 03:49:20 atatat Exp $");
     42 #endif
     43 
     44 #include <sys/param.h>
     45 
     46 #ifndef __NetBSD_Version__
     47 #error go away, you fool
     48 #elif (__NetBSD_Version__ < 105000000)
     49 #error only works with uvm
     50 #endif
     51 
     52 #include <fcntl.h>
     53 #include <errno.h>
     54 #include <unistd.h>
     55 #include <limits.h>
     56 #include <string.h>
     57 
     58 #include "pmap.h"
     59 #include "main.h"
     60 
     61 /*
     62  * strange gyrations to get the prototype for the lockdebug version of
     63  * the process_map function
     64  */
     65 #undef VERSION
     66 #define VERSION lockdebug
     67 #include "pmap.h"
     68 #undef VERSION
     69 #define VERSION regular
     70 
     71 struct cache_head lcache;
     72 struct nchashhead *nchashtbl;
     73 void *uvm_vnodeops, *uvm_deviceops, *aobj_pager, *ubc_pager;
     74 void *kernel_floor;
     75 struct vm_map *kmem_map, *mb_map, *phys_map, *exec_map, *pager_map;
     76 struct vm_map *st_map, *pt_map, *lkm_map;
     77 u_long nchash_addr, nchashtbl_addr, kernel_map_addr;
     78 int debug, verbose, recurse, page_size;
     79 int print_all, print_map, print_maps, print_solaris, print_ddb;
     80 rlim_t maxssiz;
     81 
     82 void (*process_map)(kvm_t *, struct kinfo_proc2 *,
     83 		    struct kbit *, const char *);
     84 void (*dump_vm_map)(kvm_t *, struct kinfo_proc2 *,
     85 		    struct kbit *, struct kbit *, const char *);
     86 size_t (*dump_vm_map_entry)(kvm_t *, struct kinfo_proc2 *,
     87 			    struct kbit *, struct kbit *, int);
     88 void (*dump_amap)(kvm_t *, struct kbit *);
     89 
     90 struct nlist ksyms[] = {
     91 	{ "_maxsmap" },
     92 #define NL_MAXSSIZ		0
     93 	{ "_uvm_vnodeops" },
     94 #define NL_UVM_VNODEOPS		1
     95 	{ "_uvm_deviceops" },
     96 #define NL_UVM_DEVICEOPS	2
     97 	{ "_aobj_pager" },
     98 #define NL_AOBJ_PAGER		3
     99 	{ "_ubc_pager" },
    100 #define NL_UBC_PAGER		4
    101 	{ "_kernel_map" },
    102 #define NL_KERNEL_MAP		5
    103 	{ "_nchashtbl" },
    104 #define NL_NCHASHTBL		6
    105 	{ "_nchash" },
    106 #define NL_NCHASH		7
    107 	{ "_kernel_text" },
    108 #define NL_KENTER		8
    109 	{ NULL }
    110 };
    111 
    112 struct nlist kmaps[] = {
    113 	{ "_kmem_map" },
    114 #define NL_kmem_map		0
    115 	{ "_mb_map" },
    116 #define NL_mb_map		1
    117 	{ "_phys_map" },
    118 #define NL_phys_map		2
    119 	{ "_exec_map" },
    120 #define NL_exec_map		3
    121 	{ "_pager_map" },
    122 #define NL_pager_map		4
    123 	{ "_st_map" },
    124 #define NL_st_map		5
    125 	{ "_pt_map" },
    126 #define NL_pt_map		6
    127 	{ "_lkm_map" },
    128 #define NL_lkm_map		7
    129 	{ NULL }
    130 };
    131 
    132 #define VMSPACE_ADDRESS		1
    133 #define VM_MAP_ADDRESS		2
    134 #define VM_MAP_ENTRY_ADDRESS	3
    135 #define AMAP_ADDRESS		4
    136 
    137 void check_fd(int);
    138 int using_lockdebug(kvm_t *);
    139 void load_symbols(kvm_t *);
    140 void cache_enter(int, struct namecache *);
    141 
    142 int
    143 main(int argc, char *argv[])
    144 {
    145 	kvm_t *kd;
    146 	pid_t pid;
    147 	int which, many, ch, rc;
    148 	char errbuf[_POSIX2_LINE_MAX + 1];
    149 	struct kinfo_proc2 *kproc;
    150 	char *kmem, *kernel, *t;
    151 	gid_t egid;
    152 	struct kbit kbit, *vmspace;
    153 	u_long address;
    154 
    155 	egid = getegid();
    156 	if (setegid(getgid()) == -1)
    157 		err(1, "failed to reset privileges");
    158 
    159 	check_fd(STDIN_FILENO);
    160 	check_fd(STDOUT_FILENO);
    161 	check_fd(STDERR_FILENO);
    162 
    163 	pid = -1;
    164 	which = verbose = debug = 0;
    165 	print_all = print_map = print_maps = print_solaris = print_ddb = 0;
    166 	recurse = 0;
    167 	kmem = kernel = NULL;
    168 	address = 0;
    169 	vmspace = &kbit;
    170 
    171 	while ((ch = getopt(argc, argv, "A:aD:dE:lM:mN:Pp:RrS:sV:vx")) != -1) {
    172 		switch (ch) {
    173 		case 'A':
    174 		case 'E':
    175 		case 'S':
    176 		case 'V':
    177 			if (which != 0)
    178 				errx(1, "use only one of -A, -E, -S, or -V");
    179 			errno = 0;
    180 			address = strtoul(optarg, &t, 0);
    181 			if (*t != '\0')
    182 				errx(1, "%s is not a valid address", optarg);
    183 			if (errno != 0)
    184 				err(1, "%s is not a valid address", optarg);
    185 			switch (ch) {
    186 			case 'A':	which = AMAP_ADDRESS;		break;
    187 			case 'E':	which = VM_MAP_ENTRY_ADDRESS;	break;
    188 			case 'S':	which = VMSPACE_ADDRESS;	break;
    189 			case 'V':	which = VM_MAP_ADDRESS;		break;
    190 			}
    191 			break;
    192 		case 'a':
    193 			print_all = 1;
    194 			break;
    195 		case 'd':
    196 			print_ddb = 1;
    197 			break;
    198 		case 'D':
    199 			errno = 0;
    200 			debug = strtoul(optarg, &t, 0);
    201 			if (*t != '\0')
    202 				errx(1, "%s is not a valid number", optarg);
    203 			if (errno != 0)
    204 				err(1, "%s is not a valid number", optarg);
    205 			break;
    206 		case 'l':
    207 			print_maps = 1;
    208 			break;
    209 		case 'm':
    210 			print_map = 1;
    211 			break;
    212 		case 'M':
    213 			kmem = optarg;
    214 			break;
    215 		case 'N':
    216 			kernel = optarg;
    217 			break;
    218 		case 'p':
    219 			errno = 0;
    220 			pid = strtol(optarg, &t, 0);
    221 			if (pid < 0)
    222 				errno = EINVAL;
    223 			if (*t != '\0')
    224 				errx(1, "%s is not a valid pid", optarg);
    225 			if (errno != 0)
    226 				err(1, "%s is not a valid pid", optarg);
    227 			break;
    228 		case 'P':
    229 			pid = getpid();
    230 			break;
    231 		case 'R':
    232 			recurse = 1;
    233 			break;
    234 		case 's':
    235 			print_solaris = 1;
    236 			break;
    237 		case 'v':
    238 			verbose++;
    239 			break;
    240 		case 'r':
    241 		case 'x':
    242 			errx(1, "-%c option not implemented, sorry", optopt);
    243 			/*NOTREACHED*/
    244 		case '?':
    245 		default:
    246 			fprintf(stderr, "usage: %s [-adlmPRsv] [-A address] "
    247 				"[-D number] [-E address] [-M core]\n"
    248 				"\t[-N system] [-S address] [-V address] "
    249 				"[-p pid] [pid ...]\n",
    250 				getprogname());
    251 			exit(1);
    252 		}
    253 	}
    254 	argc -= optind;
    255 	argv += optind;
    256 
    257 	/* more than one "process" to dump? */
    258 	many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0;
    259 
    260 	/* apply default */
    261 	if (print_all + print_map + print_maps + print_solaris +
    262 	    print_ddb == 0)
    263 		print_solaris = 1;
    264 
    265 	/* get privs back if it appears to be safe, otherwise toss them */
    266 	if (kernel == NULL && kmem == NULL && address == 0)
    267 		rc = setegid(egid);
    268 	else
    269 		rc = setgid(getgid());
    270 	if (rc == -1)
    271 		err(1, "failed to reset privileges");
    272 
    273 	/* start by opening libkvm */
    274 	kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
    275 	errbuf[_POSIX2_LINE_MAX] = '\0';
    276 	if (kd == NULL)
    277 		errx(1, "%s", errbuf);
    278 
    279 	/* get "bootstrap" addresses from kernel */
    280 	load_symbols(kd);
    281 
    282 	if (! using_lockdebug(kd)) {
    283 		process_map = PMAPFUNC(process_map,regular);
    284 		dump_vm_map = PMAPFUNC(dump_vm_map,regular);
    285 		dump_vm_map_entry = PMAPFUNC(dump_vm_map_entry,regular);
    286 		dump_amap = PMAPFUNC(dump_amap,regular);
    287 	}
    288 	else {
    289 		process_map = PMAPFUNC(process_map,lockdebug);
    290 		dump_vm_map = PMAPFUNC(dump_vm_map,lockdebug);
    291 		dump_vm_map_entry = PMAPFUNC(dump_vm_map_entry,lockdebug);
    292 		dump_amap = PMAPFUNC(dump_amap,lockdebug);
    293 	}
    294 
    295 	if (address) {
    296 		struct kbit kbit2, *at = &kbit2;
    297 
    298 		memset(vmspace, 0, sizeof(*vmspace));
    299 		A(at) = address;
    300 		S(at) = -1;
    301 
    302 		switch (which) {
    303 		    case VMSPACE_ADDRESS:
    304 			/* (kd, kproc, vmspace, thing) */
    305 			(*process_map)(kd, NULL, at, "vm_map");
    306 			break;
    307 		    case VM_MAP_ADDRESS:
    308 			/* (kd, proc, vmspace, vm_map, thing) */
    309 			(*dump_vm_map)(kd, NULL, vmspace, at, "vm_map");
    310 			break;
    311 		    case VM_MAP_ENTRY_ADDRESS:
    312 			/* (kd, proc, vmspace, vm_map_entry, 0) */
    313 			(*dump_vm_map_entry)(kd, NULL, vmspace, at, 0);
    314 			break;
    315 		    case AMAP_ADDRESS:
    316 			/* (kd, amap) */
    317 			(*dump_amap)(kd, at);
    318 			break;
    319 		}
    320 		exit(0);
    321 	}
    322 
    323 	do {
    324 		if (pid == -1) {
    325 			if (argc == 0)
    326 				pid = getppid();
    327 			else {
    328 				errno = 0;
    329 				pid = strtol(argv[0], &t, 0);
    330 				if (pid < 0)
    331 					errno = EINVAL;
    332 				if (*t != '\0')
    333 					errx(1, "%s is not a valid pid",
    334 					    argv[0]);
    335 				if (errno != 0)
    336 					err(1, "%s is not a valid pid",
    337 					    argv[0]);
    338 				argv++;
    339 				argc--;
    340 			}
    341 		}
    342 
    343 		/* find the process id */
    344 		if (pid == 0)
    345 			kproc = NULL;
    346 		else {
    347 			kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
    348 					     sizeof(struct kinfo_proc2), &rc);
    349 			if (kproc == NULL || rc == 0) {
    350 				errno = ESRCH;
    351 				warn("%d", pid);
    352 				pid = -1;
    353 				continue;
    354 			}
    355 		}
    356 
    357 		/* dump it */
    358 		if (many) {
    359 			if (kproc)
    360 				printf("process %d:\n", kproc->p_pid);
    361 			else
    362 				printf("kernel:\n");
    363 		}
    364 
    365 		(*process_map)(kd, kproc, vmspace, NULL);
    366 		pid = -1;
    367 	} while (argc > 0);
    368 
    369 	/* done.  go away. */
    370 	rc = kvm_close(kd);
    371 	if (rc == -1)
    372 		err(1, "kvm_close");
    373 
    374 	return (0);
    375 }
    376 
    377 void
    378 check_fd(int fd)
    379 {
    380 	struct stat st;
    381 	int n;
    382 
    383 	if (fstat(fd, &st) == -1) {
    384 		(void)close(fd);
    385 		n = open("/dev/null", O_RDWR);
    386 		if (n == fd || n == -1)
    387 			/* we're either done or we can do no more */
    388 			return;
    389 		/* if either of these fail, there's not much we can do */
    390 		(void)dup2(n, fd);
    391 		(void)close(n);
    392 		/* XXX should we exit if it fails? */
    393 	}
    394 }
    395 
    396 int
    397 using_lockdebug(kvm_t *kd)
    398 {
    399 	struct kbit kbit[3];
    400 	struct kbit *vm_map, *header, *vm_map_entry;
    401 
    402 	vm_map = &kbit[0];
    403 	header = &kbit[1];
    404 	vm_map_entry = &kbit[2];
    405 
    406 	A(vm_map) = kernel_map_addr;
    407 	S(vm_map) = sizeof(struct vm_map);
    408 	KDEREF(kd, vm_map);
    409 
    410 	A(header) = A(vm_map) + offsetof(struct vm_map, header);
    411 	S(header) = sizeof(struct vm_map_entry);
    412 	memcpy(D(header, vm_map_entry), &D(vm_map, vm_map)->header, S(header));
    413 
    414 	/*
    415 	 * the kernel *always* has map entries, but we might see a
    416 	 * zero if we're using a lockdebug kernel and haven't noticed
    417 	 * yet.
    418 	 */
    419 	if (D(vm_map, vm_map)->nentries == 0) {
    420 
    421 		/* no entries -> all pointers must point to the header */
    422 		if (P(header) == D(header, vm_map_entry)->next &&
    423 		    P(header) == D(header, vm_map_entry)->prev &&
    424 		    P(header) == D(vm_map, vm_map)->hint &&
    425 		    P(header) == D(vm_map, vm_map)->first_free)
    426 			return (0);
    427 	}
    428 	else {
    429 
    430 		P(vm_map_entry) = D(header, vm_map_entry)->next;
    431 		S(vm_map_entry) = sizeof(struct vm_map_entry);
    432 		KDEREF(kd, vm_map_entry);
    433 
    434 		/* we have entries, so there must be referential integrity */
    435 		if (D(vm_map_entry, vm_map_entry)->prev == P(header) &&
    436 		    D(header, vm_map_entry)->start <=
    437 		    D(vm_map_entry, vm_map_entry)->start &&
    438 		    D(vm_map_entry, vm_map_entry)->end <=
    439 		    D(header, vm_map_entry)->end)
    440 			return (0);
    441 	}
    442 
    443 	return (1);
    444 }
    445 
    446 void
    447 load_symbols(kvm_t *kd)
    448 {
    449 	int rc, i, mib[2];
    450 	size_t sz;
    451 
    452 	rc = kvm_nlist(kd, &ksyms[0]);
    453 	if (rc != 0) {
    454 		for (i = 0; ksyms[i].n_name != NULL; i++)
    455 			if (ksyms[i].n_value == 0)
    456 				warnx("symbol %s: not found", ksyms[i].n_name);
    457 		exit(1);
    458 	}
    459 
    460 	uvm_vnodeops =	(void*)ksyms[NL_UVM_VNODEOPS].n_value;
    461 	uvm_deviceops =	(void*)ksyms[NL_UVM_DEVICEOPS].n_value;
    462 	aobj_pager =	(void*)ksyms[NL_AOBJ_PAGER].n_value;
    463 	ubc_pager =	(void*)ksyms[NL_UBC_PAGER].n_value;
    464 
    465 	kernel_floor =	(void*)ksyms[NL_KENTER].n_value;
    466 	nchash_addr =	ksyms[NL_NCHASH].n_value;
    467 
    468 	_KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
    469 		sizeof(maxssiz));
    470 	_KDEREF(kd, ksyms[NL_NCHASHTBL].n_value, &nchashtbl_addr,
    471 	       sizeof(nchashtbl_addr));
    472 	_KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
    473 		sizeof(kernel_map_addr));
    474 
    475 	/*
    476 	 * Some of these may be missing from some platforms, for
    477 	 * example sparc, sh3, and most powerpc platforms don't
    478 	 * have a "phys_map", etc.
    479 	 */
    480 	(void)kvm_nlist(kd, &kmaps[0]);
    481 
    482 #define get_map_address(m) \
    483 	if (kmaps[CONCAT(NL_,m)].n_value != 0) \
    484 		_KDEREF(kd, kmaps[CONCAT(NL_,m)].n_value, &m, sizeof(m))
    485 
    486 	get_map_address(kmem_map);
    487 	get_map_address(mb_map);
    488 	get_map_address(phys_map);
    489 	get_map_address(exec_map);
    490 	get_map_address(pager_map);
    491 	get_map_address(st_map);
    492 	get_map_address(pt_map);
    493 	get_map_address(lkm_map);
    494 
    495 	mib[0] = CTL_HW;
    496 	mib[1] = HW_PAGESIZE;
    497 	sz = sizeof(page_size);
    498 	if (sysctl(&mib[0], 2, &page_size, &sz, NULL, 0) == -1)
    499 		err(1, "sysctl: hw.pagesize");
    500 }
    501 
    502 const char *
    503 mapname(void *addr)
    504 {
    505 
    506 	if (addr == (void*)kernel_map_addr)
    507 		return ("kernel_map");
    508 	else if (addr == kmem_map)
    509 		return ("kmem_map");
    510 	else if (addr == mb_map)
    511 		return ("mb_map");
    512 	else if (addr == phys_map)
    513 		return ("phys_map");
    514 	else if (addr == exec_map)
    515 		return ("exec_map");
    516 	else if (addr == pager_map)
    517 		return ("pager_map");
    518 	else if (addr == st_map)
    519 		return ("st_map");
    520 	else if (addr == pt_map)
    521 		return ("pt_map");
    522 	else if (addr == lkm_map)
    523 		return ("lkm_map");
    524 	else
    525 		return (NULL);
    526 }
    527 
    528 void
    529 load_name_cache(kvm_t *kd)
    530 {
    531 	struct namecache _ncp, *ncp, *oncp;
    532 	struct nchashhead _ncpp, *ncpp;
    533 	u_long nchash;
    534 	int i;
    535 
    536 	LIST_INIT(&lcache);
    537 
    538 	_KDEREF(kd, nchash_addr, &nchash, sizeof(nchash));
    539 	nchashtbl = malloc(sizeof(nchashtbl) * (int)nchash);
    540 	_KDEREF(kd, nchashtbl_addr, nchashtbl,
    541 		sizeof(nchashtbl) * (int)nchash);
    542 
    543 	ncpp = &_ncpp;
    544 
    545 	for (i = 0; i <= nchash; i++) {
    546 		ncpp = &nchashtbl[i];
    547 		oncp = NULL;
    548 		LIST_FOREACH(ncp, ncpp, nc_hash) {
    549 			if (ncp == oncp ||
    550 			    (void*)ncp < kernel_floor ||
    551 			    ncp == (void*)0xdeadbeef)
    552 				break;
    553 			oncp = ncp;
    554 			_KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp));
    555 			ncp = &_ncp;
    556 			if ((void*)ncp->nc_vp > kernel_floor &&
    557 			    ncp->nc_nlen > 0) {
    558 				if (ncp->nc_nlen > 2 ||
    559 				    ncp->nc_name[0] != '.' ||
    560 				    (ncp->nc_name[1] != '.' &&
    561 				     ncp->nc_nlen != 1))
    562 					cache_enter(i, ncp);
    563 			}
    564 		}
    565 	}
    566 }
    567 
    568 void
    569 cache_enter(int i, struct namecache *ncp)
    570 {
    571 	struct cache_entry *ce;
    572 
    573 	if (debug & DUMP_NAMEI_CACHE)
    574 		printf("[%d] ncp->nc_vp %10p, ncp->nc_dvp %10p, "
    575 		       "ncp->nc_nlen %3d [%.*s] (nc_dvpid=%lu, nc_vpid=%lu)\n",
    576 		       i, ncp->nc_vp, ncp->nc_dvp,
    577 		       ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name,
    578 		       ncp->nc_dvpid, ncp->nc_vpid);
    579 
    580 	ce = malloc(sizeof(struct cache_entry));
    581 
    582 	ce->ce_vp = ncp->nc_vp;
    583 	ce->ce_pvp = ncp->nc_dvp;
    584 	ce->ce_cid = ncp->nc_vpid;
    585 	ce->ce_pcid = ncp->nc_dvpid;
    586 	ce->ce_nlen = ncp->nc_nlen;
    587 	strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name));
    588 	ce->ce_name[MIN(ce->ce_nlen, sizeof(ce->ce_name) - 1)] = '\0';
    589 
    590 	LIST_INSERT_HEAD(&lcache, ce, ce_next);
    591 }
    592