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
nv_hashstring(const char * key)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 *
nv_var_alloc(const char * name,const mdb_nv_disc_t * disc,uintmax_t value,uint_t flags,uint_t um_flags,mdb_var_t * next)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
nv_var_free(mdb_var_t * v,uint_t um_flags)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 *
mdb_nv_create(mdb_nv_t * nv,uint_t um_flags)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
mdb_nv_destroy(mdb_nv_t * nv)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 *
mdb_nv_lookup(mdb_nv_t * nv,const char * name)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 *
nv_var_interpos(mdb_nv_t * nv,size_t i,mdb_var_t * v,mdb_var_t * w)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 *
nv_var_overload(mdb_var_t * v,mdb_var_t * w)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 *
mdb_nv_insert(mdb_nv_t * nv,const char * name,const mdb_nv_disc_t * disc,uintmax_t value,uint_t flags)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
nv_var_defn_remove(mdb_var_t * v,mdb_var_t * corpse,uint_t um_flags)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
mdb_nv_remove(mdb_nv_t * nv,mdb_var_t * corpse)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
mdb_nv_rewind(mdb_nv_t * nv)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 *
mdb_nv_advance(mdb_nv_t * nv)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 *
mdb_nv_peek(mdb_nv_t * nv)379 mdb_nv_peek(mdb_nv_t *nv)
380 {
381 return (nv->nv_iter_elt);
382 }
383
384 size_t
mdb_nv_size(mdb_nv_t * nv)385 mdb_nv_size(mdb_nv_t *nv)
386 {
387 return (nv->nv_nelems);
388 }
389
390 static int
nv_compare(const mdb_var_t ** lp,const mdb_var_t ** rp)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
mdb_nv_sort_iter(mdb_nv_t * nv,int (* func)(mdb_var_t *,void *),void * private,uint_t um_flags)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
mdb_nv_defn_iter(mdb_var_t * v,int (* func)(mdb_var_t *,void *),void * private)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
mdb_nv_get_value(const mdb_var_t * v)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
mdb_nv_set_value(mdb_var_t * v,uintmax_t l)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 *
mdb_nv_get_cookie(const mdb_var_t * v)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
mdb_nv_set_cookie(mdb_var_t * v,void * cookie)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 *
mdb_nv_get_name(const mdb_var_t * v)474 mdb_nv_get_name(const mdb_var_t *v)
475 {
476 return (NV_NAME(v));
477 }
478
479 mdb_var_t *
mdb_nv_get_ndef(const mdb_var_t * v)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