diff options
Diffstat (limited to 'runtime/runtime.cc')
-rw-r--r-- | runtime/runtime.cc | 251 |
1 files changed, 161 insertions, 90 deletions
diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 02747d0d4d..c4694ee291 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -190,7 +190,6 @@ Runtime::Runtime() abort_(nullptr), stats_enabled_(false), is_running_on_memory_tool_(RUNNING_ON_MEMORY_TOOL), - profiler_started_(false), instrumentation_(), main_thread_group_(nullptr), system_thread_group_(nullptr), @@ -212,9 +211,11 @@ Runtime::Runtime() safe_mode_(false) { CheckAsmSupportOffsetsAndSizes(); std::fill(callee_save_methods_, callee_save_methods_ + arraysize(callee_save_methods_), 0u); + interpreter::CheckInterpreterAsmConstants(); } Runtime::~Runtime() { + ATRACE_BEGIN("Runtime shutdown"); if (is_native_bridge_loaded_) { UnloadNativeBridge(); } @@ -229,48 +230,55 @@ Runtime::~Runtime() { Thread* self = Thread::Current(); const bool attach_shutdown_thread = self == nullptr; if (attach_shutdown_thread) { + ATRACE_BEGIN("Attach shutdown thread"); CHECK(AttachCurrentThread("Shutdown thread", false, nullptr, false)); + ATRACE_END(); self = Thread::Current(); } else { LOG(WARNING) << "Current thread not detached in Runtime shutdown"; } { + ATRACE_BEGIN("Wait for shutdown cond"); MutexLock mu(self, *Locks::runtime_shutdown_lock_); shutting_down_started_ = true; while (threads_being_born_ > 0) { shutdown_cond_->Wait(self); } shutting_down_ = true; + ATRACE_END(); } // Shutdown and wait for the daemons. CHECK(self != nullptr); if (IsFinishedStarting()) { + ATRACE_BEGIN("Waiting for Daemons"); self->ClearException(); self->GetJniEnv()->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons, WellKnownClasses::java_lang_Daemons_stop); + ATRACE_END(); } Trace::Shutdown(); if (attach_shutdown_thread) { + ATRACE_BEGIN("Detach shutdown thread"); DetachCurrentThread(); + ATRACE_END(); self = nullptr; } - // Shut down background profiler before the runtime exits. - if (profiler_started_) { - BackgroundMethodSamplingProfiler::Shutdown(); - } - // Make sure to let the GC complete if it is running. heap_->WaitForGcToComplete(gc::kGcCauseBackground, self); heap_->DeleteThreadPool(); - if (jit_.get() != nullptr) { + if (jit_ != nullptr) { + ATRACE_BEGIN("Delete jit"); VLOG(jit) << "Deleting jit thread pool"; // Delete thread pool before the thread list since we don't want to wait forever on the // JIT compiler threads. jit_->DeleteThreadPool(); + // Similarly, stop the profile saver thread before deleting the thread list. + jit_->StopProfileSaver(); + ATRACE_END(); } // Make sure our internal threads are dead before we start tearing down things they're using. @@ -278,11 +286,13 @@ Runtime::~Runtime() { delete signal_catcher_; // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended. + ATRACE_BEGIN("Delete thread list"); delete thread_list_; + ATRACE_END(); // Delete the JIT after thread list to ensure that there is no remaining threads which could be // accessing the instrumentation when we delete it. - if (jit_.get() != nullptr) { + if (jit_ != nullptr) { VLOG(jit) << "Deleting jit"; jit_.reset(nullptr); } @@ -290,6 +300,7 @@ Runtime::~Runtime() { // Shutdown the fault manager if it was initialized. fault_manager.Shutdown(); + ATRACE_BEGIN("Delete state"); delete monitor_list_; delete monitor_pool_; delete class_linker_; @@ -306,10 +317,12 @@ Runtime::~Runtime() { low_4gb_arena_pool_.reset(); arena_pool_.reset(); MemMap::Shutdown(); + ATRACE_END(); // TODO: acquire a static mutex on Runtime to avoid racing. CHECK(instance_ == nullptr || instance_ == this); instance_ = nullptr; + ATRACE_END(); } struct AbortState { @@ -547,15 +560,12 @@ bool Runtime::Start() { // Use !IsAotCompiler so that we get test coverage, tests are never the zygote. if (!IsAotCompiler()) { ScopedObjectAccess soa(self); - gc::space::ImageSpace* image_space = heap_->GetBootImageSpace(); - if (image_space != nullptr) { - ATRACE_BEGIN("AddImageStringsToTable"); - GetInternTable()->AddImageStringsToTable(image_space); - ATRACE_END(); - ATRACE_BEGIN("MoveImageClassesToClassTable"); - GetClassLinker()->AddBootImageClassesToClassTable(); - ATRACE_END(); - } + ATRACE_BEGIN("AddImageStringsToTable"); + GetInternTable()->AddImagesStringsToTable(heap_->GetBootImageSpaces()); + ATRACE_END(); + ATRACE_BEGIN("MoveImageClassesToClassTable"); + GetClassLinker()->AddBootImageClassesToClassTable(); + ATRACE_END(); } // If we are the zygote then we need to wait until after forking to create the code cache @@ -564,7 +574,7 @@ bool Runtime::Start() { CreateJit(); } - if (!IsImageDex2OatEnabled() || !GetHeap()->HasImageSpace()) { + if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) { ScopedObjectAccess soa(self); StackHandleScope<1> hs(soa.Self()); auto klass(hs.NewHandle<mirror::Class>(mirror::Class::GetJavaLangClass())); @@ -593,6 +603,7 @@ bool Runtime::Start() { PreInitializeNativeBridge("."); } InitNonZygoteOrPostFork(self->GetJniEnv(), + /* is_system_server */ false, NativeBridgeAction::kInitialize, GetInstructionSetString(kRuntimeISA)); } @@ -616,8 +627,7 @@ bool Runtime::Start() { if (fd >= 0) { close(fd); } else if (errno != EEXIST) { - LOG(INFO) << "Failed to access the profile file. Profiler disabled."; - return true; + LOG(WARNING) << "Failed to access the profile file. Profiler disabled."; } } @@ -682,7 +692,8 @@ bool Runtime::InitZygote() { #endif } -void Runtime::InitNonZygoteOrPostFork(JNIEnv* env, NativeBridgeAction action, const char* isa) { +void Runtime::InitNonZygoteOrPostFork( + JNIEnv* env, bool is_system_server, NativeBridgeAction action, const char* isa) { is_zygote_ = false; if (is_native_bridge_loaded_) { @@ -704,7 +715,7 @@ void Runtime::InitNonZygoteOrPostFork(JNIEnv* env, NativeBridgeAction action, co // before fork aren't attributed to an app. heap_->ResetGcPerformanceInfo(); - if (!safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) { + if (!is_system_server && !safe_mode_ && jit_options_->UseJIT() && jit_.get() == nullptr) { // Note that when running ART standalone (not zygote, nor zygote fork), // the jit may have already been created. CreateJit(); @@ -752,61 +763,92 @@ void Runtime::StartDaemonThreads() { VLOG(startup) << "Runtime::StartDaemonThreads exiting"; } +// Attempts to open dex files from image(s). Given the image location, try to find the oat file +// and open it to get the stored dex file. If the image is the first for a multi-image boot +// classpath, go on and also open the other images. static bool OpenDexFilesFromImage(const std::string& image_location, std::vector<std::unique_ptr<const DexFile>>* dex_files, size_t* failures) { DCHECK(dex_files != nullptr) << "OpenDexFilesFromImage: out-param is nullptr"; - std::string system_filename; - bool has_system = false; - std::string cache_filename_unused; - bool dalvik_cache_exists_unused; - bool has_cache_unused; - bool is_global_cache_unused; - bool found_image = gc::space::ImageSpace::FindImageFilename(image_location.c_str(), - kRuntimeISA, - &system_filename, - &has_system, - &cache_filename_unused, - &dalvik_cache_exists_unused, - &has_cache_unused, - &is_global_cache_unused); - *failures = 0; - if (!found_image || !has_system) { - return false; - } - std::string error_msg; - // We are falling back to non-executable use of the oat file because patching failed, presumably - // due to lack of space. - std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str()); - std::string oat_location = ImageHeader::GetOatLocationFromImageLocation(image_location.c_str()); - std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str())); - if (file.get() == nullptr) { - return false; - } - std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg)); - if (elf_file.get() == nullptr) { - return false; - } - std::unique_ptr<const OatFile> oat_file( - OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg)); - if (oat_file == nullptr) { - LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg; - return false; - } - for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { - if (oat_dex_file == nullptr) { - *failures += 1; - continue; + // Use a work-list approach, so that we can easily reuse the opening code. + std::vector<std::string> image_locations; + image_locations.push_back(image_location); + + for (size_t index = 0; index < image_locations.size(); ++index) { + std::string system_filename; + bool has_system = false; + std::string cache_filename_unused; + bool dalvik_cache_exists_unused; + bool has_cache_unused; + bool is_global_cache_unused; + bool found_image = gc::space::ImageSpace::FindImageFilename(image_locations[index].c_str(), + kRuntimeISA, + &system_filename, + &has_system, + &cache_filename_unused, + &dalvik_cache_exists_unused, + &has_cache_unused, + &is_global_cache_unused); + + if (!found_image || !has_system) { + return false; } - std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); - if (dex_file.get() == nullptr) { - *failures += 1; - } else { - dex_files->push_back(std::move(dex_file)); + + // We are falling back to non-executable use of the oat file because patching failed, presumably + // due to lack of space. + std::string oat_filename = + ImageHeader::GetOatLocationFromImageLocation(system_filename.c_str()); + std::string oat_location = + ImageHeader::GetOatLocationFromImageLocation(image_locations[index].c_str()); + // Note: in the multi-image case, the image location may end in ".jar," and not ".art." Handle + // that here. + if (EndsWith(oat_location, ".jar")) { + oat_location.replace(oat_location.length() - 3, 3, "oat"); + } + + std::unique_ptr<File> file(OS::OpenFileForReading(oat_filename.c_str())); + if (file.get() == nullptr) { + return false; + } + std::string error_msg; + std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg)); + if (elf_file.get() == nullptr) { + return false; + } + std::unique_ptr<const OatFile> oat_file( + OatFile::OpenWithElfFile(elf_file.release(), oat_location, nullptr, &error_msg)); + if (oat_file == nullptr) { + LOG(WARNING) << "Unable to use '" << oat_filename << "' because " << error_msg; + return false; + } + + for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + if (oat_dex_file == nullptr) { + *failures += 1; + continue; + } + std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg); + if (dex_file.get() == nullptr) { + *failures += 1; + } else { + dex_files->push_back(std::move(dex_file)); + } + } + + if (index == 0) { + // First file. See if this is a multi-image environment, and if so, enqueue the other images. + const OatHeader& boot_oat_header = oat_file->GetOatHeader(); + const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath); + if (boot_cp != nullptr) { + gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0], + boot_cp, + &image_locations); + } } + + Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file)); } - Runtime::Current()->GetOatFileManager().RegisterOatFile(std::move(oat_file)); return true; } @@ -944,7 +986,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs)); ATRACE_END(); - if (heap_->GetBootImageSpace() == nullptr && !allow_dex_file_fallback_) { + if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) { LOG(ERROR) << "Dex file fallback disabled, cannot continue without image."; ATRACE_END(); return false; @@ -1052,7 +1094,7 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U); class_linker_ = new ClassLinker(intern_table_); - if (GetHeap()->HasImageSpace()) { + if (GetHeap()->HasBootImageSpace()) { ATRACE_BEGIN("InitFromImage"); std::string error_msg; bool result = class_linker_->InitFromImage(&error_msg); @@ -1062,7 +1104,9 @@ bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { return false; } if (kIsDebugBuild) { - GetHeap()->GetBootImageSpace()->VerifyImageAllocations(); + for (auto image_space : GetHeap()->GetBootImageSpaces()) { + image_space->VerifyImageAllocations(); + } } if (boot_class_path_string_.empty()) { // The bootclasspath is not explicitly specified: construct it from the loaded dex files. @@ -1209,20 +1253,36 @@ void Runtime::InitNativeMethods() { // First set up JniConstants, which is used by both the runtime's built-in native // methods and libcore. JniConstants::init(env); - WellKnownClasses::Init(env); // Then set up the native methods provided by the runtime itself. RegisterRuntimeNativeMethods(env); - // Then set up libcore, which is just a regular JNI library with a regular JNI_OnLoad. - // Most JNI libraries can just use System.loadLibrary, but libcore can't because it's - // the library that implements System.loadLibrary! + // Initialize classes used in JNI. The initialization requires runtime native + // methods to be loaded first. + WellKnownClasses::Init(env); + + // Then set up libjavacore / libopenjdk, which are just a regular JNI libraries with + // a regular JNI_OnLoad. Most JNI libraries can just use System.loadLibrary, but + // libcore can't because it's the library that implements System.loadLibrary! { std::string error_msg; - if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, nullptr, nullptr, &error_msg)) { + if (!java_vm_->LoadNativeLibrary(env, "libjavacore.so", nullptr, + /* is_shared_namespace */ false, + nullptr, nullptr, &error_msg)) { LOG(FATAL) << "LoadNativeLibrary failed for \"libjavacore.so\": " << error_msg; } } + { + constexpr const char* kOpenJdkLibrary = kIsDebugBuild + ? "libopenjdkd.so" + : "libopenjdk.so"; + std::string error_msg; + if (!java_vm_->LoadNativeLibrary(env, kOpenJdkLibrary, nullptr, + /* is_shared_namespace */ false, + nullptr, nullptr, &error_msg)) { + LOG(FATAL) << "LoadNativeLibrary failed for \"" << kOpenJdkLibrary << "\": " << error_msg; + } + } // Initialize well known classes that may invoke runtime native methods. WellKnownClasses::LateInit(env); @@ -1638,8 +1698,31 @@ void Runtime::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) { callee_save_methods_[type] = reinterpret_cast<uintptr_t>(method); } -void Runtime::SetJitProfilingFilename(const char* profile_output_filename) { +void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths, + const std::string& profile_output_filename) { + if (jit_.get() == nullptr) { + // We are not JITing. Nothing to do. + return; + } + + VLOG(profiler) << "Register app with " << profile_output_filename + << " " << Join(code_paths, ':'); + + if (profile_output_filename.empty()) { + LOG(WARNING) << "JIT profile information will not be recorded: profile filename is empty."; + return; + } + if (!FileExists(profile_output_filename)) { + LOG(WARNING) << "JIT profile information will not be recorded: profile file does not exits."; + return; + } + if (code_paths.empty()) { + LOG(WARNING) << "JIT profile information will not be recorded: code paths is empty."; + return; + } + profile_output_filename_ = profile_output_filename; + jit_->StartProfileSaver(profile_output_filename, code_paths); } // Transaction support. @@ -1785,18 +1868,6 @@ void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::strin argv->push_back(feature_string); } -void Runtime::MaybeSaveJitProfilingInfo() { - if (jit_.get() != nullptr && !profile_output_filename_.empty()) { - jit_->SaveProfilingInfo(profile_output_filename_); - } -} - -void Runtime::UpdateProfilerState(int state) { - if (state == kProfileBackground) { - MaybeSaveJitProfilingInfo(); - } -} - void Runtime::CreateJit() { CHECK(!IsAotCompiler()); if (GetInstrumentation()->IsForcedInterpretOnly()) { |