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