1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <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 3
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
106static boolean_t g_dtj_load = B_FALSE;
107static 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 */
112static dtj_consumer_t **g_consumer_table = NULL;
113static size_t g_consumer_capacity = 0;
114static size_t g_consumer_count = 0;
115static size_t g_max_capacity_increment = MAX_CAPACITY_INCREMENT;
116static size_t g_max_consumers = 0; /* no maximum */
117static boolean_t g_init = B_FALSE;
118static pthread_mutex_t g_table_lock;
119static pthread_mutexattr_t g_table_lock_attr;
120pthread_key_t g_dtj_consumer_key;
121
122static int dtj_get_handle(JNIEnv *, jobject);
123static dtj_status_t dtj_get_java_consumer(JNIEnv *, jobject,
124    dtj_java_consumer_t *);
125static const char *dtj_getexecname(void);
126static jobject dtj_get_program_info(dtj_java_consumer_t *, dtrace_proginfo_t *);
127static jobject dtj_add_program(dtj_java_consumer_t *, dtj_program_t *);
128static void dtj_flag(uint_t *, uint_t, boolean_t *, boolean_t *);
129static boolean_t dtj_cflag(dtj_java_consumer_t *, const char *, boolean_t *,
130    boolean_t *);
131static void dtj_list_probes(JNIEnv *, jobject, jobject, jobject,
132    dtrace_probe_f *);
133static void dtj_list_compiled_probes(JNIEnv *, jobject, jobject, jobject,
134    dtrace_probe_f *);
135static int dtj_list_probe(dtrace_hdl_t *, const dtrace_probedesc_t *, void *);
136static int dtj_list_probe_detail(dtrace_hdl_t *, const dtrace_probedesc_t *,
137    void *);
138static int dtj_list_stmt(dtrace_hdl_t *, dtrace_prog_t *, dtrace_stmtdesc_t *,
139    void *);
140static boolean_t dtj_add_consumer(JNIEnv *, dtj_consumer_t *, int *);
141static dtj_consumer_t *dtj_remove_consumer(JNIEnv *, jobject);
142static dtj_consumer_t *dtj_remove_consumer_at(int);
143
144/*
145 * Gets a sequence-generated consumer ID, or NO_HANDLE if exception pending
146 */
147static int
148dtj_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 */
173static dtj_status_t
174dtj_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 */
216static boolean_t
217dtj_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 */
316static dtj_consumer_t *
317dtj_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 */
332static dtj_consumer_t *
333dtj_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 */
366static const char *
367dtj_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 */
395static jobject
396dtj_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 */
437static jobject
438dtj_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 */
468JNIEXPORT void JNICALL
469/* ARGSUSED */
470Java_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 */
483JNIEXPORT void JNICALL
484/* ARGSUSED */
485Java_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 */
508JNIEXPORT void JNICALL
509Java_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
602static void
603dtj_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 */
621static boolean_t
622dtj_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_options.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 */
669JNIEXPORT jobject JNICALL
670Java_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 */
728JNIEXPORT jobject JNICALL
729Java_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 */
793JNIEXPORT void JNICALL
794Java_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 */
871JNIEXPORT void JNICALL
872Java_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 */
922JNIEXPORT void JNICALL
923Java_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 */
1015JNIEXPORT jlong JNICALL
1016Java_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 */
1060JNIEXPORT void JNICALL
1061Java_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
1103JNIEXPORT jboolean JNICALL
1104Java_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 */
1134JNIEXPORT void JNICALL
1135Java_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 */
1163JNIEXPORT void JNICALL
1164Java_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
1185JNIEXPORT void JNICALL
1186Java_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 */
1260JNIEXPORT void JNICALL
1261Java_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 */
1276JNIEXPORT void JNICALL
1277Java_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 */
1311JNIEXPORT jint JNICALL
1312/* ARGSUSED */
1313Java_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 */
1329JNIEXPORT jlong JNICALL
1330/* ARGSUSED */
1331Java_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 */
1340JNIEXPORT jstring JNICALL
1341Java_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 */
1390JNIEXPORT jstring JNICALL
1391Java_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
1437JNIEXPORT jobject JNICALL
1438Java_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 */
1480JNIEXPORT int JNICALL
1481Java_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 */
1530JNIEXPORT void JNICALL
1531Java_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 */
1581JNIEXPORT void JNICALL
1582Java_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
1588JNIEXPORT void JNICALL
1589Java_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
1595static void
1596dtj_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 */
1656static int
1657/* ARGSUSED */
1658dtj_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*/
1682static int
1683dtj_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*/
1735static int
1736dtj_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 */
1763JNIEXPORT void JNICALL
1764Java_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
1770JNIEXPORT void JNICALL
1771Java_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
1778static void
1779dtj_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 */
1836JNIEXPORT jstring JNICALL
1837/* ARGSUSED */
1838Java_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 */
1852JNIEXPORT jstring JNICALL
1853/* ARGSUSED */
1854Java_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 */
1879JNIEXPORT void JNICALL
1880/* ARGSUSED */
1881Java_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 */
1890JNIEXPORT void JNICALL
1891/* ARGSUSED */
1892Java_org_opensolaris_os_dtrace_LocalConsumer__1setDebug(JNIEnv *env,
1893    jclass class, jboolean debug)
1894{
1895	g_dtj_util_debug = debug;
1896}
1897
1898JNIEXPORT void JNICALL
1899Java_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