xref: /illumos-gate/usr/src/lib/libdtrace_jni/common/dtrace_jni.c (revision fbd1c0dae6f4a2ccc2ce0527c7f19d3dd5ea90b8)
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 <stdio.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <libgen.h>
35 #include <assert.h>
36 #include <strings.h>
37 #include <libproc.h>
38 #include <pthread.h>
39 #include <dtrace_jni.h>
40 /* generated by javah */
41 #include <LocalConsumer.h>
42 
43 /*
44  * dtrace_jni.c defines all the native methods of the Java DTrace API.  Every
45  * native method is declared in a single class, LocalConsumer.java.
46  *
47  * Notes:
48  *
49  * The data generating loop must explicitly release every object reference it
50  * obtains in order to avoid a memory leak.  A local JNI object reference is not
51  * automatically released until control returns to java, which never happens as
52  * long as the data generating loop runs.  This applies to any JNI function that
53  * obtains an object reference (such as CallObjectMethod() or NewObject()).  A
54  * local reference is released by calling DeleteLocalRef(), which is safe to
55  * call with an exception pending.
56  *
57  * It is important to check for an exception after calling java code from native
58  * C, such as after notifying the java consumer of new data.  Failure to do this
59  * makes it possible for users of the interface to crash the JVM by throwing an
60  * exception in java code.
61  *
62  * Some JNI functions, like GetIntField() or ReleaseStringUTFChars(), do not
63  * need to be checked for exceptions.
64  *
65  * GetStringUTFChars() returns NULL if and only if an exception was thrown.
66  *
67  * It is important to stop a DTrace consumer and remove it if an exception
68  * occurs.  This API guarantees that a consumer is stopped automatically if it
69  * throws an exception.  An application running multiple DTrace consumers
70  * simultaneously should be able to continue running the others normally if any
71  * fail.
72  *
73  * Calls to libdtrace are not guaranteed to be MT-safe.  Even if they are
74  * currently MT-safe, they are not guaranteed to remain that way.  To address
75  * this, a global lock (the LocalConsumer.class reference) is used around calls
76  * to libdtrace.  In many cases, the locking is done in java, which should be
77  * indicated in this file by a comment above the function that assumes prior
78  * locking.  To access the same global lock from native C code, the JNI function
79  * MonitorEnter() is used.  Each MonitorEnter() must have a matching
80  * MonitorExit() or the application will hang (all consumer threads).  The
81  * consumer loop and the getAggregate() method require a per-consumer lock
82  * rather than a global lock; in that case the argument to MonitorEnter() and
83  * MonitorExit() is the consumerLock member of the LocalConsumer, not the
84  * LocalConsumer itself.
85  */
86 
87 /*
88  * Increment the version whenever there is a change in the interface between
89  * Java and native code, whether from Java into native code:
90  * - LocalConsumer.h (generated by javah)
91  * or from native code back into Java:
92  * - dtj_table_load() in dtj_jnitab.c
93  * Changes to dtj_load_common() in dtj_util.c should not normally require a
94  * version update, since dtj_util.c defines classes in the JDK, not classes in
95  * the Java DTrace API.
96  *
97  * This version needs to match the version in LocalConsumer.java
98  */
99 #define	DTRACE_JNI_VERSION 2
100 
101 #define	FIRST_HANDLE 0		/* sequence-generated consumer ID */
102 #define	NO_HANDLE -1
103 #define	INITIAL_CAPACITY 8	/* initial size of consumer array */
104 #define	MAX_CAPACITY_INCREMENT 1024
105 
106 static boolean_t g_dtj_load = B_FALSE;
107 static int g_handle_seq = NO_HANDLE;
108 /*
109  * key: caller's consumer handle (int)
110  * value: per-consumer data includes dtrace handle (consumer_t *)
111  */
112 static dtj_consumer_t **g_consumer_table = NULL;
113 static size_t g_consumer_capacity = 0;
114 static size_t g_consumer_count = 0;
115 static size_t g_max_capacity_increment = MAX_CAPACITY_INCREMENT;
116 static size_t g_max_consumers = 0; /* no maximum */
117 static boolean_t g_init = B_FALSE;
118 static pthread_mutex_t g_table_lock;
119 static pthread_mutexattr_t g_table_lock_attr;
120 pthread_key_t g_dtj_consumer_key;
121 
122 static int dtj_get_handle(JNIEnv *, jobject);
123 static dtj_status_t dtj_get_java_consumer(JNIEnv *, jobject,
124     dtj_java_consumer_t *);
125 static const char *dtj_getexecname(void);
126 static jobject dtj_get_program_info(dtj_java_consumer_t *, dtrace_proginfo_t *);
127 static jobject dtj_add_program(dtj_java_consumer_t *, dtj_program_t *);
128 static void dtj_flag(uint_t *, uint_t, boolean_t *, boolean_t *);
129 static boolean_t dtj_cflag(dtj_java_consumer_t *, const char *, boolean_t *,
130     boolean_t *);
131 static void dtj_list_probes(JNIEnv *, jobject, jobject, jobject,
132     dtrace_probe_f *);
133 static void dtj_list_compiled_probes(JNIEnv *, jobject, jobject, jobject,
134     dtrace_probe_f *);
135 static int dtj_list_probe(dtrace_hdl_t *, const dtrace_probedesc_t *, void *);
136 static int dtj_list_probe_detail(dtrace_hdl_t *, const dtrace_probedesc_t *,
137     void *);
138 static int dtj_list_stmt(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *,
139     void *);
140 static boolean_t dtj_add_consumer(JNIEnv *, dtj_consumer_t *, int *);
141 static dtj_consumer_t *dtj_remove_consumer(JNIEnv *, jobject);
142 static dtj_consumer_t *dtj_remove_consumer_at(int);
143 
144 /*
145  * Gets a sequence-generated consumer ID, or NO_HANDLE if exception pending
146  */
147 static int
148 dtj_get_handle(JNIEnv *jenv, jobject caller)
149 {
150 	int handle;
151 
152 	if (!g_dtj_load) {
153 		dtj_throw_illegal_state(jenv, "JNI table not loaded");
154 		return (NO_HANDLE);
155 	}
156 	handle = (*jenv)->CallIntMethod(jenv, caller, g_gethandle_jm);
157 	if ((*jenv)->ExceptionCheck(jenv)) {
158 		return (NO_HANDLE);
159 	}
160 	if (handle == NO_HANDLE) {
161 		dtj_throw_illegal_state(jenv, "no consumer handle");
162 	}
163 	return (handle);
164 }
165 
166 /*
167  * Populates the given java consumer created for use in the current native
168  * method call.  If the return value is DTJ_ERR, a java exception is pending.
169  * Throws IllegalStateException if the caller does not have a valid handle.
170  * Throws NoSuchElementException if the caller's handle is not in the global
171  * caller table.
172  */
173 static dtj_status_t
174 dtj_get_java_consumer(JNIEnv *jenv, jobject caller, dtj_java_consumer_t *jc)
175 {
176 	dtj_consumer_t *consumer;
177 	int handle = dtj_get_handle(jenv, caller);
178 	if (handle == NO_HANDLE) {
179 		return (DTJ_ERR); /* java exception pending */
180 	}
181 	(void) pthread_mutex_lock(&g_table_lock);
182 	if (g_consumer_table) {
183 		if ((handle >= 0) && (handle < g_consumer_capacity)) {
184 			consumer = g_consumer_table[handle];
185 		} else {
186 			consumer = NULL;
187 		}
188 	} else {
189 		consumer = NULL;
190 	}
191 	(void) pthread_mutex_unlock(&g_table_lock);
192 	if (consumer == NULL) {
193 		dtj_throw_no_such_element(jenv, "consumer handle %d", handle);
194 		return (DTJ_ERR);
195 	}
196 
197 	/* Initialize java consumer */
198 	bzero(jc, sizeof (dtj_java_consumer_t));
199 
200 	/* Attach per-consumer data */
201 	jc->dtjj_consumer = consumer;
202 
203 	/* Attach per-JNI-invocation data */
204 	jc->dtjj_caller = caller;
205 	jc->dtjj_jenv = jenv;
206 
207 	return (DTJ_OK);
208 }
209 
210 /*
211  * Adds a consumer to the global consumer table.
212  * Returns B_TRUE if successful; a java exception is pending otherwise.
213  * Postcondition: if successful, g_handle_seq is the handle of the consumer just
214  * added.
215  */
216 static boolean_t
217 dtj_add_consumer(JNIEnv *jenv, dtj_consumer_t *c, int *seq)
218 {
219 	int start;
220 
221 	if (!g_init) {
222 		(void) pthread_key_create(&g_dtj_consumer_key, NULL);
223 		(void) pthread_mutexattr_init(&g_table_lock_attr);
224 		(void) pthread_mutexattr_settype(&g_table_lock_attr,
225 		    PTHREAD_MUTEX_RECURSIVE);
226 		(void) pthread_mutex_init(&g_table_lock,
227 		    &g_table_lock_attr);
228 		g_init = B_TRUE;
229 	}
230 
231 	*seq = NO_HANDLE;
232 	(void) pthread_mutex_lock(&g_table_lock);
233 	if (g_consumer_table == NULL) {
234 		g_consumer_table = malloc(INITIAL_CAPACITY *
235 		    sizeof (dtj_consumer_t *));
236 		if (!g_consumer_table) {
237 			g_handle_seq = NO_HANDLE;
238 			dtj_throw_out_of_memory(jenv,
239 			    "could not allocate consumer table");
240 			(void) pthread_mutex_unlock(&g_table_lock);
241 			return (B_FALSE);
242 		}
243 		bzero(g_consumer_table, (INITIAL_CAPACITY *
244 		    sizeof (dtj_consumer_t *)));
245 		g_consumer_capacity = INITIAL_CAPACITY;
246 	} else if ((g_max_consumers > 0) && (g_consumer_count >=
247 	    g_max_consumers)) {
248 		dtj_throw_resource_limit(jenv, "Too many consumers");
249 		(void) pthread_mutex_unlock(&g_table_lock);
250 		return (B_FALSE);
251 	} else if (g_consumer_count >= g_consumer_capacity) {
252 		dtj_consumer_t **t;
253 		size_t new_capacity;
254 
255 		if (g_consumer_capacity <= g_max_capacity_increment) {
256 			new_capacity = (g_consumer_capacity * 2);
257 		} else {
258 			new_capacity = (g_consumer_capacity +
259 			    g_max_capacity_increment);
260 		}
261 
262 		if ((g_max_consumers > 0) && (new_capacity > g_max_consumers)) {
263 			new_capacity = g_max_consumers;
264 		}
265 
266 		t = realloc(g_consumer_table,
267 		    new_capacity * sizeof (dtj_consumer_t *));
268 		if (!t) {
269 			dtj_throw_out_of_memory(jenv,
270 			    "could not reallocate consumer table");
271 			(void) pthread_mutex_unlock(&g_table_lock);
272 			return (B_FALSE);
273 		}
274 
275 		g_consumer_table = t;
276 		bzero(g_consumer_table + g_consumer_capacity, ((new_capacity -
277 		    g_consumer_capacity) * sizeof (dtj_consumer_t *)));
278 		g_consumer_capacity = new_capacity;
279 	}
280 
281 	/* Look for an empty slot in the table */
282 	g_handle_seq = (g_handle_seq == NO_HANDLE
283 	    ? FIRST_HANDLE : g_handle_seq + 1);
284 	if (g_handle_seq >= g_consumer_capacity) {
285 		g_handle_seq = FIRST_HANDLE;
286 	}
287 	start = g_handle_seq; /* guard against infinite loop */
288 	while (g_consumer_table[g_handle_seq] != NULL) {
289 		++g_handle_seq;
290 		if (g_handle_seq == start) {
291 			dtj_throw_illegal_state(jenv, "consumer table full,"
292 			    " but count %d < capacity %d",
293 			    g_consumer_count, g_consumer_capacity);
294 			(void) pthread_mutex_unlock(&g_table_lock);
295 			return (B_FALSE);
296 		} else if (g_handle_seq >= g_consumer_capacity) {
297 			g_handle_seq = FIRST_HANDLE;
298 		}
299 	}
300 	g_consumer_table[g_handle_seq] = c;
301 	*seq = g_handle_seq;
302 	++g_consumer_count;
303 	(void) pthread_mutex_unlock(&g_table_lock);
304 	return (B_TRUE);
305 }
306 
307 /*
308  * Removes a consumer from the global consumer table.  The call may be initiated
309  * from Java code or from native code (because an exception has occurred).
310  * Precondition: no exception pending (any pending exception must be temporarily
311  * cleared)
312  * Returns NULL if the caller is not in the table or if this function throws an
313  * exception; either case leaves the global consumer table unchanged.
314  * Throws IllegalStateException if the caller does not have a valid handle.
315  */
316 static dtj_consumer_t *
317 dtj_remove_consumer(JNIEnv *jenv, jobject caller)
318 {
319 	dtj_consumer_t *consumer;
320 	int handle = dtj_get_handle(jenv, caller);
321 	if (handle == NO_HANDLE) {
322 		return (NULL); /* java exception pending */
323 	}
324 	consumer = dtj_remove_consumer_at(handle);
325 	return (consumer);
326 }
327 
328 /*
329  * Returns NULL if there is no consumer with the given handle.  Does not throw
330  * exceptions.
331  */
332 static dtj_consumer_t *
333 dtj_remove_consumer_at(int handle)
334 {
335 	dtj_consumer_t *consumer;
336 	(void) pthread_mutex_lock(&g_table_lock);
337 	if (g_consumer_table) {
338 		if ((handle >= 0) && (handle < g_consumer_capacity)) {
339 			consumer = g_consumer_table[handle];
340 			if (consumer != NULL) {
341 				g_consumer_table[handle] = NULL;
342 				--g_consumer_count;
343 				if (g_consumer_count == 0) {
344 					free(g_consumer_table);
345 					g_consumer_table = NULL;
346 					g_consumer_capacity = 0;
347 					g_handle_seq = NO_HANDLE;
348 				}
349 			}
350 		} else {
351 			consumer = NULL;
352 		}
353 	} else {
354 		consumer = NULL;
355 	}
356 	(void) pthread_mutex_unlock(&g_table_lock);
357 	return (consumer);
358 }
359 
360 /*
361  * Gets the name of the executable in case it is an application with an embedded
362  * JVM and not "java".  Implementation is copied from lib/mpss/common/mpss.c.
363  * The use of static auxv_t makes the MT-level unsafe.  The caller is expected
364  * to use the global lock (LocalConsumer.class).
365  */
366 static const char *
367 dtj_getexecname(void)
368 {
369 	const char	*execname = NULL;
370 	static auxv_t	auxb;
371 
372 	/*
373 	 * The first time through, read the initial aux vector that was
374 	 * passed to the process at exec(2).  Only do this once.
375 	 */
376 	int fd = open("/proc/self/auxv", O_RDONLY);
377 
378 	if (fd >= 0) {
379 		while (read(fd, &auxb, sizeof (auxv_t)) == sizeof (auxv_t)) {
380 			if (auxb.a_type == AT_SUN_EXECNAME) {
381 				execname = auxb.a_un.a_ptr;
382 				break;
383 			}
384 		}
385 		(void) close(fd);
386 	}
387 	return (execname);
388 }
389 
390 /*
391  * Add the compiled program to a list of programs the API expects to enable.
392  * Returns the Program instance identifying the listed program, or NULL if the
393  * Program constructor fails (exception pending in that case).
394  */
395 static jobject
396 dtj_add_program(dtj_java_consumer_t *jc, dtj_program_t *p)
397 {
398 	JNIEnv *jenv = jc->dtjj_jenv;
399 
400 	jobject jprogram = NULL;
401 
402 	switch (p->dtjp_type) {
403 	    case DTJ_PROGRAM_STRING:
404 		jprogram = (*jenv)->NewObject(jenv, g_program_jc,
405 		    g_proginit_jm);
406 		break;
407 	    case DTJ_PROGRAM_FILE:
408 		jprogram = (*jenv)->NewObject(jenv, g_programfile_jc,
409 		    g_fproginit_jm);
410 		break;
411 	    default:
412 		dtj_throw_illegal_argument(jenv, "unexpected program type %d\n",
413 		    p->dtjp_type);
414 	}
415 	if ((*jenv)->ExceptionCheck(jenv)) {
416 		return (NULL);
417 	}
418 
419 	/* Does not throw exceptions */
420 	(*jenv)->SetIntField(jenv, jprogram, g_progid_jf,
421 	    uu_list_numnodes(jc->dtjj_consumer->dtjc_program_list));
422 
423 	if (!dtj_list_add(jc->dtjj_consumer->dtjc_program_list, p)) {
424 		(*jenv)->DeleteLocalRef(jenv, jprogram);
425 		dtj_throw_out_of_memory(jenv,
426 		    "could not add program");
427 		return (NULL);
428 	}
429 
430 	return (jprogram);
431 }
432 
433 /*
434  * Returns a new ProgramInfo, or NULL if the constructor fails (java exception
435  * pending in that case).
436  */
437 static jobject
438 dtj_get_program_info(dtj_java_consumer_t *jc, dtrace_proginfo_t *pinfo)
439 {
440 	JNIEnv *jenv = jc->dtjj_jenv;
441 
442 	jobject minProbeAttributes = NULL;
443 	jobject minStatementAttributes = NULL;
444 	jobject programInfo = NULL; /* return value */
445 
446 	minProbeAttributes = dtj_new_attribute(jc, &pinfo->dpi_descattr);
447 	if (!minProbeAttributes) {
448 		return (NULL); /* java exception pending */
449 	}
450 	minStatementAttributes = dtj_new_attribute(jc, &pinfo->dpi_stmtattr);
451 	if (!minStatementAttributes) {
452 		(*jenv)->DeleteLocalRef(jenv, minProbeAttributes);
453 		return (NULL); /* java exception pending */
454 	}
455 
456 	programInfo = (*jenv)->NewObject(jenv, g_proginfo_jc,
457 	    g_proginfoinit_jm, minProbeAttributes, minStatementAttributes,
458 	    pinfo->dpi_matches);
459 	(*jenv)->DeleteLocalRef(jenv, minProbeAttributes);
460 	(*jenv)->DeleteLocalRef(jenv, minStatementAttributes);
461 
462 	return (programInfo);
463 }
464 
465 /*
466  * Called by LocalConsumer static initializer.
467  */
468 JNIEXPORT void JNICALL
469 /* ARGSUSED */
470 Java_org_opensolaris_os_dtrace_LocalConsumer__1checkVersion(JNIEnv *env,
471     jclass class, jint version)
472 {
473 	if (version != DTRACE_JNI_VERSION) {
474 		dtj_throw_illegal_state(env,
475 		    "LocalConsumer version %d incompatible with native "
476 		    "implementation version %d", version, DTRACE_JNI_VERSION);
477 	}
478 }
479 
480 /*
481  * Called by LocalConsumer static initializer.
482  */
483 JNIEXPORT void JNICALL
484 /* ARGSUSED */
485 Java_org_opensolaris_os_dtrace_LocalConsumer__1loadJniTable(JNIEnv *env,
486     jclass class)
487 {
488 	if (g_dtj_load) {
489 		/*
490 		 * JNI table includes a global reference to the LocalConsumer
491 		 * class, preventing the class from being unloaded.  The
492 		 * LocalConsumer static initializer should never execute more
493 		 * than once.
494 		 */
495 		dtj_throw_illegal_state(env, "JNI table already loaded");
496 		return;
497 	}
498 
499 	/* If this fails, a Java Error (e.g. NoSuchMethodError) is pending */
500 	if (dtj_load(env) == DTJ_OK) {
501 		g_dtj_load = B_TRUE;
502 	}
503 }
504 
505 /*
506  * Protected by global lock (LocalConsumer.class)
507  */
508 JNIEXPORT void JNICALL
509 Java_org_opensolaris_os_dtrace_LocalConsumer__1open(JNIEnv *env, jobject obj,
510     jobjectArray flags)
511 {
512 	dtrace_hdl_t *dtp;
513 	dtj_consumer_t *c;
514 	const char *f; /* flag name */
515 	int oflags = 0;
516 	int i, len;
517 	int id;
518 	int err;
519 
520 	jobject flag = NULL;
521 	jstring flagname = NULL;
522 
523 	if (!g_dtj_load) {
524 		dtj_throw_illegal_state(env, "JNI table not loaded");
525 		return;
526 	}
527 
528 	c = dtj_consumer_create(env);
529 	if (!c) {
530 		return; /* java exception pending */
531 	}
532 
533 	/* Get open flags */
534 	len = (flags ? (*env)->GetArrayLength(env, flags) : 0);
535 	for (i = 0; i < len; ++i) {
536 		flag = (*env)->GetObjectArrayElement(env, flags, i);
537 		if ((*env)->ExceptionCheck(env)) {
538 			dtj_consumer_destroy(c);
539 			return;
540 		}
541 
542 		flagname = (*env)->CallObjectMethod(env, flag, g_enumname_jm);
543 		(*env)->DeleteLocalRef(env, flag);
544 		if ((*env)->ExceptionCheck(env)) {
545 			dtj_consumer_destroy(c);
546 			return;
547 		}
548 		f = (*env)->GetStringUTFChars(env, flagname, NULL);
549 		if ((*env)->ExceptionCheck(env)) {
550 			(*env)->DeleteLocalRef(env, flagname);
551 			dtj_consumer_destroy(c);
552 			return;
553 		}
554 		if (strcmp(f, "ILP32") == 0) {
555 			oflags |= DTRACE_O_ILP32;
556 		} else if (strcmp(f, "LP64") == 0) {
557 			oflags |= DTRACE_O_LP64;
558 		}
559 		(*env)->ReleaseStringUTFChars(env, flagname, f);
560 		(*env)->DeleteLocalRef(env, flagname);
561 	}
562 
563 	/* Check for mutually exclusive flags */
564 	if ((oflags & DTRACE_O_ILP32) && (oflags & DTRACE_O_LP64)) {
565 		dtj_throw_illegal_argument(env,
566 		    "Cannot set both ILP32 and LP64");
567 		dtj_consumer_destroy(c);
568 		return;
569 	}
570 
571 	/*
572 	 * Make sure we can add the consumer before calling dtrace_open().
573 	 * Repeated calls to open() when the consumer table is maxed out should
574 	 * avoid calling dtrace_open().  (Normally there is no limit to the size
575 	 * of the consumer table, but the undocumented JAVA_DTRACE_MAX_CONSUMERS
576 	 * system property lets you set a limit after which
577 	 * ResourceLimitException is thrown.)
578 	 */
579 	if (!dtj_add_consumer(env, c, &id)) {
580 		dtj_consumer_destroy(c);
581 		return; /* java exception pending */
582 	}
583 
584 	(*env)->CallVoidMethod(env, obj, g_sethandle_jm, id);
585 	if ((*env)->ExceptionCheck(env)) {
586 		(void) dtj_remove_consumer_at(id);
587 		dtj_consumer_destroy(c);
588 		return;
589 	}
590 
591 	if ((dtp = dtrace_open(DTRACE_VERSION, oflags, &err)) == NULL) {
592 		dtj_java_consumer_t jc;
593 		jc.dtjj_jenv = env;
594 		dtj_throw_dtrace_exception(&jc, dtrace_errmsg(NULL, err));
595 		(void) dtj_remove_consumer_at(id);
596 		dtj_consumer_destroy(c);
597 		return;
598 	}
599 	c->dtjc_dtp = dtp; /* set consumer handle to native DTrace library */
600 }
601 
602 static void
603 dtj_flag(uint_t *flags, uint_t flag, boolean_t *get, boolean_t *set)
604 {
605 	assert((get && !set) || (set && !get));
606 
607 	if (get) {
608 		*get = (*flags & flag);
609 	} else {
610 		if (*set) {
611 			*flags |= flag;
612 		} else {
613 			*flags &= ~flag;
614 		}
615 	}
616 }
617 
618 /*
619  * Returns B_TRUE if opt is a recognized compile flag, B_FALSE otherwise.
620  */
621 static boolean_t
622 dtj_cflag(dtj_java_consumer_t *jc, const char *opt, boolean_t *get,
623     boolean_t *set)
624 {
625 	boolean_t is_cflag = B_TRUE;
626 	uint_t *flags = &jc->dtjj_consumer->dtjc_cflags;
627 
628 	/* see lib/libdtrace/common/dt_option.c */
629 	if (strcmp(opt, "argref") == 0) {
630 		dtj_flag(flags, DTRACE_C_ARGREF, get, set);
631 	} else if (strcmp(opt, "cpp") == 0) {
632 		dtj_flag(flags, DTRACE_C_CPP, get, set);
633 	} else if (strcmp(opt, "defaultargs") == 0) {
634 		dtj_flag(flags, DTRACE_C_DEFARG, get, set);
635 	} else if (strcmp(opt, "empty") == 0) {
636 		dtj_flag(flags, DTRACE_C_EMPTY, get, set);
637 	} else if (strcmp(opt, "errtags") == 0) {
638 		dtj_flag(flags, DTRACE_C_ETAGS, get, set);
639 	} else if (strcmp(opt, "knodefs") == 0) {
640 		dtj_flag(flags, DTRACE_C_KNODEF, get, set);
641 	} else if (strcmp(opt, "nolibs") == 0) {
642 		dtj_flag(flags, DTRACE_C_NOLIBS, get, set);
643 	} else if (strcmp(opt, "pspec") == 0) {
644 		dtj_flag(flags, DTRACE_C_PSPEC, get, set);
645 	} else if (strcmp(opt, "unodefs") == 0) {
646 		dtj_flag(flags, DTRACE_C_UNODEF, get, set);
647 	} else if (strcmp(opt, "verbose") == 0) {
648 		dtj_flag(flags, DTRACE_C_DIFV, get, set);
649 	} else if (strcmp(opt, "zdefs") == 0) {
650 		dtj_flag(flags, DTRACE_C_ZDEFS, get, set);
651 	} else {
652 		is_cflag = B_FALSE;
653 	}
654 
655 	if (is_cflag && set &&
656 	    (jc->dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT)) {
657 		dtj_throw_illegal_state(jc->dtjj_jenv,
658 		    "cannot set compile time option \"%s\" after calling go()",
659 		    opt);
660 		return (is_cflag);
661 	}
662 
663 	return (is_cflag);
664 }
665 
666 /*
667  * Protected by global lock (LocalConsumer.class)
668  */
669 JNIEXPORT jobject JNICALL
670 Java_org_opensolaris_os_dtrace_LocalConsumer__1compileString(JNIEnv *env,
671     jobject obj, jstring program, jobjectArray args)
672 {
673 	const char *prog;
674 	dtj_java_consumer_t jc;
675 	dtrace_hdl_t *dtp;
676 	dtj_program_t *p;
677 	int argc = 0;
678 	char **argv = NULL;
679 
680 	jstring jprogram = NULL;
681 
682 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
683 		return (NULL); /* java exception pending */
684 	}
685 	dtp = jc.dtjj_consumer->dtjc_dtp;
686 
687 	prog = (*env)->GetStringUTFChars(env, program, 0);
688 	if ((*env)->ExceptionCheck(env)) {
689 		return (NULL);
690 	}
691 
692 	p = dtj_program_create(env, DTJ_PROGRAM_STRING, prog);
693 	if (!p) {
694 		(*env)->ReleaseStringUTFChars(env, program, prog);
695 		return (NULL); /* java exception pending */
696 	}
697 
698 	if (args) {
699 		argv = dtj_get_argv(env, args, &argc);
700 		if ((*env)->ExceptionCheck(env)) {
701 			(*env)->ReleaseStringUTFChars(env, program, prog);
702 			dtj_program_destroy(p, NULL);
703 			return (NULL);
704 		}
705 	}
706 
707 	if ((p->dtjp_program = dtrace_program_strcompile(dtp,
708 	    prog, DTRACE_PROBESPEC_NAME, jc.dtjj_consumer->dtjc_cflags,
709 	    argc, argv)) == NULL) {
710 		dtj_throw_dtrace_exception(&jc,
711 		    "invalid probe specifier %s: %s",
712 		    prog, dtrace_errmsg(dtp, dtrace_errno(dtp)));
713 		(*env)->ReleaseStringUTFChars(env, program, prog);
714 		dtj_program_destroy(p, NULL);
715 		dtj_free_argv(argv);
716 		return (NULL);
717 	}
718 	(*env)->ReleaseStringUTFChars(env, program, prog);
719 	dtj_free_argv(argv);
720 
721 	jprogram = dtj_add_program(&jc, p);
722 	return (jprogram); /* NULL if exception pending */
723 }
724 
725 /*
726  * Protected by global lock (LocalConsumer.class)
727  */
728 JNIEXPORT jobject JNICALL
729 Java_org_opensolaris_os_dtrace_LocalConsumer__1compileFile(JNIEnv *env,
730     jobject obj, jstring filename, jobjectArray args)
731 {
732 	FILE *fp;
733 	const char *file;
734 	dtj_java_consumer_t jc;
735 	dtrace_hdl_t *dtp;
736 	dtj_program_t *p;
737 	int argc = 0;
738 	char **argv = NULL;
739 
740 	jstring jprogram = NULL;
741 
742 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
743 		return (NULL); /* java exception pending */
744 	}
745 	dtp = jc.dtjj_consumer->dtjc_dtp;
746 
747 	file = dtj_GetStringNativeChars(env, filename);
748 	if ((fp = fopen(file, "r")) == NULL) {
749 		dtj_throw_dtrace_exception(&jc, "failed to open %s", file);
750 		dtj_ReleaseStringNativeChars(env, filename, file);
751 		return (NULL);
752 	}
753 
754 	p = dtj_program_create(env, DTJ_PROGRAM_FILE, file);
755 	if (!p) {
756 		(void) fclose(fp);
757 		dtj_ReleaseStringNativeChars(env, filename, file);
758 		return (NULL); /* java exception pending */
759 	}
760 
761 	if (args) {
762 		argv = dtj_get_argv(env, args, &argc);
763 		if ((*env)->ExceptionCheck(env)) {
764 			(void) fclose(fp);
765 			dtj_ReleaseStringNativeChars(env, filename, file);
766 			dtj_program_destroy(p, NULL);
767 			return (NULL);
768 		}
769 	}
770 
771 	if ((p->dtjp_program = dtrace_program_fcompile(dtp,
772 	    fp, jc.dtjj_consumer->dtjc_cflags, argc, argv)) == NULL) {
773 		dtj_throw_dtrace_exception(&jc,
774 		    "failed to compile script %s: %s", file,
775 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
776 		(void) fclose(fp);
777 		dtj_ReleaseStringNativeChars(env, filename, file);
778 		dtj_program_destroy(p, NULL);
779 		dtj_free_argv(argv);
780 		return (NULL);
781 	}
782 	(void) fclose(fp);
783 	dtj_ReleaseStringNativeChars(env, filename, file);
784 	dtj_free_argv(argv);
785 
786 	jprogram = dtj_add_program(&jc, p);
787 	return (jprogram); /* NULL if exception pending */
788 }
789 
790 /*
791  * Protected by global lock (LocalConsumer.class)
792  */
793 JNIEXPORT void JNICALL
794 Java_org_opensolaris_os_dtrace_LocalConsumer__1exec(JNIEnv *env, jobject obj,
795     jobject program)
796 {
797 	dtj_java_consumer_t jc;
798 	dtrace_hdl_t *dtp;
799 	int progid = -1;
800 	uu_list_walk_t *itr;
801 	dtj_program_t *p;
802 	dtrace_proginfo_t *pinfo = NULL;
803 	int i;
804 
805 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
806 		return; /* java exception pending */
807 	}
808 	dtp = jc.dtjj_consumer->dtjc_dtp;
809 
810 	if (program) {
811 		progid = (*env)->GetIntField(env, program, g_progid_jf);
812 	}
813 
814 	if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
815 		dtj_throw_illegal_state(env, "no program compiled");
816 		return;
817 	}
818 
819 	itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
820 	for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) {
821 		/* enable all probes or those of given program only */
822 		if ((progid != -1) && (progid != i)) {
823 			continue;
824 		}
825 
826 		if (p->dtjp_enabled) {
827 			dtj_throw_illegal_state(env, "program already enabled");
828 			uu_list_walk_end(itr);
829 			return;
830 		}
831 
832 		pinfo = &p->dtjp_info;
833 		if (dtrace_program_exec(dtp, p->dtjp_program, pinfo) == -1) {
834 			dtj_throw_dtrace_exception(&jc,
835 			    "failed to enable %s: %s", p->dtjp_name,
836 			    dtrace_errmsg(dtp, dtrace_errno(dtp)));
837 			uu_list_walk_end(itr);
838 			return;
839 		}
840 		p->dtjp_enabled = B_TRUE;
841 	}
842 	uu_list_walk_end(itr);
843 
844 	if (program) {
845 		jobject programInfo = NULL;
846 
847 		if (!pinfo) {
848 			/*
849 			 * Consumer.enable() has already checked that the
850 			 * program was compiled by this consumer.  This is an
851 			 * implementation error, not a user error.
852 			 */
853 			dtj_throw_illegal_state(env, "program not found");
854 			return;
855 		}
856 
857 		programInfo = dtj_get_program_info(&jc, pinfo);
858 		if (!programInfo) {
859 			return; /* java exception pending */
860 		}
861 
862 		(*env)->SetObjectField(env, program, g_proginfo_jf,
863 		    programInfo);
864 		(*env)->DeleteLocalRef(env, programInfo);
865 	}
866 }
867 
868 /*
869  * Protected by global lock (LocalConsumer.class)
870  */
871 JNIEXPORT void JNICALL
872 Java_org_opensolaris_os_dtrace_LocalConsumer__1getProgramInfo(JNIEnv *env,
873     jobject obj, jobject program)
874 {
875 	dtj_java_consumer_t jc;
876 	dtrace_hdl_t *dtp;
877 	int progid;
878 	uu_list_walk_t *itr;
879 	dtj_program_t *p;
880 	dtrace_proginfo_t *pinfo = NULL;
881 	int i;
882 
883 	jobject programInfo = NULL;
884 
885 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
886 		return; /* java exception pending */
887 	}
888 	dtp = jc.dtjj_consumer->dtjc_dtp;
889 
890 	progid = (*env)->GetIntField(env, program, g_progid_jf);
891 
892 	if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
893 		dtj_throw_illegal_state(env, "no program compiled");
894 		return;
895 	}
896 
897 	itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
898 	for (i = 0; ((p = uu_list_walk_next(itr)) != NULL) && !pinfo; ++i) {
899 		if (progid != i) {
900 			/* get info of given program only */
901 			continue;
902 		}
903 
904 		pinfo = &p->dtjp_info;
905 		dtrace_program_info(dtp, p->dtjp_program, pinfo);
906 	}
907 	uu_list_walk_end(itr);
908 
909 	programInfo = dtj_get_program_info(&jc, pinfo);
910 	if (!programInfo) {
911 		return; /* java exception pending */
912 	}
913 
914 	(*env)->SetObjectField(env, program, g_proginfo_jf,
915 	    programInfo);
916 	(*env)->DeleteLocalRef(env, programInfo);
917 }
918 
919 /*
920  * Protected by global lock (LocalConsumer.class)
921  */
922 JNIEXPORT void JNICALL
923 Java_org_opensolaris_os_dtrace_LocalConsumer__1setOption(JNIEnv *env,
924     jobject obj, jstring option, jstring value)
925 {
926 	dtj_java_consumer_t jc;
927 	const char *opt;
928 	const char *val;
929 	boolean_t on;
930 
931 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
932 		return; /* java exception pending */
933 	}
934 
935 	opt = (*env)->GetStringUTFChars(env, option, 0);
936 	if (!opt) {
937 		dtj_throw_out_of_memory(env,
938 		    "could not allocate option string");
939 		return;
940 	}
941 
942 	if (value) {
943 		val = (*env)->GetStringUTFChars(env, value, 0);
944 		if (!val) {
945 			dtj_throw_out_of_memory(env,
946 			    "could not allocate option value string");
947 			(*env)->ReleaseStringUTFChars(env, option, opt);
948 			return;
949 		}
950 	} else {
951 		/*
952 		 * dtrace_setopt() sets option to 0 if value is NULL.  That's
953 		 * not the same thing as unsetting a boolean option, since
954 		 * libdtrace uses -2 to mean unset.  We'll leave it to
955 		 * LocalConsumer.java to disallow null or not.
956 		 */
957 		val = NULL;
958 	}
959 
960 	/*
961 	 * Check for boolean compile-time options not handled by
962 	 * dtrace_setopt().
963 	 */
964 	on = (!val || (strcmp(val, "unset") != 0));
965 	if (dtj_cflag(&jc, opt, NULL, &on)) {
966 		(*env)->ReleaseStringUTFChars(env, option, opt);
967 		if (value) {
968 			(*env)->ReleaseStringUTFChars(env, value, val);
969 		}
970 		return;
971 	}
972 
973 	/*
974 	 * The transition from INIT to GO is protected by synchronization
975 	 * (a per-consumer lock) in LocalConsumer.java, ensuring that go() and
976 	 * setOption() execute sequentially.
977 	 */
978 	if (jc.dtjj_consumer->dtjc_state != DTJ_CONSUMER_INIT) {
979 		/*
980 		 * If the consumer is already running, defer setting the option
981 		 * until we wake up from dtrace_sleep.
982 		 */
983 		dtj_request_t *request;
984 
985 
986 		(void) pthread_mutex_lock(
987 		    &jc.dtjj_consumer->dtjc_request_list_lock);
988 		request = dtj_request_create(env, DTJ_REQUEST_OPTION, opt, val);
989 		if (request) {
990 			if (!dtj_list_add(jc.dtjj_consumer->dtjc_request_list,
991 			    request)) {
992 				dtj_throw_out_of_memory(env,
993 				    "Failed to add setOption request");
994 			}
995 		} /* else java exception pending */
996 		(void) pthread_mutex_unlock(
997 		    &jc.dtjj_consumer->dtjc_request_list_lock);
998 	} else {
999 		dtrace_hdl_t *dtp = jc.dtjj_consumer->dtjc_dtp;
1000 		if (dtrace_setopt(dtp, opt, val) == -1) {
1001 			dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp,
1002 			    dtrace_errno(dtp)));
1003 		}
1004 	}
1005 
1006 	(*env)->ReleaseStringUTFChars(env, option, opt);
1007 	if (value) {
1008 		(*env)->ReleaseStringUTFChars(env, value, val);
1009 	}
1010 }
1011 
1012 /*
1013  * Protected by global lock (LocalConsumer.class)
1014  */
1015 JNIEXPORT jlong JNICALL
1016 Java_org_opensolaris_os_dtrace_LocalConsumer__1getOption(JNIEnv *env,
1017     jobject obj, jstring option)
1018 {
1019 	dtj_java_consumer_t jc;
1020 	dtrace_hdl_t *dtp;
1021 	const char *opt;
1022 	dtrace_optval_t optval;
1023 	boolean_t cflag;
1024 
1025 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1026 		return (0); /* java exception pending */
1027 	}
1028 	dtp = jc.dtjj_consumer->dtjc_dtp;
1029 
1030 	opt = (*env)->GetStringUTFChars(env, option, 0);
1031 	if (!opt) {
1032 		dtj_throw_out_of_memory(env,
1033 		    "could not allocate option string");
1034 		return (0);
1035 	}
1036 
1037 	/*
1038 	 * Check for boolean compile-time options not handled by
1039 	 * dtrace_setopt().
1040 	 */
1041 	if (dtj_cflag(&jc, opt, &cflag, NULL)) {
1042 		(*env)->ReleaseStringUTFChars(env, option, opt);
1043 		return (cflag ? 1 : DTRACEOPT_UNSET);
1044 	}
1045 
1046 	if (dtrace_getopt(dtp, opt, &optval) == -1) {
1047 		dtj_throw_dtrace_exception(&jc,
1048 		    "couldn't get option %s: %s", opt,
1049 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1050 		(*env)->ReleaseStringUTFChars(env, option, opt);
1051 		return (0);
1052 	}
1053 	(*env)->ReleaseStringUTFChars(env, option, opt);
1054 	return (optval);
1055 }
1056 
1057 /*
1058  * Throws IllegalStateException if not all compiled programs are enabled.
1059  */
1060 JNIEXPORT void JNICALL
1061 Java_org_opensolaris_os_dtrace_LocalConsumer__1checkProgramEnabling(JNIEnv *env,
1062     jobject obj)
1063 {
1064 	dtj_java_consumer_t jc;
1065 	dtj_program_t *p;
1066 	uu_list_walk_t *itr;
1067 
1068 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1069 		return; /* java exception pending */
1070 	}
1071 
1072 	if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
1073 		dtj_throw_illegal_state(env, "no program compiled");
1074 		return;
1075 	}
1076 
1077 	itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
1078 	while ((p = uu_list_walk_next(itr)) != NULL) {
1079 		if (!p->dtjp_enabled) {
1080 			const char *type;
1081 			switch (p->dtjp_type) {
1082 			case DTJ_PROGRAM_STRING:
1083 				type = "description";
1084 				break;
1085 			case DTJ_PROGRAM_FILE:
1086 				type = "script";
1087 				break;
1088 			default:
1089 				assert(p->dtjp_type == DTJ_PROGRAM_STRING ||
1090 				    p->dtjp_type == DTJ_PROGRAM_FILE);
1091 			}
1092 			dtj_throw_illegal_state(env,
1093 			    "Not all compiled probes are enabled. "
1094 			    "Compiled %s %s not enabled.",
1095 			    type, p->dtjp_name);
1096 			uu_list_walk_end(itr);
1097 			return;
1098 		}
1099 	}
1100 	uu_list_walk_end(itr);
1101 }
1102 
1103 JNIEXPORT jboolean JNICALL
1104 Java_org_opensolaris_os_dtrace_LocalConsumer__1isEnabled(JNIEnv *env,
1105     jobject obj)
1106 {
1107 	dtj_java_consumer_t jc;
1108 	dtj_program_t *p;
1109 	uu_list_walk_t *itr;
1110 
1111 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1112 		return (JNI_FALSE); /* java exception pending */
1113 	}
1114 
1115 	if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
1116 		return (JNI_FALSE); /* no program compiled */
1117 	}
1118 
1119 	itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
1120 	while ((p = uu_list_walk_next(itr)) != NULL) {
1121 		if (!p->dtjp_enabled) {
1122 			uu_list_walk_end(itr);
1123 			return (JNI_FALSE);
1124 		}
1125 	}
1126 	uu_list_walk_end(itr);
1127 
1128 	return (JNI_TRUE);
1129 }
1130 
1131 /*
1132  * Protected by global lock (LocalConsumer.class)
1133  */
1134 JNIEXPORT void JNICALL
1135 Java_org_opensolaris_os_dtrace_LocalConsumer__1go(JNIEnv *env, jobject obj)
1136 {
1137 	dtj_java_consumer_t jc;
1138 	dtrace_hdl_t *dtp;
1139 
1140 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1141 		return; /* java exception pending */
1142 	}
1143 	dtp = jc.dtjj_consumer->dtjc_dtp;
1144 
1145 	if (dtj_set_callback_handlers(&jc) != DTJ_OK) {
1146 		return; /* java exception pending */
1147 	}
1148 
1149 	if (dtrace_go(dtp) != 0) {
1150 		dtj_throw_dtrace_exception(&jc,
1151 		    "could not enable tracing: %s",
1152 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1153 		return;
1154 	}
1155 
1156 	jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_GO;
1157 }
1158 
1159 /*
1160  * Protected by global lock (LocalConsumer.class).  Called when aborting the
1161  * consumer loop before it starts.
1162  */
1163 JNIEXPORT void JNICALL
1164 Java_org_opensolaris_os_dtrace_LocalConsumer__1stop(JNIEnv *env,
1165     jobject obj)
1166 {
1167 	dtj_java_consumer_t jc;
1168 	dtrace_hdl_t *dtp;
1169 
1170 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1171 		return; /* java exception pending */
1172 	}
1173 	dtp = jc.dtjj_consumer->dtjc_dtp;
1174 
1175 	if (dtrace_stop(dtp) == -1) {
1176 		dtj_throw_dtrace_exception(&jc,
1177 		    "couldn't stop tracing: %s",
1178 		    dtrace_errmsg(jc.dtjj_consumer->dtjc_dtp,
1179 		    dtrace_errno(jc.dtjj_consumer->dtjc_dtp)));
1180 	} else {
1181 		jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_STOP;
1182 	}
1183 }
1184 
1185 JNIEXPORT void JNICALL
1186 Java_org_opensolaris_os_dtrace_LocalConsumer__1consume(JNIEnv *env,
1187     jobject obj)
1188 {
1189 	dtj_java_consumer_t jc;
1190 	dtrace_hdl_t *dtp;
1191 
1192 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1193 		return; /* java exception pending */
1194 	}
1195 	dtp = jc.dtjj_consumer->dtjc_dtp;
1196 	jc.dtjj_consumer->dtjc_state = DTJ_CONSUMER_START;
1197 
1198 	if (dtj_java_consumer_init(env, &jc) != DTJ_OK) {
1199 		return; /* java exception pending */
1200 	}
1201 
1202 	/*
1203 	 * Must set the thread-specific java consumer before starting the
1204 	 * dtrace_work() loop because the bufhandler can also be invoked by
1205 	 * getAggregate() from another thread.  The bufhandler needs access to
1206 	 * the correct JNI state specific to either the consumer loop or the
1207 	 * getAggregate() call.
1208 	 */
1209 	(void) pthread_setspecific(g_dtj_consumer_key, &jc);
1210 
1211 	if (jc.dtjj_consumer->dtjc_process_list != NULL) {
1212 		struct ps_prochandle *P;
1213 		uu_list_walk_t *itr;
1214 
1215 		/* Must not call MonitorEnter with a pending exception */
1216 		if ((*env)->ExceptionCheck(env)) {
1217 			dtj_java_consumer_fini(env, &jc);
1218 			return; /* java exception pending */
1219 		}
1220 		(*env)->MonitorEnter(env, g_caller_jc);
1221 		if ((*env)->ExceptionCheck(env)) {
1222 			dtj_java_consumer_fini(env, &jc);
1223 			return; /* java exception pending */
1224 		}
1225 
1226 		itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list,
1227 		    0);
1228 		while ((P = dtj_pointer_list_walk_next(itr)) !=
1229 		    DTJ_INVALID_PTR) {
1230 			dtrace_proc_continue(dtp, P);
1231 		}
1232 		uu_list_walk_end(itr);
1233 
1234 		(*env)->MonitorExit(env, g_caller_jc);
1235 		if ((*env)->ExceptionCheck(env)) {
1236 			dtj_java_consumer_fini(env, &jc);
1237 			return; /* java exception pending */
1238 		}
1239 	}
1240 
1241 	/*
1242 	 * Blocking call starts consumer loop.
1243 	 */
1244 	(void) dtj_consume(&jc);
1245 
1246 	dtj_java_consumer_fini(env, &jc);
1247 	/*
1248 	 * Stop the consumer after the consumer loop terminates, whether
1249 	 * normally because of the exit() action or LocalConsumer.stop(), or
1250 	 * abnormally because of an exception.  The call is ignored if the
1251 	 * consumer is already stopped.  It is safe to call dtj_stop() with a
1252 	 * pending exception.
1253 	 */
1254 	dtj_stop(&jc);
1255 }
1256 
1257 /*
1258  * Interrupts a running consumer.  May be called from any thread.
1259  */
1260 JNIEXPORT void JNICALL
1261 Java_org_opensolaris_os_dtrace_LocalConsumer__1interrupt(JNIEnv *env,
1262     jobject obj)
1263 {
1264 	dtj_java_consumer_t jc;
1265 
1266 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1267 		return; /* java exception pending */
1268 	}
1269 
1270 	jc.dtjj_consumer->dtjc_interrupt = B_TRUE;
1271 }
1272 
1273 /*
1274  * Protected by global lock (LocalConsumer.class)
1275  */
1276 JNIEXPORT void JNICALL
1277 Java_org_opensolaris_os_dtrace_LocalConsumer__1close(JNIEnv *env, jobject obj)
1278 {
1279 	dtj_java_consumer_t jc;
1280 	dtrace_hdl_t *dtp;
1281 
1282 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1283 		return; /* java exception pending */
1284 	}
1285 	dtp = jc.dtjj_consumer->dtjc_dtp;
1286 
1287 	/*
1288 	 * Need to release any created procs here, since the consumer_t
1289 	 * destructor (called by _destroy) will not have access to the dtrace
1290 	 * handle needed to release them (this function closes the dtrace
1291 	 * handle).
1292 	 */
1293 	if (jc.dtjj_consumer->dtjc_process_list != NULL) {
1294 		struct ps_prochandle *P;
1295 		uu_list_walk_t *itr;
1296 		itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_process_list,
1297 		    0);
1298 		while ((P = dtj_pointer_list_walk_next(itr)) !=
1299 		    DTJ_INVALID_PTR) {
1300 			dtrace_proc_release(dtp, P);
1301 		}
1302 		uu_list_walk_end(itr);
1303 	}
1304 
1305 	dtrace_close(dtp);
1306 }
1307 
1308 /*
1309  * Static Consumer.java method
1310  */
1311 JNIEXPORT jint JNICALL
1312 /* ARGSUSED */
1313 Java_org_opensolaris_os_dtrace_LocalConsumer__1openCount(JNIEnv *env, jclass c)
1314 {
1315 	int open;
1316 	(void) pthread_mutex_lock(&g_table_lock);
1317 	if (g_consumer_table == NULL) {
1318 		open = 0;
1319 	} else {
1320 		open = g_consumer_count;
1321 	}
1322 	(void) pthread_mutex_unlock(&g_table_lock);
1323 	return (open);
1324 }
1325 
1326 /*
1327  * Static Consumer.java method
1328  */
1329 JNIEXPORT jlong JNICALL
1330 /* ARGSUSED */
1331 Java_org_opensolaris_os_dtrace_LocalConsumer__1quantizeBucket(JNIEnv *env,
1332     jclass c, jint bucket)
1333 {
1334 	return (DTRACE_QUANTIZE_BUCKETVAL(bucket));
1335 }
1336 
1337 /*
1338  * Protected by global lock (LocalConsumer.class)
1339  */
1340 JNIEXPORT jstring JNICALL
1341 Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupKernelFunction(
1342     JNIEnv *jenv, jobject caller, jobject address)
1343 {
1344 	dtj_java_consumer_t jc;
1345 	dtrace_hdl_t *dtp;
1346 
1347 	jstring jfunc; /* return value */
1348 
1349 	GElf_Addr addr;
1350 	char dummy;
1351 	char *s;
1352 	int rc;
1353 
1354 	if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
1355 		return (NULL); /* java exception pending */
1356 	}
1357 	dtp = jc.dtjj_consumer->dtjc_dtp;
1358 
1359 	/* Does not throw exceptions */
1360 	if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) {
1361 		/* intValue() of class Number does not throw exceptions */
1362 		addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv,
1363 		    address, g_intval_jm);
1364 	} else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) {
1365 		/* longValue() of class Number does not throw exceptions */
1366 		addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv,
1367 		    address, g_longval_jm);
1368 	} else {
1369 		dtj_throw_class_cast(jenv, "Expected Number address");
1370 		return (NULL);
1371 	}
1372 
1373 	rc = dtrace_addr2str(dtp, addr, &dummy, 1);
1374 	s = malloc(rc + 1);
1375 	if (!s) {
1376 		dtj_throw_out_of_memory(jenv,
1377 		    "Failed to allocate kernel function name");
1378 		return (NULL);
1379 	}
1380 	(void) dtrace_addr2str(dtp, addr, s, rc + 1);
1381 
1382 	jfunc = (*jenv)->NewStringUTF(jenv, s);
1383 	free(s);
1384 	return (jfunc);
1385 }
1386 
1387 /*
1388  * Protected by global lock in Consumer.java
1389  */
1390 JNIEXPORT jstring JNICALL
1391 Java_org_opensolaris_os_dtrace_LocalConsumer__1lookupUserFunction(JNIEnv *jenv,
1392     jobject caller, jint pid, jobject address)
1393 {
1394 	dtj_java_consumer_t jc;
1395 	dtrace_hdl_t *dtp;
1396 
1397 	jstring jfunc; /* return value */
1398 
1399 	GElf_Addr addr;
1400 	char dummy;
1401 	char *s;
1402 	int rc;
1403 
1404 	if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
1405 		return (NULL); /* java exception pending */
1406 	}
1407 	dtp = jc.dtjj_consumer->dtjc_dtp;
1408 
1409 	/* Does not throw exceptions */
1410 	if ((*jenv)->IsInstanceOf(jenv, address, g_int_jc)) {
1411 		/* intValue() of class Number does not throw exceptions */
1412 		addr = (GElf_Addr)(uint32_t)(*jenv)->CallIntMethod(jenv,
1413 		    address, g_intval_jm);
1414 	} else if ((*jenv)->IsInstanceOf(jenv, address, g_number_jc)) {
1415 		/* longValue() of class Number does not throw exceptions */
1416 		addr = (GElf_Addr)(*jenv)->CallLongMethod(jenv,
1417 		    address, g_longval_jm);
1418 	} else {
1419 		dtj_throw_class_cast(jenv, "Expected Number address");
1420 		return (NULL);
1421 	}
1422 
1423 	rc = dtrace_uaddr2str(dtp, pid, addr, &dummy, 1);
1424 	s = malloc(rc + 1);
1425 	if (!s) {
1426 		dtj_throw_out_of_memory(jenv,
1427 		    "Failed to allocate user function name");
1428 		return (NULL);
1429 	}
1430 	(void) dtrace_uaddr2str(dtp, pid, addr, s, rc + 1);
1431 
1432 	jfunc = (*jenv)->NewStringUTF(jenv, s);
1433 	free(s);
1434 	return (jfunc);
1435 }
1436 
1437 JNIEXPORT jobject JNICALL
1438 Java_org_opensolaris_os_dtrace_LocalConsumer__1getAggregate(JNIEnv *env,
1439     jobject obj, jobject spec)
1440 {
1441 	dtj_java_consumer_t jc;
1442 
1443 	jobject aggregate = NULL;
1444 
1445 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1446 		return (NULL); /* java exception pending */
1447 	}
1448 
1449 	if (dtj_java_consumer_init(env, &jc) != DTJ_OK) {
1450 		return (NULL); /* java exception pending */
1451 	}
1452 	jc.dtjj_aggregate_spec = spec;
1453 
1454 	/*
1455 	 * Must set the thread-specific java consumer before calling any
1456 	 * function that triggers callbacks to the bufhandler set by
1457 	 * dtrace_handle_buffered().  The bufhandler needs access to the correct
1458 	 * JNI state specific to either the consumer loop or the
1459 	 * getAggregate() call.
1460 	 */
1461 	(void) pthread_setspecific(g_dtj_consumer_key, &jc);
1462 	aggregate = dtj_get_aggregate(&jc);
1463 	if (!aggregate) {
1464 		/* java exception pending */
1465 		jc.dtjj_consumer->dtjc_interrupt = B_TRUE;
1466 	}
1467 	/*
1468 	 * Cleans up only references created by this JNI invocation.  Leaves
1469 	 * cached per-consumer state untouched.
1470 	 */
1471 	dtj_java_consumer_fini(env, &jc);
1472 	return (aggregate);
1473 }
1474 
1475 /*
1476  * Returns the pid of the created process, or -1 in case of an error (java
1477  * exception pending).
1478  * Protected by global lock (LocalConsumer.class)
1479  */
1480 JNIEXPORT int JNICALL
1481 Java_org_opensolaris_os_dtrace_LocalConsumer__1createProcess(JNIEnv *jenv,
1482     jobject caller, jstring command)
1483 {
1484 	dtj_java_consumer_t jc;
1485 	dtrace_hdl_t *dtp;
1486 	struct ps_prochandle *P;
1487 	char **argv;
1488 	int argc;
1489 
1490 	if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
1491 		return (-1); /* java exception pending */
1492 	}
1493 	dtp = jc.dtjj_consumer->dtjc_dtp;
1494 
1495 	if (jc.dtjj_consumer->dtjc_process_list == NULL) {
1496 		jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create();
1497 		if (!jc.dtjj_consumer->dtjc_process_list) {
1498 			dtj_throw_out_of_memory(jenv,
1499 			    "Could not allocate process list");
1500 			return (-1);
1501 		}
1502 	}
1503 
1504 	argv = dtj_make_argv(jenv, command, &argc);
1505 	if ((*jenv)->ExceptionCheck(jenv)) {
1506 		return (-1);
1507 	}
1508 
1509 	P = dtrace_proc_create(dtp, argv[0], argv);
1510 	dtj_free_argv(argv);
1511 
1512 	if (!P) {
1513 		dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp,
1514 		    dtrace_errno(dtp)));
1515 		return (-1);
1516 	}
1517 
1518 	if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) {
1519 		dtj_throw_out_of_memory(jenv,
1520 		    "Failed to add process to process list");
1521 		dtrace_proc_release(dtp, P);
1522 		return (-1);
1523 	}
1524 	return (Pstatus(P)->pr_pid);
1525 }
1526 
1527 /*
1528  * Protected by global lock (LocalConsumer.class)
1529  */
1530 JNIEXPORT void JNICALL
1531 Java_org_opensolaris_os_dtrace_LocalConsumer__1grabProcess(JNIEnv *jenv,
1532     jobject caller, jint pid)
1533 {
1534 	dtj_java_consumer_t jc;
1535 	dtrace_hdl_t *dtp;
1536 	struct ps_prochandle *P;
1537 
1538 	if (dtj_get_java_consumer(jenv, caller, &jc) != DTJ_OK) {
1539 		return; /* java exception pending */
1540 	}
1541 	dtp = jc.dtjj_consumer->dtjc_dtp;
1542 
1543 	if (jc.dtjj_consumer->dtjc_process_list == NULL) {
1544 		jc.dtjj_consumer->dtjc_process_list = dtj_pointer_list_create();
1545 		if (jc.dtjj_consumer->dtjc_process_list == NULL) {
1546 			dtj_throw_out_of_memory(jenv,
1547 			    "Could not allocate process list");
1548 			return;
1549 		}
1550 	}
1551 
1552 	P = dtrace_proc_grab(dtp, (pid_t)pid, 0);
1553 	if (!P) {
1554 		dtj_throw_dtrace_exception(&jc, dtrace_errmsg(dtp,
1555 		    dtrace_errno(dtp)));
1556 		return;
1557 	}
1558 
1559 	if (!dtj_pointer_list_add(jc.dtjj_consumer->dtjc_process_list, P)) {
1560 		dtj_throw_out_of_memory(jenv,
1561 		    "Failed to add process to process list");
1562 		dtrace_proc_release(dtp, P);
1563 		return;
1564 	}
1565 }
1566 
1567 /*
1568  * Lists all probes, or lists matching probes (using the matching rules from
1569  * Table 4-1 of the DTrace manual).
1570  *
1571  * In the future it may be desirable to support an array of probe filters rather
1572  * than a single filter.  It could be that if a probe matched any of the given
1573  * filters, it would be included (implied logical OR).
1574  *
1575  * Protected by global lock (LocalConsumer.class)
1576  * param list: an empty list to populate (this function empties the list if it
1577  * is not empty already)
1578  * param filter: a ProbeDescription instance; the list will include only probes
1579  * that match the filter (match all probes if filter is null)
1580  */
1581 JNIEXPORT void JNICALL
1582 Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbes(JNIEnv *env,
1583     jobject obj, jobject list, jobject filter)
1584 {
1585 	dtj_list_probes(env, obj, list, filter, dtj_list_probe);
1586 }
1587 
1588 JNIEXPORT void JNICALL
1589 Java_org_opensolaris_os_dtrace_LocalConsumer__1listProbeDetail(JNIEnv *env,
1590     jobject obj, jobject list, jobject filter)
1591 {
1592 	dtj_list_probes(env, obj, list, filter, dtj_list_probe_detail);
1593 }
1594 
1595 static void
1596 dtj_list_probes(JNIEnv *env, jobject obj, jobject list, jobject filter,
1597     dtrace_probe_f *func)
1598 {
1599 	dtj_java_consumer_t jc;
1600 	dtrace_hdl_t *dtp;
1601 	dtrace_probedesc_t probe;
1602 	dtrace_probedesc_t *pdp = NULL;
1603 	const char *probestr;
1604 	int rc;
1605 
1606 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1607 		return; /* java exception pending */
1608 	}
1609 	dtp = jc.dtjj_consumer->dtjc_dtp;
1610 
1611 	jc.dtjj_probelist = list;
1612 
1613 	/* clear in-out list parameter */
1614 	(*env)->CallVoidMethod(env, list, g_listclear_jm);
1615 	if ((*env)->ExceptionCheck(env)) {
1616 		return;
1617 	}
1618 
1619 	if (filter) {
1620 		jstring jprobestr = NULL;
1621 
1622 		jprobestr = (*env)->CallObjectMethod(env, filter,
1623 		    g_tostring_jm);
1624 		if ((*env)->ExceptionCheck(env)) {
1625 			return;
1626 		}
1627 		probestr = (*env)->GetStringUTFChars(env, jprobestr, NULL);
1628 		if (!probestr) {
1629 			(*env)->DeleteLocalRef(env, jprobestr);
1630 			return; /* java exception pending */
1631 		}
1632 
1633 		bzero(&probe, sizeof (probe));
1634 		rc = dtrace_str2desc(dtp, DTRACE_PROBESPEC_NAME, probestr,
1635 		    &probe);
1636 		(*env)->ReleaseStringUTFChars(env, jprobestr, probestr);
1637 		(*env)->DeleteLocalRef(env, jprobestr);
1638 		if (rc == -1) {
1639 			dtj_throw_dtrace_exception(&jc,
1640 			    "%s is not a valid probe description: %s",
1641 			    probestr, dtrace_errmsg(dtp,
1642 			    dtrace_errno(dtp)));
1643 			return;
1644 		}
1645 
1646 		pdp = &probe;
1647 	}
1648 
1649 	(void) dtrace_probe_iter(dtp, pdp, func, &jc);
1650 }
1651 
1652 /*
1653  * Returns 0 to indicate success, or -1 to cause dtrace_probe_iter() to return a
1654  * negative value prematurely (indicating no match or failure).
1655  */
1656 static int
1657 /* ARGSUSED */
1658 dtj_list_probe(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp, void *arg)
1659 {
1660 	dtj_java_consumer_t *jc = arg;
1661 	JNIEnv *jenv = jc->dtjj_jenv;
1662 
1663 	jobject jprobedesc = NULL;
1664 
1665 	jprobedesc = dtj_new_probedesc(jc, pdp);
1666 	if (!jprobedesc) {
1667 		return (-1); /* java exception pending */
1668 	}
1669 
1670 	/* add probe to list */
1671 	(*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm,
1672 	    jprobedesc);
1673 	(*jenv)->DeleteLocalRef(jenv, jprobedesc);
1674 	if ((*jenv)->ExceptionCheck(jenv)) {
1675 		return (-1);
1676 	}
1677 
1678 	return (0);
1679 }
1680 
1681 /*ARGSUSED*/
1682 static int
1683 dtj_list_probe_detail(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp,
1684     void *arg)
1685 {
1686 	dtj_java_consumer_t *jc = arg;
1687 	JNIEnv *jenv = jc->dtjj_jenv;
1688 	dtrace_probeinfo_t p;
1689 
1690 	jobject jprobedesc = NULL;
1691 	jobject jprobeinfo = NULL;
1692 	jobject jprobe = NULL;
1693 
1694 	jprobedesc = dtj_new_probedesc(jc, pdp);
1695 	if (!jprobedesc) {
1696 		return (-1); /* java exception pending */
1697 	}
1698 
1699 	/*
1700 	 * If dtrace_probe_info() returns a non-zero value, dtrace_errno is set
1701 	 * for us.  In that case, ignore the dtrace error and simply omit probe
1702 	 * info.  That error is implicitly cleared the next time a call is made
1703 	 * using the same dtrace handle.
1704 	 */
1705 	if (dtrace_probe_info(dtp, pdp, &p) == 0) {
1706 		/* create probe info instance */
1707 		jprobeinfo = dtj_new_probeinfo(jc, &p);
1708 		if (!jprobeinfo) {
1709 			(*jenv)->DeleteLocalRef(jenv, jprobedesc);
1710 			return (-1); /* java exception pending */
1711 		}
1712 	}
1713 
1714 	/* create listed probe instance */
1715 	jprobe = (*jenv)->NewObject(jenv, g_probe_jc, g_probeinit_jm,
1716 	    jprobedesc, jprobeinfo);
1717 	(*jenv)->DeleteLocalRef(jenv, jprobedesc);
1718 	(*jenv)->DeleteLocalRef(jenv, jprobeinfo);
1719 	if (!jprobe) {
1720 		return (-1); /* java exception pending */
1721 	}
1722 
1723 	/* add probe to list */
1724 	(*jenv)->CallVoidMethod(jenv, jc->dtjj_probelist, g_listadd_jm,
1725 	    jprobe);
1726 	(*jenv)->DeleteLocalRef(jenv, jprobe);
1727 	if ((*jenv)->ExceptionCheck(jenv)) {
1728 		return (-1);
1729 	}
1730 
1731 	return (0);
1732 }
1733 
1734 /*ARGSUSED*/
1735 static int
1736 dtj_list_stmt(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
1737     dtrace_stmtdesc_t *stp, void *arg)
1738 {
1739 	dtj_java_consumer_t *jc = arg;
1740 	dtrace_ecbdesc_t *edp = stp->dtsd_ecbdesc;
1741 
1742 	if (edp == jc->dtjj_consumer->dtjc_last_probe) {
1743 		return (0);
1744 	}
1745 
1746 	if (dtrace_probe_iter(dtp, &edp->dted_probe,
1747 	    jc->dtjj_consumer->dtjc_plistfunc, arg) != 0) {
1748 		dtj_throw_dtrace_exception(jc,
1749 		    "failed to match %s:%s:%s:%s: %s",
1750 		    edp->dted_probe.dtpd_provider, edp->dted_probe.dtpd_mod,
1751 		    edp->dted_probe.dtpd_func, edp->dted_probe.dtpd_name,
1752 		    dtrace_errmsg(dtp, dtrace_errno(dtp)));
1753 		return (1);
1754 	}
1755 
1756 	jc->dtjj_consumer->dtjc_last_probe = edp;
1757 	return (0);
1758 }
1759 
1760 /*
1761  * Protected by global lock in Consumer.java
1762  */
1763 JNIEXPORT void JNICALL
1764 Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbes(JNIEnv *env,
1765     jobject obj, jobject list, jobject program)
1766 {
1767 	dtj_list_compiled_probes(env, obj, list, program, dtj_list_probe);
1768 }
1769 
1770 JNIEXPORT void JNICALL
1771 Java_org_opensolaris_os_dtrace_LocalConsumer__1listCompiledProbeDetail(
1772     JNIEnv *env, jobject obj, jobject list, jobject program)
1773 {
1774 	dtj_list_compiled_probes(env, obj, list, program,
1775 	    dtj_list_probe_detail);
1776 }
1777 
1778 static void
1779 dtj_list_compiled_probes(JNIEnv *env, jobject obj, jobject list,
1780     jobject program, dtrace_probe_f *func)
1781 {
1782 	dtj_java_consumer_t jc;
1783 	dtrace_hdl_t *dtp;
1784 	uu_list_walk_t *itr;
1785 	dtj_program_t *p;
1786 	boolean_t found;
1787 	int progid = -1;
1788 	int i;
1789 
1790 	if (dtj_get_java_consumer(env, obj, &jc) != DTJ_OK) {
1791 		return; /* java exception pending */
1792 	}
1793 	dtp = jc.dtjj_consumer->dtjc_dtp;
1794 	jc.dtjj_probelist = list;
1795 
1796 	(*env)->CallVoidMethod(env, list, g_listclear_jm);
1797 	if ((*env)->ExceptionCheck(env)) {
1798 		return;
1799 	}
1800 
1801 	if (program) {
1802 		if (dtj_list_empty(jc.dtjj_consumer->dtjc_program_list)) {
1803 			dtj_throw_no_such_element(env, "no compiled program");
1804 			return;
1805 		}
1806 		progid = (*env)->GetIntField(env, program, g_progid_jf);
1807 		if (progid == -1) {
1808 			dtj_throw_illegal_argument(env, "invalid program");
1809 			return;
1810 		}
1811 	}
1812 
1813 	jc.dtjj_consumer->dtjc_plistfunc = func;
1814 	found = B_FALSE;
1815 	itr = uu_list_walk_start(jc.dtjj_consumer->dtjc_program_list, 0);
1816 	for (i = 0; (p = uu_list_walk_next(itr)) != NULL; ++i) {
1817 		if ((progid != -1) && (progid != i)) {
1818 			continue;
1819 		}
1820 
1821 		found = B_TRUE;
1822 		(void) dtrace_stmt_iter(dtp, p->dtjp_program,
1823 		    (dtrace_stmt_f *)dtj_list_stmt, &jc);
1824 	}
1825 	uu_list_walk_end(itr);
1826 
1827 	if (program && !found) {
1828 		dtj_throw_no_such_element(env, "program not found");
1829 	}
1830 }
1831 
1832 /*
1833  * Static LocalConsumer.java method
1834  * Protected by global lock (LocalConsumer.class)
1835  */
1836 JNIEXPORT jstring JNICALL
1837 /* ARGSUSED */
1838 Java_org_opensolaris_os_dtrace_LocalConsumer__1getVersion(JNIEnv *env,
1839     jclass class)
1840 {
1841 	/*
1842 	 * Handles the case of locale-specific encoding of the user-visible
1843 	 * version string containing non-ASCII characters.
1844 	 */
1845 	return (dtj_NewStringNative(env, _dtrace_version));
1846 }
1847 
1848 /*
1849  * Static LocalConsumer.java method
1850  * Protected by global lock (LocalConsumer.class)
1851  */
1852 JNIEXPORT jstring JNICALL
1853 /* ARGSUSED */
1854 Java_org_opensolaris_os_dtrace_LocalConsumer__1getExecutableName(JNIEnv *env,
1855     jclass class)
1856 {
1857 	jstring jname = NULL;
1858 	const char *name = NULL;
1859 	char *s;
1860 	int len;
1861 
1862 	name = dtj_getexecname();
1863 	len = strlen(name);
1864 	s = malloc(len + 1);
1865 	if (!s) {
1866 		dtj_throw_out_of_memory(env, "Failed to allocate execname");
1867 		return (NULL);
1868 	}
1869 	(void) strcpy(s, name);
1870 	name = basename(s);
1871 	free(s);
1872 	jname = (*env)->NewStringUTF(env, name);
1873 	return (jname);
1874 }
1875 
1876 /*
1877  * Static LocalConsumer.java method
1878  */
1879 JNIEXPORT void JNICALL
1880 /* ARGSUSED */
1881 Java_org_opensolaris_os_dtrace_LocalConsumer__1setMaximumConsumers(JNIEnv *env,
1882     jclass class, jint max)
1883 {
1884 	g_max_consumers = max;
1885 }
1886 
1887 /*
1888  * Static LocalConsumer.java method
1889  */
1890 JNIEXPORT void JNICALL
1891 /* ARGSUSED */
1892 Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug(JNIEnv *env,
1893     jclass class, jboolean debug)
1894 {
1895 	g_dtj_util_debug = debug;
1896 }
1897 
1898 JNIEXPORT void JNICALL
1899 Java_org_opensolaris_os_dtrace_LocalConsumer__1destroy(JNIEnv *env, jobject obj)
1900 {
1901 	dtj_consumer_t *c;
1902 
1903 	c = dtj_remove_consumer(env, obj);
1904 	if (c == NULL) {
1905 		return; /* java exception pending */
1906 	}
1907 	dtj_consumer_destroy(c);
1908 }
1909