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