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