Home | History | Annotate | Download | only in mdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <mdb/mdb_debug.h>
     30 #include <mdb/mdb_string.h>
     31 #include <mdb/mdb_modapi.h>
     32 #include <mdb/mdb_err.h>
     33 #include <mdb/mdb_nv.h>
     34 #include <mdb/mdb.h>
     35 
     36 #define	NV_NAME(v) \
     37 	(((v)->v_flags & MDB_NV_EXTNAME) ? (v)->v_ename : (v)->v_lname)
     38 
     39 #define	NV_SIZE(v) \
     40 	(((v)->v_flags & MDB_NV_EXTNAME) ? sizeof (mdb_var_t) : \
     41 	sizeof (mdb_var_t) + MDB_NV_NAMELEN - 1)
     42 
     43 #define	NV_HASHSZ	211
     44 
     45 static size_t
     46 nv_hashstring(const char *key)
     47 {
     48 	size_t g, h = 0;
     49 	const char *p;
     50 
     51 	ASSERT(key != NULL);
     52 
     53 	for (p = key; *p != '\0'; p++) {
     54 		h = (h << 4) + *p;
     55 
     56 		if ((g = (h & 0xf0000000)) != 0) {
     57 			h ^= (g >> 24);
     58 			h ^= g;
     59 		}
     60 	}
     61 
     62 	return (h);
     63 }
     64 
     65 static mdb_var_t *
     66 nv_var_alloc(const char *name, const mdb_nv_disc_t *disc,
     67 	uintmax_t value, uint_t flags, uint_t um_flags, mdb_var_t *next)
     68 {
     69 	size_t nbytes = (flags & MDB_NV_EXTNAME) ? sizeof (mdb_var_t) :
     70 	    (sizeof (mdb_var_t) + MDB_NV_NAMELEN - 1);
     71 
     72 	mdb_var_t *v = mdb_alloc(nbytes, um_flags);
     73 
     74 	if (v == NULL)
     75 		return (NULL);
     76 
     77 	if (flags & MDB_NV_EXTNAME) {
     78 		v->v_ename = name;
     79 		v->v_lname[0] = 0;
     80 	} else {
     81 		(void) strncpy(v->v_lname, name, MDB_NV_NAMELEN - 1);
     82 		v->v_lname[MDB_NV_NAMELEN - 1] = '\0';
     83 		v->v_ename = NULL;
     84 	}
     85 
     86 	v->v_uvalue = value;
     87 	v->v_flags = flags & ~(MDB_NV_SILENT | MDB_NV_INTERPOS);
     88 	v->v_disc = disc;
     89 	v->v_next = next;
     90 
     91 	return (v);
     92 }
     93 
     94 static void
     95 nv_var_free(mdb_var_t *v, uint_t um_flags)
     96 {
     97 	if (um_flags & UM_GC)
     98 		return;
     99 
    100 	if (v->v_flags & MDB_NV_OVERLOAD) {
    101 		mdb_var_t *w, *nw;
    102 
    103 		for (w = v->v_ndef; w != NULL; w = nw) {
    104 			nw = w->v_ndef;
    105 			mdb_free(w, NV_SIZE(w));
    106 		}
    107 	}
    108 
    109 	mdb_free(v, NV_SIZE(v));
    110 }
    111 
    112 /*
    113  * Can return NULL only if the nv's memory allocation flags include UM_NOSLEEP
    114  */
    115 mdb_nv_t *
    116 mdb_nv_create(mdb_nv_t *nv, uint_t um_flags)
    117 {
    118 	nv->nv_hash = mdb_zalloc(sizeof (mdb_var_t *) * NV_HASHSZ, um_flags);
    119 
    120 	if (nv->nv_hash == NULL)
    121 		return (NULL);
    122 
    123 	nv->nv_hashsz = NV_HASHSZ;
    124 	nv->nv_nelems = 0;
    125 	nv->nv_iter_elt = NULL;
    126 	nv->nv_iter_bucket = 0;
    127 	nv->nv_um_flags = um_flags;
    128 
    129 	return (nv);
    130 }
    131 
    132 void
    133 mdb_nv_destroy(mdb_nv_t *nv)
    134 {
    135 	mdb_var_t *v, *w;
    136 	size_t i;
    137 
    138 	if (nv->nv_um_flags & UM_GC)
    139 		return;
    140 
    141 	for (i = 0; i < nv->nv_hashsz; i++) {
    142 		for (v = nv->nv_hash[i]; v != NULL; v = w) {
    143 			w = v->v_next;
    144 			nv_var_free(v, nv->nv_um_flags);
    145 		}
    146 	}
    147 
    148 	mdb_free(nv->nv_hash, sizeof (mdb_var_t *) * NV_HASHSZ);
    149 }
    150 
    151 mdb_var_t *
    152 mdb_nv_lookup(mdb_nv_t *nv, const char *name)
    153 {
    154 	size_t i = nv_hashstring(name) % nv->nv_hashsz;
    155 	mdb_var_t *v;
    156 
    157 	for (v = nv->nv_hash[i]; v != NULL; v = v->v_next) {
    158 		if (strcmp(NV_NAME(v), name) == 0)
    159 			return (v);
    160 	}
    161 
    162 	return (NULL);
    163 }
    164 
    165 /*
    166  * Interpose W in place of V.  We replace V with W in nv_hash, and then
    167  * set W's v_ndef overload chain to point at V.
    168  */
    169 static mdb_var_t *
    170 nv_var_interpos(mdb_nv_t *nv, size_t i, mdb_var_t *v, mdb_var_t *w)
    171 {
    172 	mdb_var_t **pvp = &nv->nv_hash[i];
    173 
    174 	while (*pvp != v) {
    175 		mdb_var_t *vp = *pvp;
    176 		ASSERT(vp != NULL);
    177 		pvp = &vp->v_next;
    178 	}
    179 
    180 	*pvp = w;
    181 	w->v_next = v->v_next;
    182 	w->v_ndef = v;
    183 	v->v_next = NULL;
    184 
    185 	return (w);
    186 }
    187 
    188 /*
    189  * Add W to the end of V's overload chain.  We simply follow v_ndef to the
    190  * end, and then append W.  We don't expect these chains to grow very long.
    191  */
    192 static mdb_var_t *
    193 nv_var_overload(mdb_var_t *v, mdb_var_t *w)
    194 {
    195 	while (v->v_ndef != NULL)
    196 		v = v->v_ndef;
    197 
    198 	v->v_ndef = w;
    199 	return (w);
    200 }
    201 
    202 /*
    203  * Can return NULL only if the nv's memory allocation flags include UM_NOSLEEP
    204  */
    205 mdb_var_t *
    206 mdb_nv_insert(mdb_nv_t *nv, const char *name, const mdb_nv_disc_t *disc,
    207     uintmax_t value, uint_t flags)
    208 {
    209 	size_t i = nv_hashstring(name) % nv->nv_hashsz;
    210 	mdb_var_t *v;
    211 
    212 	ASSERT(!(flags & MDB_NV_EXTNAME) || !(flags & MDB_NV_OVERLOAD));
    213 	ASSERT(!(flags & MDB_NV_RDONLY) || !(flags & MDB_NV_OVERLOAD));
    214 
    215 	/*
    216 	 * If the specified name is already hashed,
    217 	 * and MDB_NV_OVERLOAD is set:	insert new var into overload chain
    218 	 * and MDB_NV_RDONLY is set:	leave var unchanged, issue warning
    219 	 * otherwise:			update var with new value
    220 	 */
    221 	for (v = nv->nv_hash[i]; v != NULL; v = v->v_next) {
    222 		if (strcmp(NV_NAME(v), name) == 0) {
    223 			if (v->v_flags & MDB_NV_OVERLOAD) {
    224 				mdb_var_t *w = nv_var_alloc(NV_NAME(v), disc,
    225 				    value, flags, nv->nv_um_flags, NULL);
    226 
    227 				if (w == NULL) {
    228 					ASSERT(nv->nv_um_flags & UM_NOSLEEP);
    229 					return (NULL);
    230 				}
    231 
    232 				if (flags & MDB_NV_INTERPOS)
    233 					v = nv_var_interpos(nv, i, v, w);
    234 				else
    235 					v = nv_var_overload(v, w);
    236 
    237 			} else if (v->v_flags & MDB_NV_RDONLY) {
    238 				if (!(flags & MDB_NV_SILENT)) {
    239 					warn("cannot modify read-only "
    240 					    "variable '%s'\n", NV_NAME(v));
    241 				}
    242 			} else
    243 				v->v_uvalue = value;
    244 
    245 			ASSERT(v != NULL);
    246 			return (v);
    247 		}
    248 	}
    249 
    250 	/*
    251 	 * If the specified name was not found, initialize a new element
    252 	 * and add it to the hash table at the beginning of this chain:
    253 	 */
    254 	v = nv_var_alloc(name, disc, value, flags, nv->nv_um_flags,
    255 	    nv->nv_hash[i]);
    256 
    257 	if (v == NULL) {
    258 		ASSERT(nv->nv_um_flags & UM_NOSLEEP);
    259 		return (NULL);
    260 	}
    261 
    262 	nv->nv_hash[i] = v;
    263 	nv->nv_nelems++;
    264 
    265 	return (v);
    266 }
    267 
    268 static void
    269 nv_var_defn_remove(mdb_var_t *v, mdb_var_t *corpse, uint_t um_flags)
    270 {
    271 	mdb_var_t *w = v;
    272 
    273 	while (v->v_ndef != NULL && v->v_ndef != corpse)
    274 		v = v->v_ndef;
    275 
    276 	if (v == NULL) {
    277 		fail("var %p ('%s') not found on defn chain of %p\n",
    278 		    (void *)corpse, NV_NAME(corpse), (void *)w);
    279 	}
    280 
    281 	v->v_ndef = corpse->v_ndef;
    282 	corpse->v_ndef = NULL;
    283 	nv_var_free(corpse, um_flags);
    284 }
    285 
    286 void
    287 mdb_nv_remove(mdb_nv_t *nv, mdb_var_t *corpse)
    288 {
    289 	const char *cname = NV_NAME(corpse);
    290 	size_t i = nv_hashstring(cname) % nv->nv_hashsz;
    291 	mdb_var_t *v = nv->nv_hash[i];
    292 	mdb_var_t **pvp;
    293 
    294 	if (corpse->v_flags & MDB_NV_PERSIST) {
    295 		warn("cannot remove persistent variable '%s'\n", cname);
    296 		return;
    297 	}
    298 
    299 	if (v != corpse) {
    300 		do {
    301 			if (strcmp(NV_NAME(v), cname) == 0) {
    302 				if (corpse->v_flags & MDB_NV_OVERLOAD) {
    303 					nv_var_defn_remove(v, corpse,
    304 					    nv->nv_um_flags);
    305 					return; /* No v_next changes needed */
    306 				} else
    307 					goto notfound;
    308 			}
    309 
    310 			if (v->v_next == corpse)
    311 				break; /* Corpse is next on the chain */
    312 
    313 		} while ((v = v->v_next) != NULL);
    314 
    315 		if (v == NULL)
    316 			goto notfound;
    317 
    318 		pvp = &v->v_next;
    319 	} else
    320 		pvp = &nv->nv_hash[i];
    321 
    322 	if ((corpse->v_flags & MDB_NV_OVERLOAD) && corpse->v_ndef != NULL) {
    323 		corpse->v_ndef->v_next = corpse->v_next;
    324 		*pvp = corpse->v_ndef;
    325 		corpse->v_ndef = NULL;
    326 	} else {
    327 		*pvp = corpse->v_next;
    328 		nv->nv_nelems--;
    329 	}
    330 
    331 	nv_var_free(corpse, nv->nv_um_flags);
    332 	return;
    333 
    334 notfound:
    335 	fail("var %p ('%s') not found on hash chain: nv=%p [%lu]\n",
    336 	    (void *)corpse, cname, (void *)nv, (ulong_t)i);
    337 }
    338 
    339 void
    340 mdb_nv_rewind(mdb_nv_t *nv)
    341 {
    342 	size_t i;
    343 
    344 	for (i = 0; i < nv->nv_hashsz; i++) {
    345 		if (nv->nv_hash[i] != NULL)
    346 			break;
    347 	}
    348 
    349 	nv->nv_iter_elt = i < nv->nv_hashsz ? nv->nv_hash[i] : NULL;
    350 	nv->nv_iter_bucket = i;
    351 }
    352 
    353 mdb_var_t *
    354 mdb_nv_advance(mdb_nv_t *nv)
    355 {
    356 	mdb_var_t *v = nv->nv_iter_elt;
    357 	size_t i;
    358 
    359 	if (v == NULL)
    360 		return (NULL);
    361 
    362 	if (v->v_next != NULL) {
    363 		nv->nv_iter_elt = v->v_next;
    364 		return (v);
    365 	}
    366 
    367 	for (i = nv->nv_iter_bucket + 1; i < nv->nv_hashsz; i++) {
    368 		if (nv->nv_hash[i] != NULL)
    369 			break;
    370 	}
    371 
    372 	nv->nv_iter_elt = i < nv->nv_hashsz ? nv->nv_hash[i] : NULL;
    373 	nv->nv_iter_bucket = i;
    374 
    375 	return (v);
    376 }
    377 
    378 mdb_var_t *
    379 mdb_nv_peek(mdb_nv_t *nv)
    380 {
    381 	return (nv->nv_iter_elt);
    382 }
    383 
    384 size_t
    385 mdb_nv_size(mdb_nv_t *nv)
    386 {
    387 	return (nv->nv_nelems);
    388 }
    389 
    390 static int
    391 nv_compare(const mdb_var_t **lp, const mdb_var_t **rp)
    392 {
    393 	return (strcmp(mdb_nv_get_name(*lp), mdb_nv_get_name(*rp)));
    394 }
    395 
    396 void
    397 mdb_nv_sort_iter(mdb_nv_t *nv, int (*func)(mdb_var_t *, void *),
    398     void *private, uint_t um_flags)
    399 {
    400 	mdb_var_t **vps =
    401 	    mdb_alloc(nv->nv_nelems * sizeof (mdb_var_t *), um_flags);
    402 
    403 	if (nv->nv_nelems != 0 && vps != NULL) {
    404 		mdb_var_t *v, **vpp = vps;
    405 		size_t i;
    406 
    407 		for (mdb_nv_rewind(nv); (v = mdb_nv_advance(nv)) != NULL; )
    408 			*vpp++ = v;
    409 
    410 		qsort(vps, nv->nv_nelems, sizeof (mdb_var_t *),
    411 		    (int (*)(const void *, const void *))nv_compare);
    412 
    413 		for (vpp = vps, i = 0; i < nv->nv_nelems; i++) {
    414 			if (func(*vpp++, private) == -1)
    415 				break;
    416 		}
    417 
    418 		if (!(um_flags & UM_GC))
    419 			mdb_free(vps, nv->nv_nelems * sizeof (mdb_var_t *));
    420 	}
    421 }
    422 
    423 void
    424 mdb_nv_defn_iter(mdb_var_t *v, int (*func)(mdb_var_t *, void *), void *private)
    425 {
    426 	if (func(v, private) == -1 || !(v->v_flags & MDB_NV_OVERLOAD))
    427 		return;
    428 
    429 	for (v = v->v_ndef; v != NULL; v = v->v_ndef) {
    430 		if (func(v, private) == -1)
    431 			break;
    432 	}
    433 }
    434 
    435 uintmax_t
    436 mdb_nv_get_value(const mdb_var_t *v)
    437 {
    438 	if (v->v_disc)
    439 		return (v->v_disc->disc_get(v));
    440 
    441 	return (v->v_uvalue);
    442 }
    443 
    444 void
    445 mdb_nv_set_value(mdb_var_t *v, uintmax_t l)
    446 {
    447 	if (v->v_flags & MDB_NV_RDONLY) {
    448 		warn("cannot modify read-only variable '%s'\n", NV_NAME(v));
    449 		return;
    450 	}
    451 
    452 	if (v->v_disc)
    453 		v->v_disc->disc_set(v, l);
    454 	else
    455 		v->v_uvalue = l;
    456 }
    457 
    458 void *
    459 mdb_nv_get_cookie(const mdb_var_t *v)
    460 {
    461 	if (v->v_disc)
    462 		return ((void *)(uintptr_t)v->v_disc->disc_get(v));
    463 
    464 	return (MDB_NV_COOKIE(v));
    465 }
    466 
    467 void
    468 mdb_nv_set_cookie(mdb_var_t *v, void *cookie)
    469 {
    470 	mdb_nv_set_value(v, (uintmax_t)(uintptr_t)cookie);
    471 }
    472 
    473 const char *
    474 mdb_nv_get_name(const mdb_var_t *v)
    475 {
    476 	return (NV_NAME(v));
    477 }
    478 
    479 mdb_var_t *
    480 mdb_nv_get_ndef(const mdb_var_t *v)
    481 {
    482 	if (v->v_flags & MDB_NV_OVERLOAD)
    483 		return (v->v_ndef);
    484 
    485 	return (NULL);
    486 }
    487