From cd2ffae5ceb6d661dd361b3d248517eaab34145d Mon Sep 17 00:00:00 2001 From: Kuai Wei Date: Thu, 14 Mar 2019 22:21:12 +0800 Subject: [PATCH] [JWarmUp] port jwarmup to dragonwell 8u MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Port jwarmup to dragonwell 8u hotspot ref T19299187 Test Plan: test/hotspot/jtreg/jwarmup/Issue11272598.java test/hotspot/jtreg/jwarmup/TestDisableNCE.java test/hotspot/jtreg/jwarmup/TestNotDeoptJITMethod.sh test/hotspot/jtreg/jwarmup/issue9780156.sh test/hotspot/jtreg/jwarmup/TestEagerCompilation.java test/hotspot/jtreg/jwarmup/TestNotifyDeopt.sh test/hotspot/jtreg/jwarmup/TestCheckIfCompilationIsComplete.sh test/hotspot/jtreg/jwarmup/TestFlagAssertion.java test/hotspot/jtreg/jwarmup/TestReadLogfile.java test/hotspot/jtreg/jwarmup/TestClassInitChain.java test/hotspot/jtreg/jwarmup/TestLogFlush.java test/hotspot/jtreg/jwarmup/TestRecordNullMethodCounter.java test/hotspot/jtreg/jwarmup/TestThrowInitializaitonException.java test/hotspot/jtreg/jwarmup/TestDisableMethodData.sh test/hotspot/jtreg/jwarmup/TestMethodRecorder.java test/hotspot/jtreg/jwarmup/TestTieredCompilationInRecording.sh Reviewers: 传胜, 三红, yumin.qi Reviewed By: 传胜, 三红 Subscribers: 麦庶 Differential Revision: https://aone.alibaba-inc.com/code/D852146 --- make/linux/makefiles/mapfile-vers-debug | 3 + make/linux/makefiles/mapfile-vers-product | 3 + src/cpu/x86/vm/templateInterpreter_x86_64.cpp | 19 + src/share/vm/ci/ciEnv.cpp | 57 + src/share/vm/ci/ciEnv.hpp | 5 + src/share/vm/ci/ciMethod.cpp | 8 +- src/share/vm/classfile/classFileParser.cpp | 15 + src/share/vm/classfile/classLoaderData.cpp | 8 + src/share/vm/classfile/systemDictionary.cpp | 57 +- src/share/vm/classfile/systemDictionary.hpp | 3 + src/share/vm/classfile/vmSymbols.hpp | 1 + src/share/vm/code/nmethod.cpp | 5 + src/share/vm/compiler/compileBroker.cpp | 5 + src/share/vm/compiler/compileBroker.hpp | 3 + src/share/vm/jwarmup/jitWarmUp.cpp | 2034 +++++++++++++++++ src/share/vm/jwarmup/jitWarmUp.hpp | 876 +++++++ src/share/vm/jwarmup/jitWarmUpLog.hpp | 74 + src/share/vm/jwarmup/jitWarmUpThread.cpp | 75 + src/share/vm/jwarmup/jitWarmUpThread.hpp | 51 + src/share/vm/libadt/dict.cpp | 25 + src/share/vm/libadt/dict.hpp | 1 + src/share/vm/oops/constantPool.cpp | 131 +- src/share/vm/oops/constantPool.hpp | 30 + src/share/vm/oops/instanceKlass.cpp | 19 + src/share/vm/oops/instanceKlass.hpp | 32 + src/share/vm/oops/method.cpp | 8 + src/share/vm/oops/method.hpp | 21 + src/share/vm/oops/methodData.hpp | 2 + src/share/vm/opto/callGenerator.cpp | 3 +- src/share/vm/opto/compile.cpp | 8 + src/share/vm/opto/graphKit.cpp | 13 +- src/share/vm/opto/lcm.cpp | 3 + src/share/vm/prims/jvm.cpp | 66 + src/share/vm/prims/jvm.h | 9 + src/share/vm/prims/whitebox.cpp | 296 +++ src/share/vm/runtime/globals.hpp | 45 + src/share/vm/runtime/init.cpp | 17 + src/share/vm/runtime/mutexLocker.cpp | 6 + src/share/vm/runtime/mutexLocker.hpp | 3 + src/share/vm/runtime/safepoint.cpp | 10 + src/share/vm/runtime/thread.cpp | 15 + src/share/vm/runtime/thread.hpp | 12 + src/share/vm/utilities/hashtable.cpp | 5 + src/share/vm/utilities/ostream.cpp | 33 + src/share/vm/utilities/ostream.hpp | 16 +- src/share/vm/utilities/symbolMatcher.cpp | 151 ++ src/share/vm/utilities/symbolMatcher.hpp | 66 + test/jwarmup/Issue11272598.java | 158 ++ .../TestCheckIfCompilationIsComplete.sh | 125 + test/jwarmup/TestClassInitChain.java | 191 ++ test/jwarmup/TestClassInitOrder.java | 83 + test/jwarmup/TestDisableMethodData.sh | 171 ++ test/jwarmup/TestDisableNCE.java | 165 ++ test/jwarmup/TestEagerCompilation.java | 299 +++ test/jwarmup/TestFlagAssertion.java | 55 + test/jwarmup/TestLogFlush.java | 145 ++ test/jwarmup/TestMethodRecorder.java | 92 + test/jwarmup/TestNotDeoptJITMethod.sh | 137 ++ test/jwarmup/TestNotifyDeopt.sh | 151 ++ test/jwarmup/TestReadLogfile.java | 299 +++ test/jwarmup/TestRecordNullMethodCounter.java | 77 + .../TestThrowInitializaitonException.java | 137 ++ .../TestTieredCompilationInRecording.sh | 131 ++ test/jwarmup/issue9780156.sh | 195 ++ .../whitebox/sun/hotspot/WhiteBox.java | 11 + 65 files changed, 6960 insertions(+), 10 deletions(-) create mode 100644 src/share/vm/jwarmup/jitWarmUp.cpp create mode 100644 src/share/vm/jwarmup/jitWarmUp.hpp create mode 100644 src/share/vm/jwarmup/jitWarmUpLog.hpp create mode 100644 src/share/vm/jwarmup/jitWarmUpThread.cpp create mode 100644 src/share/vm/jwarmup/jitWarmUpThread.hpp create mode 100644 src/share/vm/utilities/symbolMatcher.cpp create mode 100644 src/share/vm/utilities/symbolMatcher.hpp create mode 100644 test/jwarmup/Issue11272598.java create mode 100644 test/jwarmup/TestCheckIfCompilationIsComplete.sh create mode 100644 test/jwarmup/TestClassInitChain.java create mode 100644 test/jwarmup/TestClassInitOrder.java create mode 100644 test/jwarmup/TestDisableMethodData.sh create mode 100644 test/jwarmup/TestDisableNCE.java create mode 100644 test/jwarmup/TestEagerCompilation.java create mode 100644 test/jwarmup/TestFlagAssertion.java create mode 100644 test/jwarmup/TestLogFlush.java create mode 100644 test/jwarmup/TestMethodRecorder.java create mode 100644 test/jwarmup/TestNotDeoptJITMethod.sh create mode 100644 test/jwarmup/TestNotifyDeopt.sh create mode 100644 test/jwarmup/TestReadLogfile.java create mode 100644 test/jwarmup/TestRecordNullMethodCounter.java create mode 100644 test/jwarmup/TestThrowInitializaitonException.java create mode 100644 test/jwarmup/TestTieredCompilationInRecording.sh create mode 100644 test/jwarmup/issue9780156.sh diff --git a/make/linux/makefiles/mapfile-vers-debug b/make/linux/makefiles/mapfile-vers-debug index f16822b20..56888300c 100644 --- a/make/linux/makefiles/mapfile-vers-debug +++ b/make/linux/makefiles/mapfile-vers-debug @@ -40,6 +40,7 @@ SUNWprivate_1.1 { JVM_AssertionStatusDirectives; JVM_Available; JVM_Bind; + JVM_CheckJWarmUpCompilationIsComplete; JVM_ClassDepth; JVM_ClassLoaderDepth; JVM_Clone; @@ -205,6 +206,8 @@ SUNWprivate_1.1 { JVM_NewArray; JVM_NewInstanceFromConstructor; JVM_NewMultiArray; + JVM_NotifyApplicationStartUpIsDone; + JVM_NotifyJVMDeoptWarmUpMethods; JVM_OnExit; JVM_Open; JVM_RaiseSignal; diff --git a/make/linux/makefiles/mapfile-vers-product b/make/linux/makefiles/mapfile-vers-product index f96c86b4b..aacebc82d 100644 --- a/make/linux/makefiles/mapfile-vers-product +++ b/make/linux/makefiles/mapfile-vers-product @@ -40,6 +40,7 @@ SUNWprivate_1.1 { JVM_AssertionStatusDirectives; JVM_Available; JVM_Bind; + JVM_CheckJWarmUpCompilationIsComplete; JVM_ClassDepth; JVM_ClassLoaderDepth; JVM_Clone; @@ -205,6 +206,8 @@ SUNWprivate_1.1 { JVM_NewArray; JVM_NewInstanceFromConstructor; JVM_NewMultiArray; + JVM_NotifyApplicationStartUpIsDone; + JVM_NotifyJVMDeoptWarmUpMethods; JVM_OnExit; JVM_Open; JVM_RaiseSignal; diff --git a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp index 209a3f676..e65b92c14 100644 --- a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp +++ b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp @@ -29,6 +29,7 @@ #include "interpreter/interpreterGenerator.hpp" #include "interpreter/interpreterRuntime.hpp" #include "interpreter/templateTable.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "oops/arrayOop.hpp" #include "oops/methodData.hpp" #include "oops/method.hpp" @@ -334,6 +335,24 @@ void InterpreterGenerator::generate_counter_incr( __ incrementl(Address(rax, MethodCounters::interpreter_invocation_counter_offset())); } + +#ifdef _LP64 + // JitWarmUp support, record method first invocation's initialization order + if (CompilationWarmUpRecording) { + Label skip_record; + JitWarmUp* jitwarmup = JitWarmUp::instance(); + assert(jitwarmup != NULL, "jitwarmup should not be NULL"); + const ExternalAddress current_init_order(jitwarmup->recorder()->current_init_order_addr()); + __ movl(rcx, Address(rax, + MethodCounters::interpreter_invocation_counter_offset())); + __ cmp32(rcx, 1); + __ jcc(Assembler::above, skip_record); + __ mov32(rcx, current_init_order); + __ movl(Address(rbx, Method::first_invoke_init_order_offset()), rcx); + __ bind(skip_record); + } +#endif + // Update standard invocation counters __ movl(rcx, invocation_counter); __ incrementl(rcx, InvocationCounter::count_increment); diff --git a/src/share/vm/ci/ciEnv.cpp b/src/share/vm/ci/ciEnv.cpp index 868798064..54b0deb42 100644 --- a/src/share/vm/ci/ciEnv.cpp +++ b/src/share/vm/ci/ciEnv.cpp @@ -672,6 +672,63 @@ ciField* ciEnv::get_field_by_index(ciInstanceKlass* accessor, GUARDED_VM_ENTRY(return get_field_by_index_impl(accessor, index);) } +// ------------------------------------------------------------------ +// ciEnv::check_field_resolved +// +// Check whether this field has been resolved. +bool ciEnv::check_field_resolved(ciInstanceKlass* accessor, + int index) { + GUARDED_VM_ENTRY( + ciConstantPoolCache* cache = accessor->field_cache(); + if (cache != NULL) { + ciField* field = (ciField*)cache->get(index); + if (field != NULL) { + return true; + } + } + CompilerThread *thread = CompilerThread::current(); + assert(accessor->get_instanceKlass()->is_linked(), "must be linked before using its constant-pool"); + constantPoolHandle cpool(thread, accessor->get_instanceKlass()->constants()); + + // Get the field's name, signature, and type. + Symbol* name = cpool->name_ref_at(index); + if (name == NULL) { + return false; + } + int name_index = cpool->name_and_type_ref_index_at(index); + int sig_index = cpool->signature_ref_index_at(name_index); + Symbol* signature = cpool->symbol_at(sig_index); + if (signature == NULL) { + return false; + } + return true; + ) +} + +// ------------------------------------------------------------------ +// +// Check if all fields needed by this method in ConstantPool are resolved +bool ciEnv::check_method_fields_all_resolved(ciMethod* method) { + ciInstanceKlass* klass = method->holder(); + ciBytecodeStream str(method); + int start = 0; + int limit = method->code_size(); + str.reset_to_bci(start); + Bytecodes::Code code; + while ((code = str.next()) != ciBytecodeStream::EOBC() && + str.cur_bci() < limit) { + if (code == Bytecodes::_getfield || + code == Bytecodes::_getstatic || + code == Bytecodes::_putfield || + code == Bytecodes::_putstatic) { + if (!check_field_resolved(klass, str.get_index_u2_cpcache())) { + return false; + } + } + } + return true; +} + // ------------------------------------------------------------------ // ciEnv::lookup_method // diff --git a/src/share/vm/ci/ciEnv.hpp b/src/share/vm/ci/ciEnv.hpp index 30ec2d743..db61d1fc1 100644 --- a/src/share/vm/ci/ciEnv.hpp +++ b/src/share/vm/ci/ciEnv.hpp @@ -126,6 +126,8 @@ private: ciInstanceKlass* accessor); ciField* get_field_by_index(ciInstanceKlass* loading_klass, int field_index); + bool check_field_resolved(ciInstanceKlass* accessor, + int index); ciMethod* get_method_by_index(constantPoolHandle cpool, int method_index, Bytecodes::Code bc, ciInstanceKlass* loading_klass); @@ -317,6 +319,9 @@ public: // Return state of appropriate compilability int compilable() { return _compilable; } + // Check if all fields needed by this method in ConstantPool are resolved + bool check_method_fields_all_resolved(ciMethod* method); + const char* retry_message() const { switch (_compilable) { case ciEnv::MethodCompilable_not_at_tier: diff --git a/src/share/vm/ci/ciMethod.cpp b/src/share/vm/ci/ciMethod.cpp index 99a9c4208..f7073b9d6 100644 --- a/src/share/vm/ci/ciMethod.cpp +++ b/src/share/vm/ci/ciMethod.cpp @@ -975,6 +975,10 @@ bool ciMethod::ensure_method_data(methodHandle h_m) { if (is_native() || is_abstract() || h_m()->is_accessor()) { return true; } + if (CompilationWarmUp && CURRENT_ENV->task()->is_jwarmup_compilation()) { + _method_data = CURRENT_ENV->get_empty_methodData(); + return false; + } if (h_m()->method_data() == NULL) { Method::build_interpreter_method_data(h_m, THREAD); if (HAS_PENDING_EXCEPTION) { @@ -1015,7 +1019,9 @@ ciMethodData* ciMethod::method_data() { Thread* my_thread = JavaThread::current(); methodHandle h_m(my_thread, get_Method()); - if (h_m()->method_data() != NULL) { + if (CompilationWarmUp && CURRENT_ENV->task()->is_jwarmup_compilation()) { + _method_data = CURRENT_ENV->get_empty_methodData(); + } else if (h_m()->method_data() != NULL) { _method_data = CURRENT_ENV->get_method_data(h_m()->method_data()); _method_data->load_data(); } else { diff --git a/src/share/vm/classfile/classFileParser.cpp b/src/share/vm/classfile/classFileParser.cpp index e23f8002f..b738e0c9e 100644 --- a/src/share/vm/classfile/classFileParser.cpp +++ b/src/share/vm/classfile/classFileParser.cpp @@ -4133,6 +4133,14 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, this_klass->set_has_default_methods(has_default_methods); this_klass->set_declares_default_methods(declares_default_methods); + if (CompilationWarmUp || CompilationWarmUpRecording) { + if (_stream->source() == NULL) { + this_klass->set_source_file_path(NULL); + } else { + this_klass->set_source_file_path(SymbolTable::new_symbol(_stream->source(), THREAD)); + } + } + if (!host_klass.is_null()) { assert (this_klass->is_anonymous(), "should be the same"); this_klass->set_host_klass(host_klass()); @@ -4270,6 +4278,13 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, instanceKlassHandle this_klass (THREAD, preserve_this_klass); debug_only(this_klass->verify();) + if (CompilationWarmUp || CompilationWarmUpRecording) { + unsigned int crc32 = ClassLoader::crc32(0, (char*)(_stream->buffer()), _stream->length()); + unsigned int class_bytes_size = _stream->length(); + this_klass->set_crc32(crc32); + this_klass->set_bytes_size(class_bytes_size); + } + // Clear class if no error has occurred so destructor doesn't deallocate it _klass = NULL; return this_klass; diff --git a/src/share/vm/classfile/classLoaderData.cpp b/src/share/vm/classfile/classLoaderData.cpp index 4dc795ec3..f2ed60b32 100644 --- a/src/share/vm/classfile/classLoaderData.cpp +++ b/src/share/vm/classfile/classLoaderData.cpp @@ -763,6 +763,14 @@ bool ClassLoaderDataGraph::do_unloading(BoolObjectClosure* is_alive_closure, boo ClassLoaderData* prev = NULL; bool seen_dead_loader = false; + // Unload PreloadClassChain + if (CompilationWarmUp) { + JitWarmUp* jwp = JitWarmUp::instance(); + assert(jwp != NULL, "santiy check"); + PreloadClassChain* chain = jwp->preloader()->chain(); + chain->do_unloading(is_alive_closure); + } + // Save previous _unloading pointer for CMS which may add to unloading list before // purging and we don't want to rewalk the previously unloaded class loader data. _saved_unloading = _unloading; diff --git a/src/share/vm/classfile/systemDictionary.cpp b/src/share/vm/classfile/systemDictionary.cpp index 90ab8cbc5..0fc7257d2 100644 --- a/src/share/vm/classfile/systemDictionary.cpp +++ b/src/share/vm/classfile/systemDictionary.cpp @@ -273,6 +273,29 @@ Klass* SystemDictionary::resolve_array_class_or_null(Symbol* class_name, return k; } +class SuperClassResolvingMark : public StackObj { +public: + SuperClassResolvingMark() { + initialize(Thread::current()); + } + + SuperClassResolvingMark(Thread* thread) { + initialize(thread); + } + + ~SuperClassResolvingMark() { + assert(CompilationWarmUp, "wrong usage"); + _thread->super_class_resolving_recursive_dec(); + } +protected: + void initialize(Thread* thread) { + assert(CompilationWarmUp, "wrong usage"); + _thread = thread; + _thread->super_class_resolving_recursive_inc(); + } +private: + Thread* _thread; +}; // Must be called for any super-class or super-interface resolution // during class definition to allow class circularity checking @@ -373,11 +396,21 @@ Klass* SystemDictionary::resolve_super_or_fail(Symbol* child_name, // java.lang.Object should have been found above assert(class_name != NULL, "null super class for resolving"); // Resolve the super class or interface, check results on return - Klass* superk = SystemDictionary::resolve_or_null(class_name, - class_loader, - protection_domain, - THREAD); - + Klass* superk = NULL; + if (CompilationWarmUp) { + SuperClassResolvingMark scrm; + superk = + SystemDictionary::resolve_or_null(class_name, + class_loader, + protection_domain, + THREAD); + } else { + superk = + SystemDictionary::resolve_or_null(class_name, + class_loader, + protection_domain, + THREAD); + } KlassHandle superk_h(THREAD, superk); // Clean up of placeholders moved so that each classloadAction registrar self-cleans up @@ -868,6 +901,14 @@ Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name, } #endif + if (CompilationWarmUp) { + if (!class_has_been_loaded) { + JitWarmUp* jwp = JitWarmUp::instance(); + assert(jwp != NULL, "sanity check"); + jwp->preloader()->resolve_loaded_klass(k()); + } + } + // return if the protection domain in NULL if (protection_domain() == NULL) return k(); @@ -1155,6 +1196,12 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name, } } ); + if (CompilationWarmUp) { + JitWarmUp* jwp = JitWarmUp::instance(); + assert(jwp != NULL, "sanity check"); + jwp->preloader()->resolve_loaded_klass(k()); + } + return k(); } diff --git a/src/share/vm/classfile/systemDictionary.hpp b/src/share/vm/classfile/systemDictionary.hpp index 987e246fa..bd20461e7 100644 --- a/src/share/vm/classfile/systemDictionary.hpp +++ b/src/share/vm/classfile/systemDictionary.hpp @@ -27,6 +27,7 @@ #include "classfile/classFileStream.hpp" #include "classfile/classLoader.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "oops/objArrayOop.hpp" #include "oops/symbol.hpp" #include "runtime/java.hpp" @@ -201,6 +202,8 @@ class SymbolPropertyTable; class SystemDictionary : AllStatic { friend class VMStructs; friend class SystemDictionaryHandles; + friend class JitWarmUp; + friend class PreloadJitInfo; public: enum WKID { diff --git a/src/share/vm/classfile/vmSymbols.hpp b/src/share/vm/classfile/vmSymbols.hpp index 7f0e82071..c9fe32512 100644 --- a/src/share/vm/classfile/vmSymbols.hpp +++ b/src/share/vm/classfile/vmSymbols.hpp @@ -410,6 +410,7 @@ template(signers_name, "signers_name") \ template(loader_data_name, "loader_data") \ template(dependencies_name, "dependencies") \ + template(jwarmup_dummy_name, "dummy") \ template(input_stream_void_signature, "(Ljava/io/InputStream;)V") \ template(getFileURL_name, "getFileURL") \ template(getFileURL_signature, "(Ljava/io/File;)Ljava/net/URL;") \ diff --git a/src/share/vm/code/nmethod.cpp b/src/share/vm/code/nmethod.cpp index 9e4edf342..e230419a3 100644 --- a/src/share/vm/code/nmethod.cpp +++ b/src/share/vm/code/nmethod.cpp @@ -34,6 +34,7 @@ #include "compiler/compilerOracle.hpp" #include "compiler/disassembler.hpp" #include "interpreter/bytecode.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "oops/methodData.hpp" #include "prims/jvmtiRedefineClassesTrace.hpp" #include "prims/jvmtiImpl.hpp" @@ -649,6 +650,10 @@ nmethod* nmethod::new_nmethod(methodHandle method, DEBUG_ONLY(nm->verify();) nm->log_new_nmethod(); } + if (CompilationWarmUpRecording && nm != NULL && comp_level >= CompilationWarmUpRecordMinLevel) { + int bci = nm->is_osr_method() ? nm->osr_entry_bci() : InvocationEntryBci; + JitWarmUp::instance()->recorder()->add_method(nm->method(), bci); + } return nm; } diff --git a/src/share/vm/compiler/compileBroker.cpp b/src/share/vm/compiler/compileBroker.cpp index bec803bdf..1156f9e5d 100644 --- a/src/share/vm/compiler/compileBroker.cpp +++ b/src/share/vm/compiler/compileBroker.cpp @@ -331,6 +331,8 @@ void CompileTask::initialize(int compile_id, _comment = comment; _failure_reason = NULL; + _is_jwarmup_compilation = false; + if (LogCompilation) { _time_queued = os::elapsed_counter(); if (hot_method.not_null()) { @@ -1594,6 +1596,9 @@ CompileTask* CompileBroker::create_compile_task(CompileQueue* queue, new_task->initialize(compile_id, method, osr_bci, comp_level, hot_method, hot_count, comment, blocking); + if (strcmp(comment, "JitWarmUp") == 0) { + new_task->mark_jwarmup_compilation(); + } queue->add(new_task); return new_task; } diff --git a/src/share/vm/compiler/compileBroker.hpp b/src/share/vm/compiler/compileBroker.hpp index ad37ff173..36ccd49b8 100644 --- a/src/share/vm/compiler/compileBroker.hpp +++ b/src/share/vm/compiler/compileBroker.hpp @@ -65,6 +65,7 @@ class CompileTask : public CHeapObj { int _hot_count; // information about its invocation counter const char* _comment; // more info about the task const char* _failure_reason; + bool _is_jwarmup_compilation; public: CompileTask() { @@ -84,6 +85,8 @@ class CompileTask : public CHeapObj { bool is_complete() const { return _is_complete; } bool is_blocking() const { return _is_blocking; } bool is_success() const { return _is_success; } + bool is_jwarmup_compilation() const { return _is_jwarmup_compilation; } + void mark_jwarmup_compilation() { _is_jwarmup_compilation = true; } nmethodLocker* code_handle() const { return _code_handle; } void set_code_handle(nmethodLocker* l) { _code_handle = l; } diff --git a/src/share/vm/jwarmup/jitWarmUp.cpp b/src/share/vm/jwarmup/jitWarmUp.cpp new file mode 100644 index 000000000..6a7addc54 --- /dev/null +++ b/src/share/vm/jwarmup/jitWarmUp.cpp @@ -0,0 +1,2034 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "precompiled.hpp" + +#include "classfile/classLoaderData.hpp" +#include "classfile/classLoaderData.inline.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "compiler/compileBroker.hpp" +#include "jwarmup/jitWarmUp.hpp" +#include "jwarmup/jitWarmUpThread.hpp" +#include "oops/method.hpp" +#include "oops/typeArrayKlass.hpp" +#include "runtime/arguments.hpp" +#include "runtime/compilationPolicy.hpp" +#include "runtime/fieldType.hpp" +#include "runtime/handles.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.hpp" +#include "utilities/hashtable.inline.hpp" +#include "utilities/stack.hpp" +#include "utilities/stack.inline.hpp" +#include "runtime/atomic.hpp" +#include "jwarmup/jitWarmUpLog.hpp" // must be last one to use customized jwarmup log + +#define JITWARMUP_VERSION 0x2 + +JitWarmUp* JitWarmUp::_instance = NULL; + +JitWarmUp::JitWarmUp() + : _state(NOT_INIT), + _version(JITWARMUP_VERSION), + _dummy_method(NULL), + _recorder(NULL), + _preloader(NULL), + _excluding_matcher(NULL) { +} + +JitWarmUp::~JitWarmUp() { + delete _recorder; + delete _preloader; +} + +JitWarmUp* JitWarmUp::create_instance() { + _instance = new JitWarmUp(); + return _instance; +} + +// JitWarmUp Init functions +JitWarmUp::JitWarmUpState JitWarmUp::init_for_recording() { + assert(CompilationWarmUpRecording && !CompilationWarmUp, "JVM option verify failure"); + _recorder = new ProfileRecorder(); + _recorder->set_holder(this); + _recorder->init(); + if (CompilationWarmUpRecordTime > 0) { + // use a thread to flush info + JitWarmUpFlushThread::spawn_wait_for_flush(CompilationWarmUpRecordTime); + } + if (_recorder->is_valid()) { + _state = JitWarmUp::IS_OK; + } else { + _state = JitWarmUp::IS_ERR; + } + return _state; +} + +JitWarmUp::JitWarmUpState JitWarmUp::init_for_warmup() { + assert(!CompilationWarmUpRecording && CompilationWarmUp, "JVM option verify"); + if (CompilationWarmUpExclude != NULL) { + _excluding_matcher = new (ResourceObj::C_HEAP, mtClass) SymbolMatcher(CompilationWarmUpExclude); + } + if (CompilationWarmUpExplicitDeopt && CompilationWarmUpDeoptTime > 0) { + log_warning(warmup)("[JitWarmUp] WARNING : CompilationWarmUpDeoptTime is unused when CompilationWarmUpExplicitDeopt is enable"); + } + _preloader = new PreloadJitInfo(); + _preloader->set_holder(this); + _preloader->init(); + if (_preloader->is_valid()) { + _state = JitWarmUp::IS_OK; + } else { + _state = JitWarmUp::IS_ERR; + } + return _state; +} + +// init JitWarmUp module +void JitWarmUp::init() { + if (CompilationWarmUp) { + init_for_warmup(); + } else if(CompilationWarmUpRecording) { + init_for_recording(); + } + // check valid + if ((CompilationWarmUpRecording || CompilationWarmUp) && !JitWarmUp::is_valid()) { + log_error(warmup)("[JitWarmUp] ERROR: init error."); + vm_exit(-1); + } +} + +JitWarmUp::JitWarmUpState JitWarmUp::flush_logfile() { + // state in error + if(_state == IS_ERR) { + return _state; + } + _recorder->flush(); + if (_recorder->is_valid()) { + _state = IS_OK; + } else { + _state = IS_ERR; + } + return _state; +} + +bool JitWarmUp::commit_compilation(methodHandle m, int bci, TRAPS) { + // use C2 compiler + int comp_level = CompLevel_full_optimization; + if (CompilationPolicy::can_be_compiled(m, comp_level)) { + // Force compilation + CompileBroker::compile_method(m, bci, comp_level, + methodHandle(), 0, + "JitWarmUp", THREAD); + return true; + } + return false; +} + +Symbol* JitWarmUp::get_class_loader_name(ClassLoaderData* cld) { + Handle class_loader(Thread::current(), cld->class_loader()); + Symbol* loader_name = NULL; + if (class_loader() != NULL) { + loader_name = PreloadJitInfo::remove_meaningless_suffix(class_loader()->klass()->name()); + } else { + loader_name = SymbolTable::new_symbol("NULL", Thread::current()); + } + return loader_name; +} + +ProfileRecorder::ProfileRecorder() + : _holder(NULL), + _logfile(NULL), + _pos(0), + _state(NOT_INIT), + _class_init_list(NULL), + _init_list_tail_node(NULL), + _dict(NULL), + _class_init_order_count(-1), + _flushed(false), + _logfile_name(NULL), + _max_symbol_length(0) { +} + +ProfileRecorder::~ProfileRecorder() { + if (!CompilationWarmUpLogfile) { + os::free((void*)logfile_name()); + } + delete _class_init_list; +} + +#define PROFILE_RECORDER_HT_SIZE 10240 + +void ProfileRecorder::init() { + assert(_state == NOT_INIT, "state error"); + if (CompilationWarmUp) { + log_error(warmup)("[JitWarmUp] ERROR: you can not set both CompilationWarmUp and CompilationWarmUpRecording"); + _state = IS_ERR; + return; + } + if (!ProfileInterpreter) { + log_error(warmup)("[JitWarmUp] ERROR: flag ProfileInterpreter must be on"); + _state = IS_ERR; + return; + } + // disable class unloading + if (ClassUnloading) { + log_error(warmup)("[JitWarmUp] ERROR: flag ClassUnloading must be off"); + _state = IS_ERR; + return; + } + if (UseConcMarkSweepGC && CMSClassUnloadingEnabled) { + log_error(warmup)("[JitWarmUp] ERROR: if use CMS gc, flag CMSClassUnloadingEnabled must be off"); + _state = IS_ERR; + return; + } + if (UseG1GC && ClassUnloadingWithConcurrentMark) { + log_error(warmup)("[JitWarmUp] ERROR: if use G1 gc, flag ClassUnloadingWithConcurrentMark must be off"); + _state = IS_ERR; + return; + } + // check class data sharing + if (UseSharedSpaces) { + log_error(warmup)("[JitWarmUp] ERROR: flag UseSharedSpaces must be off"); + _state = IS_ERR; + return; + } + // log file name + if (CompilationWarmUpLogfile == NULL) { + char* buf = (char*)os::malloc(100, mtInternal); + char fmt[] = "jwarmup_%p.profile"; + Arguments::copy_expand_pid(fmt, sizeof(fmt), buf, 100); + _logfile_name = buf; + } else { + _logfile_name = CompilationWarmUpLogfile; + } + + _class_init_list = new (ResourceObj::C_HEAP, mtInternal) LinkedListImpl(); + _dict = new ProfileRecordDictionary(PROFILE_RECORDER_HT_SIZE); + _state = IS_OK; + + log_debug(warmup)("[JitWarmUp] begin to collect, log file is %s", logfile_name()); +} + +int ProfileRecorder::assign_class_init_order(InstanceKlass* klass) { + // ignore anonymous class + if (klass->is_anonymous()) { + return -1; + } + Symbol* name = klass->name(); + Symbol* path = klass->source_file_path(); + Symbol* loader_name = JitWarmUp::get_class_loader_name(klass->class_loader_data()); + MutexLockerEx mu(ProfileRecorder_lock); + if (_init_list_tail_node == NULL) { + // add head node + _class_init_list->add(ClassSymbolEntry(name, loader_name, path)); + _init_list_tail_node = _class_init_list->head(); + } else { + _class_init_list->insert_after(ClassSymbolEntry(name, loader_name, path), + _init_list_tail_node); + _init_list_tail_node = _init_list_tail_node->next(); + } + _class_init_order_count++; +#ifndef PRODUCT + klass->set_initialize_order(_class_init_order_count); +#endif + return _class_init_order_count; +} + +void ProfileRecorder::add_method(Method* m, int bci) { + MutexLockerEx mu(ProfileRecorder_lock, Mutex::_no_safepoint_check_flag); + // if is flushed, stop adding method + if (flushed()) { + return; + } + // not deal with OSR Compilation + if (bci != InvocationEntryBci) { + return; + } + assert(is_valid(), "JitWarmUp state must be OK"); + unsigned int hash = compute_hash(m); + dict()->add_method(hash, m, bci); +} + +// NYI +void ProfileRecorder::remove_method(Method* m) { + ShouldNotCallThis(); +} + +void ProfileRecorder::update_max_symbol_length(int len) { + if (len > _max_symbol_length) { + _max_symbol_length = len; + } +} + +ProfileRecordDictionary::ProfileRecordDictionary(unsigned int size) + : Hashtable(size, sizeof(ProfileRecorderEntry)), + _count(0) { + // do nothing +} + +ProfileRecordDictionary::~ProfileRecordDictionary() { + free_buckets(); +} + +ProfileRecorderEntry* ProfileRecordDictionary::new_entry(unsigned int hash, Method* method) { + ProfileRecorderEntry* entry = (ProfileRecorderEntry*)Hashtable::new_entry(hash, method); + entry->init(); + return entry; +} + +ProfileRecorderEntry* ProfileRecordDictionary::add_method(unsigned int hash, Method* method, int bci) { + // this method should be called after a compilation task done + assert_lock_strong(ProfileRecorder_lock); + int index = hash_to_index(hash); + ProfileRecorderEntry* entry = find_entry(hash, method); + if (entry != NULL) { + return entry; + } + // not existed + entry = new_entry(hash, method); + entry->set_bci(bci); + entry->set_order(count()); + add_entry(index, entry); + _count++; + return entry; +} + +ProfileRecorderEntry* ProfileRecordDictionary::find_entry(unsigned int hash, Method* method) { + int index = hash_to_index(hash); + for (ProfileRecorderEntry* p = bucket(index); p != NULL; p = p->next()) { + if (p->literal() == method) { + return p; + } + } + return NULL; +} + +void ProfileRecordDictionary::free_entry(ProfileRecorderEntry* entry) { + Hashtable::free_entry(entry); +} + +// head section macro defines + +// offset section +#define VERSION_OFFSET 0 +#define MAGIC_NUMBER_OFFSET 4 +#define FILE_SIZE_OFFSET 8 +#define CRC32_OFFSET 12 +#define APPID_OFFSET 16 +#define MAX_SYMBOL_LENGTH_OFFSET 20 +#define RECORD_COUNT_OFFSET 24 +#define TIME_OFFSET 28 + +#define HEADER_SIZE 36 + +// width section +#define VERSION_WIDTH (MAGIC_NUMBER_OFFSET - VERSION_OFFSET) +#define MAGIC_WIDTH (FILE_SIZE_OFFSET - MAGIC_NUMBER_OFFSET) +#define FILE_SIZE_WIDTH (CRC32_OFFSET - FILE_SIZE_OFFSET) +#define CRC32_WIDTH (APPID_OFFSET - CRC32_OFFSET) +#define APPID_WIDTH (MAX_SYMBOL_LENGTH_OFFSET - APPID_OFFSET) +#define MAX_SYMBOL_LENGTH_WIDTH (RECORD_COUNT_OFFSET - MAX_SYMBOL_LENGTH_OFFSET) +#define RECORD_COUNTS_WIDTH (TIME_OFFSET - RECORD_COUNT_OFFSET) +#define TIME_WIDTH (HEADER_SIZE - TIME_OFFSET) + +// default value +#define MAGIC_NUMBER 0xBABA +#define FILE_DEFAULT_NUMBER 0 +#define CRC32_DEFAULT_NUMBER 0 + + +static char record_buf[12]; +void ProfileRecorder::write_u1(u1 value) { + *(u1*)record_buf = value; + _logfile->write(record_buf, 1); + _pos += 1; +} + +void ProfileRecorder::write_u4(u4 value) { + *(u4*)record_buf = value; + _logfile->write(record_buf, 4); + _pos += 4; +} + +void ProfileRecorder::overwrite_u4(u4 value, unsigned int offset) { + *(u4*)record_buf = value; + _logfile->write(record_buf, 4, offset); +} + +void ProfileRecorder::write_u8(u8 value) { + *(u8*)record_buf = value; + _logfile->write(record_buf, 8); + _pos += 8; +} + +void ProfileRecorder::write_string(const char* src, size_t len) { + _logfile->write(src, len); + _logfile->write("\0", 1); + _pos += len + 1; + update_max_symbol_length((int)len); +} + +void ProfileRecorder::write_string(const char* src) { + write_string(src, ::strlen(src)); +} + +#define JVM_DEFINE_CLASS_PATH "_JVM_DefineClass_" + +#define CRC32_BUF_SIZE 1024 +static char crc32_buf[CRC32_BUF_SIZE]; + +int ProfileRecorder::compute_crc32(randomAccessFileStream* fs) { + long old_position = (long)fs->ftell(); + fs->fseek(HEADER_SIZE, SEEK_SET); + int content_size = fs->fileSize() - HEADER_SIZE; + assert(content_size > 0, "sanity check"); + int loops = content_size / CRC32_BUF_SIZE; + int rest_size = content_size % CRC32_BUF_SIZE; + int crc = 0; + + for (int i = 0; i < loops; ++i) { + fs->read(crc32_buf, CRC32_BUF_SIZE, 1); + crc = ClassLoader::crc32(crc, crc32_buf, CRC32_BUF_SIZE); + } + if (rest_size > 0) { + fs->read(crc32_buf, rest_size, 1); + crc = ClassLoader::crc32(crc, crc32_buf, rest_size); + } + // reset + fs->fseek(old_position, SEEK_SET); + + return crc; +} +#undef CRC32_BUF_SIZE + + +// buffer used in write_header() +static char header_buf[HEADER_SIZE]; +void ProfileRecorder::write_header() { + assert(_logfile->is_open(), ""); + // header info + size_t offset = 0; + // version number + *(unsigned int*)header_buf = holder()->version(); + _pos += VERSION_WIDTH; + offset += VERSION_WIDTH; + // magic number + *(unsigned int*)((char*)header_buf + offset) = MAGIC_NUMBER; + _pos += MAGIC_WIDTH; + offset += MAGIC_WIDTH; + // file size + *(unsigned int*)((char*)header_buf + offset) = FILE_DEFAULT_NUMBER; + _pos += CRC32_WIDTH; + offset += CRC32_WIDTH; + // crc32 + *(unsigned int*)((char*)header_buf + offset) = CRC32_DEFAULT_NUMBER; + _pos += CRC32_WIDTH; + offset += CRC32_WIDTH; + + // App id + *(unsigned int*)((char*)header_buf + offset) = CompilationWarmUpAppID; + _pos += APPID_WIDTH; + offset += APPID_WIDTH; + + // max symbol length + *(unsigned int*)((char*)header_buf + offset) = 0; + _pos += MAX_SYMBOL_LENGTH_WIDTH; + offset += MAX_SYMBOL_LENGTH_WIDTH; + + // record counts + *(unsigned int*)((char*)header_buf + offset) = recorded_count(); + _pos += RECORD_COUNTS_WIDTH; + offset += RECORD_COUNTS_WIDTH; + // record time + *(unsigned jlong*)((char*)header_buf + offset) = os::javaTimeMillis(); + _pos += TIME_WIDTH; + offset += TIME_WIDTH; + // write to file + _logfile->write(header_buf, offset); +} + +// write class initialize order section +void ProfileRecorder::write_inited_class() { + assert(_logfile->is_open(), "log file must be opened"); + ResourceMark rm; + unsigned int begin_pos = _pos; + unsigned int size_anchor = begin_pos; + // size place holder + write_u4((u4)MAGIC_NUMBER); + // class init order, beginning from -1 + write_u4((u4)class_init_count()); + int cnt = 0; + const LinkedListNode* node = class_init_list()->head(); + while (node != NULL) { + const ClassSymbolEntry* entry = node->peek(); + char* class_name = entry->class_name()->as_C_string(); + const char* class_loader_name = NULL; + if (entry->class_loader_name() == NULL) { + class_loader_name = "NULL"; + } else { + class_loader_name = entry->class_loader_name()->as_C_string(); + } + const char* path = NULL; + if (entry->path() == NULL) { + path = JVM_DEFINE_CLASS_PATH; + } else { + path = entry->path()->as_C_string(); + } + write_string(class_name, strlen(class_name)); + write_string(class_loader_name, strlen(class_loader_name)); + write_string(path, strlen(path)); + node = node->next(); + cnt++; + } + assert(cnt == class_init_count(), "error happened in profile info record"); + unsigned int end_pos = _pos; + unsigned int section_size = end_pos - begin_pos; + overwrite_u4(section_size, size_anchor); +} + +// write profile information +void ProfileRecorder::write_record(Method* method, int bci, int order) { + ResourceMark rm; + unsigned int begin_pos = _pos; + unsigned int total_size = 0; + ConstMethod* cm = method->constMethod(); + MethodCounters* mc = method->method_counters(); + InstanceKlass* klass = cm->constants()->pool_holder(); + + unsigned int size_anchor = begin_pos; + // size place holder + write_u4((u4)MAGIC_NUMBER); + write_u4((u4)order); + + // write compilation type + u1 compilation_type = bci == -1 ? 0 : 1; + write_u1(compilation_type); + + // write method info + char* method_name = method->name()->as_C_string(); + write_string(method_name, strlen(method_name)); + char* method_sig = method->signature()->as_C_string(); + write_string(method_sig, strlen(method_sig)); + // first invoke init order + write_u4((u4)method->first_invoke_init_order()); + // bytecode size + write_u4((u4)cm->code_size()); + int method_hash = hashstr2((char *)(cm->code_base()), cm->code_size()); + write_u4((u4)method_hash); + write_u4((u4)bci); + + // write class info + char* class_name = klass->name()->as_C_string(); + Symbol* path_sym = klass->source_file_path(); + const char* path = NULL; + if (path_sym != NULL) { + path = path_sym->as_C_string(); + } else { + path = JVM_DEFINE_CLASS_PATH; + } + oop class_loader = klass->class_loader(); + const char* loader_name = NULL; + if (class_loader != NULL) { + loader_name = class_loader->klass()->name()->as_C_string(); + } else { + loader_name = "NULL"; + } + write_string(class_name, strlen(class_name)); + write_string(loader_name, strlen(loader_name)); + write_string(path, strlen(path)); + write_u4((u4)klass->bytes_size()); + write_u4((u4)klass->crc32()); + write_u4((u4)0x00); // class hash field is reserved, not used yet + + // method counters + if (mc!=NULL) { + write_u4((u4)mc->interpreter_invocation_count()); + write_u4((u4)mc->interpreter_throwout_count()); + write_u4((u4)mc->invocation_counter()->raw_counter()); + write_u4((u4)mc->backedge_counter()->raw_counter()); + } else { + log_warning(warmup)("[JitWarmUp] WARNING : method counter is NULL for method %s::%s %s", + class_name, method_name, method_sig); + write_u4((u4)0); + write_u4((u4)0); + write_u4((u4)0); + write_u4((u4)0); + } + + unsigned int end_pos = _pos; + unsigned int section_size = end_pos - begin_pos; + overwrite_u4(section_size, size_anchor); +} + +void ProfileRecorder::write_footer() { +} + +void ProfileRecorder::flush() { + MutexLockerEx mu(ProfileRecorder_lock); + if (!is_valid() || flushed()) { + return; + } + set_flushed(true); + + // open randomAccessFileStream + _logfile = new (ResourceObj::C_HEAP, mtInternal) randomAccessFileStream(logfile_name(), "wb+"); + if (_logfile == NULL || !_logfile->is_open()) { + log_error(warmup)("[JitWarmUp] ERROR : open log file error! path is %s", logfile_name()); + _state = IS_ERR; + return; + } + + // head section + write_header(); + // write class init section + write_inited_class(); + // write method profile info + for (int index = 0; index < dict()->table_size(); index++) { + for (ProfileRecorderEntry* entry = dict()->bucket(index); + entry != NULL; + entry = entry->next()) { + write_record(entry->literal(), entry->bci(), entry->order()); + } + } + // foot section + write_footer(); + + // set file size + overwrite_u4((u4)_pos, FILE_SIZE_OFFSET); + // set max symbol length + overwrite_u4((u4)_max_symbol_length, MAX_SYMBOL_LENGTH_OFFSET); + // compute and set file's crc32 + int crc32 = ProfileRecorder::compute_crc32(_logfile); + overwrite_u4((u4)crc32, CRC32_OFFSET); + + _logfile->flush(); + // close fd + delete _logfile; + _logfile = NULL; + + log_info(warmup)("[JitWarmUp] output profile info has done, file is %s", logfile_name()); +} + +// =========================== Preload Class Module ========================== // + +PreloadClassDictionary::PreloadClassDictionary(int size) + : Hashtable(size, sizeof(PreloadClassEntry)) { + // do nothing +} + +PreloadClassDictionary::~PreloadClassDictionary() { } + +PreloadClassEntry* PreloadClassDictionary::new_entry(Symbol* symbol) { + unsigned int hash = symbol->identity_hash(); + PreloadClassEntry* entry = (PreloadClassEntry*)Hashtable:: + new_entry(hash, symbol); + entry->init(); + return entry; +} + +// NYI +void PreloadClassDictionary::remove_entry(unsigned int hash_value, + unsigned int class_size, + unsigned int crc32, + Symbol* symbol) { + ShouldNotCallThis(); +} + +PreloadClassEntry* PreloadClassDictionary::find_entry(InstanceKlass* k) { + Symbol* name = k->name(); + Symbol* path = k->source_file_path(); + if (path == NULL) { + path = SymbolTable::new_symbol(JVM_DEFINE_CLASS_PATH, Thread::current()); + } + Symbol* loader_name = JitWarmUp::get_class_loader_name(k->class_loader_data()); + int hash = name->identity_hash(); + return find_entry(hash, name, loader_name, path); +} + +PreloadClassEntry* PreloadClassDictionary::find_entry(unsigned int hash_value, + Symbol* name, + Symbol* loader_name, + Symbol* path) { + int index = hash_to_index(hash_value); + for (PreloadClassEntry* p = bucket(index); p != NULL; p = p->next()) { + if (p->literal()->fast_compare(name) == 0 && + p->loader_name()->fast_compare(loader_name) == 0 && + p->path()->fast_compare(path) == 0) { + return p; + } + } + // not found + return NULL; +} + +PreloadClassEntry* PreloadClassDictionary::find_head_entry(unsigned int hash_value, + Symbol* name) { + int index = hash_to_index(hash_value); + for (PreloadClassEntry* p = bucket(index); p != NULL; p = p->next()) { + if (p->literal()->fast_compare(name) == 0) { + return p; + } + } + // not found + return NULL; +} + +PreloadClassHolder* PreloadClassDictionary::find_holder(unsigned int hash_value, + unsigned int class_size, + unsigned int crc32, + Symbol* symbol, + Symbol* loader_name, + Symbol* path) { + PreloadClassEntry* entry = find_entry(hash_value, symbol, loader_name, path); + assert(entry != NULL, "JitWarmUp log file error"); + return entry->find_holder_in_entry(class_size, crc32); +} + + +PreloadClassHolder* PreloadClassEntry::find_holder_in_entry(unsigned int size, + unsigned int crc32) { + for (PreloadClassHolder* p = this->head_holder(); p != NULL; p = p->next()) { + if (p->crc32() == crc32 && p->size() == size) { + return p; + } + } + // not found + return NULL; +} + +PreloadMethodHolder* PreloadMethodHolder::clone_and_add() { + PreloadMethodHolder* clone = new PreloadMethodHolder(*this); + clone->set_next(_next); + _next = clone; + return clone; +} + +PreloadClassEntry* PreloadClassDictionary::find_and_add_class_entry(unsigned int hash_value, + Symbol* name, + Symbol* loader_name, + Symbol* path, + int index) { + PreloadClassEntry* p = find_entry(hash_value, name, loader_name, path); + if (p == NULL) { + p = new_entry(name); + p->set_chain_offset(index); + p->set_loader_name(loader_name); + p->set_path(path); + add_entry(hash_to_index(hash_value), p); + } + return p; +} + +PreloadMethodHolder::PreloadMethodHolder(Symbol* name, Symbol* signature) + : _name(name), + _signature(signature), + _size(0), + _hash(0), + _intp_invocation_count(0), + _intp_throwout_count(0), + _invocation_count(0), + _backage_count(0), + _mounted_offset(-1), + _owns_md_list(true), + _is_deopted(false), + _next(NULL), + _resolved_method(NULL), + _md_list(new (ResourceObj::C_HEAP, mtClass) + GrowableArray(16, true, mtClass)) { + // do nothing +} + +PreloadMethodHolder::PreloadMethodHolder(PreloadMethodHolder& rhs) + : _name(rhs._name), + _signature(rhs._signature), + _size(rhs._size), + _hash(rhs._hash), + _intp_invocation_count(rhs._intp_invocation_count), + _intp_throwout_count(rhs._intp_throwout_count), + _invocation_count(rhs._invocation_count), + _backage_count(rhs._backage_count), + _mounted_offset(rhs._mounted_offset), + _owns_md_list(false), + _is_deopted(false), + _next(NULL), + _resolved_method(NULL), + _md_list(rhs._md_list) { +} + +PreloadMethodHolder::~PreloadMethodHolder() { + if (_owns_md_list) { + delete _md_list; + } +} + +bool PreloadMethodHolder::check_matching(Method* method) { + // NYI size and hash not used yet + if (name()->fast_compare(method->name()) == 0 + && signature()->fast_compare(method->signature()) == 0) { + return true; + } else { + return false; + } +} + +bool PreloadMethodHolder::is_alive(BoolObjectClosure* is_alive_closure) const { + if (_resolved_method == NULL) { + return false; + } + ClassLoaderData* data = _resolved_method->method_holder()->class_loader_data(); + return data->is_alive(is_alive_closure); +} + +PreloadClassHolder::PreloadClassHolder(Symbol* name, Symbol* loader_name, + Symbol* path, unsigned int size, + unsigned int hash, unsigned int crc32) + : _size(size), + _hash(hash), + _crc32(crc32), + _class_name(name), + _class_loader_name(loader_name), + _path(path), + _method_list(new (ResourceObj::C_HEAP, mtInternal) + GrowableArray(16, true, mtClass)), + _resolved(false), + _next(NULL) { + // do nothing +} + + +PreloadClassHolder::~PreloadClassHolder() { + delete _method_list; +} + +bool PreloadClassChain::PreloadClassChainEntry::is_all_initialized() { + int len = resolved_klasses()->length(); + if (len == 0) { + // no resolved klasses in this entry + return false; + } + for (int i = 0; i < len; i++) { + InstanceKlass* k = resolved_klasses()->at(i); + // Thread which invokes notify_application_startup_is_done might be potentially + // blocked for a long time if class initialization () gets executed + // for quite a while, e.g : reading from network. + // we use is_not_initialized() as condition here instead of !is_initialized() + // to alleviate this case. + if (k != NULL && k->is_not_initialized() && !k->is_in_error_state() ) { + return false; + } + } + return true; +} + +// warmup record methods when class is loaded, +// if class is redefined between class load and warmup compilation +// the recorded method oop is invalid, so we need check redefined +// class before warmup compilation +bool PreloadClassChain::PreloadClassChainEntry::has_redefined_class() { + int len = resolved_klasses()->length(); + for (int i = 0; i < len; i++) { + InstanceKlass* k = resolved_klasses()->at(i); + if (k != NULL && k->has_been_redefined()) { + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING: ignore redefined class after API" + " notifyApplicationStartUpIsDone : %s:%s@%s.", class_name()->as_C_string(), + loader_name()->as_C_string(), path()->as_C_string()); + return true; + } + } + return false; +} + +InstanceKlass* PreloadClassChain::PreloadClassChainEntry::get_first_uninitialized_klass() { + int len = resolved_klasses()->length(); + for (int i = 0; i < len; i++) { + InstanceKlass* k = resolved_klasses()->at(i); + if (k != NULL && k->is_not_initialized()) { + return k; + } + } + return NULL; +} + +PreloadMethodHolder* MethodHolderIterator::next() { + PreloadMethodHolder* next_holder = _cur->next(); + if (next_holder != NULL) { + _cur = next_holder; + return _cur; + } + while (_index > 0) { + _index--; + PreloadClassChain::PreloadClassChainEntry* entry = _chain->at(_index); + if (entry->method_holder() != NULL) { + _cur = entry->method_holder(); + return _cur; + } + } + _cur = NULL; + return _cur; +} + +PreloadClassChain::PreloadClassChain(unsigned int size) + : _inited_index(-1), + _loaded_index(-1), + _length(size), + _state(NOT_INITED), + _entries(new PreloadClassChainEntry[size]), + _holder(NULL), + _init_timestamp(), + _last_timestamp(), + _deopt_index(-1), + _deopt_cur_holder(NULL), + _has_unmarked_compiling_flag(false) { + _init_timestamp.update(); + _last_timestamp.update(); + state_trans_to(INITED); +} + +PreloadClassChain::~PreloadClassChain() { + delete[] _entries; +} + +bool PreloadClassChain::state_trans_to(ClassChainState new_state) { + ClassChainState old_state = current_state(); + if (old_state == new_state) { + log_warning(warmup)("JitWarmUp [WARNING]: warmup state already transfer to %d", new_state); + return true; + } + bool can_transfer = false; + switch (new_state) { + case ERROR_STATE: + if (old_state != WARMUP_DEOPTIMIZED) { + // almost all states could transfer to error state + // except jwarmup has done all work + can_transfer = true; + } + break; + default: + if (new_state == old_state + 1) { + can_transfer = true; + } + break; + } + if (can_transfer) { + if (Atomic::cmpxchg((jint)new_state, (jint*)&_state, (jint)old_state) == old_state) { + return true; + } else { + log_warning(warmup)("JitWarmUp [WARNING]: failed to transfer warmup state from %d to %d, conflict with other operation", old_state, new_state); + return false; + } + } else { + log_warning(warmup)("JitWarmUp [WARNING]: can not transfer warmup state from %d to %d", old_state, new_state); + return false; + } +} + +void PreloadClassChain::record_loaded_class(InstanceKlass* k) { + Symbol* class_name = k->name(); + unsigned int crc32 = k->crc32(); + unsigned int size = k->bytes_size(); + + // state check, if warmup compilation was done, no need to record class + if (!can_record_class()) { + return; + } + + PreloadClassEntry* class_entry = holder()->dict()->find_entry(k); + if (class_entry == NULL) { + // this class is NOT in jwarmup profile + return; + } + int chain_index = class_entry->chain_offset(); + PreloadClassHolder* holder = class_entry->find_holder_in_entry(size, crc32); + if (holder != NULL) { + if (holder->resolved()) { + // same class may be loaded by different class loaders, now we can not + // know which one is recorded in profile and should be warmup, the simple + // solution here is skip duplicated class + Thread* t = Thread::current(); + if (!t->in_super_class_resolving()) { + // just print a warning + assert(k->is_not_initialized(), "klass state error"); + assert(t->is_Java_thread(), "sanity check"); + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING : duplicate load class %s at index %d", + k->name()->as_C_string(), chain_index); + } + return; + } else { + // resolve methods in holder + MutexLockerEx mu(PreloadClassChain_lock); + int methods = k->methods()->length(); + for (int index = 0; index < methods; index++) { + Method* m = k->methods()->at(index); + resolve_method_info(m, holder); + } + { + ResourceMark rm; + log_debug(warmup)("[JitWarmUp] DEBUG : class %s at index %d is recorded", + k->name()->as_C_string(), chain_index); + } + holder->set_resolved(); + } + } else { + ResourceMark rm; + log_debug(warmup)("[JitWarmUp] DEBUG : class %s is not in profile", + k->name()->as_C_string()); + } + //set entry to loaded + { + // add loaded class to chain + MutexLockerEx mu(PreloadClassChain_lock); + assert(chain_index >= 0 && chain_index <= length(), "index out of bound"); + assert(loaded_index() >= inited_index(), "loaded index must larger than inited index"); + PreloadClassChainEntry* chain_entry = &_entries[chain_index]; + // check state + if (chain_entry->is_skipped()) { + // the loaded class is skipped to record + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING : loaded class %s at index %d is ignored", + k->name()->as_C_string(), chain_index); + return; + } else if (chain_entry->is_inited()) { + // warmup has compiled methods for this entry + return; + } + chain_entry->resolved_klasses()->append(k); + chain_entry->set_loaded(); + + // increase loaded index by chance + if (chain_index == loaded_index() + 1) { + update_loaded_index(chain_index); + } + } // end of MutexLocker guard +} + +void PreloadClassChain::mount_method_at(PreloadMethodHolder* mh, int index) { + assert(index >= 0 && index < length(), "out of bound"); + PreloadClassChainEntry* entry = &_entries[index]; + entry->add_method_holder(mh); +} + +void PreloadClassChain::update_loaded_index(int index) { + assert(index >= 0 && index < length(), "out of bound"); + // out of bound or entry is not loaded + while (index < length() && !_entries[index].is_not_loaded()) { + index++; + } + set_loaded_index(index - 1); +} + +void PreloadClassChain::compile_methodholders_queue(Stack& compile_queue) { + while (!compile_queue.is_empty()) { + PreloadMethodHolder* pmh = compile_queue.pop(); + compile_methodholder(pmh); + Thread* THREAD = Thread::current(); + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING: Exceptions happened in compiling %s", + pmh->name()->as_C_string()); + // ignore exception occurs during compilation + CLEAR_PENDING_EXCEPTION; + continue; + } + } +} + +void PreloadClassChain::warmup_impl() { + Thread* THREAD = Thread::current(); + if (!state_trans_to(WARMUP_COMPILING)) { + log_warning(warmup)("JitWarmUp [WARNING]: invalid state to begin compiling"); + return; + } + + /* iterate all PreloadClassChainEntry to submit warmup compilation*/ + bool cancel_warmup = false; + for ( int index = 0; index < length(); index++ ) { + if (cancel_warmup) { + break; + } + InstanceKlass* klass = NULL; + // the stack used for storing methods that need be compiled + Stack compile_queue; + { + MutexLockerEx mu(PreloadClassChain_lock); + PreloadClassChainEntry *entry = &_entries[index]; + switch(entry->state()) { + case PreloadClassChainEntry::_not_loaded: + // skip non-loaded class + entry->set_skipped(); + { + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING: ignore class because it is not loaded before warmup" + " : %s:%s@%s.", entry->class_name()->as_C_string(), + entry->loader_name()->as_C_string(), entry->path()->as_C_string()); + } + case PreloadClassChainEntry::_is_skipped: + break; + case PreloadClassChainEntry::_is_loaded: + // TODO: now only one klass is recorded in resolved_klasses, we can extend to initialize + // all recorded classes + // PreloadClassChain_lock is _safepoint_check_always, we can not initialize class here + // so defer initialization when mutex is out of scope + klass = entry->get_first_uninitialized_klass(); + entry->set_inited(); + case PreloadClassChainEntry::_is_inited: + if (!entry->has_redefined_class()){ + PreloadMethodHolder* mh = entry->method_holder(); + while (mh != NULL) { + compile_queue.push(mh); + mh = mh->next(); + } + } + break; + default: + { + ResourceMark rm; + log_error(warmup)("[JitWarmUp] ERROR: class %s has invalid entry state %d.", + entry->class_name()->as_C_string(), + entry->state()); + return; + } + } + } // end of mutex guard + // initialize loaded and uninitilaized klass + if (klass != NULL) { + // if exception occurs during initialization, just throws back to java code + assert(THREAD->is_Java_thread(), "sanity check"); + klass->initialize(THREAD); + if (HAS_PENDING_EXCEPTION) { + Symbol *loader = JitWarmUp::get_class_loader_name(klass->class_loader_data()); + ResourceMark rm; + log_error(warmup)("[JitWarmUp] ERROR: Exceptions happened in initializing %s being loaded by %s", + klass->name()->as_C_string(), loader->as_C_string()); + return; + } + } + { + MutexLockerEx mu(PreloadClassChain_lock); + // update loaded_index and inited_index + refresh_indexes(); + if (index > inited_index()) { + // we go beyond inited_index, can not continue + cancel_warmup = true; + } + } + // compile methods in compile_queue + compile_methodholders_queue(compile_queue); + } +} + +bool PreloadClassChain::compile_methodholder(PreloadMethodHolder* mh) { + Thread* t = Thread::current(); + methodHandle m(t, mh->resolved_method()); + if (m() == NULL || m->compiled_by_jwarmup() || m->has_compiled_code()) { + return false; + } + InstanceKlass* klass = m->constants()->pool_holder(); + // skip uninitialized klass + if (!klass->is_initialized()) { + return false; + } + + m->set_compiled_by_jwarmup(true); + // not deal with osr compilation + int bci = InvocationEntryBci; + bool ret = JitWarmUp::commit_compilation(m, bci, t); + if (ret) { + ResourceMark rm; + log_info(warmup)("[JitWarmUp] preload method %s success compiled", + m->name_and_sig_as_C_string()); + } + return ret; +} + +void PreloadClassChain::refresh_indexes() { + assert_lock_strong(PreloadClassChain_lock); + int loaded = loaded_index(); + int inited = inited_index(); + for (int i = inited + 1; i < length(); i++) { + PreloadClassChainEntry* e = &_entries[i]; + int len = e->resolved_klasses()->length(); + if (e->is_not_loaded()) { + assert(len == 0, "wrong state"); + } + if (e->is_loaded()) { + assert(len > 0, "class init chain entry state error"); + if (e->is_all_initialized()) { + e->set_inited(); + } + } + // refresh indexes + if (e->is_loaded() && i == loaded + 1) { + loaded = i; + } else if (e->is_inited() && i == inited + 1) { + loaded = i; // ???, loaded index may be reduced + inited = i; + } else if (e->is_skipped()) { + if (i == loaded + 1) { + loaded = i; + } + if (i == inited + 1) { + inited = i; + } + } else { + // break loop + break; + } + } // end of loop + assert(loaded >= inited, "loaded index must not less than inited index"); + set_loaded_index(loaded); + set_inited_index(inited); +} + +bool PreloadClassChain::should_deoptimize_methods() { + assert(CompilationWarmUp, "Sanity check"); + assert(SafepointSynchronize::is_at_safepoint(), "must be in safepoint"); + ClassChainState state = current_state(); + if (state == WARMUP_DEOPTIMIZED || state == ERROR_STATE) { + return false; + } + if (!CompilationWarmUpExplicitDeopt && CompilationWarmUpDeoptTime > 0) { + if (_init_timestamp.seconds() < CompilationWarmUpDeoptTime) { + return false; + } else if (state == WARMUP_DONE) { + state_trans_to(WARMUP_PRE_DEOPTIMIZE); + } else { + // do nothing + } + } + + if (current_state() != WARMUP_DEOPTIMIZING + && current_state() != WARMUP_PRE_DEOPTIMIZE) { + return false; + } + + Method* dummy_method = JitWarmUp::instance()->dummy_method(); + if (dummy_method == NULL || dummy_method->code() == NULL) { + // dummy method is not compiled + return false; + } + + // deoptimize interval + if (_last_timestamp.seconds() < CompilationWarmUpDeoptMinInterval) { + return false; + } + // if this safepoint doesn't allow the nested VMOperation, skip it + VM_Operation* op = VMThread::vm_operation(); + if (op != NULL && !op->allow_nested_vm_operations()) { + return false; + } + // length is too short, maybe log file corrupted + if (_length <= 1) { + return false; + } + return true; +} + +void PreloadClassChain::deopt_prologue() { + /* + * state transition: + * inside deopt_prologue, state should be WARMUP_PRE_DEOPTIMIZE or WARMUP_DEOPTIMIZING. + * If state is WARMUP_PRE_DEOPTIMIZE, we are first time to enter prologue, + * trans state to WARMUP_DEOPTIMIZING + * If state is WARMUP_DEOPTIMIZING, do nothing and return + */ + if (current_state() == WARMUP_PRE_DEOPTIMIZE) { + // first time we can start deoptimize warmup methods + if (state_trans_to(WARMUP_DEOPTIMIZING)) { + log_info(warmup)("JitWarmUp [INFO]: start deoptimize warmup methods"); + _deopt_index = length() - 1; + while (_deopt_index > 0 && _deopt_cur_holder == NULL) { + PreloadClassChain::PreloadClassChainEntry* entry = this->at(_deopt_index); + _deopt_cur_holder = entry->method_holder(); + _deopt_index--; + } + } else { + // we are in vm thread, no conflict with other threads + ShouldNotReachHere(); + } + } else { + guarantee(current_state() == WARMUP_DEOPTIMIZING, "invalid warmup state"); + } +} + +void PreloadClassChain::deopt_epilogue() { + state_trans_to(WARMUP_DEOPTIMIZED); + log_info(warmup)("JitWarmUp [INFO]: all warmup methods are deoptimized"); +} + +void PreloadClassChain::invoke_deoptimize_vmop() { + VM_Deoptimize op; + VMThread::execute(&op); +} + +void PreloadClassChain::deoptimize_methods() { + assert(SafepointSynchronize::is_at_safepoint(), "must be in safepoint"); + deopt_prologue(); + + Method* dummy_method = JitWarmUp::instance()->dummy_method(); + assert( dummy_method != NULL && dummy_method->code() != NULL, "dummy method must be compiled"); + int dummy_compile_id = dummy_method->code()->compile_id(); + + MethodHolderIterator iter(this, _deopt_cur_holder, _deopt_index); + int num = 0; + while (*iter != NULL) { + PreloadMethodHolder* pmh = *iter; + if (pmh->resolved_method() == NULL) { + iter.next(); + continue; + } + methodHandle m(pmh->resolved_method()); +#ifndef PRODUCT + m->set_deopted_by_jwarmup(true); +#endif + pmh->set_deopted(true); + if (m->code() != NULL && m->code()->compile_id() > dummy_compile_id) { + // the method is compiled after warmup, ignore it + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING : skip deoptimize %s because it is compiled after warmup", + m->name_and_sig_as_C_string()); + iter.next(); + continue; + } + int result = 0; + if (m->code() != NULL) { + m->code()->mark_for_deoptimization(); + result++; + } + result += CodeCache::mark_for_deoptimization(m()); + if (result > 0) { + ResourceMark rm; + log_warning(warmup)("[JitWarmUp] WARNING : deoptimize warmup method %s", + m->name_and_sig_as_C_string()); + num++; + } + iter.next(); + if (num == (int)CompilationWarmUpDeoptNumOfMethodsPerIter) { + break; + } + } + if (num > 0) { + invoke_deoptimize_vmop(); + } + + _last_timestamp.update(); + + // update index and cur_holder in PreloadClassChain + _deopt_index = iter.index(); + _deopt_cur_holder = *iter; + + if (*iter == NULL) { + deopt_epilogue(); + } +} + +void PreloadClassChain::do_unloading(BoolObjectClosure* is_alive) { + assert(SafepointSynchronize::is_at_safepoint(), "must be in safepoint"); + if (deopt_has_done()) { + return; + } + for (int i = 0; i < length(); i++) { + PreloadClassChainEntry* entry = this->at(i); + GrowableArray* array = entry->resolved_klasses(); + for (int i = 0; i < array->length(); i++) { + InstanceKlass* k = array->at(i); + if (k == NULL) { + continue; + } + // reset InstanceKlass pointer + ClassLoaderData* data = k->class_loader_data(); + if (!data->is_alive(is_alive)) { + array->remove_at(i); + } + } + for (PreloadMethodHolder* holder = entry->method_holder(); holder != NULL; + holder = holder->next()) { + if (holder->deopted()) { + continue; + } + if (!holder->is_alive(is_alive)) { + // reset Method pointer to NULL + holder->set_resolved_method(NULL); + } + } // end of method holder loop + } // end of entry loop +} + +PreloadMethodHolder* PreloadClassChain::resolve_method_info(Method* method, PreloadClassHolder* holder) { + // in runtime triggered by class load event + // FIX ME! O(n) search, should be binary search + // search for matched method holder that has minimal mounted_offset + PreloadMethodHolder* mh = NULL; + for (int i = 0; i < holder->method_list()->length(); i++) { + PreloadMethodHolder* current_mh = holder->method_list()->at(i); + if (current_mh->check_matching(method)) { + mh = current_mh; + break; + } + } // end of method_list loop + if (mh == NULL) { + // not found + return mh; + } else if (mh->resolved_method() == NULL) { + mh->set_resolved_method(method); + return mh; + } else { + // if the method has been resolved, it is likely same class are loaded again + // by different classloader. we clone a new holder for this method and + // append it to holder list + PreloadMethodHolder* new_holder = mh->clone_and_add(); + new_holder->set_resolved_method(method); + return new_holder; + } +} + +// JitWarmUp log parser +class JitWarmUpLogParser : CHeapObj { +public: + JitWarmUpLogParser(randomAccessFileStream* fs, PreloadJitInfo* holder); + virtual ~JitWarmUpLogParser(); + + bool valid(); + + bool parse_header(); + bool parse_class_init_section(); + + bool should_ignore_this_class(Symbol* s); + + // whether method record section parsing is finished + bool has_next(); + // parse a record from method record section + // return NULL means the record should be skipped + // or error occurred in parsing process + PreloadMethodHolder* next(); + + void inc_parsed_number() { _parsed_methods++; } + + int parsed_methods() { return _parsed_methods; } + int total_methods() { return _total_methods; } + + long file_size() { return _file_size; } + void set_file_size(long size) { _file_size = size; } + + int max_symbol_length() { return _max_symbol_length; } + + PreloadJitInfo* info_holder() { return _holder; } + void set_info_holder(PreloadJitInfo* holder) { _holder = holder; } + +private: + // disable default constructor + JitWarmUpLogParser(); + + bool _is_valid; + bool _has_parsed_header; + // current position of log file in parsing process + int _position; + // method count that successful parsed from log file + int _parsed_methods; + // method count recorded in log file + int _total_methods; + long _file_size; + randomAccessFileStream* _fs; + + int _max_symbol_length; + char* _parse_str_buf; + + PreloadJitInfo* _holder; + Arena* _arena; + + u1 read_u1(); + u4 read_u4(); + u8 read_u8(); + char* read_string(); +}; + +JitWarmUpLogParser::JitWarmUpLogParser(randomAccessFileStream* fs, PreloadJitInfo* holder) + : _is_valid(false), + _has_parsed_header(false), + _position(0), + _parsed_methods(0), + _total_methods(0), + _file_size(0), + _fs(fs), + _max_symbol_length(0), + _parse_str_buf(NULL), + _holder(holder), + _arena(new (mtInternal) Arena(mtInternal, 128)) { +} + +JitWarmUpLogParser::~JitWarmUpLogParser() { + // fileStream lifecycle is not managed by this class + delete _arena; +} + +char parse_int_buf[8]; +u1 JitWarmUpLogParser::read_u1() { + _fs->read(parse_int_buf, 1, 1); + _position += 1; + return *(u1*)parse_int_buf; +} + +u4 JitWarmUpLogParser::read_u4() { + _fs->read(parse_int_buf, 4, 1); + _position += 4; + return *(u4*)parse_int_buf; +} + +u8 JitWarmUpLogParser::read_u8() { + _fs->read(parse_int_buf, 8, 1); + _position += 8; + return *(u8*)parse_int_buf; +} + +char* JitWarmUpLogParser::read_string() { + int index = 0; + do { + _fs->read(_parse_str_buf + index, 1, 1); + index++; + } while (*(_parse_str_buf + index - 1) != '\0' + && index <= _max_symbol_length + 1); + + _position += index; + int len = index - 1; + if (len == 0) { + return NULL; + } else if (len > max_symbol_length()) { + log_error(warmup)("[JitWarmUp] ERROR : Parsed symbol length is longer than %d\n", max_symbol_length()); + return NULL; + } else { + char* str = NEW_RESOURCE_ARRAY(char, len + 1); + ::memcpy(str, _parse_str_buf, len + 1); + return str; + } +} + +#define MAX_COUNT_VALUE (1024 * 1024 * 128) + +#define LOGPARSER_ILLEGAL_STRING_CHECK(s, ret_value) \ + if (_position > end_pos) { \ + log_error(warmup)("[JitWarmUp] ERROR : read out of bound, " \ + "file format error"); \ + return ret_value; \ + } \ + if (s == NULL) { \ + _position = end_pos; \ + log_error(warmup)("[JitWarmUp] ERROR : illegal string in log file"); \ + return ret_value; \ + } + +#define LOGPARSER_ILLEGAL_COUNT_CHECK(cnt, ret_value) \ + if (_position > end_pos) { \ + log_error(warmup)("[JitWarmUp] ERROR : read out of bound, " \ + "file format error"); \ + return ret_value; \ + } \ + if ((u4)cnt > MAX_COUNT_VALUE) { \ + _position = end_pos; \ + log_error(warmup)("[JitWarmUp] ERROR : illegal count (" \ + UINT32_FORMAT ") too big", cnt); \ + return ret_value; \ + } + +bool JitWarmUpLogParser::should_ignore_this_class(Symbol* s) { + // FIXME deal with spring auto-generated + ResourceMark rm; + char* name = s->as_C_string(); + const char* CGLIB_SIG = "CGLIB$$"; + const char* ACCESSER_SUFFIX = "ConstructorAccess"; + if (::strstr(name, CGLIB_SIG) != NULL || + ::strstr(name, ACCESSER_SUFFIX) != NULL) { + return true; + } + JitWarmUp* jwp = info_holder()->holder(); + SymbolMatcher* matcher = jwp->excluding_matcher(); + if (matcher == NULL) { + return false; + } + return matcher->match(s); +} + +bool JitWarmUpLogParser::parse_header() { + int begin_pos = _position; + int end_pos = begin_pos + HEADER_SIZE; + u4 version_number = read_u4(); + u4 magic_number = read_u4(); + u4 file_size = read_u4(); + int crc32_recorded = (int)read_u4(); + u4 appid = read_u4(); + // valid version & magic number & file size + unsigned int version = JitWarmUp::instance()->version(); + if (version_number != version) { + _is_valid = false; + log_error(warmup)("[JitWarmUp] ERROR : Version not match, expect %d but %d", version, version_number); + return false; + } + if (magic_number != MAGIC_NUMBER + || (long)file_size != this->file_size()) { + _is_valid = false; + log_error(warmup)("[JitWarmUp] ERROR : illegal header"); + return false; + } + // valid appid + if (CompilationWarmUpAppID != 0 && CompilationWarmUpAppID != appid) { + _is_valid = false; + log_error(warmup)("[JitWarmUp] ERROR : illegal CompilationWarmUpAppID"); + return false; + } + // valid crc32 + int crc32_actual = ProfileRecorder::compute_crc32(_fs); + if (crc32_recorded != crc32_actual) { + _is_valid = false; + log_error(warmup)("[JitWarmUp] ERROR : log file crc32 check failure"); + return false; + } + + u4 max_symbol_length = read_u4(); + LOGPARSER_ILLEGAL_COUNT_CHECK(max_symbol_length, false); + _parse_str_buf = (char*)_arena->Amalloc(max_symbol_length + 2); + _max_symbol_length = (int)max_symbol_length; + + u4 record_count = read_u4(); + LOGPARSER_ILLEGAL_COUNT_CHECK(record_count, false); + _total_methods = record_count; + u4 utc_time = read_u8(); + _is_valid = true; + return true; +} + +#define CREATE_SYMBOL(char_name) \ + SymbolTable::new_symbol(char_name, strlen(char_name), Thread::current()) + +bool JitWarmUpLogParser::parse_class_init_section() { + ResourceMark rm; + int begin_pos = _position; + u4 section_size = read_u4(); + int end_pos = begin_pos + (int)section_size; + u4 cnt = read_u4(); + LOGPARSER_ILLEGAL_COUNT_CHECK(cnt, false); + + PreloadClassChain* chain = new PreloadClassChain(cnt); + info_holder()->set_chain(chain); + chain->set_holder(this->info_holder()); + + for (int i = 0; i < (int)cnt; i++) { + char* name_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(name_char, false); + char* loader_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(loader_char, false); + char* path_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(path_char, false); + Symbol* name = CREATE_SYMBOL(name_char); + Symbol* loader_name = CREATE_SYMBOL(loader_char); + Symbol* path = CREATE_SYMBOL(path_char); + loader_name = PreloadJitInfo::remove_meaningless_suffix(loader_name); + chain->at(i)->set_class_name(name); + chain->at(i)->set_loader_name(loader_name); + chain->at(i)->set_path(path); + // add to preload class dictionary + unsigned int hash_value = name->identity_hash(); + + PreloadClassEntry* e = info_holder()->dict()-> + find_and_add_class_entry(hash_value, name, loader_name, path, i); + + // e->chain_offset() < i : means same class symbol already existed in the chain + // should_ignore_this_class(name): means this class is in skipped list(build-in or user-defined) + // so set entry state is skipped, will be ignored in JitWarmUp + if (e->chain_offset() < i || should_ignore_this_class(name)) { + chain->at(i)->set_skipped(); + } else { + Symbol* name_no_suffix = PreloadJitInfo::remove_meaningless_suffix(name); + if (name_no_suffix->fast_compare(name) != 0) { + unsigned int hash_no_suffix = name_no_suffix->identity_hash(); + PreloadClassEntry* e_no_suffix = info_holder()->dict()-> + find_and_add_class_entry(hash_no_suffix, name_no_suffix, loader_name, path, i); + if (e_no_suffix->chain_offset() < i) { + chain->at(i)->set_skipped(); + } + } + } + } // end of for loop + + // check section size + if (_position - begin_pos != (int)section_size) { + log_error(warmup)("[JitWarmUp] ERROR : log file init section parse error."); + return false; + } + return true; +} + +bool JitWarmUpLogParser::valid() { + if(!_has_parsed_header) { + parse_header(); + } + return _is_valid; +} + +bool JitWarmUpLogParser::has_next() { + return _parsed_methods < _total_methods && _position < _file_size; +} + +PreloadMethodHolder* JitWarmUpLogParser::next() { + ResourceMark rm; + _fs->fseek(_position, SEEK_SET); + int begin_pos = _position; + u4 section_size = read_u4(); + int end_pos = begin_pos + section_size; + + u4 comp_order = read_u4(); + u1 compilation_type = read_u1(); + // 0 means standard compile + // 1 means OSR compile + if (compilation_type != 0 && compilation_type != 1) { + log_error(warmup)("[JitWarmUp] ERROR : illegal compilation type in log file"); + _position = end_pos; + return NULL; + } + // method info + char* method_name_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(method_name_char, NULL); + Symbol* method_name = CREATE_SYMBOL(method_name_char); + char* method_sig_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(method_sig_char, NULL); + Symbol* method_sig = CREATE_SYMBOL(method_sig_char); + u4 first_invoke_init_order = read_u4(); + // INVALID_FIRST_INVOKE_INIT_ORDER means no first_invoke_init_order record in log file, + // so put this method at last entry of class init chain + if ((int)first_invoke_init_order == INVALID_FIRST_INVOKE_INIT_ORDER) { + first_invoke_init_order = this->info_holder()->chain()->length() - 1; + } + u4 method_size = read_u4(); + u4 method_hash = read_u4(); + int32_t bci = (int32_t)read_u4(); + if (bci != InvocationEntryBci) { + LOGPARSER_ILLEGAL_COUNT_CHECK(bci, NULL); + } + + // class info + char* class_name_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(class_name_char, NULL); + Symbol* class_name = CREATE_SYMBOL(class_name_char); + // ignore + if (should_ignore_this_class(class_name)) { + _position = end_pos; + return NULL; + } + char* class_loader_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(class_loader_char, NULL); + Symbol* class_loader = CREATE_SYMBOL(class_loader_char); + class_loader = PreloadJitInfo::remove_meaningless_suffix(class_loader); + char* path_char = read_string(); + LOGPARSER_ILLEGAL_STRING_CHECK(path_char, NULL); + Symbol* path = CREATE_SYMBOL(path_char); + + PreloadClassDictionary* dict = this->info_holder()->dict(); + unsigned int dict_hash = class_name->identity_hash(); + PreloadClassEntry* entry = dict->find_head_entry(dict_hash, class_name); + if (entry == NULL) { + log_warning(warmup)("[JitWarmUp] WARNING : class %s is missed in init section", class_name_char); + _position = end_pos; + return NULL; + } + u4 class_size = read_u4(); + u4 class_crc32 = read_u4(); + u4 class_hash = read_u4(); + + // method counters info + u4 intp_invocation_count = read_u4(); + u4 intp_throwout_count = read_u4(); + u4 invocation_count = read_u4(); + u4 backedge_count = read_u4(); + + int class_chain_offset = entry->chain_offset(); + PreloadClassHolder* holder = entry->find_holder_in_entry(class_size, class_crc32); + if (holder == NULL) { + holder = new PreloadClassHolder(class_name, class_loader, path, class_size, class_hash, class_crc32); + entry->add_class_holder(holder); + } + PreloadMethodHolder* mh = new PreloadMethodHolder(method_name, method_sig); + mh->set_intp_invocation_count(intp_invocation_count); + mh->set_intp_throwout_count(intp_throwout_count); + mh->set_invocation_count(invocation_count); + mh->set_backage_count(backedge_count); + mh->set_bci((int)bci); + + mh->set_hash(method_hash); + mh->set_size(method_size); + + // add class init chain relation + /* + int method_chain_offset = static_cast(first_invoke_init_order) >= class_chain_offset ? first_invoke_init_order + : _holder->chain()->length() -1; + */ + int method_chain_offset = class_chain_offset; + mh->set_mounted_offset(method_chain_offset); + this->info_holder()->chain()->mount_method_at(mh, method_chain_offset); + holder->add_method(mh); + return mh; +} + +#undef MAX_SIZE_VALUE +#undef MAX_COUNT_VALUE +#undef LOGPARSER_ILLEGAL_STRING_CHECK +#undef LOGPARSER_ILLEGAL_COUNT_CHECK + +#undef CREATE_SYMBOL + +#define INIT_PRECLASS_INIT_SIZE 4*1024*1024 +#define PRELOAD_CLASS_HS_SIZE 10240 + +PreloadJitInfo::PreloadJitInfo() + : _dict(NULL), + _chain(NULL), + _loaded_count(0), + _state(NOT_INIT), + _holder(NULL), + _jvm_booted_is_done(false) { +} + +PreloadJitInfo::~PreloadJitInfo() { + delete _dict; + delete _chain; +} + +Symbol* PreloadJitInfo::remove_meaningless_suffix(Symbol* s) { + ResourceMark rm; + Thread* t = Thread::current(); + Symbol* result = s; + char* s_char = s->as_C_string(); + int len = (int)::strlen(s_char); + // first, remove $$+suffix + int i = 0; + for (i = 0; i < len - 1; i++) { + if (s_char[i] == '$' && s_char[i+1] == '$') { + break; + } + } + // has $$+suffix, remove it + if (i < len - 1) { + // example: s is $$123, convert to $ + i = i == 0 ? i = 1: i; + result = SymbolTable::new_symbol(s_char, i, t); + s_char = result->as_C_string(); + } + // second, remove number or $+number + len = (int)::strlen(s_char); + i = len - 1; + for (; i >= 0; i--) { + if (s_char[i] >= '0' && s_char[i] <= '9') { + continue; + } else if (s_char[i] == '$') { + continue; + } else { + break; + } + } + if (i != len - 1){ + i = i == -1 ? 0 : i; + result = SymbolTable::new_symbol(s_char, i + 1, t); + } + return result; +} + +void PreloadJitInfo::jvm_booted_is_done() { + _jvm_booted_is_done = true; + PreloadClassChain* chain = this->chain(); + assert(chain != NULL, "PreloadClassChain is NULL"); +} + +void PreloadClassChain::eager_load_class_in_constantpool() { + int index = 0; + int klass_index = 0; + while (true) { + InstanceKlass* current_k = NULL; + { + MutexLockerEx mu(PreloadClassChain_lock); + if (index == length()) { + break; + } + PreloadClassChain::PreloadClassChainEntry* e = this->at(index); + GrowableArray* array = e->resolved_klasses(); + assert(array != NULL, "should not be NULL"); + // skip not loaded entry + if (e->is_skipped() || e->is_not_loaded() || klass_index >= array->length()) { + index++; + // reset index + klass_index = 0; + continue; + } + current_k = array->at(klass_index); + } // end of Mutex guard + + if (current_k != NULL) { + current_k->constants()->preload_jwarmup_classes(Thread::current()); + } + klass_index++; + } // end of while +} + +void PreloadJitInfo::notify_application_startup_is_done() { + PreloadClassChain *chain = this->chain(); + assert(chain != NULL, "PreloadClassChain is NULL"); + chain->state_trans_to(PreloadClassChain::PRE_WARMUP); + + // 1st, eager load classes, do eager initialize just once + log_info(warmup)("JitWarmUp [INFO]: start eager loading classes from constant pool"); + chain->eager_load_class_in_constantpool(); + + // 2nd, warmup compilation + log_info(warmup)("JitWarmUp [INFO]: start warmup compilation"); + chain->warmup_impl(); + Thread *THREAD = Thread::current(); + // if exception occurs in warmup compilation, return and throw + if (HAS_PENDING_EXCEPTION) { + return; + } + + // 3rd, commit dummy method for compilation, check dummy method to know warmup compilation is completed + JitWarmUp *jwp = this->holder(); + Method *dm = jwp->dummy_method(); + guarantee(dm->code() == NULL, "dummy method has been compiled unexceptedly!"); + methodHandle mh(THREAD, dm); + JitWarmUp::commit_compilation(mh, InvocationEntryBci, THREAD); + if (!chain->state_trans_to(PreloadClassChain::WARMUP_DONE)) { + // warmup has been marked as WARMUP_DONE, but compilation requests are still on + // going. warmup is really done when dummy method is compiled, check the dummy + // method to determine warmup is done + log_error(warmup)("JitWarmUp [ERROR]: can not change state to WARMUP_DONE"); + } else { + log_info(warmup)("JitWarmUp [INFO]: warmup compilation is done"); + } +} + +bool PreloadJitInfo::should_load_class_eagerly(Symbol* s) { + // check black list + SymbolMatcher* matcher = holder()->excluding_matcher(); + if (matcher != NULL && matcher->match(s)) { + return false; + } + int hash = s->identity_hash(); + PreloadClassEntry* e = dict()->find_head_entry(hash, s); + // not in JitWarmUp log file + if (e == NULL) { + return false; + } + if (!CompilationWarmUpResolveClassEagerly) { + // check whether has been loaded + int offset = e->chain_offset(); + PreloadClassChain::PreloadClassChainEntry* entry = chain()->at(offset); + return entry->is_not_loaded(); + } else { + return true; + } +} + +bool PreloadJitInfo::resolve_loaded_klass(InstanceKlass* k) { + if (k == NULL) { return false; } + // has loaded before + if (k->is_jwarmup_recorded()) { + return false; + } + { + MutexLockerEx mu(PreloadClassChain_lock); + // JitWarmUp compilation is done + if (!chain()->can_record_class()) { + return false; + } + } + // record in PreloadClassChain + // set flag before actually invoking record function due to + // the record_loaded_class may trigger recursive + // class recording + k->set_jwarmup_recorded(true); + chain()->record_loaded_class(k); + return true; +} + +class RandomFileStreamGuard : StackObj { +public: + RandomFileStreamGuard(randomAccessFileStream* fs) + : _fs(fs) { + } + + ~RandomFileStreamGuard() { delete _fs; } + + randomAccessFileStream* operator ->() const { return _fs; } + randomAccessFileStream* operator ()() const { return _fs; } + +private: + randomAccessFileStream* _fs; +}; + +void PreloadJitInfo::init() { + if (CompilationWarmUpRecording) { + log_error(warmup)("[JitWarmUp] ERROR: you can not set both CompilationWarmUp and CompilationWarmUpRecording"); + _state = IS_ERR; + return; + } + // check class data sharing + if (UseSharedSpaces) { + log_error(warmup)("[JitWarmUp] ERROR: flag UseSharedSpaces must be off"); + _state = IS_ERR; + return; + } + _dict = new PreloadClassDictionary(PRELOAD_CLASS_HS_SIZE); + _loaded_count = 0; // init count + _state = IS_OK; // init state + + // parse log + if (CompilationWarmUpLogfile == NULL) { + _state = IS_ERR; + return; + } + + RandomFileStreamGuard fsg(new (ResourceObj::C_HEAP, mtInternal) randomAccessFileStream( + CompilationWarmUpLogfile, "rb+")); + JitWarmUpLogParser parser(fsg(), this); + if (!fsg->is_open()) { + log_error(warmup)("[JitWarmUp] ERROR : log file %s doesn't exist", CompilationWarmUpLogfile); + _state = IS_ERR; + return; + } + parser.set_file_size(fsg->fileSize()); + // parse header section + if (!parser.parse_header()) { + // not valid log file format + _state = IS_ERR; + return; + } + // parse class init section + if (!parser.parse_class_init_section()) { + // invalid log file format + _state = IS_ERR; + return; + } + while (parser.has_next()) { + PreloadMethodHolder* holder = parser.next(); + if (holder != NULL) { + //successfully parsed from log file + ++_loaded_count; + } + parser.inc_parsed_number(); + } +} diff --git a/src/share/vm/jwarmup/jitWarmUp.hpp b/src/share/vm/jwarmup/jitWarmUp.hpp new file mode 100644 index 000000000..c802156de --- /dev/null +++ b/src/share/vm/jwarmup/jitWarmUp.hpp @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + + +#ifndef SHARED_VM_JWARMUP_JITWARMUP_HPP +#define SHARED_VM_JWARMUP_JITWARMUP_HPP + +#include "code/codeBlob.hpp" +#include "libadt/dict.hpp" +#include "memory/allocation.hpp" +#include "oops/klass.hpp" +#include "oops/method.hpp" +#include "oops/methodCounters.hpp" +#include "oops/methodData.hpp" +#include "runtime/atomic.hpp" +#include "runtime/timer.hpp" +#include "runtime/mutexLocker.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/hashtable.hpp" +#include "utilities/linkedlist.hpp" +#include "utilities/ostream.hpp" +#include "utilities/symbolMatcher.hpp" + +// forward +class ProfileRecorder; +class PreloadJitInfo; + +#define INVALID_FIRST_INVOKE_INIT_ORDER -1 + +class JitWarmUp : public CHeapObj { +protected: + JitWarmUp(); + virtual ~JitWarmUp(); + +public: + enum JitWarmUpState { + NOT_INIT = 0, + IS_OK = 1, + IS_ERR = 2 + }; + + bool is_valid() { return _state == JitWarmUp::IS_OK; } + unsigned int version() { return _version; } + + // init in VM startup + void init(); + // init for CompilationWarmUpRecording model + JitWarmUpState init_for_recording(); + // init for CompilationWarmUp model + JitWarmUpState init_for_warmup(); + + static JitWarmUp* create_instance(); + static JitWarmUp* instance() { return _instance; } + + ProfileRecorder* recorder() { return _recorder; } + PreloadJitInfo* preloader() { return _preloader; } + + SymbolMatcher* excluding_matcher() { return _excluding_matcher; } + + void set_dummy_method(Method* m) { _dummy_method = m; } + Method* dummy_method() { return _dummy_method; } + + // write log file for JitWarmUpRecording model + JitWarmUpState flush_logfile(); + + // commit a compilation task + static bool commit_compilation(methodHandle m, int bci, TRAPS); + + // get loader name through ClassLoaderData, if ClassLoader is + // bootstrap classloader, return "NULL" + static Symbol* get_class_loader_name(ClassLoaderData* cld); + +private: + // singleton holder + static JitWarmUp* _instance; + + JitWarmUpState _state; + unsigned int _version; // JitWarmUp version, for verify logfile + Method* _dummy_method; + ProfileRecorder* _recorder; + PreloadJitInfo* _preloader; + SymbolMatcher* _excluding_matcher; +}; + +// =========================== Profile Recorder Module ========================== // + +// this hashtable stores method profile info that will be +// record into log file +class ProfileRecorderEntry : public HashtableEntry { +public: + ProfileRecorderEntry() { } + virtual ~ProfileRecorderEntry() { } + + // not use ctor function because Hashtable::new_entry is C-style design + // It don't call ctor function when Hashtable constructing a entry + void init() { + _is_deopted = false; + _bci = InvocationEntryBci; + } + + void set_bci(int bci) { _bci = bci; } + int bci() { return _bci; } + + void set_order(int order) { _order = order; } + int order() { return _order; } + + ProfileRecorderEntry* next() { + return (ProfileRecorderEntry*)HashtableEntry::next(); + } + +private: + bool _is_deopted; // not used + int _bci; // bci of compilation task, + // InvocationEntryBci means standard compilation + // other means OSR compilation + int _order; // compilation order +}; + +// a hash table stores compiled method +class ProfileRecordDictionary : public Hashtable { + friend class VMStructs; + friend class JitWarmUp; +public: + ProfileRecordDictionary(unsigned int size); + virtual ~ProfileRecordDictionary(); + + // add method into dictionary + // return entry that holds this method + ProfileRecorderEntry* add_method(unsigned int hash, Method* method, int bci); + // find a method in the dictionary + // if not found, return NULL + ProfileRecorderEntry* find_entry(unsigned int hash, Method* method); + + void free_entry(ProfileRecorderEntry* entry); + + unsigned int count() { return _count; } + + // NYI + void print(); + // virtual void print_on(outputStream* st) const; + + ProfileRecorderEntry* bucket(int i) { + return (ProfileRecorderEntry*)Hashtable::bucket(i); + } + +private: + unsigned int _count; // entry count + // create an entry for a given method + ProfileRecorderEntry* new_entry(unsigned int hash, Method* method); +}; + +// This entry used in the list that records class symbol +class ClassSymbolEntry { +private: + Symbol* _class_name; + Symbol* _class_loader_name; + Symbol* _path; +public: + ClassSymbolEntry(Symbol* class_name, Symbol* class_loader_name, Symbol* path) + : _class_name(class_name), + _class_loader_name(class_loader_name), + _path(path) { + if (_class_name != NULL) _class_name->increment_refcount(); + if (_class_loader_name != NULL) _class_loader_name->increment_refcount(); + if (_path != NULL) _path->increment_refcount(); + } + + ClassSymbolEntry() + : _class_name(NULL), + _class_loader_name(NULL), + _path(NULL) { + } + + ~ClassSymbolEntry() { + if (_class_name != NULL) _class_name->decrement_refcount(); + if (_class_loader_name != NULL) _class_loader_name->decrement_refcount(); + if (_path != NULL) _path->decrement_refcount(); + } + + Symbol* class_name() const { return _class_name; } + Symbol* class_loader_name() const { return _class_loader_name; } + Symbol* path() const { return _path; } + + // Necessary for LinkedList + bool equals(const ClassSymbolEntry& rhs) const { + return _class_name == rhs._class_name; + } +}; + +// Profiling data collection +// record compiled method in a hash table(class ProfileRecorder) +// record java class initialization order in a linkedlist(class LinkedListImpl) +class ProfileRecorder : public CHeapObj { +public: + ProfileRecorder(); + virtual ~ProfileRecorder(); + + void init(); + unsigned int recorded_count() { return _dict->count(); } + ProfileRecordDictionary* dict() { return _dict; } + + JitWarmUp* holder() { return _holder; } + void set_holder(JitWarmUp* h) { _holder = h; } + + unsigned int flushed() { return _flushed; } + void set_flushed(bool value) { _flushed = value; } + + const char* logfile_name() { return _logfile_name; } + void set_logfile_name(const char* name) { _logfile_name = name; } + + LinkedListImpl* + class_init_list() { return _class_init_list; } + + int class_init_count() { return _class_init_order_count + 1; } + + // add a method into recorder + void add_method(Method* method, int bci); + // remove a method from recorder + void remove_method(Method* method); + + // flush collected information into log file + void flush(); + + // increment class initialize count + // class recorded and increase order number, returns the increased number. + // this function is thread safe + int assign_class_init_order(InstanceKlass* klass); + + // current class init order count and its address. + address current_init_order_addr() { return (address)&_class_init_order_count; } + + unsigned int compute_hash(Method* method) { + uint64_t m_addr = (uint64_t)method; + return (m_addr >> 3) * 2654435761UL; // Knuth multiply hash + } + + bool is_valid() { return _state == IS_OK; } + + static int compute_crc32(randomAccessFileStream* fs); + +private: + enum RecorderState { + IS_OK = 0, + IS_ERR = 1, + NOT_INIT = 2 + }; + + JitWarmUp* _holder; + // output stream + randomAccessFileStream* _logfile; + // position of log file in flush() + unsigned int _pos; + RecorderState _state; + // linked list that stores orderly initialization info of java classes + LinkedListImpl* _class_init_list; + LinkedListNode* _init_list_tail_node; + // hash table that stores compiled methods + ProfileRecordDictionary* _dict; + // counter of class initialization order + volatile int _class_init_order_count; + volatile bool _flushed; + // log file name + const char* _logfile_name; + // record max symbol length in log file + int _max_symbol_length; + +private: + // flush section + void write_header(); + void write_inited_class(); + void write_record(Method* method, int bci, int order); + void write_footer(); + + void write_u1(u1 value); + void write_u4(u4 value); + void write_u8(u8 value); + void write_string(const char* src); + void write_string(const char* src, size_t len); + + void overwrite_u4(u4 value, unsigned int offset); + + void update_max_symbol_length(int len); +}; + +// =========================== Preload Class Module ========================== // + +#define CLASSCACHE_HASHTABLE_SIZE 10240 + +// forward declare +class ProfileRecorder; +class PreloadClassHolder; + +// a MDRecordInfo corresponds a ProfileData per bci (see oops/methodData.hpp) +// NYI about details +class MDRecordInfo : public CHeapObj { +public: + // empty class + MDRecordInfo() { } + ~MDRecordInfo() { } +}; + +// a method holder corresponds a method and its profile information +class PreloadMethodHolder : public CHeapObj { + friend class PreloadClassHolder; +public: + PreloadMethodHolder(Symbol* name, Symbol* signature); + PreloadMethodHolder(PreloadMethodHolder& rhs); + virtual ~PreloadMethodHolder(); + + Symbol* name() const { return _name; } + Symbol* signature() const { return _signature; } + + unsigned int intp_invocation_count() const { return _intp_invocation_count; } + unsigned int intp_throwout_count() const { return _intp_throwout_count; } + unsigned int invocation_count() const { return _invocation_count; } + unsigned int backage_count() const { return _backage_count; } + + void set_intp_invocation_count(unsigned int value) { _intp_invocation_count = value; } + void set_intp_throwout_count(unsigned int value) { _intp_throwout_count = value; } + void set_invocation_count(unsigned int value) { _invocation_count = value; } + void set_backage_count(unsigned int value) { _backage_count = value; } + + unsigned int hash() const { return _hash; } + unsigned int size() const { return _size; } + int bci() const { return _bci; } + int mounted_offset() const { return _mounted_offset; } + + void set_hash(unsigned int value) { _hash = value; } + void set_size(unsigned int value) { _size = value; } + void set_bci(int value) { _bci = value; } + void set_mounted_offset(int value) { _mounted_offset = value; } + + bool deopted() const { return _is_deopted; } + void set_deopted(bool value) { _is_deopted = value; } + + // check for size/hash + bool check_matching(Method* method); + + PreloadMethodHolder* next() const { return _next; } + void set_next(PreloadMethodHolder* h) { _next = h; } + + Method* resolved_method() const { return _resolved_method; } + void set_resolved_method(Method* m) { _resolved_method = m; } + + GrowableArray* md_list() const { return _md_list; } + void set_md_list(GrowableArray* value) { _md_list = value; } + + PreloadMethodHolder* clone_and_add(); + + // whether the resolved method is alive + bool is_alive(BoolObjectClosure* is_alive_closure) const; + +private: + // below members are parsed from log file + Symbol* _name; + Symbol* _signature; + + unsigned int _size; + unsigned int _hash; + int _bci; + + unsigned int _intp_invocation_count; + unsigned int _intp_throwout_count; + unsigned int _invocation_count; + unsigned int _backage_count; + + int _mounted_offset; + + // whether md_list array is owned by this + bool _owns_md_list; + + // whether resolved method has been deoptimized by JitWarmUp + bool _is_deopted; + + // A single LinkedList store entries for same init order + PreloadMethodHolder* _next; + // resolved method in holder's list + Method* _resolved_method; + // profile info array, shared between same PreloadMethodHolder + GrowableArray* _md_list; +}; + +// a class holder corresponds a java class +class PreloadClassHolder : public CHeapObj { +public: + PreloadClassHolder(Symbol* name, Symbol* loader_name, + Symbol* path, unsigned int size, + unsigned int hash, unsigned int crc32); + virtual ~PreloadClassHolder(); + + void add_method(PreloadMethodHolder* mh) { + assert(_method_list != NULL, "not initialize"); + _method_list->append(mh); + } + + unsigned int size() const { return _size; } + unsigned int hash() const { return _hash; } + unsigned int crc32() const { return _crc32; } + unsigned int methods_count() const { return _method_list->length(); } + Symbol* class_name() const { return _class_name; } + Symbol* class_loader_name() const { return _class_loader_name; } + Symbol* path() const { return _path; } + PreloadClassHolder* next() const { return _next; } + bool resolved() const { return _resolved; } + + void set_resolved() { _resolved = true; } + void set_next(PreloadClassHolder* h) { _next = h; } + + GrowableArray* method_list() const { return _method_list; } + +private: + // parsed from log file + unsigned int _size; + unsigned int _hash; + unsigned int _crc32; + Symbol* _class_name; + Symbol* _class_loader_name; + Symbol* _path; + // method holder list + GrowableArray* _method_list; + // if the holder is resolved + bool _resolved; + + unsigned int _class_init_chain_index; + // next class holder that has same class name + PreloadClassHolder* _next; +}; + +// a class entry corresponds a java class name symbol +// it mounts a list of PreloadClassHolder which has same name +class PreloadClassEntry : public HashtableEntry { + friend class PreloadJitInfo; +public: + PreloadClassEntry(PreloadClassHolder* holder) + : _head_holder(holder), + _chain_offset(-1), + _loader_name(NULL), + _path(NULL) { + // do nothing + } + + PreloadClassEntry() + : _head_holder(NULL), + _chain_offset(-1), + _loader_name(NULL), + _path(NULL) { + } + + virtual ~PreloadClassEntry() { } + + void init() { + _head_holder = NULL; + _chain_offset = -1; + _loader_name = NULL; + _path = NULL; + } + + PreloadClassHolder* head_holder() { return _head_holder; } + void set_head_holder(PreloadClassHolder* h) { _head_holder = h; } + + int chain_offset() { return _chain_offset; } + void set_chain_offset(int offset) { _chain_offset = offset; } + + Symbol* loader_name() { return _loader_name; } + void set_loader_name(Symbol* s) { _loader_name = s; } + Symbol* path() { return _path; } + void set_path(Symbol* s) { _path = s; } + + + PreloadClassEntry* next() { + return (PreloadClassEntry*)HashtableEntry::next(); + } + + // an entry has a chain of class holder + void add_class_holder(PreloadClassHolder* h) { + h->set_next(_head_holder); + _head_holder = h; + } + + // find class holder with specified size&crc32 + // if not found, return NULL + PreloadClassHolder* find_holder_in_entry(unsigned int size, unsigned int crc32); + +private: + PreloadClassHolder* _head_holder; // head node of holder list + int _chain_offset; // chain index is initialization order of this class, used in class PreloadClassChain + Symbol* _loader_name; // classloader name + Symbol* _path; // class file path +}; + +// a hash table stores PreloadClassEntrys that parsed from log file +// +// PreloadClassDictionary extend Hashtable +// | +// + -- ClassEntry: +- ClassHolder +- class name +// . +- size +// . +- crc32 +// . +- MethodHolder list +// . | +// . +- MDRecordInfo list +// . +// +- ClassHolder .. +// +class PreloadClassDictionary : public Hashtable { +public: + PreloadClassDictionary(int size); + virtual ~PreloadClassDictionary(); + + // remove a class entry + void remove_entry(unsigned int hash_value, unsigned int class_size, + unsigned int crc32, Symbol* symbol); + + // find a class entry with name, classloader and path + PreloadClassEntry* find_entry(unsigned int hash_value, Symbol* name, + Symbol* loader_name, Symbol* path); + + // find the first class entry with the given class name + PreloadClassEntry* find_head_entry(unsigned int hash_value, Symbol* name); + + PreloadClassEntry* find_entry(InstanceKlass* k); + + // find a specified class holder + PreloadClassHolder* find_holder(unsigned int hash_value, unsigned int class_size, + unsigned int crc32, Symbol* name, + Symbol* loader_name, Symbol* path); + + PreloadClassEntry* bucket(int i) { + return (PreloadClassEntry*)Hashtable::bucket(i); + } + + // find or create a class entry, if not found, new a entry and return it + PreloadClassEntry* find_and_add_class_entry(unsigned int hash_value, Symbol* symbol, + Symbol* loader_name, Symbol* path, + int order); + +private: + // new entry + PreloadClassEntry* new_entry(Symbol* symbol); +}; + +class PreloadClassChain; + +// traverse MethodHolder from end to begin in PreloadClassChain +class MethodHolderIterator { +public: + MethodHolderIterator() + : _chain(NULL), + _cur(NULL), + _index(-1) { + } + + MethodHolderIterator(PreloadClassChain* chain, PreloadMethodHolder* holder, int index) + : _chain(chain), + _cur(holder), + _index(index) { + } + + ~MethodHolderIterator() { /* do nothing */ } + + PreloadMethodHolder* operator*() { return _cur; } + + int index() { return _index; } + + bool initialized() { return _chain != NULL; } + + PreloadMethodHolder* next(); + +private: + PreloadClassChain* _chain; + PreloadMethodHolder* _cur; + int _index; // current holder's position in PreloadClassChain +}; + +// record java class load event +// forcely initialize class in order(from jwarmup log file) +// PreloadClassChain member layout & force initialization diagram +// +// +// state -------- | PreloadClassChainEntry : +-----------------+ +// v | | class name | +// ----- | | state | +// head entry -> | 3 | <-------------+ | method list | +// | 3 | | InstanceKlass* | +// | 3 | +-----------------+ +// | 3 | <- | method(compiled) | <- | method(compiled) | +// | 1 | +// init_index -> | 3 | <- | method(compilable) | +// | 2 | +// load_index -> | 2 | +// | 0 | +// | 2 | <- | method(not compiled) | <- | method(not compiled) | +// | 3 | +// . +// . +// | 0 | +// | 2 | +// | 0 | +// | 0 | +// | 0 | +// ----- +// Entry state: 0 : class is NOT loaded +// 1 : entry skipped +// 2 : class has been loaded +// 3 : class has been initialized +// Compiling methods list which begins the entry at index i +// need ensure that all entries before index i have been either initialized or skipped +// +class PreloadClassChain : public CHeapObj { +public: + class PreloadClassChainEntry : public CHeapObj { + public: + enum InitState { + _not_loaded = 0, + _is_skipped, + _is_loaded, + _is_inited + }; + + PreloadClassChainEntry() + : _class_name(NULL), + _loader_name(NULL), + _path(NULL), + _state(_not_loaded), + _method_holder(NULL), + _resolved_klasses(new (ResourceObj::C_HEAP, mtClass) + GrowableArray(1, true, mtClass)) { } + + PreloadClassChainEntry(Symbol* class_name, Symbol* loader_name, Symbol* path) + : _class_name(class_name), + _loader_name(loader_name), + _path(path), + _state(_not_loaded), + _method_holder(NULL), + _resolved_klasses(new (ResourceObj::C_HEAP, mtClass) + GrowableArray(1, true, mtClass)) { } + + virtual ~PreloadClassChainEntry() { } + + Symbol* class_name() const { return _class_name; } + Symbol* loader_name() const { return _loader_name; } + Symbol* path() const { return _path; } + void set_class_name(Symbol* name) { _class_name = name; } + void set_loader_name(Symbol* name) { _loader_name = name; } + void set_path(Symbol* path) { _path = path; } + + GrowableArray* resolved_klasses() + { return _resolved_klasses; } + + // entry state + bool is_not_loaded() const { return _state == _not_loaded; } + bool is_skipped() const { return _state == _is_skipped; } + bool is_loaded() const { return _state == _is_loaded; } + bool is_inited() const { return _state == _is_inited; } + void set_not_loaded() { _state = _not_loaded; } + void set_skipped() { _state = _is_skipped; } + void set_loaded() { _state = _is_loaded; } + void set_inited() { _state = _is_inited; } + + int state() { return _state; } + + void add_method_holder(PreloadMethodHolder* h) { + h->set_next(_method_holder); + _method_holder = h; + } + + bool is_all_initialized(); + + // check if any resolved classes has been redefined before warmup compilation + bool has_redefined_class(); + + InstanceKlass* get_first_uninitialized_klass(); + + PreloadMethodHolder* method_holder() { return _method_holder; } + + private: + Symbol* _class_name; + Symbol* _loader_name; + Symbol* _path; + int _state; + PreloadMethodHolder* _method_holder; + GrowableArray* _resolved_klasses; + }; + + PreloadClassChain(unsigned int size); + virtual ~PreloadClassChain(); + + enum ClassChainState { + NOT_INITED = 0, + INITED = 1, + PRE_WARMUP = 2, + WARMUP_COMPILING = 3, + WARMUP_DONE = 4, + WARMUP_PRE_DEOPTIMIZE = 5, + WARMUP_DEOPTIMIZING = 6, + WARMUP_DEOPTIMIZED = 7, + ERROR_STATE = 8 + }; + bool state_trans_to(ClassChainState new_state); + ClassChainState current_state() { return _state; } + + int inited_index() const { return _inited_index; } + int loaded_index() const { return _loaded_index; } + int length() const { return _length; } + + void set_inited_index(int index) { _inited_index = index; } + void set_loaded_index(int index) { _loaded_index = index; } + void set_length(int length) { _length = length; } + + bool has_unmarked_compiling_flag() { return _has_unmarked_compiling_flag; } + void set_has_unmarked_compiling_flag(bool value) { _has_unmarked_compiling_flag = value; } + + PreloadJitInfo* holder() { return _holder; } + void set_holder(PreloadJitInfo* preloader) { _holder = preloader; } + + bool can_do_initialize() { + return _state == PRE_WARMUP; + } + + bool notify_deopt_signal() { + return state_trans_to(WARMUP_PRE_DEOPTIMIZE); + } + + // recording loaded class is only doable before warmup compilation + bool can_record_class() { + return _state == INITED || _state == PRE_WARMUP || _state == WARMUP_COMPILING; + } + + bool deopt_has_started() { + return _state == WARMUP_DEOPTIMIZING || _state == WARMUP_DEOPTIMIZED; + } + + bool deopt_has_done() { + return _state == WARMUP_DEOPTIMIZED; + } + + // record java class load event, + // update indexes and do class loading eagerly + void record_loaded_class(InstanceKlass* klass); + + PreloadClassChainEntry* at(int index) { return &_entries[index]; } + + // refresh indexes and entry's state + void refresh_indexes(); + + // iterate preload class chain and submit warmup compilation requests + void warmup_impl(); + + // mount method + void mount_method_at(PreloadMethodHolder* mh, int index); + + // a PreloadMethodHolder represents a java method + bool compile_methodholder(PreloadMethodHolder* mh); + + // fix InstanceKlass* and Method* pointer during metaspace gc + void do_unloading(BoolObjectClosure* is_alive); + + // proactive deoptimize methods in safepoint after CompilationWarmUpDeoptTime second + void deopt_prologue(); + void deopt_epilogue(); + + bool should_deoptimize_methods(); + + // deoptimize CompilationWarmUpDeoptNumOfMethodsPerIter number methods per invocation + void deoptimize_methods(); + + // invoke a VM_Deoptimize operation + void invoke_deoptimize_vmop(); + + // load class eagerly that occurs in JitWarmUp log file through constant-pool traversal + void eager_load_class_in_constantpool(); + + // for debug + void print_not_loaded_before(int index); + void print_method_mount_before(int index); + +private: + // left of _inited_index(contain it) has been initialization + int _inited_index; + // left of _loaded_index(contain it) has been loaded + int _loaded_index; + // length of entries + int _length; + // state of PreloadClassChain + volatile ClassChainState _state; + + PreloadClassChainEntry* _entries; + + PreloadJitInfo* _holder; + + // method deoptimization support + TimeStamp _init_timestamp; // timestamp of deoptimization beginning + TimeStamp _last_timestamp; // timestamp of last deoptimization task + + int _deopt_index; + PreloadMethodHolder* _deopt_cur_holder; + + bool _has_unmarked_compiling_flag; + + void compile_methodholders_queue(Stack& compile_queue); + + // update _loaded_index + void update_loaded_index(int index); + + // assign profile info to this method + PreloadMethodHolder* resolve_method_info(Method* method, + PreloadClassHolder* holder); +}; + +// This class about preload & compile method from +// log file in JitWarmUp model +class PreloadJitInfo : public CHeapObj { +public: + enum PreloadInfoState { + NOT_INIT = 0, + IS_OK = 1, + IS_ERR = 2 + }; + + PreloadJitInfo(); + virtual ~PreloadJitInfo(); + + bool is_valid() { return _state == IS_OK; } + void init(); + + bool should_load_class_eagerly(Symbol* s); + + PreloadClassDictionary* dict() { return _dict; } + uint64_t loaded_count() { return _loaded_count; } + + PreloadClassChain* chain() { return _chain; } + void set_chain(PreloadClassChain* chain) { _chain = chain; } + + JitWarmUp* holder() { return _holder; } + void set_holder(JitWarmUp* h) { _holder = h; } + + // record java class loading and assign profile info to this klass + bool resolve_loaded_klass(InstanceKlass* klass); + + // will be called after jvm booted (in runtime/init.cpp) + void jvm_booted_is_done(); + + // will be called by JWarmUP java API nofityApplicationStartUpIsDone + void notify_application_startup_is_done(); + + // remove known meaningless suffix + static Symbol* remove_meaningless_suffix(Symbol* s); + +private: + PreloadClassDictionary* _dict; + PreloadClassChain* _chain; + uint64_t _loaded_count; // methods parsed from JitWarmUp log file + PreloadInfoState _state; + JitWarmUp* _holder; + bool _jvm_booted_is_done; +}; + +#endif //SHARED_VM_JWARMUP_JITWARMUP_HPP diff --git a/src/share/vm/jwarmup/jitWarmUpLog.hpp b/src/share/vm/jwarmup/jitWarmUpLog.hpp new file mode 100644 index 000000000..60df359b7 --- /dev/null +++ b/src/share/vm/jwarmup/jitWarmUpLog.hpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_JWARMUP_JITWARMUPLOG_HPP +#define SHARE_VM_JWARMUP_JITWARMUPLOG_HPP + +/* + * We use this file to be compatible with log framework in JDK11 + * To avoid conflict with jfr log, it's only used by jitWarmUp.cpp + */ + +#include "runtime/os.hpp" +#include "utilities/debug.hpp" + +#ifdef log_error +#undef log_error +#endif +#ifdef log_warning +#undef log_warning +#endif +#ifdef log_info +#undef log_info +#endif +#ifdef log_debug +#undef log_debug +#endif +#ifdef log_trace +#undef log_trace +#endif +#ifdef log_is_enabled +#undef log_is_enabled +#endif + +#define log_error(...) (!log_is_enabled(Error, __VA_ARGS__)) ? (void)0 : tty->print_cr +#define log_warning(...) (!log_is_enabled(Warning, __VA_ARGS__)) ? (void)0 : tty->print_cr +#define log_info(...) (!log_is_enabled(Info, __VA_ARGS__)) ? (void)0 : tty->print_cr +#define log_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : tty->print_cr +#define log_trace(...) (!log_is_enabled(Trace, __VA_ARGS__)) ? (void)0 : tty->print_cr + +#define log_is_enabled(level, ...) (LogImplWarmUp::is_level(LogLevel::level)) + +class LogImplWarmUp { + public: + static bool is_level(LogLevelType level) { + if (PrintCompilationWarmUpDetail || level >= (jint)LogLevel::Info) { + return true; + } + + return false; + } +}; + +#endif diff --git a/src/share/vm/jwarmup/jitWarmUpThread.cpp b/src/share/vm/jwarmup/jitWarmUpThread.cpp new file mode 100644 index 000000000..10b7eb605 --- /dev/null +++ b/src/share/vm/jwarmup/jitWarmUpThread.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "precompiled.hpp" + +#include "code/codeCache.hpp" +#include "jwarmup/jitWarmUp.hpp" +#include "jwarmup/jitWarmUpThread.hpp" +#include "runtime/java.hpp" +#include "runtime/mutex.hpp" +#include "runtime/mutexLocker.hpp" +#include "runtime/orderAccess.hpp" + +JitWarmUpFlushThread* JitWarmUpFlushThread::_jwp_thread = NULL; + +JitWarmUpFlushThread::JitWarmUpFlushThread(unsigned int sec) : NamedThread() { + set_name("JitWarmUp Flush Thread"); + set_sleep_sec(sec); + if (os::create_thread(this, os::vm_thread)) { + os::set_priority(this, MaxPriority); + } else { + tty->print_cr("[JitWarmUp] ERROR : failed to create JitWarmUpFlushThread"); + vm_exit(-1); + } +} + +JitWarmUpFlushThread::~JitWarmUpFlushThread() { + // do nothing +} + +void JitWarmUpFlushThread::run() { + assert(_jwp_thread == this, "sanity check"); + this->record_stack_base_and_size(); + this->_has_started = true; + os::sleep(this, 1000 * sleep_sec(), false); + JitWarmUp::instance()->flush_logfile(); + { + MutexLockerEx mu(JitWarmUpPrint_lock); + _jwp_thread = NULL; + } +} + +void JitWarmUpFlushThread::spawn_wait_for_flush(unsigned int sec) { + JitWarmUpFlushThread* t = new JitWarmUpFlushThread(sec); + _jwp_thread = t; + Thread::start(t); +} + +void JitWarmUpFlushThread::print_jwarmup_threads_on(outputStream* st) { + MutexLockerEx mu(JitWarmUpPrint_lock); + if (_jwp_thread == NULL || !_jwp_thread->has_started()) { + return; + } + st->print("\"%s\" ", _jwp_thread->name()); + _jwp_thread->print_on(st); + st->cr(); +} diff --git a/src/share/vm/jwarmup/jitWarmUpThread.hpp b/src/share/vm/jwarmup/jitWarmUpThread.hpp new file mode 100644 index 000000000..c72433335 --- /dev/null +++ b/src/share/vm/jwarmup/jitWarmUpThread.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef SHARE_VM_JWARMUP_JITWARMUPTHREAD_HPP +#define SHARE_VM_JWARMUP_JITWARMUPTHREAD_HPP + +#include "runtime/thread.hpp" + +class JitWarmUpFlushThread : public NamedThread { +protected: + JitWarmUpFlushThread(unsigned int sec); + virtual ~JitWarmUpFlushThread(); + +public: + virtual void run(); + + bool has_started() { return _has_started; } + unsigned int sleep_sec() { return _sleep_sec; } + + void set_sleep_sec(unsigned int sec) { _sleep_sec = sec; } + + static void spawn_wait_for_flush(unsigned int sec); + + static void print_jwarmup_threads_on(outputStream* st); + +private: + volatile bool _has_started; + unsigned int _sleep_sec; + + static JitWarmUpFlushThread* _jwp_thread; +}; + +#endif //SHARE_VM_JWARMUP_JITWARMUPTHREAD_HPP diff --git a/src/share/vm/libadt/dict.cpp b/src/share/vm/libadt/dict.cpp index 37559a097..706837e71 100644 --- a/src/share/vm/libadt/dict.cpp +++ b/src/share/vm/libadt/dict.cpp @@ -330,6 +330,31 @@ int hashstr(const void *t) { return (int)((sum+xsum[k]) >> 1); // Hash key, un-modulo'd table size } +// compute hash from char array with given length +int hashstr2(const char *s, int len) { + char c, k = 0; + int32_t sum = 0; + int cnt = 0; + + // make sure xsum array is initialized + if( !initflag ) { // Not initializated yet? + xsum[0] = (1<> 1); // Hash key, un-modulo'd table size +} + //------------------------------hashptr-------------------------------------- // Slimey cheap hash function; no guaranteed performance. Better than the // default for pointers, especially on MS-DOS machines. diff --git a/src/share/vm/libadt/dict.hpp b/src/share/vm/libadt/dict.hpp index dad45832d..237a8e0f9 100644 --- a/src/share/vm/libadt/dict.hpp +++ b/src/share/vm/libadt/dict.hpp @@ -89,6 +89,7 @@ class Dict : public ResourceObj { // Dictionary structure // Hashing functions int hashstr(const void *s); // Nice string hash +int hashstr2(const char *s, int len); // hash string with given length // Slimey cheap hash function; no guaranteed performance. Better than the // default for pointers, especially on MS-DOS machines. int hashptr(const void *key); diff --git a/src/share/vm/oops/constantPool.cpp b/src/share/vm/oops/constantPool.cpp index 98a11fed0..d116c8969 100644 --- a/src/share/vm/oops/constantPool.cpp +++ b/src/share/vm/oops/constantPool.cpp @@ -30,6 +30,7 @@ #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "interpreter/linkResolver.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "memory/heapInspection.hpp" #include "memory/metadataFactory.hpp" #include "memory/oopFactory.hpp" @@ -41,6 +42,8 @@ #include "runtime/javaCalls.hpp" #include "runtime/signature.hpp" #include "runtime/vframe.hpp" +#include "utilities/stack.hpp" +#include "utilities/stack.inline.hpp" PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC @@ -56,12 +59,19 @@ ConstantPool* ConstantPool::allocate(ClassLoaderData* loader_data, int length, T // the resolved_references array, which is recreated at startup time. // But that could be moved to InstanceKlass (although a pain to access from // assembly code). Maybe it could be moved to the cpCache which is RW. - return new (loader_data, size, false, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags); + if (CompilationWarmUp) { + Array* jwp_tags = NULL; + jwp_tags = MetadataFactory::new_array(loader_data, length, 0, CHECK_NULL); + return new (loader_data, size, false, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags, jwp_tags); + } else { + return new (loader_data, size, false, MetaspaceObj::ConstantPoolType, THREAD) ConstantPool(tags); + } } ConstantPool::ConstantPool(Array* tags) { set_length(tags->length()); set_tags(NULL); + set_jwp_tags(NULL); set_cache(NULL); set_reference_map(NULL); set_resolved_references(NULL); @@ -81,6 +91,37 @@ ConstantPool::ConstantPool(Array* tags) { set_tags(tags); } +ConstantPool::ConstantPool(Array* tags, Array* jwp_tags) { + assert(CompilationWarmUp, "must in CompilationWarmUp"); + assert(jwp_tags != NULL, "invariant"); + assert(jwp_tags->length() == tags->length(), "invariant"); + set_length(tags->length()); + set_tags(NULL); + set_jwp_tags(NULL); + set_cache(NULL); + set_reference_map(NULL); + set_resolved_references(NULL); + set_operands(NULL); + set_pool_holder(NULL); + set_flags(0); + + // only set to non-zero if constant pool is merged by RedefineClasses + set_version(0); + set_lock(new Monitor(Monitor::nonleaf + 2, "A constant pool lock")); + + // initialize tag array + int length = tags->length(); + for (int index = 0; index < length; index++) { + tags->at_put(index, JVM_CONSTANT_Invalid); + } + set_tags(tags); + + for (int i = 0; i < jwp_tags->length(); i++) { + jwp_tags->at_put(i, _jwp_has_not_been_traversed); + } + set_jwp_tags(jwp_tags); +} + void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) { MetadataFactory::free_metadata(loader_data, cache()); set_cache(NULL); @@ -95,6 +136,12 @@ void ConstantPool::deallocate_contents(ClassLoaderData* loader_data) { // free tag array MetadataFactory::free_array(loader_data, tags()); set_tags(NULL); + + if (CompilationWarmUp) { + assert(jwp_tags() != NULL, "should not be NULL"); + MetadataFactory::free_array(loader_data, jwp_tags()); + set_jwp_tags(NULL); + } } void ConstantPool::release_C_heap_structures() { @@ -1899,6 +1946,88 @@ void ConstantPool::preload_and_initialize_all_classes(ConstantPool* obj, TRAPS) #endif +void ConstantPool::preload_jwarmup_classes(TRAPS) { + constantPoolHandle cp(THREAD, this); + guarantee(cp->pool_holder() != NULL, "must be fully loaded"); + if (THREAD->in_eagerly_loading_class()) { + return; + } + THREAD->set_in_eagerly_loading_class(true); + Stack s; + s.push(cp->pool_holder()); + preload_jwarmup_classes_impl(s, THREAD); + THREAD->set_in_eagerly_loading_class(false); +} + +// should not be guarded in PreloadClassChain_lock +Klass* ConstantPool::resolve_class_from_slot(int which, TRAPS) { + assert(THREAD->is_Java_thread(), "must be a Java thread"); + if (CompilationWarmUpResolveClassEagerly) { + Klass* k = klass_at(which, CHECK_NULL); + return k; + } else { + // Create a handle for the mirror. This will preserve the resolved class + // until the loader_data is registered. + Handle mirror_handle; + constantPoolHandle this_oop(THREAD, this); + Symbol* name = NULL; + Handle loader; + { + if (this_oop->tag_at(which).is_unresolved_klass()) { + if (this_oop->tag_at(which).is_unresolved_klass_in_error()) { + return NULL; + } else { + name = this_oop->klass_name_at(which); + loader = Handle(THREAD, this_oop->pool_holder()->class_loader()); + } + } + } + oop protection_domain = this_oop->pool_holder()->protection_domain(); + Handle h_prot (THREAD, protection_domain); + Klass* k_oop = SystemDictionary::resolve_or_fail(name, loader, h_prot, true, THREAD); + return k_oop; + } +} + +// use bfs instead recusive +void ConstantPool::preload_jwarmup_classes_impl(Stack& s, + TRAPS) { + JitWarmUp* jwp = JitWarmUp::instance(); + while (!s.is_empty()) { + constantPoolHandle cp(s.pop()->constants()); + for (int i = 0; i< cp->length(); i++) { + bool is_unresolved = false; + Symbol* name = NULL; + { + if (cp->tag_at(i).is_unresolved_klass() && !cp->jwarmup_traversed_at(i)) { + name = cp->klass_name_at(i); + is_unresolved = true; + cp->jwarmup_has_traversed_at(i); + } + } + if (is_unresolved) { + if (name != NULL && !jwp->preloader()->should_load_class_eagerly(name)) { + continue; + } + // Load class from ConstantPool slot, if flag CompilationWarmUpResolveClassEagerly + // is on, update ConstantPool status accordingly and assign to that slot. + Klass* klass = cp->resolve_class_from_slot(i, THREAD); + if (HAS_PENDING_EXCEPTION) { + ResourceMark rm; + tty->print_cr("[JitWarmUp] WARNING : resolve %s from constant pool failed", + name->as_C_string()); + // ignore LinkageError in loading class + if (PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) { + CLEAR_PENDING_EXCEPTION; + } + } + if (klass != NULL && klass->oop_is_instance()) { + s.push((InstanceKlass*)klass); + } + } // end of if is_unresolved + } // end of loop + } // end of while +} // Printing diff --git a/src/share/vm/oops/constantPool.hpp b/src/share/vm/oops/constantPool.hpp index 0698eb40e..3f0964a6a 100644 --- a/src/share/vm/oops/constantPool.hpp +++ b/src/share/vm/oops/constantPool.hpp @@ -32,6 +32,7 @@ #include "oops/typeArrayOop.hpp" #include "runtime/handles.hpp" #include "utilities/constantTag.hpp" +#include "utilities/stack.hpp" #ifdef TARGET_ARCH_x86 # include "bytes_x86.hpp" #endif @@ -96,6 +97,13 @@ class ConstantPool : public Metadata { jobject _resolved_references; Array* _reference_map; + enum { + _jwp_has_not_been_traversed = 0, + _jwp_has_been_traversed = 1 + }; + + Array* _jwp_tags; // the jwp tag array records the corresponding tag whether is traversed + enum { _has_preresolution = 1, // Flags _on_stack = 2 @@ -114,6 +122,7 @@ class ConstantPool : public Metadata { Monitor* _lock; void set_tags(Array* tags) { _tags = tags; } + void set_jwp_tags(Array* tags) { _jwp_tags = tags; } void tag_at_put(int which, jbyte t) { tags()->at_put(which, t); } void release_tag_at_put(int which, jbyte t) { tags()->release_at_put(which, t); } @@ -164,6 +173,8 @@ class ConstantPool : public Metadata { } ConstantPool(Array* tags); + // for JWarmUP + ConstantPool(Array* tags, Array* jwp_tags); ConstantPool() { assert(DumpSharedSpaces || UseSharedSpaces, "only for CDS"); } public: static ConstantPool* allocate(ClassLoaderData* loader_data, int length, TRAPS); @@ -173,6 +184,18 @@ class ConstantPool : public Metadata { Array* tags() const { return _tags; } Array* operands() const { return _operands; } + Array* jwp_tags() const { return _jwp_tags; } + + bool jwarmup_traversed_at(int which) { + assert(0 < which && which < jwp_tags()->length(), "out of bound"); + return jwp_tags()->at(which) == _jwp_has_been_traversed; + } + + void jwarmup_has_traversed_at(int which) { + assert(which < jwp_tags()->length(), "out of bound"); + jwp_tags()->at_put(which, _jwp_has_been_traversed); + } + bool has_preresolution() const { return (_flags & _has_preresolution) != 0; } void set_has_preresolution() { _flags |= _has_preresolution; } @@ -887,6 +910,13 @@ class ConstantPool : public Metadata { // Compile the world support static void preload_and_initialize_all_classes(ConstantPool* constant_pool, TRAPS); #endif + + void preload_jwarmup_classes(TRAPS); + + Klass* resolve_class_from_slot(int which, TRAPS); + + private: + void preload_jwarmup_classes_impl(Stack& s, TRAPS); }; class SymbolHashMapEntry : public CHeapObj { diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp index 0204188ad..44af562d7 100644 --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -34,6 +34,7 @@ #include "interpreter/oopMapCache.hpp" #include "interpreter/rewriter.hpp" #include "jvmtifiles/jvmti.h" +#include "jwarmup/jitWarmUp.hpp" #include "memory/genOopClosures.inline.hpp" #include "memory/heapInspection.hpp" #include "memory/iterator.inline.hpp" @@ -322,6 +323,14 @@ InstanceKlass::InstanceKlass(int vtable_len, // Set temporary value until parseClassFile updates it with the real instance // size. set_layout_helper(Klass::instance_layout_helper(0, true)); + + set_jwarmup_recorded(false); +#ifndef PRODUCT + set_initialize_order(-1); +#endif + set_crc32(0); + set_bytes_size(0); + set_source_file_path(NULL); } @@ -376,6 +385,13 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) { // Need to take this class off the class loader data list. loader_data->remove_class(this); + if (CompilationWarmUp || CompilationWarmUpRecording) { + if (source_file_path() != NULL) { + source_file_path()->decrement_refcount(); + set_source_file_path(NULL); + } + } + // The array_klass for this class is created later, after error handling. // For class redefinition, we keep the original class so this scratch class // doesn't have an array class. Either way, assert that there is nothing @@ -884,6 +900,9 @@ void InstanceKlass::initialize_impl(instanceKlassHandle this_oop, TRAPS) { this_oop->set_init_state(being_initialized); this_oop->set_init_thread(self); } + if (CompilationWarmUpRecording) { + JitWarmUp::instance()->recorder()->assign_class_init_order(this_oop()); + } // Step 7 // Next, if C is a class rather than an interface, initialize its super class and super diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp index 36ed23274..03d4b5f4f 100644 --- a/src/share/vm/oops/instanceKlass.hpp +++ b/src/share/vm/oops/instanceKlass.hpp @@ -208,6 +208,21 @@ class InstanceKlass: public Klass { // if this class is unloaded. Symbol* _array_name; + // if not using JWarmUP, default value is 0 + unsigned int _crc32; + // if not using JWarmUP, default value is 0 + unsigned int _class_bytes_size; + + // CompilationWarmUp eager init support + bool _is_jwarmup_recorded; + + // source file path, e.g. /home/xxx/liba.jar + Symbol* _source_file_path; + +#ifndef PRODUCT + int _initialize_order; +#endif + // Number of heapOopSize words used by non-static fields in this klass // (including inherited fields but after header_size()). int _nonstatic_field_size; @@ -656,6 +671,23 @@ class InstanceKlass: public Klass { Symbol* array_name() { return _array_name; } void set_array_name(Symbol* name) { assert(_array_name == NULL || name == NULL, "name already created"); _array_name = name; } + // JWarmUP support + unsigned int crc32() { return _crc32; } + void set_crc32(unsigned int crc32) { _crc32 = crc32; } + + unsigned int bytes_size() { return _class_bytes_size; } + void set_bytes_size(unsigned int size) { _class_bytes_size = size; } + + bool is_jwarmup_recorded() { return _is_jwarmup_recorded; } + void set_jwarmup_recorded(bool value) { _is_jwarmup_recorded = value; } + + Symbol* source_file_path() { return _source_file_path; } + void set_source_file_path(Symbol* value) { _source_file_path = value; } + +#ifndef PRODUCT + unsigned int initialize_order() { return _initialize_order; } + void set_initialize_order(int order) { _initialize_order = order; } +#endif // nonstatic oop-map blocks static int nonstatic_oop_map_size(unsigned int oop_map_count) { return oop_map_count * OopMapBlock::size_in_words(); diff --git a/src/share/vm/oops/method.cpp b/src/share/vm/oops/method.cpp index 9e58c0126..3cf187208 100644 --- a/src/share/vm/oops/method.cpp +++ b/src/share/vm/oops/method.cpp @@ -32,6 +32,7 @@ #include "interpreter/bytecodes.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/oopMapCache.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "memory/gcLocker.hpp" #include "memory/generation.hpp" #include "memory/heapInspection.hpp" @@ -96,6 +97,13 @@ Method::Method(ConstMethod* xconst, AccessFlags access_flags, int size) { clear_method_counters(); set_vtable_index(Method::garbage_vtable_index); + set_first_invoke_init_order(INVALID_FIRST_INVOKE_INIT_ORDER); + set_compiled_by_jwarmup(false); + +#ifndef PRODUCT + set_deopted_by_jwarmup(false); +#endif + // Fix and bury in Method* set_interpreter_entry(NULL); // sets i2i entry and from_int set_adapter_entry(NULL); diff --git a/src/share/vm/oops/method.hpp b/src/share/vm/oops/method.hpp index 24214873e..8a1d3ed9f 100644 --- a/src/share/vm/oops/method.hpp +++ b/src/share/vm/oops/method.hpp @@ -136,6 +136,12 @@ class Method : public Metadata { nmethod* volatile _code; // Points to the corresponding piece of native code volatile address _from_interpreted_entry; // Cache of _code ? _adapter->i2c_entry() : _i2i_entry + int _first_invoke_init_order; // record class initialize order when this method first been invoked + bool _compiled_by_jwarmup; +#ifndef PRODUCT + bool _deopted_by_jwarmup; +#endif + // Constructor Method(ConstMethod* xconst, AccessFlags access_flags, int size); public: @@ -202,6 +208,21 @@ class Method : public Metadata { return constMethod()->type_annotations(); } + int first_invoke_init_order() { return _first_invoke_init_order; } + void set_first_invoke_init_order(int value) { _first_invoke_init_order = value; } + + bool compiled_by_jwarmup() { return _compiled_by_jwarmup; } + void set_compiled_by_jwarmup(bool value) { _compiled_by_jwarmup = value; } + +#ifndef PRODUCT + bool deopted_by_jwarmup() { return _deopted_by_jwarmup; } + void set_deopted_by_jwarmup(bool value) { _deopted_by_jwarmup = value; } +#endif + + static ByteSize first_invoke_init_order_offset() { + return byte_offset_of(Method, _first_invoke_init_order); + } + // Helper routine: get klass name + "." + method name + signature as // C string, for the purpose of providing more useful NoSuchMethodErrors // and fatal error handling. The string is allocated in resource diff --git a/src/share/vm/oops/methodData.hpp b/src/share/vm/oops/methodData.hpp index 3cd7cd6f1..ce28f8638 100644 --- a/src/share/vm/oops/methodData.hpp +++ b/src/share/vm/oops/methodData.hpp @@ -282,6 +282,8 @@ class ProfileData : public ResourceObj { friend class TypeEntries; friend class ReturnTypeEntry; friend class TypeStackSlotEntries; + friend class ProfileRecorder; + friend class PreloadJitInfo; private: #ifndef PRODUCT enum { diff --git a/src/share/vm/opto/callGenerator.cpp b/src/share/vm/opto/callGenerator.cpp index a79f1d294..060d1d4d5 100644 --- a/src/share/vm/opto/callGenerator.cpp +++ b/src/share/vm/opto/callGenerator.cpp @@ -207,7 +207,8 @@ JVMState* VirtualCallGenerator::generate(JVMState* jvms) { if (!UseInlineCaches || !ImplicitNullChecks || !os::zero_page_read_protected() || ((ImplicitNullCheckThreshold > 0) && caller_md && (caller_md->trap_count(Deoptimization::Reason_null_check) - >= (uint)ImplicitNullCheckThreshold))) { + >= (uint)ImplicitNullCheckThreshold)) || + (CompilationWarmUp && kit.C->env()->task()->is_jwarmup_compilation())) { // Make an explicit receiver null_check as part of this call. // Since we share a map with the caller, his JVMS gets adjusted. receiver = kit.null_check_receiver_before_call(method()); diff --git a/src/share/vm/opto/compile.cpp b/src/share/vm/opto/compile.cpp index 7d83a5ac1..4a1d09e03 100644 --- a/src/share/vm/opto/compile.cpp +++ b/src/share/vm/opto/compile.cpp @@ -728,6 +728,14 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr print_compile_messages(); + if (CompilationWarmUp) { + bool fields_resolved = ci_env->check_method_fields_all_resolved(method()); + if (!fields_resolved) { + _failure_reason = "fields needed by method are not all resolved"; + return; + } + } + _ilt = InlineTree::build_inline_tree_root(); // Even if NO memory addresses are used, MergeMem nodes must have at least 1 slice diff --git a/src/share/vm/opto/graphKit.cpp b/src/share/vm/opto/graphKit.cpp index dabc7e758..d23269683 100644 --- a/src/share/vm/opto/graphKit.cpp +++ b/src/share/vm/opto/graphKit.cpp @@ -2749,9 +2749,14 @@ bool GraphKit::seems_never_null(Node* obj, ciProfileData* data) { && obj != null() // And not the -Xcomp stupid case? && !too_many_traps(Deoptimization::Reason_null_check) ) { - if (data == NULL) + bool compiledByWarmUp = CompilationWarmUp && this->C->env()->task()->is_jwarmup_compilation(); + if (data == NULL) { + if (compiledByWarmUp) { + return false; + } // Edge case: no mature data. Be optimistic here. return true; + } // If the profile has not seen a null, assume it won't happen. assert(java_bc() == Bytecodes::_checkcast || java_bc() == Bytecodes::_instanceof || @@ -2821,6 +2826,12 @@ Node* GraphKit::maybe_cast_profiled_obj(Node* obj, bool not_null) { // type == NULL if profiling tells us this object is always null if (type != NULL) { + if (CompilationWarmUp) { + if (this->C->env()->task()->is_jwarmup_compilation()) { + return obj; + } + } + Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check; Deoptimization::DeoptReason null_reason = Deoptimization::Reason_null_check; if (!too_many_traps(null_reason) && !too_many_recompiles(null_reason) && diff --git a/src/share/vm/opto/lcm.cpp b/src/share/vm/opto/lcm.cpp index d7aa25357..7858c3a5e 100644 --- a/src/share/vm/opto/lcm.cpp +++ b/src/share/vm/opto/lcm.cpp @@ -96,6 +96,9 @@ void PhaseCFG::implicit_null_check(Block* block, Node *proj, Node *val, int allo // mechanism exists (yet) to set the switches at an os_cpu level if( !ImplicitNullChecks || MacroAssembler::needs_explicit_null_check(0)) return; + // to reduce deoptimization, disable implicit_null_check for jwarmup compilation + if (CompilationWarmUp && this->C->env()->task()->is_jwarmup_compilation()) return; + // Make sure the ptr-is-null path appears to be uncommon! float f = block->end()->as_MachIf()->_prob; if( proj->Opcode() == Op_IfTrue ) f = 1.0f - f; diff --git a/src/share/vm/prims/jvm.cpp b/src/share/vm/prims/jvm.cpp index f43dc5ed7..b45093d79 100644 --- a/src/share/vm/prims/jvm.cpp +++ b/src/share/vm/prims/jvm.cpp @@ -35,8 +35,10 @@ #include "classfile/systemDictionaryShared.hpp" #endif #include "classfile/vmSymbols.hpp" +#include "code/codeCache.hpp" #include "gc_interface/collectedHeap.inline.hpp" #include "interpreter/bytecode.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "memory/oopFactory.hpp" #include "memory/referenceType.hpp" #include "memory/universe.inline.hpp" @@ -51,6 +53,7 @@ #include "prims/nativeLookup.hpp" #include "prims/privilegedStack.hpp" #include "runtime/arguments.hpp" +#include "runtime/compilationPolicy.hpp" #include "runtime/dtraceJSDT.hpp" #include "runtime/handles.inline.hpp" #include "runtime/init.hpp" @@ -4682,3 +4685,66 @@ JVM_ENTRY(void, JVM_GetVersionInfo(JNIEnv* env, jvm_version_info* info, size_t i info->is_attachable = AttachListener::is_attach_supported(); } JVM_END + +JVM_ENTRY(void, JVM_NotifyApplicationStartUpIsDone(JNIEnv* env, jclass clz)) +{ + JVMWrapper("JVM_NotifyApplicationStartUpIsDone"); + if (!CompilationWarmUp) { + tty->print_cr("CompilationWarmUp is off, " + "notifyApplicationStartUpIsDone is invalid"); + return; + } + Handle mirror(THREAD, JNIHandles::resolve_non_null(clz)); + assert(mirror() != NULL, "sanity check"); + Klass* k = java_lang_Class::as_Klass(mirror()); + Method* dummy_method = k->lookup_method(vmSymbols::jwarmup_dummy_name(), vmSymbols::void_method_signature()); + assert(dummy_method != NULL, "Cannot find dummy method in com.alibaba.jwarmup.JWarmUp"); + JitWarmUp* jwp = JitWarmUp::instance(); + assert(jwp != NULL, "sanity check"); + jwp->set_dummy_method(dummy_method); + jwp->preloader()->notify_application_startup_is_done(); +} +JVM_END + +JVM_ENTRY(jboolean, JVM_CheckJWarmUpCompilationIsComplete(JNIEnv *env, jclass ignored)) +{ + JVMWrapper("JVM_CheckJWarmUpCompilationIsComplete"); + if (!CompilationWarmUp) { + tty->print_cr("CompilationWarmUp is off, " + "checkIfCompilationIsComplete is invalid"); + return JNI_TRUE; + } + JitWarmUp* jwp = JitWarmUp::instance(); + Method* dm = jwp->dummy_method(); + assert(dm != NULL, "sanity check"); + if (dm->code() != NULL) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} +JVM_END + +JVM_ENTRY(void, JVM_NotifyJVMDeoptWarmUpMethods(JNIEnv *env, jclass clazz)) +{ + JVMWrapper("JVM_NotifyJVMDeoptWarmUpMethods"); + if (!(CompilationWarmUp && CompilationWarmUpExplicitDeopt)) { + tty->print_cr("CompilationWarmUp or CompilationWarmUpExplicitDeopt is off, " + "notifyJVMDeoptWarmUpMethods is invalid"); + return; + } + JitWarmUp* jwp = JitWarmUp::instance(); + Method* dm = jwp->dummy_method(); + if (dm != NULL && dm->code() != NULL) { + PreloadClassChain* chain = jwp->preloader()->chain(); + assert(chain != NULL, "sanity check"); + if (chain->notify_deopt_signal()) { + tty->print_cr("JitWarmUp: receive signal to deoptimize warmup methods"); + } else { + tty->print_cr("JitWarmUp: deoptimize signal is ignore"); + } + } else { + tty->print_cr("JitWarmUp: deoptimize signal is ignore because warmup is not finished"); + } +} +JVM_END diff --git a/src/share/vm/prims/jvm.h b/src/share/vm/prims/jvm.h index 2c64341b7..138e3f584 100644 --- a/src/share/vm/prims/jvm.h +++ b/src/share/vm/prims/jvm.h @@ -1705,6 +1705,15 @@ typedef struct JDK1_1InitArgs { jint debugPort; } JDK1_1InitArgs; +JNIEXPORT void JNICALL +JVM_NotifyApplicationStartUpIsDone(JNIEnv* env, jclass clz); + +JNIEXPORT jboolean JNICALL +JVM_CheckJWarmUpCompilationIsComplete(JNIEnv* env, jclass ignored); + +JNIEXPORT void JNICALL +JVM_NotifyJVMDeoptWarmUpMethods(JNIEnv* env, jclass clz); + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ diff --git a/src/share/vm/prims/whitebox.cpp b/src/share/vm/prims/whitebox.cpp index 12251bee6..4723a1b13 100644 --- a/src/share/vm/prims/whitebox.cpp +++ b/src/share/vm/prims/whitebox.cpp @@ -1121,6 +1121,288 @@ WB_ENTRY(void, WB_PrintOsInfo(JNIEnv* env, jobject o)) os::print_os_info(tty); WB_END +WB_ENTRY(jobjectArray, WB_GetClassInitOrderList(JNIEnv* env, jobject o)) + ResourceMark rm(THREAD); + ThreadToNativeFromVM ttn(thread); + jclass clazz = env->FindClass(vmSymbols::java_lang_String()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + if (!CompilationWarmUpRecording) { + return NULL; + } + LinkedListImpl* lst = + JitWarmUp::instance()->recorder()->class_init_list(); + if (lst == NULL) { + return NULL; + } + + jsize size = (jsize)lst->size(); + jobjectArray result = NULL; + result = env->NewObjectArray(size, clazz, NULL); + if (result == NULL) { + return result; + } + + int idx = 0; + LinkedListNode* node = lst->head(); + while(node != NULL) { + ResourceMark rm_inner(THREAD); + Symbol* class_name = node->peek()->class_name(); + jobject obj = env->NewStringUTF(class_name->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + assert(idx < size, "index out of bound"); + env->SetObjectArrayElement(result, idx, obj); + idx++; + node = node->next(); + } + return result; +WB_END + +WB_ENTRY(jobjectArray, WB_GetClassChainSymbolList(JNIEnv* env, jobject o)) + ResourceMark rm(THREAD); + ThreadToNativeFromVM ttn(thread); + jclass clazz = env->FindClass(vmSymbols::java_lang_String()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + if (!CompilationWarmUp) { + return NULL; + } + + PreloadClassChain* chain = JitWarmUp::instance()->preloader()->chain(); + if (chain == NULL) { + return NULL; + } + + jsize size = (jsize)chain->length(); + jobjectArray result = NULL; + result = env->NewObjectArray(size, clazz, NULL); + if (result == NULL) { + return result; + } + + for (int i = 0; i < size; i++) { + ResourceMark rm_inner(THREAD); + Symbol* name = chain->at(i)->class_name(); + if (name == NULL) { + continue; + } + jobject obj = env->NewStringUTF(name->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, i, obj); + } + + return result; +WB_END + +WB_ENTRY(jintArray, WB_GetClassChainStateList(JNIEnv* env, jobject o)) + ResourceMark rm(THREAD); + ThreadToNativeFromVM ttn(thread); + CHECK_JNI_EXCEPTION_(env, NULL); + if (!CompilationWarmUp) { + return NULL; + } + PreloadClassChain* chain = JitWarmUp::instance()->preloader()->chain(); + if (chain == NULL) { + return NULL; + } + + jsize size = (jsize)chain->length(); + jintArray result = NULL; + result = env->NewIntArray(size); + if (result == NULL) { + return result; + } + + jint* arr = env->GetIntArrayElements(result, NULL); + for (int i = 0; i < size; i++) { + arr[i] = chain->at(i)->state(); + } + + env->ReleaseIntArrayElements(result, arr, 0); + return result; +WB_END + +WB_ENTRY(jobjectArray, WB_GetCompiledMethodList(JNIEnv* env, jobject o)) + ResourceMark rm(THREAD); + ThreadToNativeFromVM ttn(thread); + jclass clazz = env->FindClass(vmSymbols::java_lang_String()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + + if (!CompilationWarmUpRecording) { + return NULL; + } + ProfileRecordDictionary* dict = JitWarmUp::instance()->recorder()->dict(); + if (dict == NULL) { + return NULL; + } + + jsize size = (jsize)dict->count(); + jobjectArray result = NULL; + result = env->NewObjectArray(size, clazz, NULL); + if (result == NULL) { + return result; + } + + int count = 0; + for (int index = 0; index < dict->table_size(); index++) { + for (ProfileRecorderEntry* entry = dict->bucket(index); + entry != NULL; + entry = entry->next()) { + ResourceMark rm_inner(THREAD); + Symbol* method_name = entry->literal()->name(); + jobject obj = env->NewStringUTF(method_name->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + if (count >= size) { + break; + } + env->SetObjectArrayElement(result, count, obj); + count++; + } + } + + return result; +WB_END + +WB_ENTRY(jobjectArray, WB_GetClassListFromLogfile(JNIEnv* env, jobject o)) + ResourceMark rm(THREAD); + ThreadToNativeFromVM ttn(thread); + jclass clazz = env->FindClass(vmSymbols::java_lang_String()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + JitWarmUp* jwp = JitWarmUp::instance(); + if (jwp == NULL) { + return NULL; + } + PreloadJitInfo* preloader = jwp->preloader(); + if (preloader == NULL) { + return NULL; + } + PreloadClassDictionary* dict = preloader->dict(); + if (dict == NULL) { + return NULL; + } + + int entry_count = 0; + for (int index = 0; index < dict->table_size(); index++) { + for (PreloadClassEntry* entry = dict->bucket(index); + entry != NULL; + entry = entry->next()) { + entry_count++; + } + } + + jsize size = (jsize)entry_count; + jobjectArray result = NULL; + result = env->NewObjectArray(size, clazz, NULL); + if (result == NULL) { + return result; + } + + int count = 0; + for (int index = 0; index < dict->table_size(); index++) { + for (PreloadClassEntry* entry = dict->bucket(index); + entry != NULL; + entry = entry->next()) { + ResourceMark rm_inner(THREAD); + Symbol* class_name = entry->literal(); + jobject obj = env->NewStringUTF(class_name->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + //assert(index < size, "index out of bound"); + if (count >= size) { + break; + } + env->SetObjectArrayElement(result, count, obj); + count++; + } + } + return result; +WB_END + +WB_ENTRY(jobjectArray, WB_GetMethodListFromLogfile(JNIEnv* env, jobject o)) + ResourceMark rm(THREAD); + ThreadToNativeFromVM ttn(thread); + jclass clazz = env->FindClass(vmSymbols::java_lang_String()->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + + JitWarmUp* jwp = JitWarmUp::instance(); + if (jwp == NULL) { + return NULL; + } + PreloadJitInfo* preloader = jwp->preloader(); + if (preloader == NULL) { + return NULL; + } + PreloadClassDictionary* dict = preloader->dict(); + if (dict == NULL) { + return NULL; + } + + jsize size = (jsize)preloader->loaded_count(); + jobjectArray result = NULL; + result = env->NewObjectArray(size, clazz, NULL); + if (result == NULL) { + return result; + } + + int count = 0; + for (int index = 0; index < dict->table_size(); index++) { + for (PreloadClassEntry* entry = dict->bucket(index); + entry != NULL; + entry = entry->next()) { + for (PreloadClassHolder* holder = entry->head_holder(); + holder != NULL; + holder = holder->next()) { + GrowableArray* arr = holder->method_list(); + if (arr == NULL) { + continue; + } + int arr_len = arr->length(); + for (int i = 0; i < arr_len; i++ ) { + PreloadMethodHolder* mh = arr->at(i); + ResourceMark rm_inner(THREAD); + Symbol* method_name = mh->name(); + jobject obj = env->NewStringUTF(method_name->as_C_string()); + CHECK_JNI_EXCEPTION_(env, NULL); + //assert(index < size, "index out of bound"); + if (count >= size) { + break; + } + env->SetObjectArrayElement(result, count, obj); + count++; + } // end of method list loop + } // end of class holder loop + } // end of entry bucket loop + } // end of table size loop + return result; +WB_END + +WB_ENTRY(jboolean, WB_TestFixDanglingPointerInDeopt(JNIEnv* env, jobject o, jstring name)) + Handle h_name(THREAD, JNIHandles::resolve(name)); + if (h_name.is_null()) return false; + Symbol* sym = java_lang_String::as_symbol(h_name(), CHECK_false); + + JitWarmUp* jwp = JitWarmUp::instance(); + PreloadClassChain* chain = jwp->preloader()->chain(); + assert(chain != NULL, "sanity check"); + int index = chain->length() - 1; + PreloadMethodHolder* begin_holder = NULL; + while (index > 0 && begin_holder == NULL) { + PreloadClassChain::PreloadClassChainEntry* entry = chain->at(index); + begin_holder = entry->method_holder(); + index--; + } + if (begin_holder == NULL) { + return false; + } + MethodHolderIterator iter(chain, begin_holder, index); + while (*iter != NULL) { + PreloadMethodHolder* pmh = *iter; + if (pmh->name()->fast_compare(sym) == 0) { + if (pmh->resolved_method() != NULL) { + return false; + } + } + iter.next(); + } + return true; +WB_END #define CC (char*) static JNINativeMethod methods[] = { @@ -1243,6 +1525,20 @@ static JNINativeMethod methods[] = { {CC"printOsInfo", CC"()V", (void*)&WB_PrintOsInfo }, {CC"getHeapAlignment", CC"()J", (void*)&WB_GetHeapAlignment}, {CC"allocateCodeBlob", CC"(II)J", (void*)&WB_AllocateCodeBlob }, + {CC"getClassListFromLogfile", CC"()[Ljava/lang/String;", + (void*)&WB_GetClassListFromLogfile }, + {CC"getMethodListFromLogfile", CC"()[Ljava/lang/String;", + (void*)&WB_GetMethodListFromLogfile }, + {CC"getCompiledMethodList", CC"()[Ljava/lang/String;", + (void*)&WB_GetCompiledMethodList }, + {CC"getClassChainSymbolList", CC"()[Ljava/lang/String;", + (void*)&WB_GetClassChainSymbolList }, + {CC"getClassChainStateList", CC"()[I", + (void*)&WB_GetClassChainStateList }, + {CC"testFixDanglingPointerInDeopt", + CC"(Ljava/lang/String;)Z", (void*)&WB_TestFixDanglingPointerInDeopt}, + {CC"getClassInitOrderList", CC"()[Ljava/lang/String;", + (void*)&WB_GetClassInitOrderList } }; #undef CC diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index 1aa71c076..2ed1e013f 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -4026,6 +4026,51 @@ class CommandLineFlags { "Print JFR log ") \ \ product(bool, EnableJFR, false, "Enable JFR feature") \ + \ + lp64_product(bool, CompilationWarmUpRecording, false, \ + "Collect profiling information for JWarmUP") \ + \ + lp64_product(bool, CompilationWarmUp, false, \ + "Enable CompilationWarmUp from a log file") \ + \ + manageable(bool, PrintCompilationWarmUpDetail, false, \ + "Print detail information for jitWarmUp") \ + \ + lp64_product(ccstr, CompilationWarmUpLogfile, NULL, \ + "Log file name for JWarmUP") \ + \ + lp64_product(uintx, CompilationWarmUpRecordTime, 0, \ + "Sleep time (in seconds) before flushing profling " \ + "information to log file ") \ + \ + lp64_product(uintx, CompilationWarmUpAppID, 0, \ + "Application ID written in log file for verification ") \ + \ + lp64_product(ccstr, CompilationWarmUpExclude, NULL, \ + "CompilationWarmUp excluding list ") \ + \ + lp64_product(bool, CompilationWarmUpExplicitDeopt, false, \ + "Deoptimize JWarmUP methods by explicit api") \ + \ + lp64_product(uintx, CompilationWarmUpDeoptTime, 1200, \ + "Sleep time (in seconds) before deoptimizing methods " \ + "compiled by JWarmUP ") \ + \ + diagnostic(uintx, CompilationWarmUpDeoptMinInterval, 5, \ + "JWarmUp method deoptimization minimum interval (in seconds)") \ + \ + diagnostic(uintx, CompilationWarmUpDeoptNumOfMethodsPerIter, 10, \ + "The max number of methods marked for " \ + "deoptimization per iteration") \ + \ + diagnostic(bool, CompilationWarmUpResolveClassEagerly, true, \ + "resolve class from constant pool eagerly") \ + \ + lp64_product(bool, DeoptimizeBeforeWarmUp, false, \ + "Deoptimize recorded methods before JWarmUP compilation") \ + \ + lp64_product(intx, CompilationWarmUpRecordMinLevel, 3, \ + "Minimal compilation level recorded in JWarmUP recording phase") \ /* * Macros for factoring of globals diff --git a/src/share/vm/runtime/init.cpp b/src/share/vm/runtime/init.cpp index 1512ccc96..b10012296 100644 --- a/src/share/vm/runtime/init.cpp +++ b/src/share/vm/runtime/init.cpp @@ -27,6 +27,7 @@ #include "code/icBuffer.hpp" #include "gc_interface/collectedHeap.hpp" #include "interpreter/bytecodes.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "memory/universe.hpp" #include "prims/methodHandles.hpp" #include "runtime/handles.inline.hpp" @@ -106,6 +107,14 @@ jint init_globals() { if (status != JNI_OK) return status; + if (CompilationWarmUpRecording) { + JitWarmUp* jwp = JitWarmUp::create_instance(); + jwp->init_for_recording(); + if (!jwp->is_valid()) { + tty->print_cr("[JitWarmUp] ERROR: init error."); + vm_exit(-1); + } + } interpreter_init(); // before any methods loaded invocationCounter_init(); // before any methods loaded marksweep_init(); @@ -113,6 +122,14 @@ jint init_globals() { templateTable_init(); InterfaceSupport_init(); SharedRuntime::generate_stubs(); + if (CompilationWarmUp) { + JitWarmUp* jwp = JitWarmUp::create_instance(); + jwp->init_for_warmup(); + if (!jwp->is_valid()) { + tty->print_cr("[JitWarmUp] ERROR: init error."); + vm_exit(-1); + } + } universe2_init(); // dependent on codeCache_init and stubRoutines_init1 referenceProcessor_init(); jni_handles_init(); diff --git a/src/share/vm/runtime/mutexLocker.cpp b/src/share/vm/runtime/mutexLocker.cpp index 1f61967aa..0e6984e8a 100644 --- a/src/share/vm/runtime/mutexLocker.cpp +++ b/src/share/vm/runtime/mutexLocker.cpp @@ -39,6 +39,9 @@ Mutex* Patching_lock = NULL; Monitor* SystemDictionary_lock = NULL; +Mutex* ProfileRecorder_lock = NULL; +Mutex* PreloadClassChain_lock = NULL; +Mutex* JitWarmUpPrint_lock = NULL; Mutex* PackageTable_lock = NULL; Mutex* CompiledIC_lock = NULL; Mutex* InlineCacheBuffer_lock = NULL; @@ -218,6 +221,9 @@ void mutex_init() { def(JmethodIdCreation_lock , Mutex , leaf, true ); // used for creating jmethodIDs. def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread + def(ProfileRecorder_lock , Mutex , nonleaf+2, true ); // used for JitWarmUp + def(PreloadClassChain_lock , Mutex , max_nonleaf, true ); // used for JitWarmUp + def(JitWarmUpPrint_lock , Mutex , max_nonleaf, true ); // used for JitWarmUp def(PackageTable_lock , Mutex , leaf, false); def(InlineCacheBuffer_lock , Mutex , leaf, true ); def(VMStatistic_lock , Mutex , leaf, false); diff --git a/src/share/vm/runtime/mutexLocker.hpp b/src/share/vm/runtime/mutexLocker.hpp index 138e30e83..c61cdbf01 100644 --- a/src/share/vm/runtime/mutexLocker.hpp +++ b/src/share/vm/runtime/mutexLocker.hpp @@ -47,6 +47,9 @@ extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code extern Monitor* SystemDictionary_lock; // a lock on the system dictonary +extern Mutex* ProfileRecorder_lock; // a lock on the JWarmUP class ProfileRecorder +extern Mutex* PreloadClassChain_lock; // a lock on the JWarmUP preload class chain +extern Mutex* JitWarmUpPrint_lock; // a lock on the JWarmUP jstack print extern Mutex* PackageTable_lock; // a lock on the class loader package table extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access extern Mutex* InlineCacheBuffer_lock; // a lock used to guard the InlineCacheBuffer diff --git a/src/share/vm/runtime/safepoint.cpp b/src/share/vm/runtime/safepoint.cpp index 7fd03b379..e9d8da955 100644 --- a/src/share/vm/runtime/safepoint.cpp +++ b/src/share/vm/runtime/safepoint.cpp @@ -32,6 +32,7 @@ #include "code/scopeDesc.hpp" #include "gc_interface/collectedHeap.hpp" #include "interpreter/interpreter.hpp" +#include "jwarmup/jitWarmUp.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.inline.hpp" #include "oops/oop.inline.hpp" @@ -602,6 +603,15 @@ void SafepointSynchronize::do_cleanup_tasks() { event_safepoint_cleanup_task_commit(event, name); } + if (CompilationWarmUp) { + JitWarmUp* jwp = JitWarmUp::instance(); + assert(jwp != NULL, "sanity check"); + PreloadClassChain* chain = jwp->preloader()->chain(); + if (chain->should_deoptimize_methods()) { + chain->deoptimize_methods(); + } + } + // rotate log files? if (UseGCLogFileRotation) { const char* name = "rotate gc log"; diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp index d061d01ec..7d0d9cc50 100644 --- a/src/share/vm/runtime/thread.cpp +++ b/src/share/vm/runtime/thread.cpp @@ -33,6 +33,8 @@ #include "interpreter/linkResolver.hpp" #include "interpreter/oopMapCache.hpp" #include "jvmtifiles/jvmtiEnv.hpp" +#include "jwarmup/jitWarmUp.hpp" +#include "jwarmup/jitWarmUpThread.hpp" #include "memory/gcLocker.inline.hpp" #include "memory/metaspaceShared.hpp" #include "memory/oopFactory.hpp" @@ -230,6 +232,7 @@ Thread::Thread() { set_active_handles(NULL); set_free_handle_block(NULL); set_last_handle_mark(NULL); + set_in_eagerly_loading_class(false); // This initial value ==> never claimed. _oops_do_parity = 0; @@ -251,6 +254,7 @@ Thread::Thread() { _current_pending_monitor = NULL; _current_pending_monitor_is_from_java = true; _current_waiting_monitor = NULL; + _super_class_resolving_recursive_count = 0; _num_nested_signal = 0; omFreeList = NULL ; omFreeCount = 0 ; @@ -3689,6 +3693,12 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) { BiasedLocking::init(); + if (CompilationWarmUp) { + JitWarmUp* jwp = JitWarmUp::instance(); + assert(jwp != NULL, "sanity check"); + jwp->preloader()->jvm_booted_is_done(); + } + #if INCLUDE_RTM_OPT RTMLockingCounters::init(); #endif @@ -4376,6 +4386,11 @@ void Threads::print_on(outputStream* st, bool print_stacks, bool internal_format st->cr(); } CompileBroker::print_compiler_threads_on(st); + + if (CompilationWarmUpRecording) { + JitWarmUpFlushThread::print_jwarmup_threads_on(st); + st->cr(); + } st->flush(); } diff --git a/src/share/vm/runtime/thread.hpp b/src/share/vm/runtime/thread.hpp index 9688b3791..05e9528dc 100644 --- a/src/share/vm/runtime/thread.hpp +++ b/src/share/vm/runtime/thread.hpp @@ -211,6 +211,18 @@ class Thread: public ThreadShadow { void leave_signal_handler() { _num_nested_signal--; } bool is_inside_signal_handler() const { return _num_nested_signal > 0; } + // JWarmUP support + private: + int _super_class_resolving_recursive_count; + bool _in_eagerly_loading_class; + + public: + bool in_eagerly_loading_class() { return _in_eagerly_loading_class; } + void set_in_eagerly_loading_class(bool value) { _in_eagerly_loading_class = value; } + void super_class_resolving_recursive_inc() { _super_class_resolving_recursive_count++; } + void super_class_resolving_recursive_dec() { _super_class_resolving_recursive_count--; } + bool in_super_class_resolving() const { return _super_class_resolving_recursive_count > 0; } + private: // Debug tracing static void trace(const char* msg, const Thread* const thread) PRODUCT_RETURN; diff --git a/src/share/vm/utilities/hashtable.cpp b/src/share/vm/utilities/hashtable.cpp index 61c8f24f3..71fa221b6 100644 --- a/src/share/vm/utilities/hashtable.cpp +++ b/src/share/vm/utilities/hashtable.cpp @@ -390,6 +390,7 @@ template class BasicHashtable; template class Hashtable; template class RehashableHashtable; template class RehashableHashtable; +template class Hashtable; template class Hashtable; template class Hashtable; template class Hashtable; @@ -408,3 +409,7 @@ template class BasicHashtable; template class BasicHashtable; template class BasicHashtable; template class BasicHashtable; +template class Hashtable; +template class Hashtable; +template class BasicHashtable; +template class Hashtable; diff --git a/src/share/vm/utilities/ostream.cpp b/src/share/vm/utilities/ostream.cpp index 1b00f829a..57c07d317 100644 --- a/src/share/vm/utilities/ostream.cpp +++ b/src/share/vm/utilities/ostream.cpp @@ -754,6 +754,39 @@ void fileStream::flush() { fflush(_file); } +randomAccessFileStream::randomAccessFileStream() : fileStream() { } + +randomAccessFileStream::randomAccessFileStream(const char* file_name) + : fileStream(file_name) { } + +randomAccessFileStream::randomAccessFileStream(const char* file_name, const char* opentype) + : fileStream(file_name, opentype) { } + +randomAccessFileStream::randomAccessFileStream(FILE* file, bool need_close) + : fileStream(file, need_close) { } + +void randomAccessFileStream::write(const char* s, size_t len, long pos) { + assert(pos <= fileSize(), "pos check"); + if (_file != NULL) { + long old_pos = ::ftell(_file); + if (old_pos != pos) { + int ret = fseek(pos, SEEK_SET); + assert(ret != -1, "fseek return value check"); + } + size_t count = fwrite(s, 1, len, _file); + if (old_pos != pos) { + fseek(old_pos, SEEK_SET); + } + } +} + +void randomAccessFileStream::write(const char* s, size_t len) { + if (_file != NULL) { + // Make an unused local variable to avoid warning from gcc 4.x compiler. + size_t count = fwrite(s, 1, len, _file); + } +} + fdStream::fdStream(const char* file_name) { _fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666); _need_close = true; diff --git a/src/share/vm/utilities/ostream.hpp b/src/share/vm/utilities/ostream.hpp index b4e7378cf..2dfd77219 100644 --- a/src/share/vm/utilities/ostream.hpp +++ b/src/share/vm/utilities/ostream.hpp @@ -202,7 +202,7 @@ class fileStream : public outputStream { fileStream(const char* file_name); fileStream(const char* file_name, const char* opentype); fileStream(FILE* file, bool need_close = false) { _file = file; _need_close = need_close; } - ~fileStream(); + virtual ~fileStream(); bool is_open() const { return _file != NULL; } void set_need_close(bool b) { _need_close = b;} virtual void write(const char* c, size_t len); @@ -214,6 +214,20 @@ class fileStream : public outputStream { void flush(); }; +class randomAccessFileStream : public fileStream { + public: + randomAccessFileStream(); + randomAccessFileStream(const char* file_name); + randomAccessFileStream(const char* file_name, const char* opentype); + randomAccessFileStream(FILE* file, bool need_close = false); + virtual ~randomAccessFileStream() { } + virtual void write(const char* c, size_t len); + // random write support, write data to specified position + virtual void write(const char* c, size_t len, long pos); + int fseek(long offset, int pos) { return ::fseek(_file, offset, pos); } + long ftell() { return ::ftell(_file); } +}; + CDS_ONLY(extern fileStream* classlist_file;) // unlike fileStream, fdStream does unbuffered I/O by calling diff --git a/src/share/vm/utilities/symbolMatcher.cpp b/src/share/vm/utilities/symbolMatcher.cpp new file mode 100644 index 000000000..e1141e6ea --- /dev/null +++ b/src/share/vm/utilities/symbolMatcher.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "precompiled.hpp" +#include "memory/resourceArea.hpp" +#include "oops/symbol.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/symbolMatcher.hpp" + +template SymbolMatcher::SymbolMatcher(const char* regexes) + : _patterns(new (ResourceObj::C_HEAP, F) + GrowableArray(4, true, F)) { + assert(regexes != NULL, "illegal regexes"); + int size = (int)strlen(regexes); + int pattern_size = 0; + char* pattern_begin = (char*)®exes[0]; + for (int i = 0; i < size + 1; i++) { + if (regexes[i] == ',' || regexes[i] == ';' || i == size) { + add_pattern(pattern_begin, pattern_size); + // reset + pattern_size = -1; + pattern_begin = (char*)®exes[i+1]; + } + pattern_size++; + } +} + +template SymbolMatcher::~SymbolMatcher() { + delete _patterns; +} + +template void SymbolMatcher::add_pattern(const char* s, int len) { + if (len == 0) { + return; + } + _patterns->push(SymbolRegexPattern(s, len)); +} + +template bool SymbolMatcher::match(Symbol* symbol) { + ResourceMark rm; + char* s = symbol->as_C_string(); + return match(s); +} + +template bool SymbolMatcher::match(const char* s) { + int regex_num = _patterns->length(); + for (int i = 0; i < regex_num; i++) { + const char* regex = _patterns->at(i).origin_regex(); + int regex_len = _patterns->at(i).length(); + if (pattern_match(regex, regex_len, s)) { + return true; + } + } + return false; +} + +template bool SymbolMatcher::pattern_match(const char* regex, int regex_len, const char* s) { + int s_len = (int)strlen(s); + if (s_len < regex_len - 1) { + return false; + } + for (int i =0; i < regex_len; i++) { + if (regex[i] == '*') { + return true; + } + if (regex[i] == s[i]) { + continue; + } + if ((regex[i] == '.' && s[i] == '/') + || (regex[i] == '/' && s[i] == '.')) { + continue; + } + if (regex[i] != '*' && regex[i] != s[i]) { + return false; + } + } + return (s_len == regex_len); +} + +void test_symbol_matcher() { + const char* str = NULL; + const char* regexes1 = "com.alibaba.Test,org.*,org.apache.logging*"; + SymbolMatcher* matcher1 = new (ResourceObj::C_HEAP, mtClass) SymbolMatcher(regexes1); + str = "com/alibaba/Test"; + assert(matcher1->match(str) == true, ""); + + str = "com/alibaba/Test2"; + assert(matcher1->match(str) == false, ""); + + str = "org/alibaba/Test2"; + assert(matcher1->match(str) == true, ""); + + str = "org/A"; + assert(matcher1->match(str) == true, ""); + + str = "org."; + assert(matcher1->match(str) == true, ""); + + str = "."; + assert(matcher1->match(str) == false, ""); + + str = ""; + assert(matcher1->match(str) == false, ""); + + str = "org/apache/logging/Flag"; + assert(matcher1->match(str) == true, ""); + + const char* regexes2 = "com/alibaba.Test,,a.,"; + SymbolMatcher* matcher2 = new (ResourceObj::C_HEAP, mtClass) SymbolMatcher(regexes2); + + str = ""; + assert(matcher2->match(str) == false, ""); + + str = "com.alibaba.Test"; + assert(matcher2->match(str) == true, ""); + + str = "a.a"; + assert(matcher2->match(str) == false, ""); + + const char* regexes3 = "org.apache.logging*"; + SymbolMatcher* matcher3 = new (ResourceObj::C_HEAP, mtClass) SymbolMatcher(regexes3); + + str = "org/apache/logging/x"; + assert(matcher3->match(str) == true, ""); + + str = "org/apache/loggingxx"; + assert(matcher3->match(str) == true, ""); + + delete matcher1; + delete matcher2; +} + +template class SymbolMatcher; diff --git a/src/share/vm/utilities/symbolMatcher.hpp b/src/share/vm/utilities/symbolMatcher.hpp new file mode 100644 index 000000000..8c274101f --- /dev/null +++ b/src/share/vm/utilities/symbolMatcher.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef SHARED_VM_UTILITIES_SYMBOLMATCHER_HPP +#define SHARED_VM_UTILITIES_SYMBOLMATCHER_HPP + +#include "memory/allocation.hpp" +#include "utilities/growableArray.hpp" + +class SymbolRegexPattern { +public: + SymbolRegexPattern() { } + SymbolRegexPattern(const char* pattern, int length) + : _pattern(pattern), + _length(length) { + } + + ~SymbolRegexPattern() { } + + const char* origin_regex() { return _pattern; } + void set_origin_regex(char* s) { _pattern = s; } + int length() { return _length; } + void set_length(int value) { _length = value; } + +private: + const char* _pattern; + int _length; +}; + +template +class SymbolMatcher : public ResourceObj { +public: + SymbolMatcher(const char* regexes); + virtual ~SymbolMatcher(); + GrowableArray* patterns() { return _patterns; } + + bool match(Symbol* symbol); + bool match(const char* s); + +private: + void add_pattern(const char* src, int len); + bool pattern_match(const char* regex, int regex_len, const char* s); + + GrowableArray* _patterns; +}; + + +#endif //SHARED_VM_UTILITIES_SYMBOLMATCHER_HPP diff --git a/test/jwarmup/Issue11272598.java b/test/jwarmup/Issue11272598.java new file mode 100644 index 000000000..ffffc7ce2 --- /dev/null +++ b/test/jwarmup/Issue11272598.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import java.lang.reflect.Method; +import java.util.*; +import java.io.File; +import com.alibaba.jwarmup.*; +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; + +/* + * @test Issue11272598 + * @library /testlibrary + * @build Issue11272598 + * @run main/othervm Issue11272598 + * @summary test to verify jwarmup method doesn't get de-optimized many times if virtual call receiver is null + */ +public class Issue11272598 { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = null; + String classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:-Inline", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpRecordTime=10", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", + "-cp", classPath, + InnerA.class.getName(), "recording"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("foo"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUp", + "-XX:-Inline", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", //"-XX:+WhiteBoxAPI", + "-XX:+CompilationWarmUpResolveClassEagerly", + "-cp", classPath, + InnerA.class.getName(), "startup"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + output.shouldHaveExitValue(0); + assertTrue(getDeoptimizationCount(output.getOutput()) == 1); + } + + public static int getDeoptimizationCount(String output) { + String[] lines = output.split("\n"); + int count = 0; + for (String line : lines) { + if (line.contains("::foo") && line.contains("made not entrant")) { + count++; + } + } + return count; + } + + public static class InnerA { + static { + System.out.println("InnerA initialize"); + } + + @Override + public int hashCode() { + if (ls.size() > 1) { + System.out.println("avoid optimization"); + } + return 1; + } + + public static List ls = new ArrayList(); + + public static void foo(InnerA a) { + a.hashCode(); + if (ls.size() > 1) { + System.out.println("avoid optimization"); + } + } + + public static void main(String[] args) throws Exception { + if ("recording".equals(args[0])) { + System.out.println("begin recording!"); + // initialize InnerA + InnerA a = new InnerA(); + for (int i = 0; i < 20000; i++) { + foo(a); + } + Thread.sleep(1000); + foo(a); + Thread.sleep(10000); + System.out.println("done!"); + } else { + System.out.println("begin startup!"); + JWarmUp.notifyApplicationStartUpIsDone(); + // doNotCheck not in args + if (args.length < 2) { + while (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + } + for (int i = 0; i < 100000; i++) { + try { + InnerA a = new InnerA(); + foo(a); + foo(null); + if ( i % 10000 == 1) { + System.gc(); + } + } catch (Exception e) { + // do nothing + } + } + System.out.println("done!"); + } + } + } +} diff --git a/test/jwarmup/TestCheckIfCompilationIsComplete.sh b/test/jwarmup/TestCheckIfCompilationIsComplete.sh new file mode 100644 index 000000000..d4c56d2b2 --- /dev/null +++ b/test/jwarmup/TestCheckIfCompilationIsComplete.sh @@ -0,0 +1,125 @@ +#!/bin/sh +# +# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# @test +# @summary test checkIfCompilationIsComplete API +# @run shell TestCheckIfCompilationIsComplete.sh + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../test_env.sh + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +TEST_CLASS=TmpTestCheckIfCompilationComplete +TEST_SOURCE=${TEST_CLASS}.java + +################################################################################### +cat > ${TESTCLASSES}${FS}$TEST_SOURCE << EOF +import java.lang.reflect.Method; +import java.util.*; +import com.alibaba.jwarmup.*; + +public class TmpTestCheckIfCompilationComplete { + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz() throws Exception { + TmpTestCheckIfCompilationComplete a = new TmpTestCheckIfCompilationComplete(); + a.foo(); + Thread.sleep(10000); + a.foo(); + System.out.println("process is done!"); + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("recording")) { + doBiz(); + } else if (args[0].equals("compilation")) { + JWarmUp.notifyApplicationStartUpIsDone(); + while (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + System.out.println("the JWarmUp Compilation is Done"); + System.out.println("Test Done"); + } + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE >> /dev/null 2>&1 +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE}" + exit 1 +fi + +#run Recording Model +${JAVA} -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUpRecording -XX:-ClassUnloading -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpRecordTime=10 -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} recording > output.txt 2>&1 +sleep 1 +${JAVA} -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUp -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} compilation > output.txt 2>&1 + +function assert() +{ + i=0 + notify_line_no=0 + while read line + do + i=$(($i+1)) + echo $i + echo $line + if [[ $line =~ "Compilation" ]]; then + notify_line_no=$i + echo "notify_line_no is $notify_line_no" + fi + done < output.txt + if [[ $notify_line_no == $(($i-1)) ]]; then + exit 0 + else + exit -1 + fi +} + +assert + diff --git a/test/jwarmup/TestClassInitChain.java b/test/jwarmup/TestClassInitChain.java new file mode 100644 index 000000000..f13ccb6b3 --- /dev/null +++ b/test/jwarmup/TestClassInitChain.java @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.*; + +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; +/* + * @test TestClassInitChain + * @library /testlibrary /testlibrary/whitebox + * @build TestClassInitChain + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestClassInitChain + * @summary test ClassInitChain structure + */ +public class TestClassInitChain { + private static String classPath; + + private static final int HEADER_SIZE = 32; + private static final int APPID_OFFSET = 16; + + public static String generateOriginLogfile() throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = new File("./jitwarmup.log"); + classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + String className = InnerA.class.getName(); + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./" + logfile.getName(), + "-XX:CompilationWarmUpRecordTime=15", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + className, "collection"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] output profile info has done"); + output.shouldContain("process is done!"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + if (!logfile.exists()) { + throw new Error("jit log not exist"); + } + return logfile.getName(); + } + + public static OutputAnalyzer testReadLogfileAndGetResult(String filename) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:CompilationWarmUpLogfile=./" + filename, + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpAppID=123", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName(), "compilation"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + return output; + } + + public static void main(String[] args) throws Exception { + OutputAnalyzer output = null; + String fileName = generateOriginLogfile(); + // test origin log file + output = testReadLogfileAndGetResult(fileName); + output.shouldContain("Test Class Init Chain OK"); + output.shouldHaveExitValue(0); + } + + public static class InnerB { + static { + System.out.println("InnerB initialize"); + } + public Object content; + } + + public static class InnerA { + private static WhiteBox whiteBox; + static { + System.out.println("InnerA initialize"); + } + + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz() throws Exception { + InnerA a = new InnerA(); + a.foo(); + InnerB b = new InnerB(); + // avoid optimization + System.out.println("b is" + b); + Thread.sleep(20000); + a.foo(); + System.out.println("process is done!"); + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("collection")) { + doBiz(); + } else if (args[0].equals("compilation")) { + whiteBox = WhiteBox.getWhiteBox(); + String classAName = InnerA.class.getName(); + String classBName = InnerB.class.getName(); + String[] classList = whiteBox.getClassChainSymbolList(); + System.out.println("class list length is " + classList.length); + int[] stateList = whiteBox.getClassChainStateList(); + int orderA = -1; + int orderB = -1; + int stateA = -1; + int stateB = -1; + for (int i = 0; i < classList.length; i++) { + if (classList[i] == null) { + continue; + } + if (classList[i].equals(classAName)) { + orderA = i; + stateA = stateList[1]; + } + if (classList[i].equals(classBName)) { + orderB = i; + stateB = stateList[i]; + } + } + assertTrue(orderA != -1); + assertTrue(orderB != -1); + assertTrue(orderB > orderA); + assertTrue(stateA >= 2); + assertTrue(stateB >= 2); + + System.out.println("Test Class Init Chain OK"); + } + } + } +} diff --git a/test/jwarmup/TestClassInitOrder.java b/test/jwarmup/TestClassInitOrder.java new file mode 100644 index 000000000..d362b31d4 --- /dev/null +++ b/test/jwarmup/TestClassInitOrder.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; +import com.oracle.java.testlibrary.*; + +/* + * @test TestClassInitOrder + * @library /testlibrary /testlibrary/whitebox + * @build TestClassInitOrder + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:-TieredCompilation + * -XX:-UseSharedSpaces + * -XX:+CompilationWarmUpRecording + * -XX:-ClassUnloading + * -XX:+UseConcMarkSweepGC + * -XX:-CMSClassUnloadingEnabled + * -XX:+PrintCompilationWarmUpDetail + * -XX:CompilationWarmUpLogfile=./test.log + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestClassInitOrder + * @summary Verify class initialization order in JitWarmUp recording + */ +public class TestClassInitOrder { + private static WhiteBox whiteBox; + public static void main(String[] args) throws Exception { + whiteBox = WhiteBox.getWhiteBox(); + InnerA a = new InnerA(); + System.out.println("after new A"); + + InnerB b = new InnerB(); + System.out.println("after new B"); + + String[] list = whiteBox.getClassInitOrderList(); + System.out.println(list.length); + + String InnerAName = InnerA.class.getName(); + String InnerBName = InnerB.class.getName(); + int orderA = 0; + int orderB = 0; + for (int i = 0; i < list.length; i++) { + String s = list[i]; + if (s.equals(InnerAName)) { + orderA = i; + } else if (s.equals(InnerBName)) { + orderB = i; + } + } + System.out.println("Class A order is " + orderA); + System.out.println("Class B order is " + orderB); + Asserts.assertTrue(orderA < orderB); + } + + public static class InnerA { + static { + System.out.println("InnerA initialize"); + } + } + + public static class InnerB { + static { + System.out.println("InnerB initialize"); + } + } +} diff --git a/test/jwarmup/TestDisableMethodData.sh b/test/jwarmup/TestDisableMethodData.sh new file mode 100644 index 000000000..889556913 --- /dev/null +++ b/test/jwarmup/TestDisableMethodData.sh @@ -0,0 +1,171 @@ +#!/bin/sh +# +# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# @test +# @summary disable MethodData in compilation made by JWarmUp +# @run shell TestDisableMethodData.sh + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../test_env.sh + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +TEST_CLASS=TmpTestDisableMethodData +TEST_SOURCE=${TEST_CLASS}.java + +################################################################################### +cat > ${TESTCLASSES}${FS}$TEST_SOURCE << EOF +import java.lang.reflect.Method; +import java.util.*; +import com.alibaba.jwarmup.*; + +public class TmpTestDisableMethodData { + public static class TmpBase { + public void bar() { } + } + public static class TmpA extends TmpBase { + public static long a = 0; + @Override + public void bar() { a++; } + } + public static class TmpB extends TmpBase { + public static long b = 0; + @Override + public void bar() { b++; } + } + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 22000; i++) { + foo2(new TmpA()); + } + foo2(new TmpB()); + ls.add("x"); + return ls.get(0); + } + + public void foo2(TmpBase t) { + if (t instanceof TmpB) { + return; + } + String s = "aa"; + if (ls.size() > 2000) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz() throws Exception { + TmpTestDisableMethodData a = new TmpTestDisableMethodData(); + a.foo(); + Thread.sleep(10000); + a.foo(); + System.out.println("process is done!"); + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("recording")) { + doBiz(); + } else if (args[0].equals("compilation")) { + // invoke foo2 once + TmpTestDisableMethodData a = new TmpTestDisableMethodData(); + for (int i = 0; i < 9687; i++) { + a.foo2(new TmpB()); + } + System.gc(); + JWarmUp.notifyApplicationStartUpIsDone(); + while (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + // Here, a.foo2 will be compiled by JWarmup but without MethodData info. + // and, wait for the deoptimization made by JWarmup. + for (int i = 0; i < 5; i++) { + Thread.sleep(4000); + System.gc(); + } + for (int i = 0; i < 19687; i++) { + // Here, methods already be deoptimized by JWarmup. + a.foo2(new TmpB()); + } + Thread.sleep(1000); + // a.foo2 will be compiled by C2 in optimal performance. + System.gc(); + System.out.println("re-compilation done by normal C2"); + // deoptimization should occur as it is made by normal C2 compilation + doBiz(); + System.out.println("Test Done"); + } + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE >> /dev/null 2>&1 +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE}" + exit 1 +fi + +#run Recording Model +${JAVA} -XX:-Inline -XX:CompileCommand=exclude,*.foo -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUpRecording -XX:-ClassUnloading -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpRecordTime=10 -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} recording > output.txt 2>&1 +cat output.txt +echo "----------------------------------" +sleep 1 +${JAVA} -XX:-Inline -XX:CompilationWarmUpDeoptTime=3 -XX:CompileCommand=exclude,*.foo -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUp -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} compilation > output.txt 2>&1 +cat output.txt + +function assert() +{ + i=0 + notify_line_no=0 + while read line + do + i=$(($i+1)) + echo $i + echo $line + if [[ $line =~ "made not entrant" ]]; then + deopt_line_no=$i + fi + if [[ $line =~ "re-compilation" ]]; then + recom_line_no=$i + fi + done < output.txt + if [[ $deopt_line_no > $recom_line_no ]]; then + echo "deoptimization happens after normal c2 compilation, it is OK." + exit 0 + else + echo "deopt_line_no is $deopt_line_no" + echo "recom_line_no is $recom_line_no" + exit -1 + fi +} + +assert + diff --git a/test/jwarmup/TestDisableNCE.java b/test/jwarmup/TestDisableNCE.java new file mode 100644 index 000000000..42ae657aa --- /dev/null +++ b/test/jwarmup/TestDisableNCE.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; + +import java.lang.reflect.Method; +import java.util.*; +import java.io.File; +import com.alibaba.jwarmup.*; +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; + +/* + * @test TestDisableNCE + * @library /testlibrary /testlibrary/whitebox + * @build TestDisableNCE + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestDisableNCE + * @summary verify the methods submitted via JWarmup are not de-optimized because of NCE + */ +public class TestDisableNCE { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = null; + String classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:-Inline", + "-XX:-UseSharedSpaces", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpRecordTime=10", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName(), "recording"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("foo"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUp", + "-XX:-Inline", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-XX:+CompilationWarmUpResolveClassEagerly", + "-cp", classPath, + InnerA.class.getName(), "startup"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + // output.shouldNotContain("made not entrant"); + output.shouldMatch("InnerA.foo.*success compiled"); + output.shouldNotMatch("foo.*made not entrant"); + output.shouldHaveExitValue(0); + + logfile = new File("./jitwarmup.log"); + if (logfile.exists()) { + try { + logfile.delete(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + public static class InnerB { + public String cc; + } + + public static class InnerA { + static { + System.out.println("InnerA initialize"); + } + + public Object[] aa; + public Object bb; + public static List ls = new ArrayList(); + + public static void foo(InnerA a) { + // null check test + Object[] oo = a.aa; + // class check test + String[] ss = (String[])a.aa; + // instanceof test + if (a.bb instanceof InnerB) { + System.out.println("avoid opitimize"); + } + if (ss.length > 1) { + System.out.println("avoid opitimize"); + } + } + + public static void main(String[] args) throws Exception { + if ("recording".equals(args[0])) { + System.out.println("begin recording!"); + // initialize InnerA + InnerA a = new InnerA(); + a.aa = new String[0]; + a.bb = new String("xx"); + for (int i = 0; i < 20000; i++) { + foo(a); + } + Thread.sleep(1000); + foo(a); + Thread.sleep(10000); + System.out.println("done!"); + } else { + System.out.println("begin startup!"); + JWarmUp.notifyApplicationStartUpIsDone(); + while (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + try { + InnerA a = new InnerA(); + a.aa = null; + a.bb = null; + foo(a); + foo(null); + // The above NULL values should not cause the de-optimization for JWarmup compiled methods. + } catch (Exception e) { + System.out.println("NPE!"); + } + System.out.println("done!"); + } + } + } +} diff --git a/test/jwarmup/TestEagerCompilation.java b/test/jwarmup/TestEagerCompilation.java new file mode 100644 index 000000000..7d9f11612 --- /dev/null +++ b/test/jwarmup/TestEagerCompilation.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; + +import java.io.*; +import java.lang.reflect.Method; +import java.util.*; +import java.util.regex.*; + +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; +/* + * @test TestEagerCompilation + * @library /testlibrary /testlibrary/whitebox + * @build TestEagerCompilation + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestEagerCompilation + * @summary test eager compilation for method through ClassInitChain + */ +public class TestEagerCompilation { + private static String classPath; + + private static final int HEADER_SIZE = 32; + private static final int APPID_OFFSET = 16; + + public static String generateOriginLogfile() throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = new File("./jitwarmup.log"); + classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + String className = InnerA.class.getName(); + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./" + logfile.getName(), + "-XX:CompilationWarmUpRecordTime=10", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + className, "recording"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] output profile info has done"); + output.shouldContain("process is done!"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + if (!logfile.exists()) { + throw new Error("jit log not exist"); + } + return logfile.getName(); + } + + public static String generateOriginLogfileWithG1() throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = new File("./jitwarmup.log"); + classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + String className = InnerA.class.getName(); + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-ClassUnloading", + "-XX:+UseG1GC", + "-XX:-ClassUnloadingWithConcurrentMark", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUpRecording", + "-XX:CompilationWarmUpLogfile=./" + logfile.getName(), + "-XX:CompilationWarmUpRecordTime=10", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + className, "recording"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] output profile info has done"); + output.shouldContain("process is done!"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + if (!logfile.exists()) { + throw new Error("jit log not exist"); + } + return logfile.getName(); + } + + public static OutputAnalyzer testJitWarmUpJavaAPI(String filename) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:CompilationWarmUpLogfile=./" + filename, + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName(), "compilation"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + return output; + } + + public static OutputAnalyzer testJitWarmUpJavaAPIwithEagerResolve(String filename) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:CompilationWarmUpLogfile=./" + filename, + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-XX:+CompilationWarmUpResolveClassEagerly", + "-cp", classPath, + InnerA.class.getName(), "compilation"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + return output; + } + + public static OutputAnalyzer testJitWarmUpJavaAPIWithG1(String filename) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:+UseG1GC", + "-XX:-ClassUnloadingWithConcurrentMark", + "-XX:CompilationWarmUpLogfile=./" + filename, + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName(), "compilation"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + return output; + } + + public static OutputAnalyzer testJitWarmDeopt(String filename) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:CompilationWarmUpLogfile=./" + filename, + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpAppID=123", + "-XX:CompilationWarmUpDeoptTime=5", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName(), "deopt"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + return output; + } + + private static String methodName = "TestEagerCompilation$InnerA.foo2([Ljava/lang/String;)V"; + + public static void main(String[] args) throws Exception { + OutputAnalyzer output = null; + String fileName = generateOriginLogfile(); + // test origin log file + output = testJitWarmUpJavaAPI(fileName); + output.shouldContain("Test Eager Compilation OK"); + output.shouldContain(methodName); + output.shouldHaveExitValue(0); + + output = testJitWarmUpJavaAPIwithEagerResolve(fileName); + output.shouldContain("Test Eager Compilation OK"); + output.shouldContain(methodName); + output.shouldHaveExitValue(0); + + fileName = generateOriginLogfileWithG1(); + output = testJitWarmUpJavaAPIWithG1(fileName); + output.shouldContain("Test Eager Compilation OK"); + output.shouldContain(methodName); + output.shouldHaveExitValue(0); + } + + public static class InnerB { + static { + System.out.println("InnerB initialize"); + } + public Object content; + } + + public static class InnerA { + private static WhiteBox whiteBox; + static { + System.out.println("InnerA initialize"); + } + + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz() throws Exception { + InnerA a = new InnerA(); + a.foo(); + InnerB b = new InnerB(); + System.out.println(b); + Thread.sleep(15000); + a.foo(); + System.out.println("process is done!"); + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("recording")) { + doBiz(); + } else if (args[0].equals("compilation")) { + Class c = Class.forName("com.alibaba.jwarmup.JWarmUp"); + Method m2 = c.getMethod("notifyApplicationStartUpIsDone"); + m2.invoke(null); + System.out.println("invoke to notifyApplicationStartUpIsDone is Done"); + // wait for compilation + Thread.sleep(5000); + System.out.println("Test Eager Compilation OK"); + } else if (args[0].equals("optimistic")) { + // wait for compilation + Thread.sleep(5000); + System.out.println("Test Eager Compilation OK"); + } else if (args[0].equals("deopt")) { + Class c = Class.forName("com.alibaba.jwarmup.JWarmUp"); + Method m2 = c.getMethod("notifyApplicationStartUpIsDone"); + m2.invoke(null); + System.out.println("invoke to notifyApplicationStartUpIsDone is Done"); + // wait for compilation + Thread.sleep(1000); + doBiz(); + Thread.sleep(5000); + System.out.println("Test Eager Compilation OK"); + } + } + } +} diff --git a/test/jwarmup/TestFlagAssertion.java b/test/jwarmup/TestFlagAssertion.java new file mode 100644 index 000000000..331f06a72 --- /dev/null +++ b/test/jwarmup/TestFlagAssertion.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import java.io.File; +import com.oracle.java.testlibrary.*; + +/* + * @test + * @summary Test JitWarmUp model required flag + * + * @library /testlibrary + * @run main TestFlagAssertion + */ +public class TestFlagAssertion { + public static void main(String[] args) throws Exception { + ProcessBuilder pb; + OutputAnalyzer output; + + pb = ProcessTools.createJavaProcessBuilder("-XX:+CompilationWarmUpRecording", + "-XX:+TieredCompilation", + "-XX:-ProfileInterpreter", + "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] ERROR: flag ProfileInterpreter must be on"); + output.shouldContain("[JitWarmUp] ERROR: init error"); + System.out.println(output.getOutput()); + + pb = ProcessTools.createJavaProcessBuilder("-XX:+CompilationWarmUpRecording", + "-XX:-TieredCompilation", + "-XX:+ClassUnloading", + "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] ERROR: flag ClassUnloading must be off"); + output.shouldContain("[JitWarmUp] ERROR: init error"); + System.out.println(output.getOutput()); + } +} diff --git a/test/jwarmup/TestLogFlush.java b/test/jwarmup/TestLogFlush.java new file mode 100644 index 000000000..00c337111 --- /dev/null +++ b/test/jwarmup/TestLogFlush.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import java.util.*; +import java.io.File; +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; + +/* + * @test TestLogFlush + * @library /testlibrary + * @run main TestLogFlush + * @summary test flushing profiling information into log in jitwarmup recording model + */ +public class TestLogFlush { + public static void testFlush(String className) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = null; + String classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpRecordTime=10", + "-cp", classPath, + className); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] output profile info has done"); + output.shouldContain("process is done!"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + logfile = new File("./jitwarmup.log"); + if (!logfile.exists()) { + throw new Error("jit log not exist"); + } + + try { + logfile.delete(); + } catch (Throwable e) { + e.printStackTrace(); + } + + // test file name is not manually specified + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpRecordTime=10", + "-cp", classPath, + className); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] output profile info has done"); + output.shouldContain("process is done!"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + logfile = null; + File path = new File("."); + if (path.isDirectory()) { + File[] fs = path.listFiles(); + for (File f : fs) { + if (f.isFile() && f.getName().startsWith("jwarmup_")) { + logfile = f; + } + } + } + assertTrue(logfile != null); + try { + logfile.delete(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) throws Exception { + testFlush(InnerA.class.getName()); + } + + public static class InnerA { + static { + System.out.println("InnerA initialize"); + } + + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void main(String[] args) throws Exception { + InnerA a = new InnerA(); + a.foo(); + Thread.sleep(3000); + a.foo(); + Thread.sleep(15000); + System.out.println("process is done!"); + } + } +} diff --git a/test/jwarmup/TestMethodRecorder.java b/test/jwarmup/TestMethodRecorder.java new file mode 100644 index 000000000..5c111e043 --- /dev/null +++ b/test/jwarmup/TestMethodRecorder.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; +import sun.hotspot.WhiteBox; +import java.util.*; + +/* + * @test TestMethodRecorder + * @library /testlibrary /testlibrary/whitebox + * @build TestMethodRecorder + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm -Xbootclasspath/a:. -XX:-TieredCompilation + * -XX:+CompilationWarmUpRecording + * -XX:-ClassUnloading + * -XX:+UseConcMarkSweepGC + * -XX:-CMSClassUnloadingEnabled + * -XX:-UseSharedSpaces + * -XX:+PrintCompilationWarmUpDetail + * -XX:CompilationWarmUpLogfile=./test.log + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * TestMethodRecorder + * @summary Test method recording in CompilationWarmUpRecording model + */ +public class TestMethodRecorder { + private static WhiteBox whiteBox; + public static void main(String[] args) throws Exception { + whiteBox = WhiteBox.getWhiteBox(); + + InnerA a = new InnerA(); + a.foo(); + Thread.sleep(3000); + a.foo(); + + String[] list = whiteBox.getCompiledMethodList(); + System.out.println(list.length); + String methodName = "foo2"; + + boolean contain = false; + for (int i = 0; i < list.length; i++) { + String s = list[i]; + if (s.equals(methodName)) { + contain = true; + } + System.out.println("method is " + s); + } + assertTrue(contain); + } + + public static class InnerA { + static { + System.out.println("InnerA initialize"); + } + + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + } +} diff --git a/test/jwarmup/TestNotDeoptJITMethod.sh b/test/jwarmup/TestNotDeoptJITMethod.sh new file mode 100644 index 000000000..8a8af2f86 --- /dev/null +++ b/test/jwarmup/TestNotDeoptJITMethod.sh @@ -0,0 +1,137 @@ +#!/bin/sh +# +# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# @test +# @summary test JWarmUp deoptimize will skip methods compiled by JIT +# @run shell TestNotDeoptJITMethod.sh + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../test_env.sh + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +TEST_CLASS=TmpTestNotDeoptJITMethod +TEST_SOURCE=${TEST_CLASS}.java + +################################################################################### +cat > ${TESTCLASSES}${FS}$TEST_SOURCE << EOF +import java.lang.reflect.Method; +import java.util.*; +import com.alibaba.jwarmup.*; + +public class TmpTestNotDeoptJITMethod { + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo(int mode) throws Exception { + for (int i = 0; i < 12000; i++) { + foo2(aa, mode, i); + } + ls.add("x"); + return ls.get(0); + } + + public void foo2(String[] a, int mode, int index) throws Exception { + String s = "aa"; + if (mode == 1 && index == 10000) { + a[index]=s; // out of range + } + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz(int mode){ + TmpTestNotDeoptJITMethod a = new TmpTestNotDeoptJITMethod(); + try{ + a.foo(mode); + } catch (Exception ex){ + ex.printStackTrace(); + } + try{ + // re-warmup foo to let jit compile it + a.foo(0); + } catch (Exception ex){ + ex.printStackTrace(); + } + try{ + Thread.sleep(10000); + a.foo(0); + } catch (Exception ex){ + ex.printStackTrace(); + } + System.out.println("process is done!"); + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("recording")) { + doBiz(0); + } else if (args[0].equals("compilation")) { + JWarmUp.notifyApplicationStartUpIsDone(); + while (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + System.out.println("the JWarmUp Compilation is Done, notify jvm to deoptimize warmup methods"); + new Thread(()-> doBiz(1)).start(); + Thread.sleep(2000); + JWarmUp.notifyJVMDeoptWarmUpMethods(); + Thread.sleep(6000); + System.gc(); + System.out.println("Test Done"); + } + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE >> /dev/null 2>&1 +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE}" + exit 1 +fi + +#run Recording Model +${JAVA} -XX:-TieredCompilation -XX:+CompilationWarmUpRecording -XX:-ClassUnloading -XX:-UseSharedSpaces -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpRecordTime=10 -XX:CompilationWarmUpLogfile=./jitwarmup.log -XX:CompileThreshold=3000 -cp ${TESTCLASSES} ${TEST_CLASS} recording > output_record.txt 2>&1 +sleep 1 +${JAVA} -XX:-TieredCompilation -XX:+CompilationWarmUp -XX:-UseSharedSpaces -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -Xbatch -XX:CompilationWarmUpLogfile=./jitwarmup.log -XX:+CompilationWarmUpExplicitDeopt -XX:CompilationWarmUpDeoptTime=1200 -XX:-UseOnStackReplacement -cp ${TESTCLASSES} ${TEST_CLASS} compilation > output.txt 2>&1 + +function check_output() +{ + #TmpTestNotDeoptJITMethod.foo2 should be re-compiled by jit + skip_messages=`grep "skip deoptimize TmpTestNotDeoptJITMethod.foo2" output.txt|wc -l` + if (( $skip_message -ne 1 )); then + exit -1 + fi + + exit 0 +} + +check_output + diff --git a/test/jwarmup/TestNotifyDeopt.sh b/test/jwarmup/TestNotifyDeopt.sh new file mode 100644 index 000000000..3cf5adce3 --- /dev/null +++ b/test/jwarmup/TestNotifyDeopt.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# +# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# @test +# @summary test notifyJVMDeoptWarmUpMethods API +# @run shell TestNotifyDeopt.sh + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../test_env.sh + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +TEST_CLASS=TmpTestNotifyDeopt +TEST_SOURCE=${TEST_CLASS}.java + +################################################################################### +cat > ${TESTCLASSES}${FS}$TEST_SOURCE << EOF +import java.lang.reflect.Method; +import java.util.*; +import com.alibaba.jwarmup.*; + +public class TmpTestNotifyDeopt { + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz() { + try { + TmpTestNotifyDeopt a = new TmpTestNotifyDeopt(); + a.foo(); + Thread.sleep(10000); + a.foo(); + System.out.println("process is done!"); + } catch( Exception ex ){ + ex.printStackTrace(); + System.exit(-1); + } + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("recording")) { + doBiz(); + } else if (args[0].equals("compilation")) { + JWarmUp.notifyApplicationStartUpIsDone(); + while (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + System.out.println("the JWarmUp Compilation is Done, notify jvm to deoptimize warmup methods"); + new Thread(()->doBiz()); + JWarmUp.notifyJVMDeoptWarmUpMethods(); + Thread.sleep(6000); + System.gc(); + System.out.println("Test Done"); + } + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE >> /dev/null 2>&1 +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE}" + exit 1 +fi + +#run Recording Model +${JAVA} -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUpRecording -XX:-ClassUnloading -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpRecordTime=10 -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} recording > output.txt 2>&1 +sleep 1 +${JAVA} -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUp -XX:+PrintCompilationWarmUpDetail -XX:CompileThreshold=500000 -XX:CompilationWarmUpLogfile=./jitwarmup.log -XX:+CompilationWarmUpExplicitDeopt -XX:CompilationWarmUpDeoptTime=1200 -cp ${TESTCLASSES} ${TEST_CLASS} compilation > output.txt 2>&1 + +function check_output() +{ + # check warning for conflict CompilationWarmUpDeoptTime + deopt_time_warning=`grep "WARNING : CompilationWarmUpDeoptTime is unused" output.txt|wc -l` + if [[ $deopt_time_warning -ne 1 ]]; then + echo "err by deopt time $deopt_time_warning" + exit -1 + fi + + # check warmup method is deoptimized + deopt_method_messages=`grep "WARNING : deoptimize warmup method" output.txt|wc -l` + if [[ $deopt_method_messages -eq 0 ]]; then + echo "err by deopt messages $deopt_method_messages" + exit -1 + fi + + # check every deoptimized method is compiled by warmup + warmup_methods=`grep "preload method" output.txt|awk '{print $4}'` + deopt_methods=`grep "WARNING : deoptimize warmup method" output.txt|awk '{print $7}'` + echo "warmup:$warmup_methods" + echo "deopt:$deopt_methods" + for m in $deopt_methods + do + found=0 + for wm in $warmup_methods + do + if [[ $m == $wm ]]; then + found=1 + fi + done + if [[ $found -ne 1 ]]; then + echo "not found $m" + exit -1 + fi + done + exit 0 +} + +check_output + diff --git a/test/jwarmup/TestReadLogfile.java b/test/jwarmup/TestReadLogfile.java new file mode 100644 index 000000000..ff58a8841 --- /dev/null +++ b/test/jwarmup/TestReadLogfile.java @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.*; + +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; +/* + * @test TestReadLogfile + * @library /testlibrary /testlibrary/whitebox + * @build TestReadLogfile + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestReadLogfile + * @summary test jitwarmup read class init section & method section from log file + */ +public class TestReadLogfile { + private static String classPath; + + private static final int HEADER_SIZE = 36; + private static final int APPID_OFFSET = 16; + + public static String generateOriginLogfile() throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = new File("./jitwarmup.log"); + classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + String className = InnerA.class.getName(); + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./" + logfile.getName(), + "-XX:CompilationWarmUpRecordTime=10", + "-XX:CompilationWarmUpAppID=123", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + className, "collection"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("[JitWarmUp] output profile info has done"); + output.shouldContain("process is done!"); + output.shouldHaveExitValue(0); + System.out.println(output.getOutput()); + + if (!logfile.exists()) { + throw new Error("jit log not exist"); + } + return logfile.getName(); + } + + public static OutputAnalyzer testReadLogfileAndGetResult(String filename) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:CompilationWarmUpLogfile=./" + filename, + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpAppID=123", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + "-cp", classPath, + TestReadLogfile.InnerA.class.getName(), "compilation"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + return output; + } + + private static byte[] getFileContent(String name) throws IOException{ + ByteBuffer byteBuffer = null; + FileChannel fc = null; + File originFile = null; + FileInputStream ifs = null; + try { + originFile = new File("./" + name); + ifs = new FileInputStream(originFile); + fc = ifs.getChannel(); + byteBuffer = ByteBuffer.allocate((int)fc.size()); + while (fc.read(byteBuffer) > 0) { + // read + } + } finally { + if (fc != null) { + fc.close(); + ifs.close(); + } + } + return byteBuffer.array(); + } + + private static File createNewFile(String fileName) throws IOException { + File f = new File("./" + fileName); + if (f.exists()) { + f.delete(); + } + f.createNewFile(); + return f; + } + + private static int readIntAsJavaInteger(RandomAccessFile f) throws IOException { + byte b1 = f.readByte(); + byte b2 = f.readByte(); + byte b3 = f.readByte(); + byte b4 = f.readByte(); + int result = (b1 & 0xff) | ((b2 << 8) & 0xff00) + | ((b3 << 16) & 0xff0000) | (b4 << 24); + return result; + } + + private static byte[] IntegerAsBytes(Integer res) { + byte[] targets = new byte[4]; + targets[0] = (byte) (res & 0xff); + targets[1] = (byte) ((res >> 8) & 0xff); + targets[2] = (byte) ((res >> 16) & 0xff); + targets[3] = (byte) (res >>> 24); + return targets; + } + + // illegal header + public static String generateIllegalLogfile1(String originLogfileName) throws IOException { + String fileName = "jitwarmup_1.log"; + File f = createNewFile(fileName); + byte[] originContent = getFileContent(originLogfileName); + RandomAccessFile raf = new RandomAccessFile(f, "rw"); + raf.write(originContent, 0, originContent.length); + byte[] illegalHeader = {0x1,0x2,0x0,0x0,(byte)0xba,(byte)0xbb}; + raf.seek(0); + raf.write(illegalHeader, 0, illegalHeader.length); + raf.close(); + return fileName; + } + + // illegal class init section size, size too large + public static String generateIllegalLogfile2(String originLogfileName) throws IOException { + String fileName = "jitwarmup_2.log"; + File f = createNewFile(fileName); + byte[] originContent = getFileContent(originLogfileName); + RandomAccessFile raf = new RandomAccessFile(f, "rw"); + raf.write(originContent, 0, originContent.length); + raf.seek(HEADER_SIZE); + int origin_size = readIntAsJavaInteger(raf); + raf.seek(HEADER_SIZE); + System.out.println("origin size is" + origin_size); + raf.write(IntegerAsBytes(origin_size + 18)); // illegal size + raf.close(); + return fileName; + } + + // illegal record section size, size too large + public static String generateIllegalLogfile3(String originLogfileName) throws IOException { + String fileName = "jitwarmup_3.log"; + File f = createNewFile(fileName); + byte[] originContent = getFileContent(originLogfileName); + RandomAccessFile raf = new RandomAccessFile(f, "rw"); + raf.write(originContent, 0, originContent.length); + raf.seek(HEADER_SIZE); + int init_section_size = readIntAsJavaInteger(raf); + raf.seek(HEADER_SIZE + init_section_size); + int record_section_size = readIntAsJavaInteger(raf); + System.out.println("record_section size is" + record_section_size); + raf.write(IntegerAsBytes(Integer.MAX_VALUE - 1)); // illegal size, too big + raf.close(); + return fileName; + } + + // illegal app id + public static String generateIllegalLogfile4(String originLogfileName) throws IOException { + String fileName = "jitwarmup_4.log"; + File f = createNewFile(fileName); + byte[] originContent = getFileContent(originLogfileName); + RandomAccessFile raf = new RandomAccessFile(f, "rw"); + raf.write(originContent, 0, originContent.length); + raf.seek(APPID_OFFSET); + int origin_appid = readIntAsJavaInteger(raf); + raf.seek(APPID_OFFSET); + System.out.println("origin appid is" + origin_appid); + raf.write(IntegerAsBytes(origin_appid + 1)); // illegal appid + raf.close(); + return fileName; + } + + public static void main(String[] args) throws Exception { + OutputAnalyzer output = null; + String originLogfileName = generateOriginLogfile(); + // test origin log file + output = testReadLogfileAndGetResult(originLogfileName); + output.shouldContain("read log file OK"); + output.shouldHaveExitValue(0); + // generate and test illegal log file header + output = testReadLogfileAndGetResult(generateIllegalLogfile1(originLogfileName)); + output.shouldNotContain("read log file OK"); + // generate and test illegal class init section size + output = testReadLogfileAndGetResult(generateIllegalLogfile2(originLogfileName)); + output.shouldNotContain("read log file OK"); + // generate and test illegal record section size + output = testReadLogfileAndGetResult(generateIllegalLogfile3(originLogfileName)); + output.shouldNotContain("read log file OK"); + // generate and test illegal appid + output = testReadLogfileAndGetResult(generateIllegalLogfile4(originLogfileName)); + output.shouldNotContain("read log file OK"); + } + + public static class InnerA { + private static WhiteBox whiteBox; + static { + System.out.println("InnerA initialize"); + } + + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("collection")) { + InnerA a = new InnerA(); + a.foo(); + Thread.sleep(15000); + a.foo(); + System.out.println("process is done!"); + } else if (args[0].equals("compilation")) { + whiteBox = WhiteBox.getWhiteBox(); + String className = InnerA.class.getName(); + String[] classList = whiteBox.getClassListFromLogfile(); + System.out.println("class list length is " + classList.length); + boolean containClass = false; + for (int i = 0; i < classList.length; i++) { + if (classList[i].equals(className)) { + containClass = true; + System.out.println("contain class OK"); + } + } + + String methodName = "foo2"; + String[] methodList = whiteBox.getMethodListFromLogfile(); + System.out.println("method list length is " + methodList.length); + assertTrue(methodList.length > 0); + boolean containMethod = false; + for (int i = 0; i < methodList.length; i++) { + if (methodList[i].equals(methodName)) { + containMethod = true; + System.out.println("contain method OK"); + } + } + + if (containClass && containMethod) { + System.out.println("read log file OK"); + } + } + } + } +} diff --git a/test/jwarmup/TestRecordNullMethodCounter.java b/test/jwarmup/TestRecordNullMethodCounter.java new file mode 100644 index 000000000..6711e7dfc --- /dev/null +++ b/test/jwarmup/TestRecordNullMethodCounter.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; + +import java.lang.reflect.Method; +import java.util.*; +import java.io.File; +import com.alibaba.jwarmup.*; +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; + +/* + * @test TestRecordNullMethodCounter + * @library /testlibrary /testlibrary/whitebox + * @build TestRecordNullMethodCounter + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestRecordNullMethodCounter + * @summary test jwarmup record doesn't crash if method counter is null + */ +public class TestRecordNullMethodCounter { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = null; + String classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-Xcomp", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpRecordTime=5", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName()); + output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + output.shouldContain("[JitWarmUp] WARNING : method counter is NULL for method"); + System.out.println(output.getOutput()); + } + + public static class InnerA { + public static void main(String[] args) throws Exception { + java.lang.Thread.sleep(10000); // sleep 10s + } + } +} diff --git a/test/jwarmup/TestThrowInitializaitonException.java b/test/jwarmup/TestThrowInitializaitonException.java new file mode 100644 index 000000000..5276160ae --- /dev/null +++ b/test/jwarmup/TestThrowInitializaitonException.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Alibaba designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +import sun.hotspot.WhiteBox; + +import java.lang.reflect.Method; +import java.util.*; +import java.io.File; +import com.alibaba.jwarmup.*; +import com.oracle.java.testlibrary.*; +import static com.oracle.java.testlibrary.Asserts.*; + +/* + * @test TestThrowInitializaitonException + * @library /testlibrary + * @build TestThrowInitializaitonException + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main/othervm TestThrowInitializaitonException + * @summary test throwing ExceptionInInitializerError in notifyApplicationStartUpIsDone API + */ +public class TestThrowInitializaitonException { + + public static void main(String[] args) throws Exception { + ProcessBuilder pb = null; + OutputAnalyzer output = null; + File logfile = null; + String classPath = System.getProperty("test.class.path"); + try { + output = ProcessTools.executeProcess("pwd"); + System.out.println(output.getOutput()); + } catch (Throwable e) { + e.printStackTrace(); + } + + pb = ProcessTools.createJavaProcessBuilder("-Xbootclasspath/a:.", + "-XX:+CompilationWarmUpRecording", + "-XX:-ClassUnloading", + "-XX:-Inline", + "-XX:+UseConcMarkSweepGC", + "-XX:-CMSClassUnloadingEnabled", + "-XX:-UseSharedSpaces", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:CompilationWarmUpRecordTime=10", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", + "-XX:+WhiteBoxAPI", + "-cp", classPath, + InnerA.class.getName(), "recording"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + + // test read logfile + pb = ProcessTools.createJavaProcessBuilder("-XX:-TieredCompilation", + "-Xbootclasspath/a:.", + "-XX:-UseSharedSpaces", + "-XX:+CompilationWarmUp", + "-XX:-Inline", + "-XX:CompilationWarmUpLogfile=./jitwarmup.log", + "-XX:+PrintCompilationWarmUpDetail", + "-XX:+PrintCompilation", + "-XX:+UnlockDiagnosticVMOptions", "-XX:+WhiteBoxAPI", + // "-XX:+DeoptimizeBeforeWarmUp", + "-cp", classPath, + InnerA.class.getName(), "startup"); + output = new OutputAnalyzer(pb.start()); + System.out.println(output.getOutput()); + output.shouldContain("catched java.lang.ExceptionInInitializerError"); + output.shouldHaveExitValue(0); + + logfile = new File("./jitwarmup.log"); + if (logfile.exists()) { + try { + logfile.delete(); + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + public static class InnerB { + static { + System.out.println("InnerB initialize"); + if (InnerA.flag) { + throw new NullPointerException(); + } + } + } + + public static class InnerA { + static { + System.out.println("InnerA initialize"); + } + + public static boolean flag = true; + + public static void main(String[] args) throws Exception { + if ("recording".equals(args[0])) { + System.out.println("begin recording!"); + try { + InnerA a = new InnerA(); + InnerB b = new InnerB(); + } catch (Throwable e) { + // ignore + } + Thread.sleep(10000); + System.out.println("done!"); + } else { + try { + System.out.println("begin startup!"); + JWarmUp.notifyApplicationStartUpIsDone(); + Thread.sleep(3000); + } catch (Throwable e) { + System.out.println("catched " + e.getClass().getName()); + } + } + } + } +} diff --git a/test/jwarmup/TestTieredCompilationInRecording.sh b/test/jwarmup/TestTieredCompilationInRecording.sh new file mode 100644 index 000000000..5ebe81386 --- /dev/null +++ b/test/jwarmup/TestTieredCompilationInRecording.sh @@ -0,0 +1,131 @@ +#!/bin/sh +# +# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# @test +# @summary test TieredCompilation in JWarmUp recording phase +# @run shell TestTieredCompilationInRecording.sh + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../test_env.sh + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +TEST_CLASS=TmpTestTieredCompilationInRecording +TEST_SOURCE=${TEST_CLASS}.java + +################################################################################### +cat > ${TESTCLASSES}${FS}$TEST_SOURCE << EOF +import java.lang.reflect.Method; +import java.util.*; +import com.alibaba.jwarmup.*; + +public class TmpTestTieredCompilationInRecording { + public static String[] aa = new String[0]; + public static List ls = new ArrayList(); + public String foo() { + for (int i = 0; i < 12000; i++) { + foo2(aa); + } + ls.add("x"); + return ls.get(0); + } + + public void foo2(String[] a) { + String s = "aa"; + if (ls.size() > 100 && a.length < 100) { + ls.clear(); + } else { + ls.add(s); + } + } + + public static void doBiz() throws Exception { + TmpTestTieredCompilationInRecording a = new TmpTestTieredCompilationInRecording(); + a.foo(); + Thread.sleep(1000); + a.foo(); + a.foo(); + Thread.sleep(1000); + a.foo(); + Thread.sleep(1000); + a.foo(); + Thread.sleep(10000); + a.foo(); + System.out.println("process is done!"); + } + + public static void main(String[] args) throws Exception { + if (args[0].equals("collection")) { + doBiz(); + } else if (args[0].equals("startup")) { + JWarmUp.notifyApplicationStartUpIsDone(); + if (!JWarmUp.checkIfCompilationIsComplete()) { + Thread.sleep(1000); + } + System.out.println("the JWarmUp Compilation is Done"); + System.out.println("Test Done"); + } + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE >> /dev/null 2>&1 +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE}" + exit 1 +fi + +#run Recording Model +${JAVA} -XX:+CompilationWarmUpRecording -XX:-ClassUnloading -XX:-UseSharedSpaces -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpRecordTime=10 -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} collection > output.txt.recording 2>&1 +sleep 1 +${JAVA} -XX:-TieredCompilation -XX:+CompilationWarmUp -XX:-UseSharedSpaces -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_CLASS} startup > output.txt 2>&1 + +function assert() +{ + i=0 + has_foo2=0 + while read line + do + i=$(($i+1)) + echo $i + echo $line + if [[ $line =~ "foo2" ]]; then + has_foo2=$i + fi + done < output.txt + if [[ $has_foo2 != 0 ]]; then + exit 0 + else + exit -1 + fi +} + +assert + diff --git a/test/jwarmup/issue9780156.sh b/test/jwarmup/issue9780156.sh new file mode 100644 index 000000000..1ab02c7e8 --- /dev/null +++ b/test/jwarmup/issue9780156.sh @@ -0,0 +1,195 @@ +#!/bin/sh +# +# Copyright (c) 2019 Alibaba Group Holding Limited. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Alibaba designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# + +# @test +# @summary test issue 9780156 +# @run shell issue9780156.sh + +if [ "${TESTSRC}" = "" ] +then + TESTSRC=${PWD} + echo "TESTSRC not set. Using "${TESTSRC}" as default" +fi +echo "TESTSRC=${TESTSRC}" +## Adding common setup Variables for running shell tests. +. ${TESTSRC}/../test_env.sh + +JAVA=${TESTJAVA}${FS}bin${FS}java +JAVAC=${TESTJAVA}${FS}bin${FS}javac +JAR=${TESTJAVA}${FS}bin${FS}jar +TEST_MAIN_CLASS=TmpTestMain +TEST_MAIN_SOURCE=${TEST_MAIN_CLASS}.java + +TEST_CLASS_A=TmpClassA +TEST_SOURCE_A=${TEST_CLASS_A}.java + +TEST_CLASS_B=TmpClassB +TEST_SOURCE_B=${TEST_CLASS_B}.java + +JARNAME=testjar.jar + +################################################################################### + +cat > ${TESTCLASSES}${FS}$TEST_SOURCE_A << EOF +import java.util.*; +public class TmpClassA { + static { + System.out.println("init TmpClassA"); + } + + public static void fooA() { + System.out.println(TmpClassB.class); + } + + TmpClassB bb; +} +EOF + +cat > ${TESTCLASSES}${FS}$TEST_SOURCE_B << EOF +public class TmpClassB { + static { + System.out.println("init TmpClassB"); + } +} + +EOF + +cat > ${TESTCLASSES}${FS}$TEST_MAIN_SOURCE << EOF +import java.lang.reflect.Method; +import java.util.*; +import java.io.*; +import java.net.*; + +public class TmpTestMain { + public static class TmpClassloader extends URLClassLoader { + public TmpClassloader(URL[] urls) { + super(urls); + } + + public TmpClassloader(String dir) throws MalformedURLException { + super(new URL[] { new URL("file:" + basePath + File.separatorChar + dir + File.separatorChar + jarName) }); + } + + public static String basePath = "${TESTCLASSES}"; + + public static String jarName = "${JARNAME}"; + } + + public static void foo(String dir) throws Exception { + TmpClassloader tcl = new TmpClassloader(dir); + Class c1 = tcl.loadClass("TmpClassA"); + c1.newInstance(); + Class c2 = tcl.loadClass("TmpClassB"); + c2.newInstance(); + } + + public static void bar(String dir) throws Exception { + TmpClassloader tcl = new TmpClassloader(dir); + Class c1 = tcl.loadClass("TmpClassA"); + c1.newInstance(); + } + + public static void main(String[] args) throws Exception { + try { + foo("dir1"); + bar("dir2"); + Class c = Class.forName("com.alibaba.jwarmup.JWarmUp"); + Method m = c.getMethod("notifyApplicationStartUpIsDone"); + Method m2 = c.getMethod("checkIfCompilationIsComplete"); + m.invoke(null); + while ((Boolean) m2.invoke(null) == false) { + Thread.sleep(1000); + } + System.out.println("the JWarmUp Compilation is Done"); + System.out.println("Test Done"); + foo("dir2"); + Thread.sleep(6000); + } catch (Exception e) { + e.printStackTrace(); + } + } +} +EOF + +# Do compilation +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_MAIN_SOURCE +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_MAIN_SOURCE}" + exit 1 +fi + +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE_A +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE_A}" + exit 1 +fi + +${JAVAC} -cp ${TESTCLASSES} -d ${TESTCLASSES} ${TESTCLASSES}${FS}$TEST_SOURCE_B +if [ $? != '0' ] +then + printf "Failed to compile ${TESTCLASSES}${FS}${TEST_SOURCE_B}" + exit 1 +fi + +rm -f ${JARNAME} + +cp ${TESTCLASSES}${FS}${TEST_CLASS_A}.class . +cp ${TESTCLASSES}${FS}${TEST_CLASS_B}.class . + +rm ${TESTCLASSES}${FS}${TEST_CLASS_A}.class +rm ${TESTCLASSES}${FS}${TEST_CLASS_B}.class + +${JAR} cvf ${JARNAME} ${TEST_CLASS_A}.class ${TEST_CLASS_B}.class + +mkdir -p ${TESTCLASSES}${FS}dir1${FS} +mkdir -p ${TESTCLASSES}${FS}dir2${FS} +cp ${JARNAME} ${TESTCLASSES}${FS}dir1${FS} +cp ${JARNAME} ${TESTCLASSES}${FS}dir2${FS} + + +#run Recording Model +${JAVA} -verbose:class -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUpRecording -XX:-ClassUnloading -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpRecordTime=5 -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_MAIN_CLASS} > output.txt 2>&1 +sleep 1 +${JAVA} -verbose:class -XX:-TieredCompilation -XX:-UseSharedSpaces -XX:+CompilationWarmUp -XX:+PrintCompilation -XX:+PrintCompilationWarmUpDetail -XX:CompilationWarmUpLogfile=./jitwarmup.log -cp ${TESTCLASSES} ${TEST_MAIN_CLASS} > output.txt 2>&1 + +function assert() +{ + i=0 + while read line + do + echo $line + if [[ $line =~ "Loaded TmpClassB" ]]; then + i=$(($i+1)) + echo $i + fi + done < output.txt + if [[ $i == 3 ]]; then + exit 0 + else + exit -1 + fi +} + +assert + diff --git a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java index af0584d36..ecaf83afb 100644 --- a/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java +++ b/test/testlibrary/whitebox/sun/hotspot/WhiteBox.java @@ -213,6 +213,17 @@ public class WhiteBox { public native int getContextForObject(Object obj); public native void printRegionInfo(int context); + // JitWarmUp + public native String[] getClassInitOrderList(); + public native String[] getCompiledMethodList(); + public native String[] getClassListFromLogfile(); + public native String[] getClassLoadedRecordList(); + public native String[] getMethodListFromLogfile(); + public native boolean forceCompileInJitWarmUp(Executable method); + public native String[] getClassChainSymbolList(); + public native int[] getClassChainStateList(); + public native boolean testFixDanglingPointerInDeopt(String name); + // VM flags public native void setBooleanVMFlag(String name, boolean value); public native void setIntxVMFlag(String name, long value); -- GitLab