1 /* 2 * Copyright (c) 2012, 2014, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 #include "precompiled.hpp" 26 27 #include "memory/metadataFactory.hpp" 28 #include "memory/universe.hpp" 29 #include "oops/oop.inline.hpp" 30 31 #include "classfile/symbolTable.hpp" 32 #include "classfile/classLoaderData.hpp" 33 34 #include "prims/whitebox.hpp" 35 #include "prims/wbtestmethods/parserTests.hpp" 36 37 #include "runtime/arguments.hpp" 38 #include "runtime/interfaceSupport.hpp" 39 #include "runtime/os.hpp" 40 #include "utilities/array.hpp" 41 #include "utilities/debug.hpp" 42 #include "utilities/macros.hpp" 43 #include "utilities/exceptions.hpp" 44 45 #if INCLUDE_ALL_GCS 46 #include "gc_implementation/parallelScavenge/parallelScavengeHeap.inline.hpp" 47 #include "gc_implementation/g1/concurrentMark.hpp" 48 #include "gc_implementation/g1/g1CollectedHeap.inline.hpp" 49 #include "gc_implementation/g1/heapRegionRemSet.hpp" 50 #endif // INCLUDE_ALL_GCS 51 52 #if INCLUDE_NMT 53 #include "services/mallocSiteTable.hpp" 54 #include "services/memTracker.hpp" 55 #include "utilities/nativeCallStack.hpp" 56 #endif // INCLUDE_NMT 57 58 #include "compiler/compileBroker.hpp" 59 #include "runtime/compilationPolicy.hpp" 60 61 PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC 62 63 #define SIZE_T_MAX_VALUE ((size_t) -1) 64 65 bool WhiteBox::_used = false; 66 67 WB_ENTRY(jlong, WB_GetObjectAddress(JNIEnv* env, jobject o, jobject obj)) 68 return (jlong)(void*)JNIHandles::resolve(obj); 69 WB_END 70 71 WB_ENTRY(jint, WB_GetHeapOopSize(JNIEnv* env, jobject o)) 72 return heapOopSize; 73 WB_END 74 75 76 class WBIsKlassAliveClosure : public KlassClosure { 77 Symbol* _name; 78 bool _found; 79 public: 80 WBIsKlassAliveClosure(Symbol* name) : _name(name), _found(false) {} 81 82 void do_klass(Klass* k) { 83 if (_found) return; 84 Symbol* ksym = k->name(); 85 if (ksym->fast_compare(_name) == 0) { 86 _found = true; 87 } 88 } 89 90 bool found() const { 91 return _found; 92 } 93 }; 94 95 WB_ENTRY(jboolean, WB_IsClassAlive(JNIEnv* env, jobject target, jstring name)) 96 Handle h_name = JNIHandles::resolve(name); 97 if (h_name.is_null()) return false; 98 Symbol* sym = java_lang_String::as_symbol(h_name, CHECK_false); 99 TempNewSymbol tsym(sym); // Make sure to decrement reference count on sym on return 100 101 WBIsKlassAliveClosure closure(sym); 102 ClassLoaderDataGraph::classes_do(&closure); 103 104 return closure.found(); 105 WB_END 106 107 WB_ENTRY(jlong, WB_GetCompressedOopsMaxHeapSize(JNIEnv* env, jobject o)) { 108 return (jlong)Arguments::max_heap_for_compressed_oops(); 109 } 110 WB_END 111 112 WB_ENTRY(void, WB_PrintHeapSizes(JNIEnv* env, jobject o)) { 113 CollectorPolicy * p = Universe::heap()->collector_policy(); 114 gclog_or_tty->print_cr("Minimum heap "SIZE_FORMAT" Initial heap " 115 SIZE_FORMAT" Maximum heap "SIZE_FORMAT" Min alignment "SIZE_FORMAT" Max alignment "SIZE_FORMAT, 116 p->min_heap_byte_size(), p->initial_heap_byte_size(), p->max_heap_byte_size(), 117 p->space_alignment(), p->heap_alignment()); 118 } 119 WB_END 120 121 #ifndef PRODUCT 122 // Forward declaration 123 void TestReservedSpace_test(); 124 void TestReserveMemorySpecial_test(); 125 void TestVirtualSpace_test(); 126 void TestMetaspaceAux_test(); 127 #endif 128 129 WB_ENTRY(void, WB_RunMemoryUnitTests(JNIEnv* env, jobject o)) 130 #ifndef PRODUCT 131 TestReservedSpace_test(); 132 TestReserveMemorySpecial_test(); 133 TestVirtualSpace_test(); 134 TestMetaspaceAux_test(); 135 #endif 136 WB_END 137 138 WB_ENTRY(void, WB_ReadFromNoaccessArea(JNIEnv* env, jobject o)) 139 size_t granularity = os::vm_allocation_granularity(); 140 ReservedHeapSpace rhs(100 * granularity, granularity, false, NULL); 141 VirtualSpace vs; 142 vs.initialize(rhs, 50 * granularity); 143 144 //Check if constraints are complied 145 if (!( UseCompressedOops && rhs.base() != NULL && 146 Universe::narrow_oop_base() != NULL && 147 Universe::narrow_oop_use_implicit_null_checks() )) { 148 tty->print_cr("WB_ReadFromNoaccessArea method is useless:\n " 149 "\tUseCompressedOops is %d\n" 150 "\trhs.base() is "PTR_FORMAT"\n" 151 "\tUniverse::narrow_oop_base() is "PTR_FORMAT"\n" 152 "\tUniverse::narrow_oop_use_implicit_null_checks() is %d", 153 UseCompressedOops, 154 rhs.base(), 155 Universe::narrow_oop_base(), 156 Universe::narrow_oop_use_implicit_null_checks()); 157 return; 158 } 159 tty->print_cr("Reading from no access area... "); 160 tty->print_cr("*(vs.low_boundary() - rhs.noaccess_prefix() / 2 ) = %c", 161 *(vs.low_boundary() - rhs.noaccess_prefix() / 2 )); 162 WB_END 163 164 static jint wb_stress_virtual_space_resize(size_t reserved_space_size, 165 size_t magnitude, size_t iterations) { 166 size_t granularity = os::vm_allocation_granularity(); 167 ReservedHeapSpace rhs(reserved_space_size * granularity, granularity, false, NULL); 168 VirtualSpace vs; 169 if (!vs.initialize(rhs, 0)) { 170 tty->print_cr("Failed to initialize VirtualSpace. Can't proceed."); 171 return 3; 172 } 173 174 long seed = os::random(); 175 tty->print_cr("Random seed is %ld", seed); 176 os::init_random(seed); 177 178 for (size_t i = 0; i < iterations; i++) { 179 180 // Whether we will shrink or grow 181 bool shrink = os::random() % 2L == 0; 182 183 // Get random delta to resize virtual space 184 size_t delta = (size_t)os::random() % magnitude; 185 186 // If we are about to shrink virtual space below zero, then expand instead 187 if (shrink && vs.committed_size() < delta) { 188 shrink = false; 189 } 190 191 // Resizing by delta 192 if (shrink) { 193 vs.shrink_by(delta); 194 } else { 195 // If expanding fails expand_by will silently return false 196 vs.expand_by(delta, true); 197 } 198 } 199 return 0; 200 } 201 202 WB_ENTRY(jint, WB_StressVirtualSpaceResize(JNIEnv* env, jobject o, 203 jlong reserved_space_size, jlong magnitude, jlong iterations)) 204 tty->print_cr("reservedSpaceSize="JLONG_FORMAT", magnitude="JLONG_FORMAT", " 205 "iterations="JLONG_FORMAT"\n", reserved_space_size, magnitude, 206 iterations); 207 if (reserved_space_size < 0 || magnitude < 0 || iterations < 0) { 208 tty->print_cr("One of variables printed above is negative. Can't proceed.\n"); 209 return 1; 210 } 211 212 // sizeof(size_t) depends on whether OS is 32bit or 64bit. sizeof(jlong) is 213 // always 8 byte. That's why we should avoid overflow in case of 32bit platform. 214 if (sizeof(size_t) < sizeof(jlong)) { 215 jlong size_t_max_value = (jlong) SIZE_T_MAX_VALUE; 216 if (reserved_space_size > size_t_max_value || magnitude > size_t_max_value 217 || iterations > size_t_max_value) { 218 tty->print_cr("One of variables printed above overflows size_t. Can't proceed.\n"); 219 return 2; 220 } 221 } 222 223 return wb_stress_virtual_space_resize((size_t) reserved_space_size, 224 (size_t) magnitude, (size_t) iterations); 225 WB_END 226 227 WB_ENTRY(jboolean, WB_isObjectInOldGen(JNIEnv* env, jobject o, jobject obj)) 228 oop p = JNIHandles::resolve(obj); 229 #if INCLUDE_ALL_GCS 230 if (UseG1GC) { 231 G1CollectedHeap* g1 = G1CollectedHeap::heap(); 232 const HeapRegion* hr = g1->heap_region_containing(p); 233 if (hr == NULL) { 234 return false; 235 } 236 return !(hr->is_young()); 237 } else if (UseParallelGC) { 238 ParallelScavengeHeap* psh = ParallelScavengeHeap::heap(); 239 return !psh->is_in_young(p); 240 } 241 #endif // INCLUDE_ALL_GCS 242 GenCollectedHeap* gch = GenCollectedHeap::heap(); 243 return !gch->is_in_young(p); 244 WB_END 245 246 WB_ENTRY(jlong, WB_GetObjectSize(JNIEnv* env, jobject o, jobject obj)) 247 oop p = JNIHandles::resolve(obj); 248 return p->size() * HeapWordSize; 249 WB_END 250 251 #if INCLUDE_ALL_GCS 252 WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj)) 253 G1CollectedHeap* g1 = G1CollectedHeap::heap(); 254 oop result = JNIHandles::resolve(obj); 255 const HeapRegion* hr = g1->heap_region_containing(result); 256 return hr->isHumongous(); 257 WB_END 258 259 WB_ENTRY(jlong, WB_G1NumFreeRegions(JNIEnv* env, jobject o)) 260 G1CollectedHeap* g1 = G1CollectedHeap::heap(); 261 size_t nr = g1->num_free_regions(); 262 return (jlong)nr; 263 WB_END 264 265 WB_ENTRY(jboolean, WB_G1InConcurrentMark(JNIEnv* env, jobject o)) 266 G1CollectedHeap* g1 = G1CollectedHeap::heap(); 267 ConcurrentMark* cm = g1->concurrent_mark(); 268 return cm->concurrent_marking_in_progress(); 269 WB_END 270 271 WB_ENTRY(jint, WB_G1RegionSize(JNIEnv* env, jobject o)) 272 return (jint)HeapRegion::GrainBytes; 273 WB_END 274 #endif // INCLUDE_ALL_GCS 275 276 #if INCLUDE_NMT 277 // Alloc memory using the test memory type so that we can use that to see if 278 // NMT picks it up correctly 279 WB_ENTRY(jlong, WB_NMTMalloc(JNIEnv* env, jobject o, jlong size)) 280 jlong addr = 0; 281 addr = (jlong)(uintptr_t)os::malloc(size, mtTest); 282 return addr; 283 WB_END 284 285 // Alloc memory with pseudo call stack. The test can create psudo malloc 286 // allocation site to stress the malloc tracking. 287 WB_ENTRY(jlong, WB_NMTMallocWithPseudoStack(JNIEnv* env, jobject o, jlong size, jint pseudo_stack)) 288 address pc = (address)(size_t)pseudo_stack; 289 NativeCallStack stack(&pc, 1); 290 return (jlong)os::malloc(size, mtTest, stack); 291 WB_END 292 293 // Free the memory allocated by NMTAllocTest 294 WB_ENTRY(void, WB_NMTFree(JNIEnv* env, jobject o, jlong mem)) 295 os::free((void*)(uintptr_t)mem, mtTest); 296 WB_END 297 298 WB_ENTRY(jlong, WB_NMTReserveMemory(JNIEnv* env, jobject o, jlong size)) 299 jlong addr = 0; 300 301 addr = (jlong)(uintptr_t)os::reserve_memory(size); 302 MemTracker::record_virtual_memory_type((address)addr, mtTest); 303 304 return addr; 305 WB_END 306 307 308 WB_ENTRY(void, WB_NMTCommitMemory(JNIEnv* env, jobject o, jlong addr, jlong size)) 309 os::commit_memory((char *)(uintptr_t)addr, size, !ExecMem); 310 MemTracker::record_virtual_memory_type((address)(uintptr_t)addr, mtTest); 311 WB_END 312 313 WB_ENTRY(void, WB_NMTUncommitMemory(JNIEnv* env, jobject o, jlong addr, jlong size)) 314 os::uncommit_memory((char *)(uintptr_t)addr, size); 315 WB_END 316 317 WB_ENTRY(void, WB_NMTReleaseMemory(JNIEnv* env, jobject o, jlong addr, jlong size)) 318 os::release_memory((char *)(uintptr_t)addr, size); 319 WB_END 320 321 WB_ENTRY(jboolean, WB_NMTIsDetailSupported(JNIEnv* env)) 322 return MemTracker::tracking_level() == NMT_detail; 323 WB_END 324 325 WB_ENTRY(void, WB_NMTOverflowHashBucket(JNIEnv* env, jobject o, jlong num)) 326 address pc = (address)1; 327 for (jlong index = 0; index < num; index ++) { 328 NativeCallStack stack(&pc, 1); 329 os::malloc(0, mtTest, stack); 330 pc += MallocSiteTable::hash_buckets(); 331 } 332 WB_END 333 334 335 #endif // INCLUDE_NMT 336 337 static jmethodID reflected_method_to_jmid(JavaThread* thread, JNIEnv* env, jobject method) { 338 assert(method != NULL, "method should not be null"); 339 ThreadToNativeFromVM ttn(thread); 340 return env->FromReflectedMethod(method); 341 } 342 343 WB_ENTRY(void, WB_DeoptimizeAll(JNIEnv* env, jobject o)) 344 MutexLockerEx mu(Compile_lock); 345 CodeCache::mark_all_nmethods_for_deoptimization(); 346 VM_Deoptimize op; 347 VMThread::execute(&op); 348 WB_END 349 350 WB_ENTRY(jint, WB_DeoptimizeMethod(JNIEnv* env, jobject o, jobject method, jboolean is_osr)) 351 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 352 int result = 0; 353 CHECK_JNI_EXCEPTION_(env, result); 354 MutexLockerEx mu(Compile_lock); 355 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 356 nmethod* code; 357 if (is_osr) { 358 int bci = InvocationEntryBci; 359 while ((code = mh->lookup_osr_nmethod_for(bci, CompLevel_none, false)) != NULL) { 360 code->mark_for_deoptimization(); 361 ++result; 362 bci = code->osr_entry_bci() + 1; 363 } 364 } else { 365 code = mh->code(); 366 } 367 if (code != NULL) { 368 code->mark_for_deoptimization(); 369 ++result; 370 } 371 result += CodeCache::mark_for_deoptimization(mh()); 372 if (result > 0) { 373 VM_Deoptimize op; 374 VMThread::execute(&op); 375 } 376 return result; 377 WB_END 378 379 WB_ENTRY(jboolean, WB_IsMethodCompiled(JNIEnv* env, jobject o, jobject method, jboolean is_osr)) 380 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 381 CHECK_JNI_EXCEPTION_(env, JNI_FALSE); 382 MutexLockerEx mu(Compile_lock); 383 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 384 nmethod* code = is_osr ? mh->lookup_osr_nmethod_for(InvocationEntryBci, CompLevel_none, false) : mh->code(); 385 if (code == NULL) { 386 return JNI_FALSE; 387 } 388 return (code->is_alive() && !code->is_marked_for_deoptimization()); 389 WB_END 390 391 WB_ENTRY(jboolean, WB_IsMethodCompilable(JNIEnv* env, jobject o, jobject method, jint comp_level, jboolean is_osr)) 392 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 393 CHECK_JNI_EXCEPTION_(env, JNI_FALSE); 394 MutexLockerEx mu(Compile_lock); 395 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 396 if (is_osr) { 397 return CompilationPolicy::can_be_osr_compiled(mh, comp_level); 398 } else { 399 return CompilationPolicy::can_be_compiled(mh, comp_level); 400 } 401 WB_END 402 403 WB_ENTRY(jboolean, WB_IsMethodQueuedForCompilation(JNIEnv* env, jobject o, jobject method)) 404 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 405 CHECK_JNI_EXCEPTION_(env, JNI_FALSE); 406 MutexLockerEx mu(Compile_lock); 407 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 408 return mh->queued_for_compilation(); 409 WB_END 410 411 WB_ENTRY(jint, WB_GetMethodCompilationLevel(JNIEnv* env, jobject o, jobject method, jboolean is_osr)) 412 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 413 CHECK_JNI_EXCEPTION_(env, CompLevel_none); 414 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 415 nmethod* code = is_osr ? mh->lookup_osr_nmethod_for(InvocationEntryBci, CompLevel_none, false) : mh->code(); 416 return (code != NULL ? code->comp_level() : CompLevel_none); 417 WB_END 418 419 WB_ENTRY(void, WB_MakeMethodNotCompilable(JNIEnv* env, jobject o, jobject method, jint comp_level, jboolean is_osr)) 420 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 421 CHECK_JNI_EXCEPTION(env); 422 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 423 if (is_osr) { 424 mh->set_not_osr_compilable(comp_level, true /* report */, "WhiteBox"); 425 } else { 426 mh->set_not_compilable(comp_level, true /* report */, "WhiteBox"); 427 } 428 WB_END 429 430 WB_ENTRY(jint, WB_GetMethodEntryBci(JNIEnv* env, jobject o, jobject method)) 431 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 432 CHECK_JNI_EXCEPTION_(env, InvocationEntryBci); 433 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 434 nmethod* code = mh->lookup_osr_nmethod_for(InvocationEntryBci, CompLevel_none, false); 435 return (code != NULL && code->is_osr_method() ? code->osr_entry_bci() : InvocationEntryBci); 436 WB_END 437 438 WB_ENTRY(jboolean, WB_TestSetDontInlineMethod(JNIEnv* env, jobject o, jobject method, jboolean value)) 439 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 440 CHECK_JNI_EXCEPTION_(env, JNI_FALSE); 441 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 442 bool result = mh->dont_inline(); 443 mh->set_dont_inline(value == JNI_TRUE); 444 return result; 445 WB_END 446 447 WB_ENTRY(jint, WB_GetCompileQueueSize(JNIEnv* env, jobject o, jint comp_level)) 448 if (comp_level == CompLevel_any) { 449 return CompileBroker::queue_size(CompLevel_full_optimization) /* C2 */ + 450 CompileBroker::queue_size(CompLevel_full_profile) /* C1 */; 451 } else { 452 return CompileBroker::queue_size(comp_level); 453 } 454 WB_END 455 456 WB_ENTRY(jboolean, WB_TestSetForceInlineMethod(JNIEnv* env, jobject o, jobject method, jboolean value)) 457 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 458 CHECK_JNI_EXCEPTION_(env, JNI_FALSE); 459 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 460 bool result = mh->force_inline(); 461 mh->set_force_inline(value == JNI_TRUE); 462 return result; 463 WB_END 464 465 WB_ENTRY(jboolean, WB_EnqueueMethodForCompilation(JNIEnv* env, jobject o, jobject method, jint comp_level, jint bci)) 466 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 467 CHECK_JNI_EXCEPTION_(env, JNI_FALSE); 468 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 469 nmethod* nm = CompileBroker::compile_method(mh, bci, comp_level, mh, mh->invocation_count(), "WhiteBox", THREAD); 470 MutexLockerEx mu(Compile_lock); 471 return (mh->queued_for_compilation() || nm != NULL); 472 WB_END 473 474 class VM_WhiteBoxOperation : public VM_Operation { 475 public: 476 VM_WhiteBoxOperation() { } 477 VMOp_Type type() const { return VMOp_WhiteBoxOperation; } 478 bool allow_nested_vm_operations() const { return true; } 479 }; 480 481 class AlwaysFalseClosure : public BoolObjectClosure { 482 public: 483 bool do_object_b(oop p) { return false; } 484 }; 485 486 static AlwaysFalseClosure always_false; 487 488 class VM_WhiteBoxCleanMethodData : public VM_WhiteBoxOperation { 489 public: 490 VM_WhiteBoxCleanMethodData(MethodData* mdo) : _mdo(mdo) { } 491 void doit() { 492 _mdo->clean_method_data(&always_false); 493 } 494 private: 495 MethodData* _mdo; 496 }; 497 498 WB_ENTRY(void, WB_ClearMethodState(JNIEnv* env, jobject o, jobject method)) 499 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 500 CHECK_JNI_EXCEPTION(env); 501 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 502 MutexLockerEx mu(Compile_lock); 503 MethodData* mdo = mh->method_data(); 504 MethodCounters* mcs = mh->method_counters(); 505 506 if (mdo != NULL) { 507 mdo->init(); 508 ResourceMark rm; 509 int arg_count = mdo->method()->size_of_parameters(); 510 for (int i = 0; i < arg_count; i++) { 511 mdo->set_arg_modified(i, 0); 512 } 513 VM_WhiteBoxCleanMethodData op(mdo); 514 VMThread::execute(&op); 515 } 516 517 mh->clear_not_c1_compilable(); 518 mh->clear_not_c2_compilable(); 519 mh->clear_not_c2_osr_compilable(); 520 NOT_PRODUCT(mh->set_compiled_invocation_count(0)); 521 if (mcs != NULL) { 522 mcs->backedge_counter()->init(); 523 mcs->invocation_counter()->init(); 524 mcs->set_interpreter_invocation_count(0); 525 mcs->set_interpreter_throwout_count(0); 526 527 #ifdef TIERED 528 mcs->set_rate(0.0F); 529 mh->set_prev_event_count(0); 530 mh->set_prev_time(0); 531 #endif 532 } 533 WB_END 534 535 template <typename T> 536 static bool GetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAt)(const char*, T*)) { 537 if (name == NULL) { 538 return false; 539 } 540 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 541 const char* flag_name = env->GetStringUTFChars(name, NULL); 542 bool result = (*TAt)(flag_name, value); 543 env->ReleaseStringUTFChars(name, flag_name); 544 return result; 545 } 546 547 template <typename T> 548 static bool SetVMFlag(JavaThread* thread, JNIEnv* env, jstring name, T* value, bool (*TAtPut)(const char*, T*, Flag::Flags)) { 549 if (name == NULL) { 550 return false; 551 } 552 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 553 const char* flag_name = env->GetStringUTFChars(name, NULL); 554 bool result = (*TAtPut)(flag_name, value, Flag::INTERNAL); 555 env->ReleaseStringUTFChars(name, flag_name); 556 return result; 557 } 558 559 template <typename T> 560 static jobject box(JavaThread* thread, JNIEnv* env, Symbol* name, Symbol* sig, T value) { 561 ResourceMark rm(thread); 562 jclass clazz = env->FindClass(name->as_C_string()); 563 CHECK_JNI_EXCEPTION_(env, NULL); 564 jmethodID methodID = env->GetStaticMethodID(clazz, 565 vmSymbols::valueOf_name()->as_C_string(), 566 sig->as_C_string()); 567 CHECK_JNI_EXCEPTION_(env, NULL); 568 jobject result = env->CallStaticObjectMethod(clazz, methodID, value); 569 CHECK_JNI_EXCEPTION_(env, NULL); 570 return result; 571 } 572 573 static jobject booleanBox(JavaThread* thread, JNIEnv* env, jboolean value) { 574 return box(thread, env, vmSymbols::java_lang_Boolean(), vmSymbols::Boolean_valueOf_signature(), value); 575 } 576 static jobject integerBox(JavaThread* thread, JNIEnv* env, jint value) { 577 return box(thread, env, vmSymbols::java_lang_Integer(), vmSymbols::Integer_valueOf_signature(), value); 578 } 579 static jobject longBox(JavaThread* thread, JNIEnv* env, jlong value) { 580 return box(thread, env, vmSymbols::java_lang_Long(), vmSymbols::Long_valueOf_signature(), value); 581 } 582 /* static jobject floatBox(JavaThread* thread, JNIEnv* env, jfloat value) { 583 return box(thread, env, vmSymbols::java_lang_Float(), vmSymbols::Float_valueOf_signature(), value); 584 }*/ 585 static jobject doubleBox(JavaThread* thread, JNIEnv* env, jdouble value) { 586 return box(thread, env, vmSymbols::java_lang_Double(), vmSymbols::Double_valueOf_signature(), value); 587 } 588 589 WB_ENTRY(jobject, WB_GetBooleanVMFlag(JNIEnv* env, jobject o, jstring name)) 590 bool result; 591 if (GetVMFlag <bool> (thread, env, name, &result, &CommandLineFlags::boolAt)) { 592 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 593 return booleanBox(thread, env, result); 594 } 595 return NULL; 596 WB_END 597 598 WB_ENTRY(jobject, WB_GetIntxVMFlag(JNIEnv* env, jobject o, jstring name)) 599 intx result; 600 if (GetVMFlag <intx> (thread, env, name, &result, &CommandLineFlags::intxAt)) { 601 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 602 return longBox(thread, env, result); 603 } 604 return NULL; 605 WB_END 606 607 WB_ENTRY(jobject, WB_GetUintxVMFlag(JNIEnv* env, jobject o, jstring name)) 608 uintx result; 609 if (GetVMFlag <uintx> (thread, env, name, &result, &CommandLineFlags::uintxAt)) { 610 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 611 return longBox(thread, env, result); 612 } 613 return NULL; 614 WB_END 615 616 WB_ENTRY(jobject, WB_GetUint64VMFlag(JNIEnv* env, jobject o, jstring name)) 617 uint64_t result; 618 if (GetVMFlag <uint64_t> (thread, env, name, &result, &CommandLineFlags::uint64_tAt)) { 619 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 620 return longBox(thread, env, result); 621 } 622 return NULL; 623 WB_END 624 625 WB_ENTRY(jobject, WB_GetDoubleVMFlag(JNIEnv* env, jobject o, jstring name)) 626 double result; 627 if (GetVMFlag <double> (thread, env, name, &result, &CommandLineFlags::doubleAt)) { 628 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 629 return doubleBox(thread, env, result); 630 } 631 return NULL; 632 WB_END 633 634 WB_ENTRY(jstring, WB_GetStringVMFlag(JNIEnv* env, jobject o, jstring name)) 635 ccstr ccstrResult; 636 if (GetVMFlag <ccstr> (thread, env, name, &ccstrResult, &CommandLineFlags::ccstrAt)) { 637 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 638 jstring result = env->NewStringUTF(ccstrResult); 639 CHECK_JNI_EXCEPTION_(env, NULL); 640 return result; 641 } 642 return NULL; 643 WB_END 644 645 WB_ENTRY(void, WB_SetBooleanVMFlag(JNIEnv* env, jobject o, jstring name, jboolean value)) 646 bool result = value == JNI_TRUE ? true : false; 647 SetVMFlag <bool> (thread, env, name, &result, &CommandLineFlags::boolAtPut); 648 WB_END 649 650 WB_ENTRY(void, WB_SetIntxVMFlag(JNIEnv* env, jobject o, jstring name, jlong value)) 651 intx result = value; 652 SetVMFlag <intx> (thread, env, name, &result, &CommandLineFlags::intxAtPut); 653 WB_END 654 655 WB_ENTRY(void, WB_SetUintxVMFlag(JNIEnv* env, jobject o, jstring name, jlong value)) 656 uintx result = value; 657 SetVMFlag <uintx> (thread, env, name, &result, &CommandLineFlags::uintxAtPut); 658 WB_END 659 660 WB_ENTRY(void, WB_SetUint64VMFlag(JNIEnv* env, jobject o, jstring name, jlong value)) 661 uint64_t result = value; 662 SetVMFlag <uint64_t> (thread, env, name, &result, &CommandLineFlags::uint64_tAtPut); 663 WB_END 664 665 WB_ENTRY(void, WB_SetDoubleVMFlag(JNIEnv* env, jobject o, jstring name, jdouble value)) 666 double result = value; 667 SetVMFlag <double> (thread, env, name, &result, &CommandLineFlags::doubleAtPut); 668 WB_END 669 670 WB_ENTRY(void, WB_SetStringVMFlag(JNIEnv* env, jobject o, jstring name, jstring value)) 671 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 672 const char* ccstrValue = (value == NULL) ? NULL : env->GetStringUTFChars(value, NULL); 673 ccstr ccstrResult = ccstrValue; 674 bool needFree; 675 { 676 ThreadInVMfromNative ttvfn(thread); // back to VM 677 needFree = SetVMFlag <ccstr> (thread, env, name, &ccstrResult, &CommandLineFlags::ccstrAtPut); 678 } 679 if (value != NULL) { 680 env->ReleaseStringUTFChars(value, ccstrValue); 681 } 682 if (needFree) { 683 FREE_C_HEAP_ARRAY(char, ccstrResult, mtInternal); 684 } 685 WB_END 686 687 688 WB_ENTRY(jboolean, WB_IsInStringTable(JNIEnv* env, jobject o, jstring javaString)) 689 ResourceMark rm(THREAD); 690 int len; 691 jchar* name = java_lang_String::as_unicode_string(JNIHandles::resolve(javaString), len, CHECK_false); 692 return (StringTable::lookup(name, len) != NULL); 693 WB_END 694 695 WB_ENTRY(void, WB_FullGC(JNIEnv* env, jobject o)) 696 Universe::heap()->collector_policy()->set_should_clear_all_soft_refs(true); 697 Universe::heap()->collect(GCCause::_last_ditch_collection); 698 #if INCLUDE_ALL_GCS 699 if (UseG1GC) { 700 // Needs to be cleared explicitly for G1 701 Universe::heap()->collector_policy()->set_should_clear_all_soft_refs(false); 702 } 703 #endif // INCLUDE_ALL_GCS 704 WB_END 705 706 WB_ENTRY(void, WB_YoungGC(JNIEnv* env, jobject o)) 707 Universe::heap()->collect(GCCause::_wb_young_gc); 708 WB_END 709 710 WB_ENTRY(void, WB_ReadReservedMemory(JNIEnv* env, jobject o)) 711 // static+volatile in order to force the read to happen 712 // (not be eliminated by the compiler) 713 static char c; 714 static volatile char* p; 715 716 p = os::reserve_memory(os::vm_allocation_granularity(), NULL, 0); 717 if (p == NULL) { 718 THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), "Failed to reserve memory"); 719 } 720 721 c = *p; 722 WB_END 723 724 WB_ENTRY(jstring, WB_GetCPUFeatures(JNIEnv* env, jobject o)) 725 const char* cpu_features = VM_Version::cpu_features(); 726 ThreadToNativeFromVM ttn(thread); 727 jstring features_string = env->NewStringUTF(cpu_features); 728 729 CHECK_JNI_EXCEPTION_(env, NULL); 730 731 return features_string; 732 WB_END 733 734 735 WB_ENTRY(jobjectArray, WB_GetNMethod(JNIEnv* env, jobject o, jobject method, jboolean is_osr)) 736 ResourceMark rm(THREAD); 737 jmethodID jmid = reflected_method_to_jmid(thread, env, method); 738 CHECK_JNI_EXCEPTION_(env, NULL); 739 methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); 740 nmethod* code = is_osr ? mh->lookup_osr_nmethod_for(InvocationEntryBci, CompLevel_none, false) : mh->code(); 741 jobjectArray result = NULL; 742 if (code == NULL) { 743 return result; 744 } 745 int insts_size = code->insts_size(); 746 747 ThreadToNativeFromVM ttn(thread); 748 jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string()); 749 CHECK_JNI_EXCEPTION_(env, NULL); 750 result = env->NewObjectArray(2, clazz, NULL); 751 if (result == NULL) { 752 return result; 753 } 754 755 jobject obj = integerBox(thread, env, code->comp_level()); 756 CHECK_JNI_EXCEPTION_(env, NULL); 757 env->SetObjectArrayElement(result, 0, obj); 758 759 jbyteArray insts = env->NewByteArray(insts_size); 760 CHECK_JNI_EXCEPTION_(env, NULL); 761 env->SetByteArrayRegion(insts, 0, insts_size, (jbyte*) code->insts_begin()); 762 env->SetObjectArrayElement(result, 1, insts); 763 764 return result; 765 WB_END 766 767 768 int WhiteBox::array_bytes_to_length(size_t bytes) { 769 return Array<u1>::bytes_to_length(bytes); 770 } 771 772 WB_ENTRY(jlong, WB_AllocateMetaspace(JNIEnv* env, jobject wb, jobject class_loader, jlong size)) 773 if (size < 0) { 774 THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), 775 err_msg("WB_AllocateMetaspace: size is negative: " JLONG_FORMAT, size)); 776 } 777 778 oop class_loader_oop = JNIHandles::resolve(class_loader); 779 ClassLoaderData* cld = class_loader_oop != NULL 780 ? java_lang_ClassLoader::loader_data(class_loader_oop) 781 : ClassLoaderData::the_null_class_loader_data(); 782 783 void* metadata = MetadataFactory::new_writeable_array<u1>(cld, WhiteBox::array_bytes_to_length((size_t)size), thread); 784 785 return (jlong)(uintptr_t)metadata; 786 WB_END 787 788 WB_ENTRY(void, WB_FreeMetaspace(JNIEnv* env, jobject wb, jobject class_loader, jlong addr, jlong size)) 789 oop class_loader_oop = JNIHandles::resolve(class_loader); 790 ClassLoaderData* cld = class_loader_oop != NULL 791 ? java_lang_ClassLoader::loader_data(class_loader_oop) 792 : ClassLoaderData::the_null_class_loader_data(); 793 794 MetadataFactory::free_array(cld, (Array<u1>*)(uintptr_t)addr); 795 WB_END 796 797 //Some convenience methods to deal with objects from java 798 int WhiteBox::offset_for_field(const char* field_name, oop object, 799 Symbol* signature_symbol) { 800 assert(field_name != NULL && strlen(field_name) > 0, "Field name not valid"); 801 Thread* THREAD = Thread::current(); 802 803 //Get the class of our object 804 Klass* arg_klass = object->klass(); 805 //Turn it into an instance-klass 806 InstanceKlass* ik = InstanceKlass::cast(arg_klass); 807 808 //Create symbols to look for in the class 809 TempNewSymbol name_symbol = SymbolTable::lookup(field_name, (int) strlen(field_name), 810 THREAD); 811 812 //To be filled in with an offset of the field we're looking for 813 fieldDescriptor fd; 814 815 Klass* res = ik->find_field(name_symbol, signature_symbol, &fd); 816 if (res == NULL) { 817 tty->print_cr("Invalid layout of %s at %s", ik->external_name(), 818 name_symbol->as_C_string()); 819 fatal("Invalid layout of preloaded class"); 820 } 821 822 //fetch the field at the offset we've found 823 int dest_offset = fd.offset(); 824 825 return dest_offset; 826 } 827 828 829 const char* WhiteBox::lookup_jstring(const char* field_name, oop object) { 830 int offset = offset_for_field(field_name, object, 831 vmSymbols::string_signature()); 832 oop string = object->obj_field(offset); 833 if (string == NULL) { 834 return NULL; 835 } 836 const char* ret = java_lang_String::as_utf8_string(string); 837 return ret; 838 } 839 840 bool WhiteBox::lookup_bool(const char* field_name, oop object) { 841 int offset = 842 offset_for_field(field_name, object, vmSymbols::bool_signature()); 843 bool ret = (object->bool_field(offset) == JNI_TRUE); 844 return ret; 845 } 846 847 void WhiteBox::register_methods(JNIEnv* env, jclass wbclass, JavaThread* thread, JNINativeMethod* method_array, int method_count) { 848 ResourceMark rm; 849 ThreadToNativeFromVM ttnfv(thread); // can't be in VM when we call JNI 850 851 // one by one registration natives for exception catching 852 jclass no_such_method_error_klass = env->FindClass(vmSymbols::java_lang_NoSuchMethodError()->as_C_string()); 853 CHECK_JNI_EXCEPTION(env); 854 for (int i = 0, n = method_count; i < n; ++i) { 855 // Skip dummy entries 856 if (method_array[i].fnPtr == NULL) continue; 857 if (env->RegisterNatives(wbclass, &method_array[i], 1) != 0) { 858 jthrowable throwable_obj = env->ExceptionOccurred(); 859 if (throwable_obj != NULL) { 860 env->ExceptionClear(); 861 if (env->IsInstanceOf(throwable_obj, no_such_method_error_klass)) { 862 // NoSuchMethodError is thrown when a method can't be found or a method is not native. 863 // Ignoring the exception since it is not preventing use of other WhiteBox methods. 864 tty->print_cr("Warning: 'NoSuchMethodError' on register of sun.hotspot.WhiteBox::%s%s", 865 method_array[i].name, method_array[i].signature); 866 } 867 } else { 868 // Registration failed unexpectedly. 869 tty->print_cr("Warning: unexpected error on register of sun.hotspot.WhiteBox::%s%s. All methods will be unregistered", 870 method_array[i].name, method_array[i].signature); 871 env->UnregisterNatives(wbclass); 872 break; 873 } 874 } 875 } 876 } 877 878 #define CC (char*) 879 880 static JNINativeMethod methods[] = { 881 {CC"getObjectAddress", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectAddress }, 882 {CC"getObjectSize", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectSize }, 883 {CC"isObjectInOldGen", CC"(Ljava/lang/Object;)Z", (void*)&WB_isObjectInOldGen }, 884 {CC"getHeapOopSize", CC"()I", (void*)&WB_GetHeapOopSize }, 885 {CC"isClassAlive0", CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive }, 886 {CC"parseCommandLine", 887 CC"(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", 888 (void*) &WB_ParseCommandLine 889 }, 890 {CC"getCompressedOopsMaxHeapSize", CC"()J", 891 (void*)&WB_GetCompressedOopsMaxHeapSize}, 892 {CC"printHeapSizes", CC"()V", (void*)&WB_PrintHeapSizes }, 893 {CC"runMemoryUnitTests", CC"()V", (void*)&WB_RunMemoryUnitTests}, 894 {CC"readFromNoaccessArea",CC"()V", (void*)&WB_ReadFromNoaccessArea}, 895 {CC"stressVirtualSpaceResize",CC"(JJJ)I", (void*)&WB_StressVirtualSpaceResize}, 896 #if INCLUDE_ALL_GCS 897 {CC"g1InConcurrentMark", CC"()Z", (void*)&WB_G1InConcurrentMark}, 898 {CC"g1IsHumongous", CC"(Ljava/lang/Object;)Z", (void*)&WB_G1IsHumongous }, 899 {CC"g1NumFreeRegions", CC"()J", (void*)&WB_G1NumFreeRegions }, 900 {CC"g1RegionSize", CC"()I", (void*)&WB_G1RegionSize }, 901 #endif // INCLUDE_ALL_GCS 902 #if INCLUDE_NMT 903 {CC"NMTMalloc", CC"(J)J", (void*)&WB_NMTMalloc }, 904 {CC"NMTMallocWithPseudoStack", CC"(JI)J", (void*)&WB_NMTMallocWithPseudoStack}, 905 {CC"NMTFree", CC"(J)V", (void*)&WB_NMTFree }, 906 {CC"NMTReserveMemory", CC"(J)J", (void*)&WB_NMTReserveMemory }, 907 {CC"NMTCommitMemory", CC"(JJ)V", (void*)&WB_NMTCommitMemory }, 908 {CC"NMTUncommitMemory", CC"(JJ)V", (void*)&WB_NMTUncommitMemory }, 909 {CC"NMTReleaseMemory", CC"(JJ)V", (void*)&WB_NMTReleaseMemory }, 910 {CC"NMTOverflowHashBucket", CC"(J)V", (void*)&WB_NMTOverflowHashBucket}, 911 {CC"NMTIsDetailSupported",CC"()Z", (void*)&WB_NMTIsDetailSupported}, 912 #endif // INCLUDE_NMT 913 {CC"deoptimizeAll", CC"()V", (void*)&WB_DeoptimizeAll }, 914 {CC"deoptimizeMethod", CC"(Ljava/lang/reflect/Executable;Z)I", 915 (void*)&WB_DeoptimizeMethod }, 916 {CC"isMethodCompiled", CC"(Ljava/lang/reflect/Executable;Z)Z", 917 (void*)&WB_IsMethodCompiled }, 918 {CC"isMethodCompilable", CC"(Ljava/lang/reflect/Executable;IZ)Z", 919 (void*)&WB_IsMethodCompilable}, 920 {CC"isMethodQueuedForCompilation", 921 CC"(Ljava/lang/reflect/Executable;)Z", (void*)&WB_IsMethodQueuedForCompilation}, 922 {CC"makeMethodNotCompilable", 923 CC"(Ljava/lang/reflect/Executable;IZ)V", (void*)&WB_MakeMethodNotCompilable}, 924 {CC"testSetDontInlineMethod", 925 CC"(Ljava/lang/reflect/Executable;Z)Z", (void*)&WB_TestSetDontInlineMethod}, 926 {CC"getMethodCompilationLevel", 927 CC"(Ljava/lang/reflect/Executable;Z)I", (void*)&WB_GetMethodCompilationLevel}, 928 {CC"getMethodEntryBci", 929 CC"(Ljava/lang/reflect/Executable;)I", (void*)&WB_GetMethodEntryBci}, 930 {CC"getCompileQueueSize", 931 CC"(I)I", (void*)&WB_GetCompileQueueSize}, 932 {CC"testSetForceInlineMethod", 933 CC"(Ljava/lang/reflect/Executable;Z)Z", (void*)&WB_TestSetForceInlineMethod}, 934 {CC"enqueueMethodForCompilation", 935 CC"(Ljava/lang/reflect/Executable;II)Z", (void*)&WB_EnqueueMethodForCompilation}, 936 {CC"clearMethodState", 937 CC"(Ljava/lang/reflect/Executable;)V", (void*)&WB_ClearMethodState}, 938 {CC"setBooleanVMFlag", CC"(Ljava/lang/String;Z)V",(void*)&WB_SetBooleanVMFlag}, 939 {CC"setIntxVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetIntxVMFlag}, 940 {CC"setUintxVMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetUintxVMFlag}, 941 {CC"setUint64VMFlag", CC"(Ljava/lang/String;J)V",(void*)&WB_SetUint64VMFlag}, 942 {CC"setDoubleVMFlag", CC"(Ljava/lang/String;D)V",(void*)&WB_SetDoubleVMFlag}, 943 {CC"setStringVMFlag", CC"(Ljava/lang/String;Ljava/lang/String;)V", 944 (void*)&WB_SetStringVMFlag}, 945 {CC"getBooleanVMFlag", CC"(Ljava/lang/String;)Ljava/lang/Boolean;", 946 (void*)&WB_GetBooleanVMFlag}, 947 {CC"getIntxVMFlag", CC"(Ljava/lang/String;)Ljava/lang/Long;", 948 (void*)&WB_GetIntxVMFlag}, 949 {CC"getUintxVMFlag", CC"(Ljava/lang/String;)Ljava/lang/Long;", 950 (void*)&WB_GetUintxVMFlag}, 951 {CC"getUint64VMFlag", CC"(Ljava/lang/String;)Ljava/lang/Long;", 952 (void*)&WB_GetUint64VMFlag}, 953 {CC"getDoubleVMFlag", CC"(Ljava/lang/String;)Ljava/lang/Double;", 954 (void*)&WB_GetDoubleVMFlag}, 955 {CC"getStringVMFlag", CC"(Ljava/lang/String;)Ljava/lang/String;", 956 (void*)&WB_GetStringVMFlag}, 957 {CC"isInStringTable", CC"(Ljava/lang/String;)Z", (void*)&WB_IsInStringTable }, 958 {CC"fullGC", CC"()V", (void*)&WB_FullGC }, 959 {CC"youngGC", CC"()V", (void*)&WB_YoungGC }, 960 {CC"readReservedMemory", CC"()V", (void*)&WB_ReadReservedMemory }, 961 {CC"allocateMetaspace", 962 CC"(Ljava/lang/ClassLoader;J)J", (void*)&WB_AllocateMetaspace }, 963 {CC"freeMetaspace", 964 CC"(Ljava/lang/ClassLoader;JJ)V", (void*)&WB_FreeMetaspace }, 965 {CC"getCPUFeatures", CC"()Ljava/lang/String;", (void*)&WB_GetCPUFeatures }, 966 {CC"getNMethod", CC"(Ljava/lang/reflect/Executable;Z)[Ljava/lang/Object;", 967 (void*)&WB_GetNMethod }, 968 }; 969 970 #undef CC 971 972 JVM_ENTRY(void, JVM_RegisterWhiteBoxMethods(JNIEnv* env, jclass wbclass)) 973 { 974 if (WhiteBoxAPI) { 975 // Make sure that wbclass is loaded by the null classloader 976 instanceKlassHandle ikh = instanceKlassHandle(JNIHandles::resolve(wbclass)->klass()); 977 Handle loader(ikh->class_loader()); 978 if (loader.is_null()) { 979 WhiteBox::register_methods(env, wbclass, thread, methods, sizeof(methods) / sizeof(methods[0])); 980 WhiteBox::register_extended(env, wbclass, thread); 981 WhiteBox::set_used(); 982 } 983 } 984 } 985 JVM_END