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