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