1 /* 2 * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "util.h" 27 #include "invoker.h" 28 #include "eventHandler.h" 29 #include "threadControl.h" 30 #include "outStream.h" 31 32 static jrawMonitorID invokerLock; 33 34 void 35 invoker_initialize(void) 36 { 37 invokerLock = debugMonitorCreate("JDWP Invocation Lock"); 38 } 39 40 void 41 invoker_reset(void) 42 { 43 } 44 45 void invoker_lock(void) 46 { 47 debugMonitorEnter(invokerLock); 48 } 49 50 void invoker_unlock(void) 51 { 52 debugMonitorExit(invokerLock); 53 } 54 55 static jbyte 56 returnTypeTag(char *signature) 57 { 58 char *tagPtr = strchr(signature, SIGNATURE_END_ARGS); 59 JDI_ASSERT(tagPtr); 60 tagPtr++; /* 1st character after the end of args */ 61 return (jbyte)*tagPtr; 62 } 63 64 static jbyte 65 nextArgumentTypeTag(void **cursor) 66 { 67 char *tagPtr = *cursor; 68 jbyte argumentTag = (jbyte)*tagPtr; 69 70 if (*tagPtr != SIGNATURE_END_ARGS) { 71 /* Skip any array modifiers */ 72 while (*tagPtr == JDWP_TAG(ARRAY)) { 73 tagPtr++; 74 } 75 /* Skip class name */ 76 if (*tagPtr == JDWP_TAG(OBJECT)) { 77 tagPtr = strchr(tagPtr, SIGNATURE_END_CLASS) + 1; 78 JDI_ASSERT(tagPtr); 79 } else { 80 /* Skip primitive sig */ 81 tagPtr++; 82 } 83 } 84 85 *cursor = tagPtr; 86 return argumentTag; 87 } 88 89 static jbyte 90 firstArgumentTypeTag(char *signature, void **cursor) 91 { 92 JDI_ASSERT(signature[0] == SIGNATURE_BEGIN_ARGS); 93 *cursor = signature + 1; /* skip to the first arg */ 94 return nextArgumentTypeTag(cursor); 95 } 96 97 98 /* 99 * Note: argument refs may be destroyed on out-of-memory error 100 */ 101 static jvmtiError 102 createGlobalRefs(JNIEnv *env, InvokeRequest *request) 103 { 104 jvmtiError error; 105 jclass clazz = NULL; 106 jobject instance = NULL; 107 jint argIndex; 108 jbyte argumentTag; 109 jvalue *argument; 110 void *cursor; 111 jobject *argRefs = NULL; 112 113 error = JVMTI_ERROR_NONE; 114 115 if ( request->argumentCount > 0 ) { 116 /*LINTED*/ 117 argRefs = jvmtiAllocate((jint)(request->argumentCount*sizeof(jobject))); 118 if ( argRefs==NULL ) { 119 error = AGENT_ERROR_OUT_OF_MEMORY; 120 } else { 121 /*LINTED*/ 122 (void)memset(argRefs, 0, request->argumentCount*sizeof(jobject)); 123 } 124 } 125 126 if ( error == JVMTI_ERROR_NONE ) { 127 saveGlobalRef(env, request->clazz, &clazz); 128 if (clazz == NULL) { 129 error = AGENT_ERROR_OUT_OF_MEMORY; 130 } 131 } 132 133 if ( error == JVMTI_ERROR_NONE && request->instance != NULL ) { 134 saveGlobalRef(env, request->instance, &instance); 135 if (instance == NULL) { 136 error = AGENT_ERROR_OUT_OF_MEMORY; 137 } 138 } 139 140 if ( error == JVMTI_ERROR_NONE && argRefs!=NULL ) { 141 argIndex = 0; 142 argumentTag = firstArgumentTypeTag(request->methodSignature, &cursor); 143 argument = request->arguments; 144 while (argumentTag != SIGNATURE_END_ARGS) { 145 if ( argIndex > request->argumentCount ) { 146 break; 147 } 148 if ((argumentTag == JDWP_TAG(OBJECT)) || 149 (argumentTag == JDWP_TAG(ARRAY))) { 150 /* Create a global ref for any non-null argument */ 151 if (argument->l != NULL) { 152 saveGlobalRef(env, argument->l, &argRefs[argIndex]); 153 if (argRefs[argIndex] == NULL) { 154 error = AGENT_ERROR_OUT_OF_MEMORY; 155 break; 156 } 157 } 158 } 159 argument++; 160 argIndex++; 161 argumentTag = nextArgumentTypeTag(&cursor); 162 } 163 } 164 165 #ifdef FIXUP /* Why isn't this an error? */ 166 /* Make sure the argument count matches */ 167 if ( error == JVMTI_ERROR_NONE && argIndex != request->argumentCount ) { 168 error = AGENT_ERROR_INVALID_COUNT; 169 } 170 #endif 171 172 /* Finally, put the global refs into the request if no errors */ 173 if ( error == JVMTI_ERROR_NONE ) { 174 request->clazz = clazz; 175 request->instance = instance; 176 if ( argRefs!=NULL ) { 177 argIndex = 0; 178 argumentTag = firstArgumentTypeTag(request->methodSignature, &cursor); 179 argument = request->arguments; 180 while ( argIndex < request->argumentCount ) { 181 if ((argumentTag == JDWP_TAG(OBJECT)) || 182 (argumentTag == JDWP_TAG(ARRAY))) { 183 argument->l = argRefs[argIndex]; 184 } 185 argument++; 186 argIndex++; 187 argumentTag = nextArgumentTypeTag(&cursor); 188 } 189 jvmtiDeallocate(argRefs); 190 } 191 return JVMTI_ERROR_NONE; 192 193 } else { 194 /* Delete global references */ 195 if ( clazz != NULL ) { 196 tossGlobalRef(env, &clazz); 197 } 198 if ( instance != NULL ) { 199 tossGlobalRef(env, &instance); 200 } 201 if ( argRefs!=NULL ) { 202 for ( argIndex=0; argIndex < request->argumentCount; argIndex++ ) { 203 if ( argRefs[argIndex] != NULL ) { 204 tossGlobalRef(env, &argRefs[argIndex]); 205 } 206 } 207 jvmtiDeallocate(argRefs); 208 } 209 } 210 211 return error; 212 } 213 214 static jvmtiError 215 fillInvokeRequest(JNIEnv *env, InvokeRequest *request, 216 jbyte invokeType, jbyte options, jint id, 217 jthread thread, jclass clazz, jmethodID method, 218 jobject instance, 219 jvalue *arguments, jint argumentCount) 220 { 221 jvmtiError error; 222 if (!request->available) { 223 /* 224 * Thread is not at a point where it can invoke. 225 */ 226 return AGENT_ERROR_INVALID_THREAD; 227 } 228 if (request->pending) { 229 /* 230 * Pending invoke 231 */ 232 return AGENT_ERROR_ALREADY_INVOKING; 233 } 234 235 request->invokeType = invokeType; 236 request->options = options; 237 request->detached = JNI_FALSE; 238 request->id = id; 239 request->clazz = clazz; 240 request->method = method; 241 request->instance = instance; 242 request->arguments = arguments; 243 request->arguments = arguments; 244 request->argumentCount = argumentCount; 245 246 request->returnValue.j = 0; 247 request->exception = 0; 248 249 /* 250 * Squirrel away the method signature 251 */ 252 error = methodSignature(method, NULL, &request->methodSignature, NULL); 253 if (error != JVMTI_ERROR_NONE) { 254 return error; 255 } 256 257 /* 258 * The given references for class and instance are not guaranteed 259 * to be around long enough for invocation, so create new ones 260 * here. 261 */ 262 error = createGlobalRefs(env, request); 263 if (error != JVMTI_ERROR_NONE) { 264 jvmtiDeallocate(request->methodSignature); 265 return error; 266 } 267 268 request->pending = JNI_TRUE; 269 request->available = JNI_FALSE; 270 return JVMTI_ERROR_NONE; 271 } 272 273 void 274 invoker_enableInvokeRequests(jthread thread) 275 { 276 InvokeRequest *request; 277 278 JDI_ASSERT(thread); 279 280 debugMonitorEnter(invokerLock); 281 request = threadControl_getInvokeRequest(thread); 282 if (request == NULL) { 283 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 284 } 285 286 request->available = JNI_TRUE; 287 debugMonitorExit(invokerLock); 288 } 289 290 jvmtiError 291 invoker_requestInvoke(jbyte invokeType, jbyte options, jint id, 292 jthread thread, jclass clazz, jmethodID method, 293 jobject instance, 294 jvalue *arguments, jint argumentCount) 295 { 296 JNIEnv *env = getEnv(); 297 InvokeRequest *request; 298 jvmtiError error = JVMTI_ERROR_NONE; 299 300 debugMonitorEnter(invokerLock); 301 request = threadControl_getInvokeRequest(thread); 302 if (request != NULL) { 303 error = fillInvokeRequest(env, request, invokeType, options, id, 304 thread, clazz, method, instance, 305 arguments, argumentCount); 306 } 307 debugMonitorExit(invokerLock); 308 309 if (error == JVMTI_ERROR_NONE) { 310 if (options & JDWP_INVOKE_OPTIONS(SINGLE_THREADED) ) { 311 /* true means it is okay to unblock the commandLoop thread */ 312 (void)threadControl_resumeThread(thread, JNI_TRUE); 313 } else { 314 (void)threadControl_resumeAll(); 315 } 316 } 317 318 return error; 319 } 320 321 static void 322 invokeConstructor(JNIEnv *env, InvokeRequest *request) 323 { 324 jobject object; 325 object = JNI_FUNC_PTR(env,NewObjectA)(env, request->clazz, 326 request->method, 327 request->arguments); 328 request->returnValue.l = NULL; 329 if (object != NULL) { 330 saveGlobalRef(env, object, &(request->returnValue.l)); 331 } 332 } 333 334 static void 335 invokeStatic(JNIEnv *env, InvokeRequest *request) 336 { 337 switch(returnTypeTag(request->methodSignature)) { 338 case JDWP_TAG(OBJECT): 339 case JDWP_TAG(ARRAY): { 340 jobject object; 341 object = JNI_FUNC_PTR(env,CallStaticObjectMethodA)(env, 342 request->clazz, 343 request->method, 344 request->arguments); 345 request->returnValue.l = NULL; 346 if (object != NULL) { 347 saveGlobalRef(env, object, &(request->returnValue.l)); 348 } 349 break; 350 } 351 352 353 case JDWP_TAG(BYTE): 354 request->returnValue.b = JNI_FUNC_PTR(env,CallStaticByteMethodA)(env, 355 request->clazz, 356 request->method, 357 request->arguments); 358 break; 359 360 case JDWP_TAG(CHAR): 361 request->returnValue.c = JNI_FUNC_PTR(env,CallStaticCharMethodA)(env, 362 request->clazz, 363 request->method, 364 request->arguments); 365 break; 366 367 case JDWP_TAG(FLOAT): 368 request->returnValue.f = JNI_FUNC_PTR(env,CallStaticFloatMethodA)(env, 369 request->clazz, 370 request->method, 371 request->arguments); 372 break; 373 374 case JDWP_TAG(DOUBLE): 375 request->returnValue.d = JNI_FUNC_PTR(env,CallStaticDoubleMethodA)(env, 376 request->clazz, 377 request->method, 378 request->arguments); 379 break; 380 381 case JDWP_TAG(INT): 382 request->returnValue.i = JNI_FUNC_PTR(env,CallStaticIntMethodA)(env, 383 request->clazz, 384 request->method, 385 request->arguments); 386 break; 387 388 case JDWP_TAG(LONG): 389 request->returnValue.j = JNI_FUNC_PTR(env,CallStaticLongMethodA)(env, 390 request->clazz, 391 request->method, 392 request->arguments); 393 break; 394 395 case JDWP_TAG(SHORT): 396 request->returnValue.s = JNI_FUNC_PTR(env,CallStaticShortMethodA)(env, 397 request->clazz, 398 request->method, 399 request->arguments); 400 break; 401 402 case JDWP_TAG(BOOLEAN): 403 request->returnValue.z = JNI_FUNC_PTR(env,CallStaticBooleanMethodA)(env, 404 request->clazz, 405 request->method, 406 request->arguments); 407 break; 408 409 case JDWP_TAG(VOID): 410 JNI_FUNC_PTR(env,CallStaticVoidMethodA)(env, 411 request->clazz, 412 request->method, 413 request->arguments); 414 break; 415 416 default: 417 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature"); 418 break; 419 } 420 } 421 422 static void 423 invokeVirtual(JNIEnv *env, InvokeRequest *request) 424 { 425 switch(returnTypeTag(request->methodSignature)) { 426 case JDWP_TAG(OBJECT): 427 case JDWP_TAG(ARRAY): { 428 jobject object; 429 object = JNI_FUNC_PTR(env,CallObjectMethodA)(env, 430 request->instance, 431 request->method, 432 request->arguments); 433 request->returnValue.l = NULL; 434 if (object != NULL) { 435 saveGlobalRef(env, object, &(request->returnValue.l)); 436 } 437 break; 438 } 439 440 case JDWP_TAG(BYTE): 441 request->returnValue.b = JNI_FUNC_PTR(env,CallByteMethodA)(env, 442 request->instance, 443 request->method, 444 request->arguments); 445 break; 446 447 case JDWP_TAG(CHAR): 448 request->returnValue.c = JNI_FUNC_PTR(env,CallCharMethodA)(env, 449 request->instance, 450 request->method, 451 request->arguments); 452 break; 453 454 case JDWP_TAG(FLOAT): 455 request->returnValue.f = JNI_FUNC_PTR(env,CallFloatMethodA)(env, 456 request->instance, 457 request->method, 458 request->arguments); 459 break; 460 461 case JDWP_TAG(DOUBLE): 462 request->returnValue.d = JNI_FUNC_PTR(env,CallDoubleMethodA)(env, 463 request->instance, 464 request->method, 465 request->arguments); 466 break; 467 468 case JDWP_TAG(INT): 469 request->returnValue.i = JNI_FUNC_PTR(env,CallIntMethodA)(env, 470 request->instance, 471 request->method, 472 request->arguments); 473 break; 474 475 case JDWP_TAG(LONG): 476 request->returnValue.j = JNI_FUNC_PTR(env,CallLongMethodA)(env, 477 request->instance, 478 request->method, 479 request->arguments); 480 break; 481 482 case JDWP_TAG(SHORT): 483 request->returnValue.s = JNI_FUNC_PTR(env,CallShortMethodA)(env, 484 request->instance, 485 request->method, 486 request->arguments); 487 break; 488 489 case JDWP_TAG(BOOLEAN): 490 request->returnValue.z = JNI_FUNC_PTR(env,CallBooleanMethodA)(env, 491 request->instance, 492 request->method, 493 request->arguments); 494 break; 495 496 case JDWP_TAG(VOID): 497 JNI_FUNC_PTR(env,CallVoidMethodA)(env, 498 request->instance, 499 request->method, 500 request->arguments); 501 break; 502 503 default: 504 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature"); 505 break; 506 } 507 } 508 509 static void 510 invokeNonvirtual(JNIEnv *env, InvokeRequest *request) 511 { 512 switch(returnTypeTag(request->methodSignature)) { 513 case JDWP_TAG(OBJECT): 514 case JDWP_TAG(ARRAY): { 515 jobject object; 516 object = JNI_FUNC_PTR(env,CallNonvirtualObjectMethodA)(env, 517 request->instance, 518 request->clazz, 519 request->method, 520 request->arguments); 521 request->returnValue.l = NULL; 522 if (object != NULL) { 523 saveGlobalRef(env, object, &(request->returnValue.l)); 524 } 525 break; 526 } 527 528 case JDWP_TAG(BYTE): 529 request->returnValue.b = JNI_FUNC_PTR(env,CallNonvirtualByteMethodA)(env, 530 request->instance, 531 request->clazz, 532 request->method, 533 request->arguments); 534 break; 535 536 case JDWP_TAG(CHAR): 537 request->returnValue.c = JNI_FUNC_PTR(env,CallNonvirtualCharMethodA)(env, 538 request->instance, 539 request->clazz, 540 request->method, 541 request->arguments); 542 break; 543 544 case JDWP_TAG(FLOAT): 545 request->returnValue.f = JNI_FUNC_PTR(env,CallNonvirtualFloatMethodA)(env, 546 request->instance, 547 request->clazz, 548 request->method, 549 request->arguments); 550 break; 551 552 case JDWP_TAG(DOUBLE): 553 request->returnValue.d = JNI_FUNC_PTR(env,CallNonvirtualDoubleMethodA)(env, 554 request->instance, 555 request->clazz, 556 request->method, 557 request->arguments); 558 break; 559 560 case JDWP_TAG(INT): 561 request->returnValue.i = JNI_FUNC_PTR(env,CallNonvirtualIntMethodA)(env, 562 request->instance, 563 request->clazz, 564 request->method, 565 request->arguments); 566 break; 567 568 case JDWP_TAG(LONG): 569 request->returnValue.j = JNI_FUNC_PTR(env,CallNonvirtualLongMethodA)(env, 570 request->instance, 571 request->clazz, 572 request->method, 573 request->arguments); 574 break; 575 576 case JDWP_TAG(SHORT): 577 request->returnValue.s = JNI_FUNC_PTR(env,CallNonvirtualShortMethodA)(env, 578 request->instance, 579 request->clazz, 580 request->method, 581 request->arguments); 582 break; 583 584 case JDWP_TAG(BOOLEAN): 585 request->returnValue.z = JNI_FUNC_PTR(env,CallNonvirtualBooleanMethodA)(env, 586 request->instance, 587 request->clazz, 588 request->method, 589 request->arguments); 590 break; 591 592 case JDWP_TAG(VOID): 593 JNI_FUNC_PTR(env,CallNonvirtualVoidMethodA)(env, 594 request->instance, 595 request->clazz, 596 request->method, 597 request->arguments); 598 break; 599 600 default: 601 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature"); 602 break; 603 } 604 } 605 606 jboolean 607 invoker_doInvoke(jthread thread) 608 { 609 JNIEnv *env; 610 jboolean startNow; 611 InvokeRequest *request; 612 613 JDI_ASSERT(thread); 614 615 debugMonitorEnter(invokerLock); 616 617 request = threadControl_getInvokeRequest(thread); 618 if (request == NULL) { 619 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 620 } 621 622 request->available = JNI_FALSE; 623 startNow = request->pending && !request->started; 624 625 if (startNow) { 626 request->started = JNI_TRUE; 627 } 628 debugMonitorExit(invokerLock); 629 630 if (!startNow) { 631 return JNI_FALSE; 632 } 633 634 env = getEnv(); 635 636 WITH_LOCAL_REFS(env, 2) { /* 1 for obj return values, 1 for exception */ 637 638 jobject exception; 639 640 JNI_FUNC_PTR(env,ExceptionClear)(env); 641 642 switch (request->invokeType) { 643 case INVOKE_CONSTRUCTOR: 644 invokeConstructor(env, request); 645 break; 646 case INVOKE_STATIC: 647 invokeStatic(env, request); 648 break; 649 case INVOKE_INSTANCE: 650 if (request->options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) { 651 invokeNonvirtual(env, request); 652 } else { 653 invokeVirtual(env, request); 654 } 655 break; 656 default: 657 JDI_ASSERT(JNI_FALSE); 658 } 659 request->exception = NULL; 660 exception = JNI_FUNC_PTR(env,ExceptionOccurred)(env); 661 if (exception != NULL) { 662 JNI_FUNC_PTR(env,ExceptionClear)(env); 663 saveGlobalRef(env, exception, &(request->exception)); 664 } 665 666 } END_WITH_LOCAL_REFS(env); 667 668 return JNI_TRUE; 669 } 670 671 void 672 invoker_completeInvokeRequest(jthread thread) 673 { 674 JNIEnv *env = getEnv(); 675 PacketOutputStream out; 676 jbyte tag; 677 jobject exc; 678 jvalue returnValue; 679 jint id; 680 InvokeRequest *request; 681 jboolean detached; 682 683 JDI_ASSERT(thread); 684 685 /* Prevent gcc errors on uninitialized variables. */ 686 tag = 0; 687 exc = NULL; 688 id = 0; 689 690 eventHandler_lock(); /* for proper lock order */ 691 debugMonitorEnter(invokerLock); 692 693 request = threadControl_getInvokeRequest(thread); 694 if (request == NULL) { 695 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 696 } 697 698 JDI_ASSERT(request->pending); 699 JDI_ASSERT(request->started); 700 701 request->pending = JNI_FALSE; 702 request->started = JNI_FALSE; 703 request->available = JNI_TRUE; /* For next time around */ 704 705 detached = request->detached; 706 if (!detached) { 707 if (request->options & JDWP_INVOKE_OPTIONS(SINGLE_THREADED)) { 708 (void)threadControl_suspendThread(thread, JNI_FALSE); 709 } else { 710 (void)threadControl_suspendAll(); 711 } 712 713 if (request->invokeType == INVOKE_CONSTRUCTOR) { 714 /* 715 * Although constructors technically have a return type of 716 * void, we return the object created. 717 */ 718 tag = specificTypeKey(env, request->returnValue.l); 719 } else { 720 tag = returnTypeTag(request->methodSignature); 721 } 722 id = request->id; 723 exc = request->exception; 724 returnValue = request->returnValue; 725 } 726 727 /* 728 * Give up the lock before I/O operation 729 */ 730 debugMonitorExit(invokerLock); 731 eventHandler_unlock(); 732 733 734 if (!detached) { 735 outStream_initReply(&out, id); 736 (void)outStream_writeValue(env, &out, tag, returnValue); 737 (void)outStream_writeObjectTag(env, &out, exc); 738 (void)outStream_writeObjectRef(env, &out, exc); 739 outStream_sendReply(&out); 740 } 741 } 742 743 jboolean 744 invoker_isEnabled(jthread thread) 745 { 746 InvokeRequest *request; 747 jboolean isEnabled; 748 749 JDI_ASSERT(thread); 750 debugMonitorEnter(invokerLock); 751 request = threadControl_getInvokeRequest(thread); 752 if (request == NULL) { 753 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 754 } 755 isEnabled = request->available; 756 debugMonitorExit(invokerLock); 757 return isEnabled; 758 } 759 760 void 761 invoker_detach(InvokeRequest *request) 762 { 763 JDI_ASSERT(request); 764 debugMonitorEnter(invokerLock); 765 request->detached = JNI_TRUE; 766 debugMonitorExit(invokerLock); 767 }