Home | History | Annotate | Line # | Download | only in pmap
main.c revision 1.6
      1 /*	$NetBSD: main.c,v 1.6 2003/03/29 18:01:21 he 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.6 2003/03/29 18:01:21 he 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 struct nlist ksyms[] = {
     83 	{ "_maxsmap" },
     84 #define NL_MAXSSIZ		0
     85 	{ "_uvm_vnodeops" },
     86 #define NL_UVM_VNODEOPS		1
     87 	{ "_uvm_deviceops" },
     88 #define NL_UVM_DEVICEOPS	2
     89 	{ "_aobj_pager" },
     90 #define NL_AOBJ_PAGER		3
     91 	{ "_ubc_pager" },
     92 #define NL_UBC_PAGER		4
     93 	{ "_kernel_map" },
     94 #define NL_KERNEL_MAP		5
     95 	{ "_nchashtbl" },
     96 #define NL_NCHASHTBL		6
     97 	{ "_nchash" },
     98 #define NL_NCHASH		7
     99 	{ "_kernel_text" },
    100 #define NL_KENTER		8
    101 	{ NULL }
    102 };
    103 
    104 struct nlist kmaps[] = {
    105 	{ "_kmem_map" },
    106 #define NL_kmem_map		0
    107 	{ "_mb_map" },
    108 #define NL_mb_map		1
    109 	{ "_phys_map" },
    110 #define NL_phys_map		2
    111 	{ "_exec_map" },
    112 #define NL_exec_map		3
    113 	{ "_pager_map" },
    114 #define NL_pager_map		4
    115 	{ "_st_map" },
    116 #define NL_st_map		5
    117 	{ "_pt_map" },
    118 #define NL_pt_map		6
    119 	{ "_lkm_map" },
    120 #define NL_lkm_map		7
    121 	{ NULL }
    122 };
    123 
    124 void check_fd(int);
    125 int using_lockdebug(kvm_t *);
    126 void load_symbols(kvm_t *);
    127 void cache_enter(int, struct namecache *);
    128 
    129 int
    130 main(int argc, char *argv[])
    131 {
    132 	kvm_t *kd;
    133 	pid_t pid;
    134 	int many, ch, rc;
    135 	char errbuf[_POSIX2_LINE_MAX + 1];
    136 	struct kinfo_proc2 *kproc;
    137 	char *kmem, *kernel;
    138 	gid_t egid;
    139 
    140 	egid = getegid();
    141 	if (setegid(getgid()) == -1)
    142 		err(1, "failed to reset privileges");
    143 
    144 	check_fd(STDIN_FILENO);
    145 	check_fd(STDOUT_FILENO);
    146 	check_fd(STDERR_FILENO);
    147 
    148 	pid = -1;
    149 	verbose = debug = 0;
    150 	print_all = print_map = print_maps = print_solaris = print_ddb = 0;
    151 	recurse = 0;
    152 	kmem = kernel = NULL;
    153 
    154 	while ((ch = getopt(argc, argv, "aD:dlmM:N:p:PRrsvx")) != -1) {
    155 		switch (ch) {
    156 		case 'a':
    157 			print_all = 1;
    158 			break;
    159 		case 'd':
    160 			print_ddb = 1;
    161 			break;
    162 		case 'D':
    163 			debug = strtol(optarg, NULL, 0);
    164 			break;
    165 		case 'l':
    166 			print_maps = 1;
    167 			break;
    168 		case 'm':
    169 			print_map = 1;
    170 			break;
    171 		case 'M':
    172 			kmem = optarg;
    173 			break;
    174 		case 'N':
    175 			kernel = optarg;
    176 			break;
    177 		case 'p':
    178 			pid = strtol(optarg, NULL, 0);
    179 			break;
    180 		case 'P':
    181 			pid = getpid();
    182 			break;
    183 		case 'R':
    184 			recurse = 1;
    185 			break;
    186 		case 's':
    187 			print_solaris = 1;
    188 			break;
    189 		case 'v':
    190 			verbose++;
    191 			break;
    192 		case 'r':
    193 		case 'x':
    194 			errx(1, "-%c option not implemented, sorry", optopt);
    195 			/*NOTREACHED*/
    196 		case '?':
    197 		default:
    198 			fprintf(stderr, "usage: %s [-adlmPsv] [-D number] "
    199 				"[-M core] [-N system] [-p pid] [pid ...]\n",
    200 				getprogname());
    201 			exit(1);
    202 		}
    203 	}
    204 	argc -= optind;
    205 	argv += optind;
    206 
    207 	/* more than one "process" to dump? */
    208 	many = (argc > 1 - (pid == -1 ? 0 : 1)) ? 1 : 0;
    209 
    210 	/* apply default */
    211 	if (print_all + print_map + print_maps + print_solaris +
    212 	    print_ddb == 0)
    213 		print_solaris = 1;
    214 
    215 	/* get privs back if it appears to be safe, otherwise toss them */
    216 	if (kernel == NULL && kmem == NULL)
    217 		rc = setegid(egid);
    218 	else
    219 		rc = setgid(getgid());
    220 	if (rc == -1)
    221 		err(1, "failed to reset privileges");
    222 
    223 	/* start by opening libkvm */
    224 	kd = kvm_openfiles(kernel, kmem, NULL, O_RDONLY, errbuf);
    225 	errbuf[_POSIX2_LINE_MAX] = '\0';
    226 	if (kd == NULL)
    227 		errx(1, "%s", errbuf);
    228 
    229 	/* get "bootstrap" addresses from kernel */
    230 	load_symbols(kd);
    231 
    232 	if (! using_lockdebug(kd))
    233 		process_map = PMAPFUNC(process_map,regular);
    234 	else
    235 		process_map = PMAPFUNC(process_map,lockdebug);
    236 
    237 	do {
    238 		if (pid == -1) {
    239 			if (argc == 0)
    240 				pid = getppid();
    241 			else {
    242 				pid = strtol(argv[0], NULL, 0);
    243 				argv++;
    244 				argc--;
    245 			}
    246 		}
    247 
    248 		/* find the process id */
    249 		if (pid == 0)
    250 			kproc = NULL;
    251 		else {
    252 			kproc = kvm_getproc2(kd, KERN_PROC_PID, pid,
    253 					     sizeof(struct kinfo_proc2), &rc);
    254 			if (kproc == NULL || rc == 0) {
    255 				errno = ESRCH;
    256 				warn("%d", pid);
    257 				pid = -1;
    258 				continue;
    259 			}
    260 		}
    261 
    262 		/* dump it */
    263 		if (many) {
    264 			if (kproc)
    265 				printf("process %d:\n", kproc->p_pid);
    266 			else
    267 				printf("kernel:\n");
    268 		}
    269 
    270 		(*process_map)(kd, pid, kproc);
    271 		pid = -1;
    272 	} while (argc > 0);
    273 
    274 	/* done.  go away. */
    275 	rc = kvm_close(kd);
    276 	if (rc == -1)
    277 		err(1, "kvm_close");
    278 
    279 	return (0);
    280 }
    281 
    282 void
    283 check_fd(int fd)
    284 {
    285 	struct stat st;
    286 	int n;
    287 
    288 	if (fstat(fd, &st) == -1) {
    289 		(void)close(fd);
    290 		n = open("/dev/null", O_RDWR);
    291 		if (n == fd || n == -1)
    292 			/* we're either done or we can do no more */
    293 			return;
    294 		/* if either of these fail, there's not much we can do */
    295 		(void)dup2(n, fd);
    296 		(void)close(n);
    297 		/* XXX should we exit if it fails? */
    298 	}
    299 }
    300 
    301 int
    302 using_lockdebug(kvm_t *kd)
    303 {
    304 	struct kbit kbit[3];
    305 	struct kbit *vm_map, *header, *vm_map_entry;
    306 
    307 	vm_map = &kbit[0];
    308 	header = &kbit[1];
    309 	vm_map_entry = &kbit[2];
    310 
    311 	A(vm_map) = kernel_map_addr;
    312 	S(vm_map) = sizeof(struct vm_map);
    313 	KDEREF(kd, vm_map);
    314 
    315 	A(header) = A(vm_map) + offsetof(struct vm_map, header);
    316 	S(header) = sizeof(struct vm_map_entry);
    317 	memcpy(D(header, vm_map_entry), &D(vm_map, vm_map)->header, S(header));
    318 
    319 	/*
    320 	 * the kernel *always* has map entries, but we might see a
    321 	 * zero if we're using a lockdebug kernel and haven't noticed
    322 	 * yet.
    323 	 */
    324 	if (D(vm_map, vm_map)->nentries == 0) {
    325 
    326 		/* no entries -> all pointers must point to the header */
    327 		if (P(header) == D(header, vm_map_entry)->next &&
    328 		    P(header) == D(header, vm_map_entry)->prev &&
    329 		    P(header) == D(vm_map, vm_map)->hint &&
    330 		    P(header) == D(vm_map, vm_map)->first_free)
    331 			return (0);
    332 	}
    333 	else {
    334 
    335 		P(vm_map_entry) = D(header, vm_map_entry)->next;
    336 		S(vm_map_entry) = sizeof(struct vm_map_entry);
    337 		KDEREF(kd, vm_map_entry);
    338 
    339 		/* we have entries, so there must be referential integrity */
    340 		if (D(vm_map_entry, vm_map_entry)->prev == P(header) &&
    341 		    D(header, vm_map_entry)->start <=
    342 		    D(vm_map_entry, vm_map_entry)->start &&
    343 		    D(vm_map_entry, vm_map_entry)->end <=
    344 		    D(header, vm_map_entry)->end)
    345 			return (0);
    346 	}
    347 
    348 	return (1);
    349 }
    350 
    351 void
    352 load_symbols(kvm_t *kd)
    353 {
    354 	int rc, i, mib[2];
    355 	size_t sz;
    356 
    357 	rc = kvm_nlist(kd, &ksyms[0]);
    358 	if (rc != 0) {
    359 		for (i = 0; ksyms[i].n_name != NULL; i++)
    360 			if (ksyms[i].n_value == 0)
    361 				warnx("symbol %s: not found", ksyms[i].n_name);
    362 		exit(1);
    363 	}
    364 
    365 	uvm_vnodeops =	(void*)ksyms[NL_UVM_VNODEOPS].n_value;
    366 	uvm_deviceops =	(void*)ksyms[NL_UVM_DEVICEOPS].n_value;
    367 	aobj_pager =	(void*)ksyms[NL_AOBJ_PAGER].n_value;
    368 	ubc_pager =	(void*)ksyms[NL_UBC_PAGER].n_value;
    369 
    370 	kernel_floor =	(void*)ksyms[NL_KENTER].n_value;
    371 	nchash_addr =	ksyms[NL_NCHASH].n_value;
    372 
    373 	_KDEREF(kd, ksyms[NL_MAXSSIZ].n_value, &maxssiz,
    374 		sizeof(maxssiz));
    375 	_KDEREF(kd, ksyms[NL_NCHASHTBL].n_value, &nchashtbl_addr,
    376 	       sizeof(nchashtbl_addr));
    377 	_KDEREF(kd, ksyms[NL_KERNEL_MAP].n_value, &kernel_map_addr,
    378 		sizeof(kernel_map_addr));
    379 
    380 	/*
    381 	 * Some of these may be missing from some platforms, for
    382 	 * example sparc, sh3, and most powerpc platforms don't
    383 	 * have a "phys_map", etc.
    384 	 */
    385 	(void)kvm_nlist(kd, &kmaps[0]);
    386 
    387 #define get_map_address(m) \
    388 	if (kmaps[CONCAT(NL_,m)].n_value != 0) \
    389 		_KDEREF(kd, kmaps[CONCAT(NL_,m)].n_value, &m, sizeof(m))
    390 
    391 	get_map_address(kmem_map);
    392 	get_map_address(mb_map);
    393 	get_map_address(phys_map);
    394 	get_map_address(exec_map);
    395 	get_map_address(pager_map);
    396 	get_map_address(st_map);
    397 	get_map_address(pt_map);
    398 	get_map_address(lkm_map);
    399 
    400 	mib[0] = CTL_HW;
    401 	mib[1] = HW_PAGESIZE;
    402 	sz = sizeof(page_size);
    403 	if (sysctl(&mib[0], 2, &page_size, &sz, NULL, 0) == -1)
    404 		err(1, "sysctl: hw.pagesize");
    405 }
    406 
    407 const char *
    408 mapname(void *addr)
    409 {
    410 
    411 	if (addr == (void*)kernel_map_addr)
    412 		return ("kernel_map");
    413 	else if (addr == kmem_map)
    414 		return ("kmem_map");
    415 	else if (addr == mb_map)
    416 		return ("mb_map");
    417 	else if (addr == phys_map)
    418 		return ("phys_map");
    419 	else if (addr == exec_map)
    420 		return ("exec_map");
    421 	else if (addr == pager_map)
    422 		return ("pager_map");
    423 	else if (addr == st_map)
    424 		return ("st_map");
    425 	else if (addr == pt_map)
    426 		return ("pt_map");
    427 	else if (addr == lkm_map)
    428 		return ("lkm_map");
    429 	else
    430 		return (NULL);
    431 }
    432 
    433 void
    434 load_name_cache(kvm_t *kd)
    435 {
    436 	struct namecache _ncp, *ncp, *oncp;
    437 	struct nchashhead _ncpp, *ncpp;
    438 	u_long nchash;
    439 	int i;
    440 
    441 	LIST_INIT(&lcache);
    442 
    443 	_KDEREF(kd, nchash_addr, &nchash, sizeof(nchash));
    444 	nchashtbl = malloc(sizeof(nchashtbl) * (int)nchash);
    445 	_KDEREF(kd, nchashtbl_addr, nchashtbl,
    446 		sizeof(nchashtbl) * (int)nchash);
    447 
    448 	ncpp = &_ncpp;
    449 
    450 	for (i = 0; i <= nchash; i++) {
    451 		ncpp = &nchashtbl[i];
    452 		oncp = NULL;
    453 		LIST_FOREACH(ncp, ncpp, nc_hash) {
    454 			if (ncp == oncp ||
    455 			    (void*)ncp < kernel_floor ||
    456 			    ncp == (void*)0xdeadbeef)
    457 				break;
    458 			oncp = ncp;
    459 			_KDEREF(kd, (u_long)ncp, &_ncp, sizeof(*ncp));
    460 			ncp = &_ncp;
    461 			if ((void*)ncp->nc_vp > kernel_floor &&
    462 			    ncp->nc_nlen > 0) {
    463 				if (ncp->nc_nlen > 2 ||
    464 				    ncp->nc_name[0] != '.' ||
    465 				    (ncp->nc_name[1] != '.' &&
    466 				     ncp->nc_nlen != 1))
    467 					cache_enter(i, ncp);
    468 			}
    469 		}
    470 	}
    471 }
    472 
    473 void
    474 cache_enter(int i, struct namecache *ncp)
    475 {
    476 	struct cache_entry *ce;
    477 
    478 	if (debug & DUMP_NAMEI_CACHE)
    479 		printf("[%d] ncp->nc_vp %10p, ncp->nc_dvp %10p, "
    480 		       "ncp->nc_nlen %3d [%.*s] (nc_dvpid=%lu, nc_vpid=%lu)\n",
    481 		       i, ncp->nc_vp, ncp->nc_dvp,
    482 		       ncp->nc_nlen, ncp->nc_nlen, ncp->nc_name,
    483 		       ncp->nc_dvpid, ncp->nc_vpid);
    484 
    485 	ce = malloc(sizeof(struct cache_entry));
    486 
    487 	ce->ce_vp = ncp->nc_vp;
    488 	ce->ce_pvp = ncp->nc_dvp;
    489 	ce->ce_cid = ncp->nc_vpid;
    490 	ce->ce_pcid = ncp->nc_dvpid;
    491 	ce->ce_nlen = ncp->nc_nlen;
    492 	strncpy(ce->ce_name, ncp->nc_name, sizeof(ce->ce_name));
    493 	ce->ce_name[MIN(ce->ce_nlen, sizeof(ce->ce_name) - 1)] = '\0';
    494 
    495 	LIST_INSERT_HEAD(&lcache, ce, ce_next);
    496 }
    497