diff options
| -rw-r--r-- | Android.mk | 11 | ||||
| -rw-r--r-- | build/apex/Android.bp | 37 | ||||
| -rwxr-xr-x | build/apex/runtests.sh | 9 | ||||
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 11 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 81 | ||||
| -rw-r--r-- | runtime/class_linker.h | 10 | ||||
| -rw-r--r-- | runtime/class_loader_context.cc | 221 | ||||
| -rw-r--r-- | runtime/class_loader_context_test.cc | 311 | ||||
| -rw-r--r-- | runtime/class_root.h | 1 | ||||
| -rw-r--r-- | runtime/gc/collector/garbage_collector.cc | 13 | ||||
| -rw-r--r-- | runtime/gc/collector/garbage_collector.h | 4 | ||||
| -rw-r--r-- | runtime/gc/heap.cc | 8 | ||||
| -rw-r--r-- | runtime/gc/heap.h | 2 | ||||
| -rw-r--r-- | runtime/runtime.cc | 10 | ||||
| -rw-r--r-- | runtime/runtime.h | 3 | ||||
| -rw-r--r-- | test/testrunner/target_config.py | 9 | ||||
| -rwxr-xr-x | tools/build_linux_bionic_tests.sh | 5 |
17 files changed, 634 insertions, 112 deletions
diff --git a/Android.mk b/Android.mk index b9f617035c..d6392201cb 100644 --- a/Android.mk +++ b/Android.mk @@ -326,6 +326,17 @@ endif ####################### +# Android Runtime APEX. + +include $(CLEAR_VARS) +LOCAL_MODULE := com.android.runtime +# TODO: Select the debug module (`com.android.runtime.debug`) for +# userdebug and eng products. +LOCAL_REQUIRED_MODULES := com.android.runtime.release +include $(BUILD_PHONY_PACKAGE) + + +####################### # Fake packages for ART # The art-runtime package depends on the core ART libraries and binaries. It exists so we can diff --git a/build/apex/Android.bp b/build/apex/Android.bp index 8bddb5dfe7..bfaacb10fa 100644 --- a/build/apex/Android.bp +++ b/build/apex/Android.bp @@ -72,8 +72,41 @@ apex_key { private_key: "runtime.pem", } +// TODO: Introduce `apex_defaults` to factor common parts of `apex` +// module definitions below? + +// Release version of the Runtime APEX module (not containing debug +// variants nor tools), included in user builds. Also used for +// storage-constrained devices in userdebug and eng builds. +apex { + name: "com.android.runtime.release", + compile_multilib: "both", + manifest: "manifest.json", + native_shared_libs: art_runtime_base_native_shared_libs + + art_runtime_fake_native_shared_libs, + multilib: { + both: { + // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64` + // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp). + binaries: art_runtime_base_binaries_both, + }, + prefer32: { + binaries: art_runtime_base_binaries_prefer32, + }, + first: { + binaries: [], + } + }, + key: "com.android.runtime.key", + // TODO: Also package a `ld.config.txt` config file (to be placed in `etc/`). + // ... +} + +// "Debug" version of the Runtime APEX module (containing both release and +// debug variants, as well as additional tools), included in userdebug and +// eng build. apex { - name: "com.android.runtime", + name: "com.android.runtime.debug", compile_multilib: "both", manifest: "manifest.json", native_shared_libs: art_runtime_base_native_shared_libs @@ -87,7 +120,7 @@ apex { }, prefer32: { binaries: art_runtime_base_binaries_prefer32 - + art_runtime_debug_binaries_prefer32 + + art_runtime_debug_binaries_prefer32, }, first: { binaries: art_tools_binaries, diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh index 6af2a8bc5c..86cd8cb758 100755 --- a/build/apex/runtests.sh +++ b/build/apex/runtests.sh @@ -79,7 +79,8 @@ function finish { trap finish EXIT -apex_module="com.android.runtime" +# TODO: Also exercise the Release Runtime APEX (`com.android.runtime.release`). +apex_module="com.android.runtime.debug" # Build the Android Runtime APEX package (optional). $build_apex_p && say "Building package" && make "$apex_module" @@ -190,10 +191,8 @@ check_library libprofiled.so # libsigchain.so # libtombstoned_client.so # libunwindstack.so -# libvixl-arm64.so -# libvixl-arm.so -# libvixld-arm64.so -# libvixld-arm.so +# libvixl.so +# libvixld.so # ... # # ? diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index baeebd9371..1fa21d51fc 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -1202,6 +1202,17 @@ TEST_F(Dex2oatClassLoaderContextTest, ChainContext) { RunTest(context.c_str(), expected_classpath_key.c_str(), true); } +TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) { + std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested"); + std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex"); + + std::string context = "PCL[" + GetTestDexFileName("Nested") + "]" + + "{PCL[" + GetTestDexFileName("MultiDex") + "]}"; + std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" + + "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}"; + RunTest(context.c_str(), expected_classpath_key.c_str(), true); +} + class Dex2oatDeterminism : public Dex2oatTest {}; TEST_F(Dex2oatDeterminism, UnloadCompile) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index ce7dfaf8af..639fa7ec92 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -798,6 +798,8 @@ bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> b FindSystemClass(self, "Ljava/lang/StackTraceElement;")); SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass, FindSystemClass(self, "[Ljava/lang/StackTraceElement;")); + SetClassRoot(ClassRoot::kJavaLangClassLoaderArrayClass, + FindSystemClass(self, "[Ljava/lang/ClassLoader;")); // Create conflict tables that depend on the class linker. runtime->FixupConflictTables(); @@ -8994,21 +8996,14 @@ void ClassLinker::AllocAndSetPrimitiveArrayClassRoot(Thread* self, CheckSystemClass(self, primitive_array_class, descriptor); } -jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, - const std::vector<const DexFile*>& dex_files, - jclass loader_class, - jobject parent_loader) { - CHECK(self->GetJniEnv()->IsSameObject(loader_class, - WellKnownClasses::dalvik_system_PathClassLoader) || - self->GetJniEnv()->IsSameObject(loader_class, - WellKnownClasses::dalvik_system_DelegateLastClassLoader)); - - // SOAAlreadyRunnable is protected, and we need something to add a global reference. - // We could move the jobject to the callers, but all call-sites do this... - ScopedObjectAccessUnchecked soa(self); +ObjPtr<mirror::ClassLoader> ClassLinker::CreateWellKnownClassLoader( + Thread* self, + const std::vector<const DexFile*>& dex_files, + Handle<mirror::Class> loader_class, + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries, + Handle<mirror::ClassLoader> parent_loader) { - // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. - StackHandleScope<6> hs(self); + StackHandleScope<5> hs(self); ArtField* dex_elements_field = jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements); @@ -9098,8 +9093,8 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, } // Create the class loader.. - Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class)); - Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self)); + Handle<mirror::ClassLoader> h_class_loader = hs.NewHandle<mirror::ClassLoader>( + ObjPtr<mirror::ClassLoader>::DownCast(loader_class->AllocObject(self))); DCHECK(h_class_loader != nullptr); // Set DexPathList. ArtField* path_list_field = @@ -9115,15 +9110,59 @@ jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, "parent", "Ljava/lang/ClassLoader;"); DCHECK(parent_field != nullptr); + if (parent_loader.Get() == nullptr) { + ScopedObjectAccessUnchecked soa(self); + ObjPtr<mirror::Object> boot_loader(soa.Decode<mirror::Class>( + WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self)); + parent_field->SetObject<false>(h_class_loader.Get(), boot_loader); + } else { + parent_field->SetObject<false>(h_class_loader.Get(), parent_loader.Get()); + } + + ArtField* shared_libraries_field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + DCHECK(shared_libraries_field != nullptr); + shared_libraries_field->SetObject<false>(h_class_loader.Get(), shared_libraries.Get()); - ObjPtr<mirror::Object> parent = (parent_loader != nullptr) - ? soa.Decode<mirror::ClassLoader>(parent_loader) - : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self); - parent_field->SetObject<false>(h_class_loader.Get(), parent); + return h_class_loader.Get(); +} + +jobject ClassLinker::CreateWellKnownClassLoader(Thread* self, + const std::vector<const DexFile*>& dex_files, + jclass loader_class, + jobject parent_loader) { + CHECK(self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_PathClassLoader) || + self->GetJniEnv()->IsSameObject(loader_class, + WellKnownClasses::dalvik_system_DelegateLastClassLoader)); + + // SOAAlreadyRunnable is protected, and we need something to add a global reference. + // We could move the jobject to the callers, but all call-sites do this... + ScopedObjectAccessUnchecked soa(self); + + // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex. + StackHandleScope<3> hs(self); + + Handle<mirror::Class> h_loader_class = + hs.NewHandle<mirror::Class>(soa.Decode<mirror::Class>(loader_class)); + Handle<mirror::ClassLoader> parent = + hs.NewHandle<mirror::ClassLoader>(ObjPtr<mirror::ClassLoader>::DownCast( + (parent_loader != nullptr) + ? soa.Decode<mirror::ClassLoader>(parent_loader) + : nullptr)); + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries = + hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr); + + ObjPtr<mirror::ClassLoader> loader = CreateWellKnownClassLoader( + self, + dex_files, + h_loader_class, + shared_libraries, + parent); // Make it a global ref and return. ScopedLocalRef<jobject> local_ref( - soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get())); + soa.Env(), soa.Env()->AddLocalReference<jobject>(loader)); return soa.Env()->NewGlobalRef(local_ref.get()); } diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a65299a514..47931fec75 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -586,6 +586,16 @@ class ClassLinker { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::dex_lock_); + // Non-GlobalRef version of CreateWellKnownClassLoader + ObjPtr<mirror::ClassLoader> CreateWellKnownClassLoader( + Thread* self, + const std::vector<const DexFile*>& dex_files, + Handle<mirror::Class> loader_class, + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries, + Handle<mirror::ClassLoader> parent_loader) + REQUIRES_SHARED(Locks::mutator_lock_) + REQUIRES(!Locks::dex_lock_); + PointerSize GetImagePointerSize() const { return image_pointer_size_; } diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc index 0bae60a886..7ca6f86fe3 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -24,11 +24,14 @@ #include "base/stl_util.h" #include "class_linker.h" #include "class_loader_utils.h" +#include "class_root.h" #include "dex/art_dex_file_loader.h" #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "handle_scope-inl.h" #include "jni/jni_internal.h" +#include "mirror/object_array-alloc-inl.h" +#include "nativehelper/scoped_local_ref.h" #include "oat_file_assistant.h" #include "obj_ptr-inl.h" #include "runtime.h" @@ -107,6 +110,39 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string } } +static size_t FindMatchingSharedLibraryCloseMarker(const std::string& spec, + size_t shared_library_open_index) { + // Counter of opened shared library marker we've encountered so far. + uint32_t counter = 1; + // The index at which we're operating in the loop. + uint32_t string_index = shared_library_open_index + 1; + size_t shared_library_close = std::string::npos; + while (counter != 0) { + shared_library_close = + spec.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index); + size_t shared_library_open = + spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index); + if (shared_library_close == std::string::npos) { + // No matching closing marker. Return an error. + break; + } + + if ((shared_library_open == std::string::npos) || + (shared_library_close < shared_library_open)) { + // We have seen a closing marker. Decrement the counter. + --counter; + // Move the search index forward. + string_index = shared_library_close + 1; + } else { + // New nested opening marker. Increment the counter and move the search + // index after the marker. + ++counter; + string_index = shared_library_open + 1; + } + } + return shared_library_close; +} + // The expected format is: // "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]{ClassLoaderType2[...]}". // The checksum part of the format is expected only if parse_cheksums is true. @@ -160,7 +196,9 @@ std::unique_ptr<ClassLoaderContext::ClassLoaderInfo> ClassLoaderContext::ParseCl } } - if (class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) { + if ((class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) && + (class_loader_spec[class_loader_spec.length() - 2] != kClassLoaderSharedLibraryOpeningMark)) { + // Non-empty list of shared libraries. size_t start_index = class_loader_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark); if (start_index == std::string::npos) { return nullptr; @@ -168,8 +206,43 @@ std::unique_ptr<ClassLoaderContext::ClassLoaderInfo> ClassLoaderContext::ParseCl std::string shared_libraries_spec = class_loader_spec.substr(start_index + 1, class_loader_spec.length() - start_index - 2); std::vector<std::string> shared_libraries; - Split(shared_libraries_spec, kClassLoaderSharedLibrarySeparator, &shared_libraries); - for (const std::string& shared_library_spec : shared_libraries) { + size_t cursor = 0; + while (cursor != shared_libraries_spec.length()) { + size_t shared_library_separator = + shared_libraries_spec.find_first_of(kClassLoaderSharedLibrarySeparator, cursor); + size_t shared_library_open = + shared_libraries_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, cursor); + std::string shared_library_spec; + if (shared_library_separator == std::string::npos) { + // Only one shared library, for example: + // PCL[...] + shared_library_spec = + shared_libraries_spec.substr(cursor, shared_libraries_spec.length() - cursor); + cursor = shared_libraries_spec.length(); + } else if ((shared_library_open == std::string::npos) || + (shared_library_open > shared_library_separator)) { + // We found a shared library without nested shared libraries, for example: + // PCL[...]#PCL[...]{...} + shared_library_spec = + shared_libraries_spec.substr(cursor, shared_library_separator - cursor); + cursor = shared_library_separator + 1; + } else { + // The shared library contains nested shared libraries. Find the matching closing shared + // marker for it. + size_t closing_marker = + FindMatchingSharedLibraryCloseMarker(shared_libraries_spec, shared_library_open); + if (closing_marker == std::string::npos) { + // No matching closing marker, return an error. + return nullptr; + } + shared_library_spec = shared_libraries_spec.substr(cursor, closing_marker + 1 - cursor); + cursor = closing_marker + 1; + if (cursor != shared_libraries_spec.length() && + shared_libraries_spec[cursor] == kClassLoaderSharedLibrarySeparator) { + // Pass the shared library separator marker. + ++cursor; + } + } std::unique_ptr<ClassLoaderInfo> shared_library( ParseInternal(shared_library_spec, parse_checksums)); if (shared_library == nullptr) { @@ -250,50 +323,24 @@ ClassLoaderContext::ClassLoaderInfo* ClassLoaderContext::ParseInternal( // The class loader spec contains shared libraries. Find the matching closing // shared library marker for it. - // Counter of opened shared library marker we've encountered so far. - uint32_t counter = 1; - // The index at which we're operating in the loop. - uint32_t string_index = first_shared_library_open + 1; - while (counter != 0) { - size_t shared_library_close = - remaining.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index); - size_t shared_library_open = - remaining.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index); - if (shared_library_close == std::string::npos) { - // No matching closing market. Return an error. - LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; - return nullptr; - } - - if ((shared_library_open == std::string::npos) || - (shared_library_close < shared_library_open)) { - // We have seen a closing marker. Decrement the counter. - --counter; - if (counter == 0) { - // Found the matching closing marker. - class_loader_spec = remaining.substr(0, shared_library_close + 1); - - // Compute the remaining string to analyze. - if (remaining.size() == shared_library_close + 1) { - remaining = ""; - } else if ((remaining.size() == shared_library_close + 2) || - (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) { - LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; - return nullptr; - } else { - remaining = remaining.substr(shared_library_close + 2, - remaining.size() - shared_library_close - 2); - } - } else { - // Move the search index forward. - string_index = shared_library_close + 1; - } - } else { - // New nested opening marker. Increment the counter and move the search - // index after the marker. - ++counter; - string_index = shared_library_open + 1; - } + uint32_t shared_library_close = + FindMatchingSharedLibraryCloseMarker(remaining, first_shared_library_open); + if (shared_library_close == std::string::npos) { + LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; + return nullptr; + } + class_loader_spec = remaining.substr(0, shared_library_close + 1); + + // Compute the remaining string to analyze. + if (remaining.size() == shared_library_close + 1) { + remaining = ""; + } else if ((remaining.size() == shared_library_close + 2) || + (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) { + LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec; + return nullptr; + } else { + remaining = remaining.substr(shared_library_close + 2, + remaining.size() - shared_library_close - 2); } } @@ -571,20 +618,57 @@ static jclass GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type) { UNREACHABLE(); } -static jobject CreateClassLoaderInternal(Thread* self, - const ClassLoaderContext::ClassLoaderInfo& info) +static ObjPtr<mirror::ClassLoader> CreateClassLoaderInternal( + Thread* self, + ScopedObjectAccess& soa, + const ClassLoaderContext::ClassLoaderInfo& info, + bool add_compilation_sources, + const std::vector<const DexFile*>& compilation_sources) REQUIRES_SHARED(Locks::mutator_lock_) { - CHECK(info.shared_libraries.empty()) << "Class loader shared library not implemented yet"; - jobject parent = nullptr; + StackHandleScope<3> hs(self); + MutableHandle<mirror::ObjectArray<mirror::ClassLoader>> libraries( + hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr)); + + if (!info.shared_libraries.empty()) { + libraries.Assign(mirror::ObjectArray<mirror::ClassLoader>::Alloc( + self, + GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(), + info.shared_libraries.size())); + for (uint32_t i = 0; i < info.shared_libraries.size(); ++i) { + // We should only add the compilation sources to the first class loader. + libraries->Set(i, + CreateClassLoaderInternal( + self, + soa, + *info.shared_libraries[i].get(), + /* add_compilation_sources= */ false, + compilation_sources)); + } + } + + MutableHandle<mirror::ClassLoader> parent = hs.NewHandle<mirror::ClassLoader>(nullptr); if (info.parent != nullptr) { - parent = CreateClassLoaderInternal(self, *info.parent.get()); + // We should only add the compilation sources to the first class loader. + parent.Assign(CreateClassLoaderInternal( + self, soa, *info.parent.get(), /* add_compilation_sources= */ false, compilation_sources)); } std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector( info.opened_dex_files); + if (add_compilation_sources) { + // For the first class loader, its classpath comes first, followed by compilation sources. + // This ensures that whenever we need to resolve classes from it the classpath elements + // come first. + class_path_files.insert(class_path_files.end(), + compilation_sources.begin(), + compilation_sources.end()); + } + Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>( + soa.Decode<mirror::Class>(GetClassLoaderClass(info.type))); return Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader( self, class_path_files, - GetClassLoaderClass(info.type), + loader_class, + libraries, parent); } @@ -598,30 +682,21 @@ jobject ClassLoaderContext::CreateClassLoader( ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); if (class_loader_chain_ == nullptr) { + CHECK(special_shared_library_); return class_linker->CreatePathClassLoader(self, compilation_sources); } // Create the class loader of the parent. - jobject parent = nullptr; - if (class_loader_chain_->parent != nullptr) { - parent = CreateClassLoaderInternal(self, *class_loader_chain_->parent.get()); - } - - // We set up all the parents. Move on to create the first class loader. - // Its classpath comes first, followed by compilation sources. This ensures that whenever - // we need to resolve classes from it the classpath elements come first. - - std::vector<const DexFile*> first_class_loader_classpath = MakeNonOwningPointerVector( - class_loader_chain_->opened_dex_files); - first_class_loader_classpath.insert(first_class_loader_classpath.end(), - compilation_sources.begin(), - compilation_sources.end()); - - return class_linker->CreateWellKnownClassLoader( - self, - first_class_loader_classpath, - GetClassLoaderClass(class_loader_chain_->type), - parent); + ObjPtr<mirror::ClassLoader> loader = + CreateClassLoaderInternal(self, + soa, + *class_loader_chain_.get(), + /* add_compilation_sources= */ true, + compilation_sources); + // Make it a global ref and return. + ScopedLocalRef<jobject> local_ref( + soa.Env(), soa.Env()->AddLocalReference<jobject>(loader)); + return soa.Env()->NewGlobalRef(local_ref.get()); } std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const { diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc index f3e2ac00ba..e4aae47239 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -19,12 +19,14 @@ #include <gtest/gtest.h> #include "android-base/strings.h" +#include "art_field-inl.h" #include "base/dchecked_vector.h" #include "base/stl_util.h" #include "class_linker.h" #include "common_runtime_test.h" #include "dex/dex_file.h" #include "handle_scope-inl.h" +#include "jni/jni_internal.h" #include "mirror/class.h" #include "mirror/class_loader.h" #include "mirror/object-inl.h" @@ -284,6 +286,25 @@ TEST_F(ClassLoaderContextTest, ParseEnclosingSharedLibraries) { VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); } +TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries1) { + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( + "PCL[]{PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}}"); + VerifyContextSize(context.get(), 1); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s4.dex"); +} + +TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries2) { + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create( + "PCL[]{PCL[s1.dex]{PCL[s2.dex]}#PCL[s2.dex]#" + "PCL[s3.dex]#PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}#PCL[s5.dex]{PCL[s6.dex]}}"); + VerifyContextSize(context.get(), 1); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 2, "s3.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 3, "s4.dex"); + VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 4, "s5.dex"); +} + TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) { std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create("DLC[]"); @@ -316,6 +337,10 @@ TEST_F(ClassLoaderContextTest, ParseInvalidValidContexts) { ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]}")); ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]{")); ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC{DLC[s4.dex]}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{##}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]#}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]##}")); + ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]{PCL[s3.dex]}#}")); } TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) { @@ -605,6 +630,292 @@ TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) { soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); } +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + ":" + CreateClassPath(classpath_dex_b) + "]{" + + "DLC[" + CreateClassPath(classpath_dex_c) + "]#" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]}"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<4> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : classpath_dex_b) { + class_loader_1_dex_files.push_back(dex.get()); + } + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify the shared libraries. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 2); + + // Verify the first shared library. + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_DelegateLastClassLoader, + class_loader_2_dex_files); + raw_shared_libraries = field->GetObject(class_loader_2.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Verify the second shared library. + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1)); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // All class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_1->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} + +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_b) + "]};" + + "PCL[" + CreateClassPath(classpath_dex_c) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]}"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<6> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify its shared library. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_b); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_2_dex_files); + raw_shared_libraries = field->GetObject(class_loader_2.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Verify the parent. + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent()); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + + // Verify its shared library. + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0)); + std::vector<const DexFile*> class_loader_4_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_4, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_4_dex_files); + raw_shared_libraries = field->GetObject(class_loader_4.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} + +TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) { + // Setup the context. + std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC"); + std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD"); + + std::string context_spec = + "PCL[" + CreateClassPath(classpath_dex_a) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_b) + "]{" + + "PCL[" + CreateClassPath(classpath_dex_c) + "]}};" + + "PCL[" + CreateClassPath(classpath_dex_d) + "]"; + + std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec); + ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, "")); + + // Setup the compilation sources. + std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex"); + std::vector<const DexFile*> compilation_sources_raw = + MakeNonOwningPointerVector(compilation_sources); + + // Create the class loader. + jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw); + ASSERT_TRUE(jclass_loader != nullptr); + + // Verify the class loader. + ScopedObjectAccess soa(Thread::Current()); + + StackHandleScope<6> hs(soa.Self()); + Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle( + soa.Decode<mirror::ClassLoader>(jclass_loader)); + + // For the first class loader the class path dex files must come first and then the + // compilation sources. + std::vector<const DexFile*> class_loader_1_dex_files = + MakeNonOwningPointerVector(classpath_dex_a); + for (auto& dex : compilation_sources_raw) { + class_loader_1_dex_files.push_back(dex); + } + VerifyClassLoaderDexFiles(soa, + class_loader_1, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_1_dex_files); + + // Verify its shared library. + ArtField* field = + jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders); + ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0)); + std::vector<const DexFile*> class_loader_2_dex_files = + MakeNonOwningPointerVector(classpath_dex_b); + VerifyClassLoaderDexFiles(soa, + class_loader_2, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_2_dex_files); + + // Verify the shared library dependency of the shared library. + raw_shared_libraries = field->GetObject(class_loader_2.Get()); + ASSERT_TRUE(raw_shared_libraries != nullptr); + + Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2( + hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>())); + ASSERT_EQ(shared_libraries_2->GetLength(), 1); + + Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0)); + std::vector<const DexFile*> class_loader_3_dex_files = + MakeNonOwningPointerVector(classpath_dex_c); + VerifyClassLoaderDexFiles(soa, + class_loader_3, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_3_dex_files); + raw_shared_libraries = field->GetObject(class_loader_3.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Verify the parent. + Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent()); + std::vector<const DexFile*> class_loader_4_dex_files = + MakeNonOwningPointerVector(classpath_dex_d); + VerifyClassLoaderDexFiles(soa, + class_loader_4, + WellKnownClasses::dalvik_system_PathClassLoader, + class_loader_4_dex_files); + raw_shared_libraries = field->GetObject(class_loader_4.Get()); + ASSERT_TRUE(raw_shared_libraries == nullptr); + + // Class loaders should have the BootClassLoader as a parent. + ASSERT_TRUE(class_loader_2->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_3->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); + ASSERT_TRUE(class_loader_4->GetParent()->GetClass() == + soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)); +} TEST_F(ClassLoaderContextTest, RemoveSourceLocations) { std::unique_ptr<ClassLoaderContext> context = diff --git a/runtime/class_root.h b/runtime/class_root.h index 1cd135f2aa..1ff48457d6 100644 --- a/runtime/class_root.h +++ b/runtime/class_root.h @@ -101,6 +101,7 @@ class VarHandle; M(kLongArrayClass, "[J", mirror::PrimitiveArray<int64_t>) \ M(kShortArrayClass, "[S", mirror::PrimitiveArray<int16_t>) \ M(kJavaLangStackTraceElementArrayClass, "[Ljava/lang/StackTraceElement;", mirror::ObjectArray<mirror::StackTraceElement>) \ + M(kJavaLangClassLoaderArrayClass, "[Ljava/lang/ClassLoader;", mirror::ObjectArray<mirror::ClassLoader>) \ M(kDalvikSystemClassExt, "Ldalvik/system/ClassExt;", mirror::ClassExt) // Well known mirror::Class roots accessed via ClassLinker::GetClassRoots(). diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc index 5e3692ea9a..0294db7b7e 100644 --- a/runtime/gc/collector/garbage_collector.cc +++ b/runtime/gc/collector/garbage_collector.cc @@ -77,8 +77,9 @@ void GarbageCollector::RegisterPause(uint64_t nano_length) { void GarbageCollector::ResetCumulativeStatistics() { cumulative_timings_.Reset(); - total_time_ns_ = 0; - total_freed_objects_ = 0; + total_thread_cpu_time_ns_ = 0u; + total_time_ns_ = 0u; + total_freed_objects_ = 0u; total_freed_bytes_ = 0; MutexLock mu(Thread::Current(), pause_histogram_lock_); pause_histogram_.Reset(); @@ -88,6 +89,7 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName())); Thread* self = Thread::Current(); uint64_t start_time = NanoTime(); + uint64_t thread_cpu_start_time = ThreadCpuNanoTime(); Iteration* current_iteration = GetCurrentIteration(); current_iteration->Reset(gc_cause, clear_soft_references); // Note transaction mode is single-threaded and there's no asynchronous GC and this flag doesn't @@ -102,6 +104,8 @@ void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) { total_freed_bytes_ += current_iteration->GetFreedBytes() + current_iteration->GetFreedLargeObjectBytes(); uint64_t end_time = NanoTime(); + uint64_t thread_cpu_end_time = ThreadCpuNanoTime(); + total_thread_cpu_time_ns_ += thread_cpu_end_time - thread_cpu_start_time; current_iteration->SetDurationNs(end_time - start_time); if (Locks::mutator_lock_->IsExclusiveHeld(self)) { // The entire GC was paused, clear the fake pauses which might be in the pause times and add @@ -159,8 +163,9 @@ void GarbageCollector::ResetMeasurements() { pause_histogram_.Reset(); } cumulative_timings_.Reset(); - total_time_ns_ = 0; - total_freed_objects_ = 0; + total_thread_cpu_time_ns_ = 0u; + total_time_ns_ = 0u; + total_freed_objects_ = 0u; total_freed_bytes_ = 0; } diff --git a/runtime/gc/collector/garbage_collector.h b/runtime/gc/collector/garbage_collector.h index f722e8d855..2857881456 100644 --- a/runtime/gc/collector/garbage_collector.h +++ b/runtime/gc/collector/garbage_collector.h @@ -81,6 +81,9 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark void SwapBitmaps() REQUIRES(Locks::heap_bitmap_lock_) REQUIRES_SHARED(Locks::mutator_lock_); + uint64_t GetTotalCpuTime() const { + return total_thread_cpu_time_ns_; + } uint64_t GetTotalPausedTimeNs() REQUIRES(!pause_histogram_lock_); int64_t GetTotalFreedBytes() const { return total_freed_bytes_; @@ -146,6 +149,7 @@ class GarbageCollector : public RootVisitor, public IsMarkedVisitor, public Mark std::string name_; // Cumulative statistics. Histogram<uint64_t> pause_histogram_ GUARDED_BY(pause_histogram_lock_); + uint64_t total_thread_cpu_time_ns_; uint64_t total_time_ns_; uint64_t total_freed_objects_; int64_t total_freed_bytes_; diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index f0f81fc67e..e7f14c39d4 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1062,6 +1062,14 @@ void Heap::RemoveSpace(space::Space* space) { } } +uint64_t Heap::GetTotalGcCpuTime() { + uint64_t sum = 0; + for (auto& collector : garbage_collectors_) { + sum += collector->GetTotalCpuTime(); + } + return sum; +} + void Heap::DumpGcPerformanceInfo(std::ostream& os) { // Dump cumulative timings. os << "Dumping cumulative Gc timings\n"; diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c3ee5267b5..a43f3156f5 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -395,6 +395,8 @@ class Heap { REQUIRES(!Locks::heap_bitmap_lock_) REQUIRES(Locks::mutator_lock_); + uint64_t GetTotalGcCpuTime(); + // Set target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.setTargetHeapUtilization. void SetTargetHeapUtilization(float target); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 19c1623d1f..f016e874ca 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -278,6 +278,7 @@ Runtime::Runtime() // Initially assume we perceive jank in case the process state is never updated. process_state_(kProcessStateJankPerceptible), zygote_no_threads_(false), + process_cpu_start_time_(ProcessCpuNanoTime()), verifier_logging_threshold_ms_(100) { static_assert(Runtime::kCalleeSaveSize == static_cast<uint32_t>(CalleeSaveType::kLastCalleeSaveType), "Unexpected size"); @@ -321,11 +322,20 @@ Runtime::~Runtime() { } if (dump_gc_performance_on_shutdown_) { + process_cpu_end_time_ = ProcessCpuNanoTime(); ScopedLogSeverity sls(LogSeverity::INFO); // This can't be called from the Heap destructor below because it // could call RosAlloc::InspectAll() which needs the thread_list // to be still alive. heap_->DumpGcPerformanceInfo(LOG_STREAM(INFO)); + + uint64_t process_cpu_time = process_cpu_end_time_ - process_cpu_start_time_; + uint64_t gc_cpu_time = heap_->GetTotalGcCpuTime(); + float ratio = static_cast<float>(gc_cpu_time) / process_cpu_time; + LOG_STREAM(INFO) << "GC CPU time " << PrettyDuration(gc_cpu_time) + << " out of process CPU time " << PrettyDuration(process_cpu_time) + << " (" << ratio << ")" + << "\n"; } if (jit_ != nullptr) { diff --git a/runtime/runtime.h b/runtime/runtime.h index a696c2845e..3c057f3c41 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -1109,6 +1109,9 @@ class Runtime { MemMap protected_fault_page_; + uint64_t process_cpu_start_time_; + uint64_t process_cpu_end_time_; + uint32_t verifier_logging_threshold_ms_; DISALLOW_COPY_AND_ASSIGN(Runtime); diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index b1878133f0..077129fa6c 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -331,10 +331,9 @@ target_config = { }, 'art-linux-bionic-x64': { 'build': '{ANDROID_BUILD_TOP}/art/tools/build_linux_bionic_tests.sh {MAKE_OPTIONS}', - # Currently failing on the build-bots due to some library search path issue. - # 'run-test': ['--run-test-option=--bionic', - # '--host', - # '--64', - # '--no-build-dependencies'], + 'run-test': ['--run-test-option=--bionic', + '--host', + '--64', + '--no-build-dependencies'], }, } diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh index 3ee7d506aa..2b178f2cff 100755 --- a/tools/build_linux_bionic_tests.sh +++ b/tools/build_linux_bionic_tests.sh @@ -29,6 +29,7 @@ fi source build/envsetup.sh >&/dev/null # for get_build_var out_dir=$(get_build_var OUT_DIR) +host_out=$(get_build_var HOST_OUT) # TODO(b/31559095) Figure out a better way to do this. # @@ -80,8 +81,8 @@ bionic_targets=( $soong_out/bin/hiddenapi $soong_out/bin/hprof-conv $soong_out/bin/timeout_dumper - $(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g") - $(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")) + $(find $host_out/lib64 -type f | sed "s:$host_out:$soong_out:g") + $(find $host_out/nativetest64 -type f | sed "s:$host_out:$soong_out:g")) echo building ${bionic_targets[*]} |