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 request = threadControl_getInvokeRequest(thread); 281 if (request == NULL) { 282 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 283 } 284 285 request->available = JNI_TRUE; 286 } 287 288 jvmtiError 289 invoker_requestInvoke(jbyte invokeType, jbyte options, jint id, 290 jthread thread, jclass clazz, jmethodID method, 291 jobject instance, 292 jvalue *arguments, jint argumentCount) 293 { 294 JNIEnv *env = getEnv(); 295 InvokeRequest *request; 296 jvmtiError error = JVMTI_ERROR_NONE; 297 298 debugMonitorEnter(invokerLock); 299 request = threadControl_getInvokeRequest(thread); 300 if (request != NULL) { 301 error = fillInvokeRequest(env, request, invokeType, options, id, 302 thread, clazz, method, instance, 303 arguments, argumentCount); 304 } 305 debugMonitorExit(invokerLock); 306 307 if (error == JVMTI_ERROR_NONE) { 308 if (options & JDWP_INVOKE_OPTIONS(SINGLE_THREADED) ) { 309 /* true means it is okay to unblock the commandLoop thread */ 310 (void)threadControl_resumeThread(thread, JNI_TRUE); 311 } else { 312 (void)threadControl_resumeAll(); 313 } 314 } 315 316 return error; 317 } 318 319 static void 320 invokeConstructor(JNIEnv *env, InvokeRequest *request) 321 { 322 jobject object; 323 object = JNI_FUNC_PTR(env,NewObjectA)(env, request->clazz, 324 request->method, 325 request->arguments); 326 request->returnValue.l = NULL; 327 if (object != NULL) { 328 saveGlobalRef(env, object, &(request->returnValue.l)); 329 } 330 } 331 332 static void 333 invokeStatic(JNIEnv *env, InvokeRequest *request) 334 { 335 switch(returnTypeTag(request->methodSignature)) { 336 case JDWP_TAG(OBJECT): 337 case JDWP_TAG(ARRAY): { 338 jobject object; 339 object = JNI_FUNC_PTR(env,CallStaticObjectMethodA)(env, 340 request->clazz, 341 request->method, 342 request->arguments); 343 request->returnValue.l = NULL; 344 if (object != NULL) { 345 saveGlobalRef(env, object, &(request->returnValue.l)); 346 } 347 break; 348 } 349 350 351 case JDWP_TAG(BYTE): 352 request->returnValue.b = JNI_FUNC_PTR(env,CallStaticByteMethodA)(env, 353 request->clazz, 354 request->method, 355 request->arguments); 356 break; 357 358 case JDWP_TAG(CHAR): 359 request->returnValue.c = JNI_FUNC_PTR(env,CallStaticCharMethodA)(env, 360 request->clazz, 361 request->method, 362 request->arguments); 363 break; 364 365 case JDWP_TAG(FLOAT): 366 request->returnValue.f = JNI_FUNC_PTR(env,CallStaticFloatMethodA)(env, 367 request->clazz, 368 request->method, 369 request->arguments); 370 break; 371 372 case JDWP_TAG(DOUBLE): 373 request->returnValue.d = JNI_FUNC_PTR(env,CallStaticDoubleMethodA)(env, 374 request->clazz, 375 request->method, 376 request->arguments); 377 break; 378 379 case JDWP_TAG(INT): 380 request->returnValue.i = JNI_FUNC_PTR(env,CallStaticIntMethodA)(env, 381 request->clazz, 382 request->method, 383 request->arguments); 384 break; 385 386 case JDWP_TAG(LONG): 387 request->returnValue.j = JNI_FUNC_PTR(env,CallStaticLongMethodA)(env, 388 request->clazz, 389 request->method, 390 request->arguments); 391 break; 392 393 case JDWP_TAG(SHORT): 394 request->returnValue.s = JNI_FUNC_PTR(env,CallStaticShortMethodA)(env, 395 request->clazz, 396 request->method, 397 request->arguments); 398 break; 399 400 case JDWP_TAG(BOOLEAN): 401 request->returnValue.z = JNI_FUNC_PTR(env,CallStaticBooleanMethodA)(env, 402 request->clazz, 403 request->method, 404 request->arguments); 405 break; 406 407 case JDWP_TAG(VOID): 408 JNI_FUNC_PTR(env,CallStaticVoidMethodA)(env, 409 request->clazz, 410 request->method, 411 request->arguments); 412 break; 413 414 default: 415 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature"); 416 break; 417 } 418 } 419 420 static void 421 invokeVirtual(JNIEnv *env, InvokeRequest *request) 422 { 423 switch(returnTypeTag(request->methodSignature)) { 424 case JDWP_TAG(OBJECT): 425 case JDWP_TAG(ARRAY): { 426 jobject object; 427 object = JNI_FUNC_PTR(env,CallObjectMethodA)(env, 428 request->instance, 429 request->method, 430 request->arguments); 431 request->returnValue.l = NULL; 432 if (object != NULL) { 433 saveGlobalRef(env, object, &(request->returnValue.l)); 434 } 435 break; 436 } 437 438 case JDWP_TAG(BYTE): 439 request->returnValue.b = JNI_FUNC_PTR(env,CallByteMethodA)(env, 440 request->instance, 441 request->method, 442 request->arguments); 443 break; 444 445 case JDWP_TAG(CHAR): 446 request->returnValue.c = JNI_FUNC_PTR(env,CallCharMethodA)(env, 447 request->instance, 448 request->method, 449 request->arguments); 450 break; 451 452 case JDWP_TAG(FLOAT): 453 request->returnValue.f = JNI_FUNC_PTR(env,CallFloatMethodA)(env, 454 request->instance, 455 request->method, 456 request->arguments); 457 break; 458 459 case JDWP_TAG(DOUBLE): 460 request->returnValue.d = JNI_FUNC_PTR(env,CallDoubleMethodA)(env, 461 request->instance, 462 request->method, 463 request->arguments); 464 break; 465 466 case JDWP_TAG(INT): 467 request->returnValue.i = JNI_FUNC_PTR(env,CallIntMethodA)(env, 468 request->instance, 469 request->method, 470 request->arguments); 471 break; 472 473 case JDWP_TAG(LONG): 474 request->returnValue.j = JNI_FUNC_PTR(env,CallLongMethodA)(env, 475 request->instance, 476 request->method, 477 request->arguments); 478 break; 479 480 case JDWP_TAG(SHORT): 481 request->returnValue.s = JNI_FUNC_PTR(env,CallShortMethodA)(env, 482 request->instance, 483 request->method, 484 request->arguments); 485 break; 486 487 case JDWP_TAG(BOOLEAN): 488 request->returnValue.z = JNI_FUNC_PTR(env,CallBooleanMethodA)(env, 489 request->instance, 490 request->method, 491 request->arguments); 492 break; 493 494 case JDWP_TAG(VOID): 495 JNI_FUNC_PTR(env,CallVoidMethodA)(env, 496 request->instance, 497 request->method, 498 request->arguments); 499 break; 500 501 default: 502 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature"); 503 break; 504 } 505 } 506 507 static void 508 invokeNonvirtual(JNIEnv *env, InvokeRequest *request) 509 { 510 switch(returnTypeTag(request->methodSignature)) { 511 case JDWP_TAG(OBJECT): 512 case JDWP_TAG(ARRAY): { 513 jobject object; 514 object = JNI_FUNC_PTR(env,CallNonvirtualObjectMethodA)(env, 515 request->instance, 516 request->clazz, 517 request->method, 518 request->arguments); 519 request->returnValue.l = NULL; 520 if (object != NULL) { 521 saveGlobalRef(env, object, &(request->returnValue.l)); 522 } 523 break; 524 } 525 526 case JDWP_TAG(BYTE): 527 request->returnValue.b = JNI_FUNC_PTR(env,CallNonvirtualByteMethodA)(env, 528 request->instance, 529 request->clazz, 530 request->method, 531 request->arguments); 532 break; 533 534 case JDWP_TAG(CHAR): 535 request->returnValue.c = JNI_FUNC_PTR(env,CallNonvirtualCharMethodA)(env, 536 request->instance, 537 request->clazz, 538 request->method, 539 request->arguments); 540 break; 541 542 case JDWP_TAG(FLOAT): 543 request->returnValue.f = JNI_FUNC_PTR(env,CallNonvirtualFloatMethodA)(env, 544 request->instance, 545 request->clazz, 546 request->method, 547 request->arguments); 548 break; 549 550 case JDWP_TAG(DOUBLE): 551 request->returnValue.d = JNI_FUNC_PTR(env,CallNonvirtualDoubleMethodA)(env, 552 request->instance, 553 request->clazz, 554 request->method, 555 request->arguments); 556 break; 557 558 case JDWP_TAG(INT): 559 request->returnValue.i = JNI_FUNC_PTR(env,CallNonvirtualIntMethodA)(env, 560 request->instance, 561 request->clazz, 562 request->method, 563 request->arguments); 564 break; 565 566 case JDWP_TAG(LONG): 567 request->returnValue.j = JNI_FUNC_PTR(env,CallNonvirtualLongMethodA)(env, 568 request->instance, 569 request->clazz, 570 request->method, 571 request->arguments); 572 break; 573 574 case JDWP_TAG(SHORT): 575 request->returnValue.s = JNI_FUNC_PTR(env,CallNonvirtualShortMethodA)(env, 576 request->instance, 577 request->clazz, 578 request->method, 579 request->arguments); 580 break; 581 582 case JDWP_TAG(BOOLEAN): 583 request->returnValue.z = JNI_FUNC_PTR(env,CallNonvirtualBooleanMethodA)(env, 584 request->instance, 585 request->clazz, 586 request->method, 587 request->arguments); 588 break; 589 590 case JDWP_TAG(VOID): 591 JNI_FUNC_PTR(env,CallNonvirtualVoidMethodA)(env, 592 request->instance, 593 request->clazz, 594 request->method, 595 request->arguments); 596 break; 597 598 default: 599 EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"Invalid method signature"); 600 break; 601 } 602 } 603 604 jboolean 605 invoker_doInvoke(jthread thread) 606 { 607 JNIEnv *env; 608 jboolean startNow; 609 InvokeRequest *request; 610 611 JDI_ASSERT(thread); 612 613 debugMonitorEnter(invokerLock); 614 615 request = threadControl_getInvokeRequest(thread); 616 if (request == NULL) { 617 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 618 } 619 620 request->available = JNI_FALSE; 621 startNow = request->pending && !request->started; 622 623 if (startNow) { 624 request->started = JNI_TRUE; 625 } 626 debugMonitorExit(invokerLock); 627 628 if (!startNow) { 629 return JNI_FALSE; 630 } 631 632 env = getEnv(); 633 634 WITH_LOCAL_REFS(env, 2) { /* 1 for obj return values, 1 for exception */ 635 636 jobject exception; 637 638 JNI_FUNC_PTR(env,ExceptionClear)(env); 639 640 switch (request->invokeType) { 641 case INVOKE_CONSTRUCTOR: 642 invokeConstructor(env, request); 643 break; 644 case INVOKE_STATIC: 645 invokeStatic(env, request); 646 break; 647 case INVOKE_INSTANCE: 648 if (request->options & JDWP_INVOKE_OPTIONS(NONVIRTUAL) ) { 649 invokeNonvirtual(env, request); 650 } else { 651 invokeVirtual(env, request); 652 } 653 break; 654 default: 655 JDI_ASSERT(JNI_FALSE); 656 } 657 request->exception = NULL; 658 exception = JNI_FUNC_PTR(env,ExceptionOccurred)(env); 659 if (exception != NULL) { 660 JNI_FUNC_PTR(env,ExceptionClear)(env); 661 saveGlobalRef(env, exception, &(request->exception)); 662 } 663 664 } END_WITH_LOCAL_REFS(env); 665 666 return JNI_TRUE; 667 } 668 669 void 670 invoker_completeInvokeRequest(jthread thread) 671 { 672 JNIEnv *env = getEnv(); 673 PacketOutputStream out; 674 jbyte tag; 675 jobject exc; 676 jvalue returnValue; 677 jint id; 678 InvokeRequest *request; 679 jboolean detached; 680 681 JDI_ASSERT(thread); 682 683 /* Prevent gcc errors on uninitialized variables. */ 684 tag = 0; 685 exc = NULL; 686 id = 0; 687 688 eventHandler_lock(); /* for proper lock order */ 689 debugMonitorEnter(invokerLock); 690 691 request = threadControl_getInvokeRequest(thread); 692 if (request == NULL) { 693 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 694 } 695 696 JDI_ASSERT(request->pending); 697 JDI_ASSERT(request->started); 698 699 request->pending = JNI_FALSE; 700 request->started = JNI_FALSE; 701 request->available = JNI_TRUE; /* For next time around */ 702 703 detached = request->detached; 704 if (!detached) { 705 if (request->options & JDWP_INVOKE_OPTIONS(SINGLE_THREADED)) { 706 (void)threadControl_suspendThread(thread, JNI_FALSE); 707 } else { 708 (void)threadControl_suspendAll(); 709 } 710 711 if (request->invokeType == INVOKE_CONSTRUCTOR) { 712 /* 713 * Although constructors technically have a return type of 714 * void, we return the object created. 715 */ 716 tag = specificTypeKey(env, request->returnValue.l); 717 } else { 718 tag = returnTypeTag(request->methodSignature); 719 } 720 id = request->id; 721 exc = request->exception; 722 returnValue = request->returnValue; 723 } 724 725 /* 726 * Give up the lock before I/O operation 727 */ 728 debugMonitorExit(invokerLock); 729 eventHandler_unlock(); 730 731 732 if (!detached) { 733 outStream_initReply(&out, id); 734 (void)outStream_writeValue(env, &out, tag, returnValue); 735 (void)outStream_writeObjectTag(env, &out, exc); 736 (void)outStream_writeObjectRef(env, &out, exc); 737 outStream_sendReply(&out); 738 } 739 } 740 741 jboolean 742 invoker_isPending(jthread thread) 743 { 744 InvokeRequest *request; 745 746 JDI_ASSERT(thread); 747 request = threadControl_getInvokeRequest(thread); 748 if (request == NULL) { 749 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 750 } 751 return request->pending; 752 } 753 754 jboolean 755 invoker_isEnabled(jthread thread) 756 { 757 InvokeRequest *request; 758 759 JDI_ASSERT(thread); 760 request = threadControl_getInvokeRequest(thread); 761 if (request == NULL) { 762 EXIT_ERROR(AGENT_ERROR_INVALID_THREAD, "getting thread invoke request"); 763 } 764 return request->available; 765 } 766 767 void 768 invoker_detach(InvokeRequest *request) 769 { 770 JDI_ASSERT(request); 771 debugMonitorEnter(invokerLock); 772 request->detached = JNI_TRUE; 773 debugMonitorExit(invokerLock); 774 }