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