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