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