xref: /illumos-gate/usr/src/lib/libdtrace_jni/common/dtj_util.c (revision 03100a6332bd4edc7a53091fcf7c9a7131bcdaa7)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2007 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 <stdlib.h>
30 #include <stddef.h>
31 #include <sys/types.h>
32 #include <pthread.h>
33 #include <string.h>
34 #include <dtj_util.h>
35 
36 /*
37  * dtj_util.c separates functionality that is generally useful from
38  * that which is specific to the Java DTrace API.  If moved to a separate
39  * library, this functionality could be shared by other JNI wrappers.
40  */
41 
42 boolean_t g_dtj_util_debug = B_FALSE;
43 static boolean_t g_dtj_load_common = B_FALSE;
44 
45 /* NativeException */
46 jclass g_nx_jc = 0;
47 jmethodID g_nxinit_jm = 0;
48 
49 /* java.io.Serializable */
50 jclass g_serial_jc = 0;
51 
52 /* java.lang.Number */
53 jclass g_number_jc = 0;
54 jmethodID g_shortval_jm = 0;
55 jmethodID g_intval_jm = 0;
56 jmethodID g_longval_jm = 0;
57 
58 /* java.lang.Byte */
59 jclass g_byte_jc = 0;
60 jmethodID g_byteinit_jm = 0;
61 
62 /* java.lang.Character */
63 jclass g_char_jc = 0;
64 jmethodID g_charinit_jm = 0;
65 jmethodID g_charval_jm = 0;
66 
67 /* java.lang.Short */
68 jclass g_short_jc = 0;
69 jmethodID g_shortinit_jm = 0;
70 
71 /* java.lang.Integer */
72 jclass g_int_jc = 0;
73 jmethodID g_intinit_jm = 0;
74 
75 /* java.lang.Long */
76 jclass g_long_jc = 0;
77 jmethodID g_longinit_jm = 0;
78 
79 /* java.lang.String */
80 jclass g_string_jc = 0;
81 jmethodID g_strinit_bytes_jm = 0;
82 jmethodID g_strbytes_jm = 0;
83 jmethodID g_trim_jm = 0;
84 
85 /* java.lang.StringBuilder */
86 jclass g_buf_jc = 0;
87 jmethodID g_bufinit_jm = 0;
88 jmethodID g_buf_append_char_jm = 0;
89 jmethodID g_buf_append_int_jm = 0;
90 jmethodID g_buf_append_long_jm = 0;
91 jmethodID g_buf_append_str_jm = 0;
92 jmethodID g_buf_append_obj_jm = 0;
93 jmethodID g_buflen_jm = 0;
94 jmethodID g_bufsetlen_jm = 0;
95 
96 /* java.lang.Object */
97 jclass g_object_jc = 0;
98 jmethodID g_tostring_jm = 0;
99 jmethodID g_equals_jm = 0;
100 
101 /* java.lang.Enum */
102 jclass g_enum_jc = 0;
103 jmethodID g_enumname_jm = 0;
104 
105 /* List */
106 jclass g_list_jc = 0;
107 jmethodID g_listclear_jm = 0;
108 jmethodID g_listadd_jm = 0;
109 jmethodID g_listget_jm = 0;
110 jmethodID g_listsize_jm = 0;
111 
112 /* Global list pools */
113 static uu_list_pool_t *g_pointer_pool = NULL;
114 static uu_list_pool_t *g_string_pool = NULL;
115 
116 static dtj_status_t dtj_get_jni_classes(JNIEnv *, uu_list_t *, uu_list_pool_t *,
117     uu_list_pool_t *, uu_list_pool_t *, const dtj_table_entry_t *);
118 static dtj_status_t dtj_cache_jni_methods(JNIEnv *, dtj_java_class_t *);
119 static dtj_status_t dtj_cache_jni_fields(JNIEnv *, dtj_java_class_t *);
120 
121 /* Constructors */
122 static dtj_java_class_t *dtj_java_class_create(JNIEnv *, jclass *, char *,
123     uu_list_pool_t *, uu_list_pool_t *, uu_list_pool_t *);
124 static dtj_java_method_t *dtj_java_method_create(JNIEnv *, jmethodID *, char *,
125     char *, uu_list_pool_t *);
126 static dtj_java_method_t *dtj_java_static_method_create(JNIEnv *, jmethodID *,
127     char *, char *, uu_list_pool_t *);
128 static dtj_java_field_t *dtj_java_field_create(JNIEnv *, jfieldID *, char *,
129     char *, uu_list_pool_t *);
130 static dtj_java_field_t *dtj_java_static_field_create(JNIEnv *, jfieldID *,
131     char *, char *, uu_list_pool_t *);
132 
133 /* Destructors */
134 static void dtj_java_class_destroy(void *, void *);
135 static void dtj_java_method_destroy(void *, void *);
136 static void dtj_java_field_destroy(void *, void *);
137 
138 /* Comparison functions, uu_compare_fn_t signature */
139 static int dtj_java_class_cmp(const void *, const void *, void *);
140 static int dtj_java_method_cmp(const void *, const void *, void *);
141 static int dtj_java_field_cmp(const void *, const void *, void *);
142 
143 /* Java Throwable */
144 static void dtj_throw(JNIEnv *, jclass, const char *, va_list *);
145 
146 /* Support for uu_list_t wrappers */
147 static boolean_t dtj_check_pointer_pool(void);
148 static boolean_t dtj_check_string_pool(void);
149 
150 dtj_status_t
151 dtj_load_common(JNIEnv *jenv)
152 {
153 	dtj_status_t status;
154 
155 	static const dtj_table_entry_t table[] = {
156 		/* NativeException */
157 		{ JCLASS,  &g_nx_jc,
158 			"org/opensolaris/os/dtrace/NativeException" },
159 		{ JMETHOD, &g_nxinit_jm, CONSTRUCTOR,
160 			"(Ljava/lang/String;ILjava/lang/Throwable;)V" },
161 
162 		/* java.io.Serializable */
163 		{ JCLASS,  &g_serial_jc, "java/io/Serializable" },
164 
165 		/* java.lang.Number */
166 		{ JCLASS,  &g_number_jc, "java/lang/Number" },
167 		{ JMETHOD, &g_shortval_jm, "shortValue", "()S" },
168 		{ JMETHOD, &g_intval_jm, "intValue", "()I" },
169 		{ JMETHOD, &g_longval_jm, "longValue", "()J" },
170 
171 		/* java.lang.Byte */
172 		{ JCLASS,  &g_byte_jc, "java/lang/Byte" },
173 		{ JMETHOD, &g_byteinit_jm, CONSTRUCTOR, "(B)V" },
174 
175 		/* java.lang.Character */
176 		{ JCLASS,  &g_char_jc, "java/lang/Character" },
177 		{ JMETHOD, &g_charinit_jm, CONSTRUCTOR, "(C)V" },
178 		{ JMETHOD, &g_charval_jm, "charValue", "()C" },
179 
180 		/* java.lang.Short */
181 		{ JCLASS,  &g_short_jc, "java/lang/Short" },
182 		{ JMETHOD, &g_shortinit_jm, CONSTRUCTOR, "(S)V" },
183 
184 		/* java.lang.Integer */
185 		{ JCLASS,  &g_int_jc, "java/lang/Integer" },
186 		{ JMETHOD, &g_intinit_jm, CONSTRUCTOR, "(I)V" },
187 
188 		/* java.lang.Long */
189 		{ JCLASS,  &g_long_jc, "java/lang/Long" },
190 		{ JMETHOD, &g_longinit_jm, CONSTRUCTOR, "(J)V" },
191 
192 		/* java.lang.String */
193 		{ JCLASS,  &g_string_jc, "java/lang/String" },
194 		{ JMETHOD, &g_strinit_bytes_jm, CONSTRUCTOR, "([B)V" },
195 		{ JMETHOD, &g_strbytes_jm, "getBytes", "()[B" },
196 		{ JMETHOD, &g_trim_jm, "trim", "()Ljava/lang/String;" },
197 
198 		/* java.lang.StringBuilder */
199 		{ JCLASS,  &g_buf_jc, "java/lang/StringBuilder" },
200 		{ JMETHOD, &g_bufinit_jm, CONSTRUCTOR, "()V" },
201 		{ JMETHOD, &g_buf_append_char_jm, "append",
202 			"(C)Ljava/lang/StringBuilder;" },
203 		{ JMETHOD, &g_buf_append_int_jm, "append",
204 			"(I)Ljava/lang/StringBuilder;" },
205 		{ JMETHOD, &g_buf_append_long_jm, "append",
206 			"(J)Ljava/lang/StringBuilder;" },
207 		{ JMETHOD, &g_buf_append_str_jm, "append",
208 			"(Ljava/lang/String;)Ljava/lang/StringBuilder;" },
209 		{ JMETHOD, &g_buf_append_obj_jm, "append",
210 			"(Ljava/lang/Object;)Ljava/lang/StringBuilder;" },
211 		{ JMETHOD, &g_buflen_jm, "length", "()I" },
212 		{ JMETHOD, &g_bufsetlen_jm, "setLength", "(I)V" },
213 
214 		/* java.lang.Object */
215 		{ JCLASS,  &g_object_jc, "java/lang/Object" },
216 		{ JMETHOD, &g_tostring_jm, "toString",
217 			"()Ljava/lang/String;" },
218 		{ JMETHOD, &g_equals_jm, "equals",
219 			"(Ljava/lang/Object;)Z" },
220 
221 		/* java.lang.Enum */
222 		{ JCLASS,  &g_enum_jc, "java/lang/Enum" },
223 		{ JMETHOD, &g_enumname_jm, "name",
224 			"()Ljava/lang/String;" },
225 
226 		/* List */
227 		{ JCLASS, &g_list_jc, "java/util/List" },
228 		{ JMETHOD, &g_listclear_jm, "clear", "()V" },
229 		{ JMETHOD, &g_listadd_jm, "add", "(Ljava/lang/Object;)Z" },
230 		{ JMETHOD, &g_listget_jm, "get", "(I)Ljava/lang/Object;" },
231 		{ JMETHOD, &g_listsize_jm, "size", "()I" },
232 
233 		{ DTJ_TYPE_END }
234 	};
235 
236 	status = dtj_cache_jni_classes(jenv, table);
237 	if (status == DTJ_OK) {
238 		g_dtj_load_common = B_TRUE;
239 	}
240 	return (status);
241 }
242 
243 static int
244 /* ARGSUSED */
245 dtj_java_class_cmp(const void * v1, const void * v2, void *arg)
246 {
247 	const dtj_java_class_t *c1 = v1;
248 	const dtj_java_class_t *c2 = v2;
249 	return (strcmp(c1->djc_name, c2->djc_name));
250 }
251 
252 static int
253 /* ARGSUSED */
254 dtj_java_method_cmp(const void *v1, const void *v2, void *arg)
255 {
256 	int cmp;
257 	const dtj_java_method_t *m1 = v1;
258 	const dtj_java_method_t *m2 = v2;
259 	cmp = strcmp(m1->djm_name, m2->djm_name);
260 	if (cmp == 0) {
261 		cmp = strcmp(m1->djm_signature, m2->djm_signature);
262 	}
263 	return (cmp);
264 }
265 
266 static int
267 /* ARGSUSED */
268 dtj_java_field_cmp(const void *v1, const void *v2, void *arg)
269 {
270 	const dtj_java_field_t *f1 = v1;
271 	const dtj_java_field_t *f2 = v2;
272 	return (strcmp(f1->djf_name, f2->djf_name));
273 }
274 
275 static dtj_java_class_t *
276 dtj_java_class_create(JNIEnv *jenv, jclass *jc, char *name,
277     uu_list_pool_t *classpool, uu_list_pool_t *methodpool,
278     uu_list_pool_t *fieldpool)
279 {
280 	dtj_java_class_t *c = uu_zalloc(sizeof (dtj_java_class_t));
281 	if (c) {
282 		uu_list_node_init(c, &c->djc_node, classpool);
283 		c->djc_ptr = jc;
284 		c->djc_name = name;
285 		c->djc_methods = uu_list_create(methodpool, NULL,
286 		    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
287 		if (!c->djc_methods) {
288 			dtj_throw_out_of_memory(jenv,
289 			    "Failed method list creation");
290 			uu_list_node_fini(c, &c->djc_node, classpool);
291 			free(c);
292 			c = NULL;
293 		}
294 		c->djc_fields = uu_list_create(fieldpool, NULL,
295 		    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
296 		if (!c->djc_fields) {
297 			dtj_throw_out_of_memory(jenv,
298 			    "Failed field list creation");
299 			uu_list_destroy(c->djc_methods);
300 			c->djc_methods = NULL;
301 			uu_list_node_fini(c, &c->djc_node, classpool);
302 			free(c);
303 			c = NULL;
304 		}
305 	} else {
306 		dtj_throw_out_of_memory(jenv,
307 		    "Failed to allocate class description");
308 	}
309 	return (c);
310 }
311 
312 static dtj_java_method_t *
313 dtj_java_method_create(JNIEnv *jenv, jmethodID *jm, char *name, char *signature,
314     uu_list_pool_t *methodpool)
315 {
316 	dtj_java_method_t *m = uu_zalloc(sizeof (dtj_java_method_t));
317 	if (m) {
318 		uu_list_node_init(m, &m->djm_node, methodpool);
319 		m->djm_ptr = jm;
320 		m->djm_name = name;
321 		m->djm_signature = signature;
322 		m->djm_static = B_FALSE;
323 	} else {
324 		dtj_throw_out_of_memory(jenv,
325 		    "Failed to allocate method description");
326 	}
327 	return (m);
328 }
329 
330 static dtj_java_method_t *
331 dtj_java_static_method_create(JNIEnv *jenv, jmethodID *jm, char *name,
332     char *signature, uu_list_pool_t *methodpool)
333 {
334 	dtj_java_method_t *m = dtj_java_method_create(jenv, jm, name, signature,
335 	    methodpool);
336 	if (m) {
337 		m->djm_static = B_TRUE;
338 	}
339 	return (m);
340 }
341 
342 static dtj_java_field_t *
343 dtj_java_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type,
344     uu_list_pool_t *fieldpool)
345 {
346 	dtj_java_field_t *f = uu_zalloc(sizeof (dtj_java_field_t));
347 	if (f) {
348 		uu_list_node_init(f, &f->djf_node, fieldpool);
349 		f->djf_ptr = jf;
350 		f->djf_name = name;
351 		f->djf_type = type;
352 		f->djf_static = B_FALSE;
353 	} else {
354 		dtj_throw_out_of_memory(jenv,
355 		    "Failed to allocate field description");
356 	}
357 	return (f);
358 }
359 
360 static dtj_java_field_t *
361 dtj_java_static_field_create(JNIEnv *jenv, jfieldID *jf, char *name, char *type,
362     uu_list_pool_t *fieldpool)
363 {
364 	dtj_java_field_t *f = dtj_java_field_create(jenv, jf, name, type,
365 	    fieldpool);
366 	if (f) {
367 		f->djf_static = B_TRUE;
368 	}
369 	return (f);
370 }
371 
372 static void
373 /* ARGSUSED */
374 dtj_java_class_destroy(void *v, void *arg)
375 {
376 	if (v) {
377 		dtj_java_class_t *c = v;
378 		c->djc_ptr = NULL;  /* do not free user-defined storage */
379 		c->djc_name = NULL; /* string literal */
380 		dtj_list_destroy(c->djc_methods, dtj_java_method_destroy, NULL);
381 		dtj_list_destroy(c->djc_fields, dtj_java_field_destroy, NULL);
382 		c->djc_methods = NULL;
383 		c->djc_fields = NULL;
384 		uu_free(v);
385 	}
386 }
387 
388 static void
389 /* ARGSUSED */
390 dtj_java_method_destroy(void *v, void *arg)
391 {
392 	if (v) {
393 		dtj_java_method_t *m = v;
394 		m->djm_ptr = NULL;	/* do not free user-defined space */
395 		m->djm_name = NULL;	/* string literal */
396 		m->djm_signature = NULL;	/* string literal */
397 		uu_free(v);
398 	}
399 }
400 
401 static void
402 /* ARGSUSED */
403 dtj_java_field_destroy(void *v, void *arg)
404 {
405 	if (v) {
406 		dtj_java_field_t *f = v;
407 		f->djf_ptr = NULL;  /* do not free user-defined space */
408 		f->djf_name = NULL; /* string literal */
409 		f->djf_type = NULL; /* string literal */
410 		uu_free(f);
411 	}
412 }
413 
414 dtj_status_t
415 dtj_cache_jni_classes(JNIEnv *jenv, const dtj_table_entry_t *table)
416 {
417 	dtj_java_class_t *class;
418 	uu_list_pool_t *classpool;
419 	uu_list_pool_t *methodpool;
420 	uu_list_pool_t *fieldpool;
421 	uu_list_t *classes;
422 	uu_list_walk_t *itr;
423 	jclass jc;
424 	jclass gjc;
425 	dtj_status_t status;
426 
427 	classpool = uu_list_pool_create("classpool",
428 	    sizeof (dtj_java_class_t),
429 	    offsetof(dtj_java_class_t, djc_node), dtj_java_class_cmp,
430 	    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
431 	if (!classpool) {
432 		dtj_throw_out_of_memory(jenv, "failed class pool creation");
433 		return (DTJ_ERR);
434 	}
435 	methodpool = uu_list_pool_create("methodpool",
436 	    sizeof (dtj_java_method_t),
437 	    offsetof(dtj_java_method_t, djm_node), dtj_java_method_cmp,
438 	    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
439 	if (!methodpool) {
440 		dtj_throw_out_of_memory(jenv, "failed method pool creation");
441 		return (DTJ_ERR);
442 	}
443 	fieldpool = uu_list_pool_create("fieldpool",
444 	    sizeof (dtj_java_field_t),
445 	    offsetof(dtj_java_field_t, djf_node), dtj_java_field_cmp,
446 	    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
447 	if (!fieldpool) {
448 		dtj_throw_out_of_memory(jenv, "failed field pool creation");
449 		return (DTJ_ERR);
450 	}
451 
452 	classes = uu_list_create(classpool, NULL,
453 	    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
454 	if (!classes) {
455 		dtj_throw_out_of_memory(jenv, "failed class list creation");
456 		return (DTJ_ERR);
457 	}
458 
459 	status = dtj_get_jni_classes(jenv, classes, classpool, methodpool,
460 	    fieldpool, table);
461 	if (status != DTJ_OK) {
462 		/* java error pending */
463 		return (status);
464 	}
465 
466 	itr = uu_list_walk_start(classes, 0);
467 	while ((class = uu_list_walk_next(itr)) != NULL) {
468 		jc = (*jenv)->FindClass(jenv, class->djc_name);
469 		if (!jc) {
470 			/* NoClassDefFoundError pending */
471 			return (DTJ_ERR);
472 		}
473 		gjc = (*jenv)->NewGlobalRef(jenv, jc);
474 		(*jenv)->DeleteLocalRef(jenv, jc);
475 		if (!gjc) {
476 			dtj_throw_out_of_memory(jenv,
477 			    "Failed to create global class reference");
478 			return (DTJ_ERR);
479 		}
480 		*(class->djc_ptr) = gjc;
481 		status = dtj_cache_jni_methods(jenv, class);
482 		if (status != DTJ_OK) {
483 			/* java error pending */
484 			return (status);
485 		}
486 		status = dtj_cache_jni_fields(jenv, class);
487 		if (status != DTJ_OK) {
488 			/* java error pending */
489 			return (status);
490 		}
491 	}
492 	uu_list_walk_end(itr);
493 	dtj_list_destroy(classes, dtj_java_class_destroy, NULL);
494 	uu_list_pool_destroy(classpool);
495 	uu_list_pool_destroy(methodpool);
496 	uu_list_pool_destroy(fieldpool);
497 	return (DTJ_OK);
498 }
499 
500 /*
501  * Converts JNI table entry desriptions into java_class_t descriptors.
502  */
503 static dtj_status_t
504 dtj_get_jni_classes(JNIEnv *jenv, uu_list_t *classes,
505     uu_list_pool_t *classpool, uu_list_pool_t *methodpool,
506     uu_list_pool_t *fieldpool, const dtj_table_entry_t *table)
507 {
508 	int i;
509 	dtj_java_class_t *c = NULL;
510 	dtj_java_method_t *m;
511 	dtj_java_field_t *f;
512 
513 	for (i = 0; table[i].djte_type != DTJ_TYPE_END; ++i) {
514 		/*
515 		 * Class not added until all of its method and field information
516 		 * is attached, so we defer adding a class until the next
517 		 * element with type JCLASS.
518 		 */
519 		switch (table[i].djte_type) {
520 		case JCLASS:
521 			if (c) {
522 				/* previous class */
523 				if (!dtj_list_add(classes, c)) {
524 					dtj_throw_out_of_memory(jenv,
525 					    "Failed to add class description");
526 					/*
527 					 * In response to an error return value,
528 					 * the caller will delete the class
529 					 * descriptions list with any
530 					 * descriptions created so far.
531 					 */
532 					return (DTJ_ERR);
533 				}
534 			}
535 			c = dtj_java_class_create(jenv,
536 			    (jclass *)table[i].djte_addr, table[i].djte_name,
537 			    classpool, methodpool, fieldpool);
538 			if (!c) {
539 				/* OutOfMemoryError pending */
540 				return (DTJ_ERR);
541 			}
542 			break;
543 		case JMETHOD:
544 			if (!c) {
545 				dtj_throw_illegal_state(jenv,
546 				    "method description not preceded "
547 				    "by class description");
548 				return (DTJ_ERR);
549 			}
550 			m = dtj_java_method_create(jenv,
551 			    (jmethodID *)table[i].djte_addr,
552 			    table[i].djte_name, table[i].djte_desc,
553 			    methodpool);
554 			if (!m) {
555 				/* OutOfMemoryError pending */
556 				return (DTJ_ERR);
557 			}
558 			if (!dtj_list_add(c->djc_methods, m)) {
559 				dtj_throw_out_of_memory(jenv,
560 				    "Failed to add method description");
561 				return (DTJ_ERR);
562 			}
563 			break;
564 		case JMETHOD_STATIC:
565 			if (!c) {
566 				dtj_throw_illegal_state(jenv,
567 				    "static method description not preceded "
568 				    "by class description");
569 				return (DTJ_ERR);
570 			}
571 			m = dtj_java_static_method_create(jenv,
572 			    (jmethodID *)table[i].djte_addr,
573 			    table[i].djte_name, table[i].djte_desc,
574 			    methodpool);
575 			if (!m) {
576 				/* OutOfMemoryError pending */
577 				return (DTJ_ERR);
578 			}
579 			if (!dtj_list_add(c->djc_methods, m)) {
580 				dtj_throw_out_of_memory(jenv,
581 				    "Failed to add static method description");
582 				return (DTJ_ERR);
583 			}
584 			break;
585 		case JFIELD:
586 			if (!c) {
587 				dtj_throw_illegal_state(jenv,
588 				    "field description not preceded "
589 				    "by class description");
590 				return (DTJ_ERR);
591 			}
592 			f = dtj_java_field_create(jenv,
593 			    (jfieldID *)table[i].djte_addr,
594 			    table[i].djte_name, table[i].djte_desc,
595 			    fieldpool);
596 			if (!f) {
597 				/* OutOfMemoryError pending */
598 				return (DTJ_ERR);
599 			}
600 			if (!dtj_list_add(c->djc_fields, f)) {
601 				dtj_throw_out_of_memory(jenv,
602 				    "Failed to add field description");
603 				return (DTJ_ERR);
604 			}
605 			break;
606 		case JFIELD_STATIC:
607 			if (!c) {
608 				dtj_throw_illegal_state(jenv,
609 				    "static field description not preceded "
610 				    "by class description");
611 				return (DTJ_ERR);
612 			}
613 			f = dtj_java_static_field_create(jenv,
614 			    (jfieldID *)table[i].djte_addr,
615 			    table[i].djte_name, table[i].djte_desc,
616 			    fieldpool);
617 			if (!f) {
618 				/* OutOfMemoryError pending */
619 				return (DTJ_ERR);
620 			}
621 			if (!dtj_list_add(c->djc_fields, f)) {
622 				dtj_throw_out_of_memory(jenv,
623 				    "Failed to add static field description");
624 				return (DTJ_ERR);
625 			}
626 			break;
627 		default:
628 			dtj_throw_illegal_state(jenv,
629 			    "Unexpected jni_type_e: %d", table[i].djte_type);
630 			return (DTJ_ERR);
631 		}
632 	}
633 	if (c) {
634 		/* last class */
635 		if (!dtj_list_add(classes, c)) {
636 			dtj_throw_out_of_memory(jenv,
637 			    "Failed to add class description");
638 			return (DTJ_ERR);
639 		}
640 	}
641 
642 	return (DTJ_OK);
643 }
644 
645 static dtj_status_t
646 dtj_cache_jni_methods(JNIEnv *jenv, dtj_java_class_t *c)
647 {
648 	dtj_java_method_t *method;
649 	jmethodID jm;
650 	uu_list_walk_t *itr;
651 	itr = uu_list_walk_start(c->djc_methods, 0);
652 	while ((method = uu_list_walk_next(itr)) != NULL) {
653 		if (method->djm_static) {
654 			jm = (*jenv)->GetStaticMethodID(jenv, *(c->djc_ptr),
655 			    method->djm_name, method->djm_signature);
656 		} else {
657 			jm = (*jenv)->GetMethodID(jenv, *(c->djc_ptr),
658 			    method->djm_name, method->djm_signature);
659 		}
660 		if (jm == 0) {
661 			/*
662 			 * The pending NoSuchMethodError gives only the
663 			 * method name, which is not so helpful for
664 			 * overloaded methods and methods such as <init>
665 			 * that have the same name in multiple classes.
666 			 * Clear the pending error and throw one that
667 			 * includes the class name and the method
668 			 * signature.
669 			 */
670 			jclass jc;
671 			char msg[DTJ_MSG_SIZE];
672 			(*jenv)->ExceptionClear(jenv);
673 			(void) snprintf(msg, sizeof (msg), "%s %s %s",
674 			    c->djc_name, method->djm_name,
675 			    method->djm_signature);
676 
677 			jc = (*jenv)->FindClass(jenv,
678 			    "java/lang/NoSuchMethodError");
679 			(*jenv)->ThrowNew(jenv, jc, msg);
680 			(*jenv)->DeleteLocalRef(jenv, jc);
681 			return (DTJ_ERR);
682 		}
683 		*(method->djm_ptr) = jm;
684 	}
685 	uu_list_walk_end(itr);
686 	return (DTJ_OK);
687 }
688 
689 static dtj_status_t
690 dtj_cache_jni_fields(JNIEnv *jenv, dtj_java_class_t *c)
691 {
692 	dtj_java_field_t *field;
693 	jfieldID jf;
694 	uu_list_walk_t *itr;
695 	itr = uu_list_walk_start(c->djc_fields, 0);
696 	while ((field = uu_list_walk_next(itr)) != NULL) {
697 		if (field->djf_static) {
698 			jf = (*jenv)->GetStaticFieldID(jenv, *(c->djc_ptr),
699 			    field->djf_name, field->djf_type);
700 		} else {
701 			jf = (*jenv)->GetFieldID(jenv, *(c->djc_ptr),
702 			    field->djf_name, field->djf_type);
703 		}
704 		if (jf == 0) {
705 			jclass jc;
706 			char msg[DTJ_MSG_SIZE];
707 			(*jenv)->ExceptionClear(jenv);
708 			(void) snprintf(msg, sizeof (msg),
709 			    "%s.%s signature: %s", c->djc_name,
710 			    field->djf_name, field->djf_type);
711 
712 			jc = (*jenv)->FindClass(jenv,
713 			    "java/lang/NoSuchFieldError");
714 			(*jenv)->ThrowNew(jenv, jc, msg);
715 			(*jenv)->DeleteLocalRef(jenv, jc);
716 			return (DTJ_ERR);
717 		}
718 		*(field->djf_ptr) = jf;
719 	}
720 	uu_list_walk_end(itr);
721 	return (DTJ_OK);
722 }
723 
724 
725 /* Common utilities */
726 
727 static void
728 dtj_throw(JNIEnv *jenv, jclass jc, const char *fmt, va_list *ap)
729 {
730 	char msg[DTJ_MSG_SIZE];
731 	(void) vsnprintf(msg, sizeof (msg), fmt, *ap);
732 	(*jenv)->ThrowNew(jenv, jc, msg);
733 }
734 
735 void
736 dtj_throw_out_of_memory(JNIEnv *jenv, const char *fmt, ...)
737 {
738 	va_list ap;
739 	jclass jc;
740 	/*
741 	 * JNI documentation unclear whether NewGlobalRef() can throw
742 	 * OutOfMemoryError, so we'll make this function safe in case
743 	 * OutOfMemoryError has already been thrown
744 	 */
745 	if ((*jenv)->ExceptionCheck(jenv)) {
746 		return;
747 	}
748 	jc = (*jenv)->FindClass(jenv,
749 	    "java/lang/OutOfMemoryError");
750 	va_start(ap, fmt);
751 	dtj_throw(jenv, jc, fmt, &ap);
752 	(*jenv)->DeleteLocalRef(jenv, jc);
753 	va_end(ap);
754 }
755 
756 void
757 dtj_throw_null_pointer(JNIEnv *jenv, const char *fmt, ...)
758 {
759 	va_list ap;
760 	jclass jc = (*jenv)->FindClass(jenv,
761 	    "java/lang/NullPointerException");
762 	va_start(ap, fmt);
763 	dtj_throw(jenv, jc, fmt, &ap);
764 	(*jenv)->DeleteLocalRef(jenv, jc);
765 	va_end(ap);
766 }
767 
768 void
769 dtj_throw_illegal_state(JNIEnv *jenv, const char *fmt, ...)
770 {
771 	va_list ap;
772 	jclass jc = (*jenv)->FindClass(jenv,
773 	    "java/lang/IllegalStateException");
774 	va_start(ap, fmt);
775 	dtj_throw(jenv, jc, fmt, &ap);
776 	(*jenv)->DeleteLocalRef(jenv, jc);
777 	va_end(ap);
778 }
779 
780 void
781 dtj_throw_illegal_argument(JNIEnv *jenv, const char *fmt, ...)
782 {
783 	va_list ap;
784 	jclass jc = (*jenv)->FindClass(jenv,
785 	    "java/lang/IllegalArgumentException");
786 	va_start(ap, fmt);
787 	dtj_throw(jenv, jc, fmt, &ap);
788 	(*jenv)->DeleteLocalRef(jenv, jc);
789 	va_end(ap);
790 }
791 
792 void
793 dtj_throw_no_such_element(JNIEnv *jenv, const char *fmt, ...)
794 {
795 	va_list ap;
796 	jclass jc = (*jenv)->FindClass(jenv,
797 	    "java/util/NoSuchElementException");
798 	va_start(ap, fmt);
799 	dtj_throw(jenv, jc, fmt, &ap);
800 	(*jenv)->DeleteLocalRef(jenv, jc);
801 	va_end(ap);
802 }
803 
804 void
805 dtj_throw_class_cast(JNIEnv *jenv, const char *fmt, ...)
806 {
807 	va_list ap;
808 	jclass jc = (*jenv)->FindClass(jenv,
809 	    "java/lang/ClassCastException");
810 	va_start(ap, fmt);
811 	dtj_throw(jenv, jc, fmt, &ap);
812 	(*jenv)->DeleteLocalRef(jenv, jc);
813 	va_end(ap);
814 }
815 
816 void
817 dtj_throw_assertion(JNIEnv *jenv, const char *fmt, ...)
818 {
819 	va_list ap;
820 	jclass jc = (*jenv)->FindClass(jenv,
821 	    "java/lang/AssertionError");
822 	va_start(ap, fmt);
823 	dtj_throw(jenv, jc, fmt, &ap);
824 	(*jenv)->DeleteLocalRef(jenv, jc);
825 	va_end(ap);
826 }
827 
828 void
829 dtj_throw_resource_limit(JNIEnv *jenv, const char *fmt, ...)
830 {
831 	va_list ap;
832 	jclass jc = (*jenv)->FindClass(jenv,
833 	    "org/opensolaris/os/dtrace/ResourceLimitException");
834 	va_start(ap, fmt);
835 	dtj_throw(jenv, jc, fmt, &ap);
836 	(*jenv)->DeleteLocalRef(jenv, jc);
837 	va_end(ap);
838 }
839 
840 void
841 dtj_wrap_exception(JNIEnv *jenv, const char *file, int line)
842 {
843 	jthrowable e = NULL;
844 	jthrowable nx = NULL;
845 	jstring jfile = NULL;
846 
847 	e = (*jenv)->ExceptionOccurred(jenv);
848 	if (!e) {
849 		return;
850 	}
851 
852 	if (!g_dtj_load_common) {
853 		return;
854 	}
855 
856 	(*jenv)->ExceptionClear(jenv);
857 
858 	/* Unsafe to test while exception pending */
859 	if ((*jenv)->IsInstanceOf(jenv, e, g_nx_jc)) {
860 		/* Already wrapped */
861 		(*jenv)->Throw(jenv, e);
862 		(*jenv)->DeleteLocalRef(jenv, e);
863 		return;
864 	}
865 
866 	jfile = dtj_NewStringNative(jenv, file);
867 	if ((*jenv)->ExceptionCheck(jenv)) {
868 		/*
869 		 * Only wrap the exception if possible, otherwise just throw the
870 		 * original exception.
871 		 */
872 		(*jenv)->ExceptionClear(jenv);
873 		(*jenv)->Throw(jenv, e);
874 		(*jenv)->DeleteLocalRef(jenv, e);
875 		return;
876 	}
877 
878 	nx = (jthrowable)(*jenv)->NewObject(jenv, g_nx_jc, g_nxinit_jm,
879 	    jfile, line, e);
880 	(*jenv)->DeleteLocalRef(jenv, jfile);
881 	if ((*jenv)->ExceptionCheck(jenv)) {
882 		(*jenv)->ExceptionClear(jenv);
883 		(*jenv)->Throw(jenv, e);
884 		(*jenv)->DeleteLocalRef(jenv, e);
885 		return;
886 	}
887 
888 	(*jenv)->DeleteLocalRef(jenv, e);
889 	(*jenv)->Throw(jenv, nx);
890 	(*jenv)->DeleteLocalRef(jenv, nx);
891 }
892 
893 /*
894  * Calls the given java object's toString() method and prints the value to
895  * stdout.  Useful for debugging.  Guaranteed that no exception is pending when
896  * this function returns.
897  */
898 void
899 dtj_print_object(JNIEnv *jenv, jobject jobj)
900 {
901 	jstring jstr;
902 	const char *cstr;
903 
904 	if (!g_dtj_load_common) {
905 		dtj_throw_illegal_state(jenv,
906 		    "dtj_load_common() has not been called");
907 		(*jenv)->ExceptionDescribe(jenv); /* clears the exception */
908 		return;
909 	}
910 
911 	if (!jobj) {
912 		(void) printf("null\n");
913 		return;
914 	}
915 
916 	jstr = (*jenv)->CallObjectMethod(jenv, jobj, g_tostring_jm);
917 	if ((*jenv)->ExceptionCheck(jenv)) {
918 		(*jenv)->ExceptionDescribe(jenv); /* clears the exception */
919 		return;
920 	}
921 	cstr = (*jenv)->GetStringUTFChars(jenv, jstr, 0);
922 	if (cstr) {
923 		(void) printf("%s\n", cstr);
924 	} else {
925 		(*jenv)->ExceptionDescribe(jenv); /* clears the exception */
926 		(*jenv)->DeleteLocalRef(jenv, jstr);
927 		return;
928 	}
929 	(*jenv)->ReleaseStringUTFChars(jenv, jstr, cstr);
930 	(*jenv)->DeleteLocalRef(jenv, jstr);
931 }
932 
933 jstring
934 dtj_format_string(JNIEnv *jenv, const char *fmt, ...)
935 {
936 	va_list ap;
937 	char str[DTJ_MSG_SIZE];
938 
939 	jstring jstr = NULL;
940 
941 	va_start(ap, fmt);
942 	(void) vsnprintf(str, sizeof (str), fmt, ap);
943 	va_end(ap);
944 
945 	jstr = dtj_NewStringNative(jenv, str);
946 	/* return NULL if OutOfMemoryError pending */
947 	return (jstr);
948 }
949 
950 jstring
951 dtj_NewStringNative(JNIEnv *jenv, const char *str)
952 {
953 	jstring result;
954 	jbyteArray bytes = 0;
955 	int len;
956 
957 	if (!g_dtj_load_common) {
958 		dtj_throw_illegal_state(jenv,
959 		    "dtj_load_common() has not been called");
960 		return (NULL);
961 	}
962 
963 	len = strlen(str);
964 
965 	bytes = (*jenv)->NewByteArray(jenv, len);
966 	if (!bytes) {
967 		return (NULL); /* OutOfMemoryError pending */
968 	}
969 	(*jenv)->SetByteArrayRegion(jenv, bytes, 0, len,
970 	    (jbyte *)str);
971 	if ((*jenv)->ExceptionCheck(jenv)) {
972 		(*jenv)->DeleteLocalRef(jenv, bytes);
973 		return (NULL); /* ArrayIndexOutOfBoundsException pending */
974 	}
975 	result = (*jenv)->NewObject(jenv, g_string_jc, g_strinit_bytes_jm,
976 	    bytes);
977 	(*jenv)->DeleteLocalRef(jenv, bytes);
978 	/* return NULL result if exception pending */
979 	return (result);
980 }
981 
982 char *
983 dtj_GetStringNativeChars(JNIEnv *jenv, jstring jstr)
984 {
985 	jbyteArray bytes = NULL;
986 
987 	jint len;
988 	char *result = NULL;
989 
990 	if (!g_dtj_load_common) {
991 		dtj_throw_illegal_state(jenv,
992 		    "dtj_load_common() has not been called");
993 		return (NULL);
994 	}
995 
996 	bytes = (*jenv)->CallObjectMethod(jenv, jstr, g_strbytes_jm);
997 	if ((*jenv)->ExceptionCheck(jenv)) {
998 		return (NULL); /* OutOfMemoryError pending */
999 	}
1000 	/* Does not throw exceptions */
1001 	len = (*jenv)->GetArrayLength(jenv, bytes);
1002 	result = malloc(len + 1);
1003 	if (!result) {
1004 		(*jenv)->DeleteLocalRef(jenv, bytes);
1005 		dtj_throw_out_of_memory(jenv,
1006 		    "could not allocate native chars");
1007 		return (NULL);
1008 	}
1009 
1010 	/* Skip check for ArrayIndexOutOfBoundsException */
1011 	(*jenv)->GetByteArrayRegion(jenv, bytes, 0, len,
1012 	    (jbyte *)result);
1013 	(*jenv)->DeleteLocalRef(jenv, bytes);
1014 	result[len] = '\0'; /* NUL-terminate */
1015 
1016 	return (result);
1017 }
1018 
1019 void
1020 /* ARGSUSED */
1021 dtj_ReleaseStringNativeChars(JNIEnv *jenv, jstring jstr, const char *str)
1022 {
1023 	free((void *)str);
1024 }
1025 
1026 char **
1027 dtj_get_argv(JNIEnv *jenv, jobjectArray args, int *argc)
1028 {
1029 	char **argv = NULL; /* return value */
1030 	const char *str;
1031 	int i;
1032 
1033 	jstring jstr = NULL;
1034 
1035 	if (!g_dtj_load_common) {
1036 		dtj_throw_illegal_state(jenv,
1037 		    "dtj_load_common() has not been called");
1038 		return (NULL);
1039 	}
1040 
1041 	*argc = (*jenv)->GetArrayLength(jenv, args);
1042 	/*
1043 	 * Initialize all string pointers to NULL so that in case of an error
1044 	 * filling in the array, free_argv() will not attempt to free the
1045 	 * unallocated elements.  Also NULL-terminate the string array for
1046 	 * functions that expect terminating NULL rather than rely on argc.
1047 	 */
1048 	argv = uu_zalloc((sizeof (char *)) * (*argc + 1));
1049 	if (!argv) {
1050 		dtj_throw_out_of_memory(jenv, "Failed to allocate args array");
1051 		return (NULL);
1052 	}
1053 
1054 	for (i = 0; i < *argc; ++i) {
1055 		jstr = (*jenv)->GetObjectArrayElement(jenv, args, i);
1056 		if ((*jenv)->ExceptionCheck(jenv)) {
1057 			dtj_free_argv(argv);
1058 			return (NULL);
1059 		}
1060 		str = dtj_GetStringNativeChars(jenv, jstr);
1061 		if ((*jenv)->ExceptionCheck(jenv)) {
1062 			dtj_free_argv(argv);
1063 			(*jenv)->DeleteLocalRef(jenv, jstr);
1064 			return (NULL);
1065 		}
1066 		argv[i] = malloc(strlen(str) + 1);
1067 		if (!argv[i]) {
1068 			dtj_throw_out_of_memory(jenv, "Failed to allocate arg");
1069 			dtj_free_argv(argv);
1070 			dtj_ReleaseStringNativeChars(jenv, jstr, str);
1071 			(*jenv)->DeleteLocalRef(jenv, jstr);
1072 			return (NULL);
1073 		}
1074 		(void) strcpy(argv[i], str);
1075 		dtj_ReleaseStringNativeChars(jenv, jstr, str);
1076 		(*jenv)->DeleteLocalRef(jenv, jstr);
1077 		jstr = NULL;
1078 	}
1079 
1080 	return (argv);
1081 }
1082 
1083 char **
1084 dtj_make_argv(JNIEnv *jenv, jstring command, int *argc)
1085 {
1086 	const char *ws = "\f\n\r\t\v ";
1087 	char **argv = NULL; /* return value */
1088 	const char *cmd; /* native command string */
1089 	char *s; /* writable command */
1090 	char *tok; /* token */
1091 	int len;
1092 
1093 	if (!g_dtj_load_common) {
1094 		dtj_throw_illegal_state(jenv,
1095 		    "dtj_load_common() has not been called");
1096 		return (NULL);
1097 	}
1098 
1099 	if (!command) {
1100 		dtj_throw_null_pointer(jenv, "command is null");
1101 		return (NULL);
1102 	} else if ((*jenv)->GetStringLength(jenv, command) == 0) {
1103 		dtj_throw_illegal_argument(jenv, "command is empty");
1104 		return (NULL);
1105 	}
1106 
1107 	cmd = dtj_GetStringNativeChars(jenv, command);
1108 	if ((*jenv)->ExceptionCheck(jenv)) {
1109 		return (NULL);
1110 	}
1111 	len = strlen(cmd);
1112 	s = malloc(len + 1);
1113 	if (!s) {
1114 		dtj_throw_out_of_memory(jenv,
1115 		    "failed to allocate command string");
1116 		dtj_ReleaseStringNativeChars(jenv, command, cmd);
1117 		return (NULL);
1118 	}
1119 	(void) strcpy(s, cmd);
1120 	/*
1121 	 * Initialize all string pointers to NULL so that in case of an error
1122 	 * filling in the array, free_argv() will not attempt to free the
1123 	 * unallocated elements.  Also NULL-terminate the string array for
1124 	 * functions that expect terminating NULL rather than rely on argc.
1125 	 * Allow for maximum length resulting from single-character tokens
1126 	 * separated by single spaces.
1127 	 */
1128 	argv = uu_zalloc(sizeof (char *) * (len / 2 + 1));
1129 	if (!argv) {
1130 		dtj_throw_out_of_memory(jenv, "failed to allocate args array");
1131 		free(s);
1132 		dtj_ReleaseStringNativeChars(jenv, command, cmd);
1133 		return (NULL);
1134 	}
1135 
1136 	*argc = 0;
1137 	for (tok = strtok(s, ws); tok != NULL; tok = strtok(NULL, ws)) {
1138 		argv[*argc] = malloc(strlen(tok) + 1);
1139 		if (!argv[*argc]) {
1140 			dtj_throw_out_of_memory(jenv, "Failed to allocate arg");
1141 			dtj_free_argv(argv);
1142 			free(s);
1143 			dtj_ReleaseStringNativeChars(jenv, command, cmd);
1144 			return (NULL);
1145 		}
1146 		(void) strcpy(argv[(*argc)++], tok);
1147 	}
1148 
1149 	if (*argc == 0) {
1150 		dtj_throw_illegal_argument(jenv, "command is blank");
1151 		dtj_free_argv(argv);
1152 		free(s);
1153 		dtj_ReleaseStringNativeChars(jenv, command, cmd);
1154 		return (NULL);
1155 	}
1156 
1157 	free(s);
1158 	dtj_ReleaseStringNativeChars(jenv, command, cmd);
1159 	return (argv);
1160 }
1161 
1162 void
1163 dtj_free_argv(char **argv)
1164 {
1165 	if (argv) {
1166 		char **s = argv;
1167 		while (*s) {
1168 			free((void *)*s);
1169 			*s++ = NULL;
1170 		}
1171 		free((void *)argv);
1172 	}
1173 }
1174 
1175 
1176 /* Wrappers for uu_list_t */
1177 
1178 int
1179 /* ARGSUSED */
1180 dtj_pointer_list_entry_cmp(const void *v1, const void *v2, void *arg)
1181 {
1182 	const dtj_pointer_list_entry_t *p1 = v1;
1183 	const dtj_pointer_list_entry_t *p2 = v2;
1184 
1185 	/*
1186 	 * It is not valid to compare pointers using the relational operators
1187 	 * unless they point to elements in the same array.
1188 	 */
1189 	uint64_t x = (uintptr_t)p1->dple_ptr;
1190 	uint64_t y = (uintptr_t)p2->dple_ptr;
1191 	int rc;
1192 	rc = ((x > y) ? 1 : ((x < y) ? -1 : 0));
1193 	return (rc);
1194 }
1195 
1196 int
1197 /* ARGSUSED */
1198 dtj_string_list_entry_cmp(const void *v1, const void *v2, void *arg)
1199 {
1200 	const dtj_string_list_entry_t *p1 = v1;
1201 	const dtj_string_list_entry_t *p2 = v2;
1202 	const char *s1 = p1->dsle_value;
1203 	const char *s2 = p2->dsle_value;
1204 	if (s1 == NULL) {
1205 		return (s2 == NULL ? 0 : -1);
1206 	}
1207 	if (s2 == NULL) {
1208 		return (1);
1209 	}
1210 	return (strcmp(s1, s2));
1211 }
1212 
1213 static boolean_t
1214 dtj_check_pointer_pool(void)
1215 {
1216 	if (g_pointer_pool == NULL) {
1217 		g_pointer_pool = uu_list_pool_create("g_pointer_pool",
1218 		    sizeof (dtj_pointer_list_entry_t),
1219 		    offsetof(dtj_pointer_list_entry_t, dple_node),
1220 		    dtj_pointer_list_entry_cmp,
1221 		    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
1222 		if (g_pointer_pool == NULL) {
1223 			return (B_FALSE);
1224 		}
1225 	}
1226 	return (B_TRUE);
1227 }
1228 
1229 uu_list_t *
1230 dtj_pointer_list_create(void)
1231 {
1232 	uu_list_t *list;
1233 
1234 	if (!dtj_check_pointer_pool()) {
1235 		return (NULL);
1236 	}
1237 
1238 	list = uu_list_create(g_pointer_pool, NULL,
1239 	    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
1240 	return (list);
1241 }
1242 
1243 dtj_pointer_list_entry_t *
1244 dtj_pointer_list_entry_create(void *p)
1245 {
1246 	dtj_pointer_list_entry_t *e;
1247 
1248 	if (!dtj_check_pointer_pool()) {
1249 		return (NULL);
1250 	}
1251 
1252 	e = uu_zalloc(sizeof (dtj_pointer_list_entry_t));
1253 	if (e) {
1254 		uu_list_node_init(e, &e->dple_node, g_pointer_pool);
1255 		e->dple_ptr = p;
1256 	}
1257 	return (e);
1258 }
1259 
1260 static boolean_t
1261 dtj_check_string_pool(void)
1262 {
1263 	if (g_string_pool == NULL) {
1264 		g_string_pool = uu_list_pool_create("g_string_pool",
1265 		    sizeof (dtj_string_list_entry_t),
1266 		    offsetof(dtj_string_list_entry_t, dsle_node),
1267 		    dtj_string_list_entry_cmp,
1268 		    (g_dtj_util_debug ? UU_LIST_POOL_DEBUG : 0));
1269 		if (g_string_pool == NULL) {
1270 			return (B_FALSE);
1271 		}
1272 	}
1273 	return (B_TRUE);
1274 }
1275 
1276 uu_list_t *
1277 dtj_string_list_create(void)
1278 {
1279 	uu_list_t *list;
1280 
1281 	if (!dtj_check_string_pool()) {
1282 		return (NULL);
1283 	}
1284 
1285 	list = uu_list_create(g_string_pool, NULL,
1286 	    (g_dtj_util_debug ? UU_LIST_DEBUG : 0));
1287 	return (list);
1288 }
1289 
1290 dtj_string_list_entry_t *
1291 dtj_string_list_entry_create(const char *s)
1292 {
1293 	dtj_string_list_entry_t *e;
1294 
1295 	if (!dtj_check_string_pool()) {
1296 		return (NULL);
1297 	}
1298 
1299 	e = uu_zalloc(sizeof (dtj_string_list_entry_t));
1300 	if (e) {
1301 		uu_list_node_init(e, &e->dsle_node, g_string_pool);
1302 		if (s) {
1303 			e->dsle_value = malloc(strlen(s) + 1);
1304 			if (e->dsle_value) {
1305 				(void) strcpy(e->dsle_value, s);
1306 			} else {
1307 				uu_list_node_fini(e, &e->dsle_node,
1308 				    g_string_pool);
1309 				uu_free(e);
1310 				e = NULL;
1311 			}
1312 		}
1313 	}
1314 	return (e);
1315 }
1316 
1317 void
1318 dtj_pointer_list_entry_destroy(void *v,
1319     dtj_value_destroy_f *value_destroy, void *arg)
1320 {
1321 	if (v) {
1322 		dtj_pointer_list_entry_t *e = v;
1323 		if (value_destroy) {
1324 			value_destroy(e->dple_ptr, arg);
1325 		}
1326 		uu_list_node_fini(e, &e->dple_node, g_pointer_pool);
1327 		e->dple_ptr = NULL;
1328 		uu_free(v);
1329 	}
1330 }
1331 
1332 void
1333 /* ARGSUSED */
1334 dtj_string_list_entry_destroy(void *v, void *arg)
1335 {
1336 	if (v) {
1337 		dtj_string_list_entry_t *e = v;
1338 		free(e->dsle_value);
1339 		uu_list_node_fini(e, &e->dsle_node, g_string_pool);
1340 		e->dsle_value = NULL;
1341 		uu_free(v);
1342 	}
1343 }
1344 
1345 void
1346 dtj_list_clear(uu_list_t *list, dtj_value_destroy_f *value_destroy,
1347     void *arg)
1348 {
1349 	void *cookie; /* needed for uu_list_teardown */
1350 	void *value;
1351 
1352 	if (!list) {
1353 		return;
1354 	}
1355 
1356 	cookie = NULL;
1357 	if (value_destroy) {
1358 		while ((value = uu_list_teardown(list, &cookie)) != NULL) {
1359 			value_destroy(value, arg);
1360 		}
1361 	} else {
1362 		while ((value = uu_list_teardown(list, &cookie)) != NULL) {
1363 		}
1364 	}
1365 }
1366 
1367 void
1368 dtj_list_destroy(uu_list_t *list,
1369     dtj_value_destroy_f *value_destroy, void *arg)
1370 {
1371 	dtj_list_clear(list, value_destroy, arg);
1372 	uu_list_destroy(list);
1373 }
1374 
1375 void
1376 dtj_pointer_list_clear(uu_list_t *list,
1377     dtj_value_destroy_f *value_destroy, void *arg)
1378 {
1379 	void *cookie; /* needed for uu_list_teardown */
1380 	dtj_pointer_list_entry_t *e;
1381 
1382 	if (!list) {
1383 		return;
1384 	}
1385 
1386 	cookie = NULL;
1387 	while ((e = uu_list_teardown(list, &cookie)) != NULL) {
1388 		dtj_pointer_list_entry_destroy(e, value_destroy, arg);
1389 	}
1390 }
1391 
1392 void
1393 dtj_pointer_list_destroy(uu_list_t *list,
1394     dtj_value_destroy_f *value_destroy, void *arg)
1395 {
1396 	dtj_pointer_list_clear(list, value_destroy, arg);
1397 	uu_list_destroy(list);
1398 }
1399 
1400 void
1401 dtj_string_list_clear(uu_list_t *list)
1402 {
1403 	dtj_list_clear(list, dtj_string_list_entry_destroy, NULL);
1404 }
1405 
1406 void
1407 dtj_string_list_destroy(uu_list_t *list)
1408 {
1409 	dtj_list_destroy(list, dtj_string_list_entry_destroy, NULL);
1410 }
1411 
1412 boolean_t
1413 dtj_list_empty(uu_list_t *list)
1414 {
1415 	return (uu_list_numnodes(list) == 0);
1416 }
1417 
1418 boolean_t
1419 dtj_list_add(uu_list_t *list, void *value)
1420 {
1421 	return (uu_list_insert_before(list, NULL, value) == 0);
1422 }
1423 
1424 boolean_t
1425 dtj_pointer_list_add(uu_list_t *list, void *p)
1426 {
1427 	dtj_pointer_list_entry_t *e = dtj_pointer_list_entry_create(p);
1428 	if (!e) {
1429 		return (B_FALSE);
1430 	}
1431 	return (dtj_list_add(list, e));
1432 }
1433 
1434 void *
1435 dtj_pointer_list_walk_next(uu_list_walk_t *itr)
1436 {
1437 	dtj_pointer_list_entry_t *e = uu_list_walk_next(itr);
1438 	if (!e) {
1439 		return (DTJ_INVALID_PTR);
1440 	}
1441 	return (e->dple_ptr);
1442 }
1443 
1444 void *
1445 dtj_pointer_list_first(uu_list_t *list)
1446 {
1447 	dtj_pointer_list_entry_t *e = uu_list_first(list);
1448 	if (!e) {
1449 		/* NULL is a valid value; use -1 for invalid */
1450 		return (DTJ_INVALID_PTR);
1451 	}
1452 	return (e->dple_ptr);
1453 }
1454 
1455 void *
1456 dtj_pointer_list_last(uu_list_t *list)
1457 {
1458 	dtj_pointer_list_entry_t *e = uu_list_last(list);
1459 	if (!e) {
1460 		/* NULL is a valid value; use -1 for invalid */
1461 		return (DTJ_INVALID_PTR);
1462 	}
1463 	return (e->dple_ptr);
1464 }
1465 
1466 boolean_t
1467 dtj_string_list_add(uu_list_t *list, const char *s)
1468 {
1469 	dtj_string_list_entry_t *e = dtj_string_list_entry_create(s);
1470 	if (!e) {
1471 		return (B_FALSE);
1472 	}
1473 	return (dtj_list_add(list, e));
1474 }
1475 
1476 const char *
1477 dtj_string_list_walk_next(uu_list_walk_t *itr)
1478 {
1479 	dtj_string_list_entry_t *e = uu_list_walk_next(itr);
1480 	if (!e) {
1481 		return (DTJ_INVALID_STR);
1482 	}
1483 	return (e->dsle_value);
1484 }
1485 
1486 const char *
1487 dtj_string_list_first(uu_list_t *list)
1488 {
1489 	dtj_string_list_entry_t *e = uu_list_first(list);
1490 	if (!e) {
1491 		/* NULL is a valid string value; use -1 for invalid */
1492 		return (DTJ_INVALID_STR);
1493 	}
1494 	return (e->dsle_value);
1495 }
1496 
1497 const char *
1498 dtj_string_list_last(uu_list_t *list)
1499 {
1500 	dtj_string_list_entry_t *e = uu_list_last(list);
1501 	if (!e) {
1502 		/* NULL is a valid string value; use -1 for invalid */
1503 		return (DTJ_INVALID_STR);
1504 	}
1505 	return (e->dsle_value);
1506 }
1507