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