diff options
author | 2017-04-28 15:28:29 +0100 | |
---|---|---|
committer | 2017-05-02 17:48:53 +0100 | |
commit | 1352f13e76aec57c522f9323cbe04d1368273a94 (patch) | |
tree | 757c7d9c055f4dadfff5814d702d17d938630763 | |
parent | 3f5f85d9382416b053359c3c1f11ab824843f3b4 (diff) |
Split image_test and oatdump_test.
With sufficient -j, splitting the oatdump_test makes gtests
finish faster, splitting image_test makes valgring tests
finish faster.
make -j 48 valgrind-test-art-host-gtest
- before: #### make completed successfully (05:20:16 (hh:mm:ss)) ####
- after: #### make completed successfully (03:05:43 (hh:mm:ss)) ####
Note that the new image_*test variants are still running
long after the rest of the tests is finished. Further
splitting may still be beneficial for host tests. Target
tests with lower -j parameter do not benefit that much.
Test: make -j 48 valgrind-test-art-host-gtest
Bug: 36849303
Change-Id: I2b887e6b3266f9d3efd7b4d71dde0fcf012829d2
-rw-r--r-- | build/Android.gtest.mk | 2 | ||||
-rw-r--r-- | compiler/Android.bp | 1 | ||||
-rw-r--r-- | compiler/image_test.cc | 483 | ||||
-rw-r--r-- | compiler/image_test.h | 497 | ||||
-rw-r--r-- | compiler/image_write_read_test.cc | 33 | ||||
-rw-r--r-- | oatdump/Android.bp | 5 | ||||
-rw-r--r-- | oatdump/oatdump_image_test.cc | 43 | ||||
-rw-r--r-- | oatdump/oatdump_test.cc | 225 | ||||
-rw-r--r-- | oatdump/oatdump_test.h | 229 |
9 files changed, 814 insertions, 704 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 11af1c0ca8..c4374f7101 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -227,6 +227,8 @@ ART_GTEST_oatdump_test_TARGET_DEPS := \ $(TARGET_CORE_IMAGE_DEFAULT_64) \ $(TARGET_CORE_IMAGE_DEFAULT_32) \ oatdump +ART_GTEST_oatdump_image_test_HOST_DEPS := $(ART_GTEST_oatdump_test_HOST_DEPS) +ART_GTEST_oatdump_image_test_TARGET_DEPS := $(ART_GTEST_oatdump_test_TARGET_DEPS) # Profile assistant tests requires profman utility. ART_GTEST_profile_assistant_test_HOST_DEPS := \ diff --git a/compiler/Android.bp b/compiler/Android.bp index 6ef866a3c6..a2b07af810 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -338,6 +338,7 @@ art_cc_test { "elf_writer_test.cc", "exception_test.cc", "image_test.cc", + "image_write_read_test.cc", "jni/jni_compiler_test.cc", "linker/multi_oat_relative_patcher_test.cc", "linker/output_stream_test.cc", diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 7e53d8d2ab..9d7aff769b 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -14,492 +14,17 @@ * limitations under the License. */ -#include "image.h" - -#include <memory> -#include <string> +#include <string.h> #include <vector> -#include "android-base/stringprintf.h" +#include "image_test.h" -#include "art_method-inl.h" -#include "base/unix_file/fd_file.h" -#include "class_linker-inl.h" -#include "compiler_callbacks.h" -#include "common_compiler_test.h" -#include "debug/method_debug_info.h" -#include "dex/quick_compiler_callbacks.h" -#include "driver/compiler_options.h" -#include "elf_writer.h" -#include "elf_writer_quick.h" -#include "gc/space/image_space.h" -#include "image_writer.h" -#include "linker/buffered_output_stream.h" -#include "linker/file_output_stream.h" -#include "linker/multi_oat_relative_patcher.h" -#include "lock_word.h" -#include "mirror/object-inl.h" -#include "oat_writer.h" +#include "image.h" #include "scoped_thread_state_change-inl.h" -#include "signal_catcher.h" -#include "utils.h" +#include "thread.h" namespace art { -static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS; - -struct CompilationHelper { - std::vector<std::string> dex_file_locations; - std::vector<ScratchFile> image_locations; - std::vector<std::unique_ptr<const DexFile>> extra_dex_files; - std::vector<ScratchFile> image_files; - std::vector<ScratchFile> oat_files; - std::vector<ScratchFile> vdex_files; - std::string image_dir; - - void Compile(CompilerDriver* driver, - ImageHeader::StorageMode storage_mode); - - std::vector<size_t> GetImageObjectSectionSizes(); - - ~CompilationHelper(); -}; - -class ImageTest : public CommonCompilerTest { - protected: - virtual void SetUp() { - ReserveImageSpace(); - CommonCompilerTest::SetUp(); - } - - void TestWriteRead(ImageHeader::StorageMode storage_mode); - - void Compile(ImageHeader::StorageMode storage_mode, - CompilationHelper& out_helper, - const std::string& extra_dex = "", - const std::initializer_list<std::string>& image_classes = {}); - - void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { - CommonCompilerTest::SetUpRuntimeOptions(options); - callbacks_.reset(new QuickCompilerCallbacks( - verification_results_.get(), - CompilerCallbacks::CallbackMode::kCompileBootImage)); - options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); - } - - std::unordered_set<std::string>* GetImageClasses() OVERRIDE { - return new std::unordered_set<std::string>(image_classes_); - } - - ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) - REQUIRES_SHARED(Locks::mutator_lock_) { - PointerSize pointer_size = class_linker_->GetImagePointerSize(); - for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { - if (strcmp(origin->GetName(), m.GetName()) == 0 && - origin->GetSignature() == m.GetSignature()) { - return &m; - } - } - return nullptr; - } - - private: - std::unordered_set<std::string> image_classes_; -}; - -CompilationHelper::~CompilationHelper() { - for (ScratchFile& image_file : image_files) { - image_file.Unlink(); - } - for (ScratchFile& oat_file : oat_files) { - oat_file.Unlink(); - } - for (ScratchFile& vdex_file : vdex_files) { - vdex_file.Unlink(); - } - const int rmdir_result = rmdir(image_dir.c_str()); - CHECK_EQ(0, rmdir_result); -} - -std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() { - std::vector<size_t> ret; - for (ScratchFile& image_file : image_files) { - std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); - CHECK(file.get() != nullptr); - ImageHeader image_header; - CHECK_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); - CHECK(image_header.IsValid()); - ret.push_back(image_header.GetImageSize()); - } - return ret; -} - -void CompilationHelper::Compile(CompilerDriver* driver, - ImageHeader::StorageMode storage_mode) { - ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); - std::vector<const DexFile*> class_path = class_linker->GetBootClassPath(); - - for (const std::unique_ptr<const DexFile>& dex_file : extra_dex_files) { - { - ScopedObjectAccess soa(Thread::Current()); - // Inject in boot class path so that the compiler driver can see it. - class_linker->AppendToBootClassPath(soa.Self(), *dex_file.get()); - } - class_path.push_back(dex_file.get()); - } - - // Enable write for dex2dex. - for (const DexFile* dex_file : class_path) { - dex_file_locations.push_back(dex_file->GetLocation()); - if (dex_file->IsReadOnly()) { - dex_file->EnableWrite(); - } - } - { - // Create a generic tmp file, to be the base of the .art and .oat temporary files. - ScratchFile location; - for (int i = 0; i < static_cast<int>(class_path.size()); ++i) { - std::string cur_location = - android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i); - image_locations.push_back(ScratchFile(cur_location)); - } - } - std::vector<std::string> image_filenames; - for (ScratchFile& file : image_locations) { - std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA)); - image_filenames.push_back(image_filename); - size_t pos = image_filename.rfind('/'); - CHECK_NE(pos, std::string::npos) << image_filename; - if (image_dir.empty()) { - image_dir = image_filename.substr(0, pos); - int mkdir_result = mkdir(image_dir.c_str(), 0700); - CHECK_EQ(0, mkdir_result) << image_dir; - } - image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str()))); - } - - std::vector<std::string> oat_filenames; - std::vector<std::string> vdex_filenames; - for (const std::string& image_filename : image_filenames) { - std::string oat_filename = ReplaceFileExtension(image_filename, "oat"); - oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str()))); - oat_filenames.push_back(oat_filename); - std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex"); - vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str()))); - vdex_filenames.push_back(vdex_filename); - } - - std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map; - std::vector<const char*> oat_filename_vector; - for (const std::string& file : oat_filenames) { - oat_filename_vector.push_back(file.c_str()); - } - std::vector<const char*> image_filename_vector; - for (const std::string& file : image_filenames) { - image_filename_vector.push_back(file.c_str()); - } - size_t image_idx = 0; - for (const DexFile* dex_file : class_path) { - dex_file_to_oat_index_map.emplace(dex_file, image_idx); - ++image_idx; - } - // TODO: compile_pic should be a test argument. - std::unique_ptr<ImageWriter> writer(new ImageWriter(*driver, - kRequestedImageBase, - /*compile_pic*/false, - /*compile_app_image*/false, - storage_mode, - oat_filename_vector, - dex_file_to_oat_index_map)); - { - { - jobject class_loader = nullptr; - TimingLogger timings("ImageTest::WriteRead", false, false); - TimingLogger::ScopedTiming t("CompileAll", &timings); - driver->SetDexFilesForOatFile(class_path); - driver->CompileAll(class_loader, class_path, /* verifier_deps */ nullptr, &timings); - - t.NewTiming("WriteElf"); - SafeMap<std::string, std::string> key_value_store; - std::vector<const char*> dex_filename_vector; - for (size_t i = 0; i < class_path.size(); ++i) { - dex_filename_vector.push_back(""); - } - key_value_store.Put(OatHeader::kBootClassPathKey, - gc::space::ImageSpace::GetMultiImageBootClassPath( - dex_filename_vector, - oat_filename_vector, - image_filename_vector)); - - std::vector<std::unique_ptr<ElfWriter>> elf_writers; - std::vector<std::unique_ptr<OatWriter>> oat_writers; - for (ScratchFile& oat_file : oat_files) { - elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), - &driver->GetCompilerOptions(), - oat_file.GetFile())); - elf_writers.back()->Start(); - oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, - &timings, - /*profile_compilation_info*/nullptr)); - } - - std::vector<OutputStream*> rodata; - std::vector<std::unique_ptr<MemMap>> opened_dex_files_map; - std::vector<std::unique_ptr<const DexFile>> opened_dex_files; - // Now that we have finalized key_value_store_, start writing the oat file. - for (size_t i = 0, size = oat_writers.size(); i != size; ++i) { - const DexFile* dex_file = class_path[i]; - rodata.push_back(elf_writers[i]->StartRoData()); - ArrayRef<const uint8_t> raw_dex_file( - reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), - dex_file->GetHeader().file_size_); - oat_writers[i]->AddRawDexFileSource(raw_dex_file, - dex_file->GetLocation().c_str(), - dex_file->GetLocationChecksum()); - - std::unique_ptr<MemMap> cur_opened_dex_files_map; - std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files; - bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( - kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(), - rodata.back(), - driver->GetInstructionSet(), - driver->GetInstructionSetFeatures(), - &key_value_store, - /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. - /* update_input_vdex */ false, - &cur_opened_dex_files_map, - &cur_opened_dex_files); - ASSERT_TRUE(dex_files_ok); - - if (cur_opened_dex_files_map != nullptr) { - opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map)); - for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) { - // dex_file_oat_index_map_.emplace(dex_file.get(), i); - opened_dex_files.push_back(std::move(cur_dex_file)); - } - } else { - ASSERT_TRUE(cur_opened_dex_files.empty()); - } - } - bool image_space_ok = writer->PrepareImageAddressSpace(); - ASSERT_TRUE(image_space_ok); - - if (kIsVdexEnabled) { - for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { - std::unique_ptr<BufferedOutputStream> vdex_out( - MakeUnique<BufferedOutputStream>( - MakeUnique<FileOutputStream>(vdex_files[i].GetFile()))); - oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); - oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); - } - } - - for (size_t i = 0, size = oat_files.size(); i != size; ++i) { - linker::MultiOatRelativePatcher patcher(driver->GetInstructionSet(), - driver->GetInstructionSetFeatures()); - OatWriter* const oat_writer = oat_writers[i].get(); - ElfWriter* const elf_writer = elf_writers[i].get(); - std::vector<const DexFile*> cur_dex_files(1u, class_path[i]); - oat_writer->Initialize(driver, writer.get(), cur_dex_files); - oat_writer->PrepareLayout(&patcher); - size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); - size_t text_size = oat_writer->GetOatSize() - rodata_size; - elf_writer->PrepareDynamicSection(rodata_size, - text_size, - oat_writer->GetBssSize(), - oat_writer->GetBssRootsOffset()); - - writer->UpdateOatFileLayout(i, - elf_writer->GetLoadedSize(), - oat_writer->GetOatDataOffset(), - oat_writer->GetOatSize()); - - bool rodata_ok = oat_writer->WriteRodata(rodata[i]); - ASSERT_TRUE(rodata_ok); - elf_writer->EndRoData(rodata[i]); - - OutputStream* text = elf_writer->StartText(); - bool text_ok = oat_writer->WriteCode(text); - ASSERT_TRUE(text_ok); - elf_writer->EndText(text); - - bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); - ASSERT_TRUE(header_ok); - - writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader()); - - elf_writer->WriteDynamicSection(); - elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); - - bool success = elf_writer->End(); - ASSERT_TRUE(success); - } - } - - bool success_image = writer->Write(kInvalidFd, - image_filename_vector, - oat_filename_vector); - ASSERT_TRUE(success_image); - - for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) { - const char* oat_filename = oat_filenames[i].c_str(); - std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); - ASSERT_TRUE(oat_file != nullptr); - bool success_fixup = ElfWriter::Fixup(oat_file.get(), - writer->GetOatDataBegin(i)); - ASSERT_TRUE(success_fixup); - ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file " - << oat_filename; - } - } -} - -void ImageTest::Compile(ImageHeader::StorageMode storage_mode, - CompilationHelper& helper, - const std::string& extra_dex, - const std::initializer_list<std::string>& image_classes) { - for (const std::string& image_class : image_classes) { - image_classes_.insert(image_class); - } - CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); - // Set inline filter values. - compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); - image_classes_.clear(); - if (!extra_dex.empty()) { - helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); - } - helper.Compile(compiler_driver_.get(), storage_mode); - if (image_classes.begin() != image_classes.end()) { - // Make sure the class got initialized. - ScopedObjectAccess soa(Thread::Current()); - ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - for (const std::string& image_class : image_classes) { - mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); - EXPECT_TRUE(klass != nullptr); - EXPECT_TRUE(klass->IsInitialized()); - } - } -} - -void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { - CompilationHelper helper; - Compile(storage_mode, /*out*/ helper); - std::vector<uint64_t> image_file_sizes; - for (ScratchFile& image_file : helper.image_files) { - std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); - ASSERT_TRUE(file.get() != nullptr); - ImageHeader image_header; - ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); - ASSERT_TRUE(image_header.IsValid()); - const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap); - ASSERT_GE(bitmap_section.Offset(), sizeof(image_header)); - ASSERT_NE(0U, bitmap_section.Size()); - - gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_TRUE(heap->HaveContinuousSpaces()); - gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); - ASSERT_FALSE(space->IsImageSpace()); - ASSERT_TRUE(space != nullptr); - ASSERT_TRUE(space->IsMallocSpace()); - image_file_sizes.push_back(file->GetLength()); - } - - ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr); - std::unordered_set<std::string> image_classes(*compiler_driver_->GetImageClasses()); - - // Need to delete the compiler since it has worker threads which are attached to runtime. - compiler_driver_.reset(); - - // Tear down old runtime before making a new one, clearing out misc state. - - // Remove the reservation of the memory for use to load the image. - // Need to do this before we reset the runtime. - UnreserveImageSpace(); - - helper.extra_dex_files.clear(); - runtime_.reset(); - java_lang_dex_file_ = nullptr; - - MemMap::Init(); - - RuntimeOptions options; - std::string image("-Ximage:"); - image.append(helper.image_locations[0].GetFilename()); - options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr))); - // By default the compiler this creates will not include patch information. - options.push_back(std::make_pair("-Xnorelocate", nullptr)); - - if (!Runtime::Create(options, false)) { - LOG(FATAL) << "Failed to create runtime"; - return; - } - runtime_.reset(Runtime::Current()); - // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, - // give it away now and then switch to a more managable ScopedObjectAccess. - Thread::Current()->TransitionFromRunnableToSuspended(kNative); - ScopedObjectAccess soa(Thread::Current()); - ASSERT_TRUE(runtime_.get() != nullptr); - class_linker_ = runtime_->GetClassLinker(); - - gc::Heap* heap = Runtime::Current()->GetHeap(); - ASSERT_TRUE(heap->HasBootImageSpace()); - ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace()); - - // We loaded the runtime with an explicit image, so it must exist. - ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size()); - for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) { - std::unique_ptr<const DexFile> dex( - LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str())); - ASSERT_TRUE(dex != nullptr); - uint64_t image_file_size = image_file_sizes[i]; - gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i]; - ASSERT_TRUE(image_space != nullptr); - if (storage_mode == ImageHeader::kStorageModeUncompressed) { - // Uncompressed, image should be smaller than file. - ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size); - } else if (image_file_size > 16 * KB) { - // Compressed, file should be smaller than image. Not really valid for small images. - ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize()); - } - - image_space->VerifyImageAllocations(); - uint8_t* image_begin = image_space->Begin(); - uint8_t* image_end = image_space->End(); - if (i == 0) { - // This check is only valid for image 0. - CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin)); - } - for (size_t j = 0; j < dex->NumClassDefs(); ++j) { - const DexFile::ClassDef& class_def = dex->GetClassDef(j); - const char* descriptor = dex->GetClassDescriptor(class_def); - mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor); - EXPECT_TRUE(klass != nullptr) << descriptor; - if (image_classes.find(descriptor) == image_classes.end()) { - EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end || - reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor; - } else { - // Image classes should be located inside the image. - EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor; - EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor; - } - EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); - } - } -} - -TEST_F(ImageTest, WriteReadUncompressed) { - TestWriteRead(ImageHeader::kStorageModeUncompressed); -} - -TEST_F(ImageTest, WriteReadLZ4) { - TestWriteRead(ImageHeader::kStorageModeLZ4); -} - -TEST_F(ImageTest, WriteReadLZ4HC) { - TestWriteRead(ImageHeader::kStorageModeLZ4HC); -} - TEST_F(ImageTest, TestImageLayout) { std::vector<size_t> image_sizes; std::vector<size_t> image_sizes_extra; diff --git a/compiler/image_test.h b/compiler/image_test.h new file mode 100644 index 0000000000..2f15ff4815 --- /dev/null +++ b/compiler/image_test.h @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_IMAGE_TEST_H_ +#define ART_COMPILER_IMAGE_TEST_H_ + +#include "image.h" + +#include <memory> +#include <string> +#include <vector> + +#include "android-base/stringprintf.h" + +#include "art_method-inl.h" +#include "base/unix_file/fd_file.h" +#include "class_linker-inl.h" +#include "compiler_callbacks.h" +#include "common_compiler_test.h" +#include "debug/method_debug_info.h" +#include "dex/quick_compiler_callbacks.h" +#include "driver/compiler_options.h" +#include "elf_writer.h" +#include "elf_writer_quick.h" +#include "gc/space/image_space.h" +#include "image_writer.h" +#include "linker/buffered_output_stream.h" +#include "linker/file_output_stream.h" +#include "linker/multi_oat_relative_patcher.h" +#include "lock_word.h" +#include "mirror/object-inl.h" +#include "oat_writer.h" +#include "scoped_thread_state_change-inl.h" +#include "signal_catcher.h" +#include "utils.h" + +namespace art { + +static const uintptr_t kRequestedImageBase = ART_BASE_ADDRESS; + +struct CompilationHelper { + std::vector<std::string> dex_file_locations; + std::vector<ScratchFile> image_locations; + std::vector<std::unique_ptr<const DexFile>> extra_dex_files; + std::vector<ScratchFile> image_files; + std::vector<ScratchFile> oat_files; + std::vector<ScratchFile> vdex_files; + std::string image_dir; + + void Compile(CompilerDriver* driver, + ImageHeader::StorageMode storage_mode); + + std::vector<size_t> GetImageObjectSectionSizes(); + + ~CompilationHelper(); +}; + +class ImageTest : public CommonCompilerTest { + protected: + virtual void SetUp() { + ReserveImageSpace(); + CommonCompilerTest::SetUp(); + } + + void TestWriteRead(ImageHeader::StorageMode storage_mode); + + void Compile(ImageHeader::StorageMode storage_mode, + CompilationHelper& out_helper, + const std::string& extra_dex = "", + const std::initializer_list<std::string>& image_classes = {}); + + void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { + CommonCompilerTest::SetUpRuntimeOptions(options); + callbacks_.reset(new QuickCompilerCallbacks( + verification_results_.get(), + CompilerCallbacks::CallbackMode::kCompileBootImage)); + options->push_back(std::make_pair("compilercallbacks", callbacks_.get())); + } + + std::unordered_set<std::string>* GetImageClasses() OVERRIDE { + return new std::unordered_set<std::string>(image_classes_); + } + + ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { + if (strcmp(origin->GetName(), m.GetName()) == 0 && + origin->GetSignature() == m.GetSignature()) { + return &m; + } + } + return nullptr; + } + + private: + std::unordered_set<std::string> image_classes_; +}; + +inline CompilationHelper::~CompilationHelper() { + for (ScratchFile& image_file : image_files) { + image_file.Unlink(); + } + for (ScratchFile& oat_file : oat_files) { + oat_file.Unlink(); + } + for (ScratchFile& vdex_file : vdex_files) { + vdex_file.Unlink(); + } + const int rmdir_result = rmdir(image_dir.c_str()); + CHECK_EQ(0, rmdir_result); +} + +inline std::vector<size_t> CompilationHelper::GetImageObjectSectionSizes() { + std::vector<size_t> ret; + for (ScratchFile& image_file : image_files) { + std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); + CHECK(file.get() != nullptr); + ImageHeader image_header; + CHECK_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); + CHECK(image_header.IsValid()); + ret.push_back(image_header.GetImageSize()); + } + return ret; +} + +inline void CompilationHelper::Compile(CompilerDriver* driver, + ImageHeader::StorageMode storage_mode) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + std::vector<const DexFile*> class_path = class_linker->GetBootClassPath(); + + for (const std::unique_ptr<const DexFile>& dex_file : extra_dex_files) { + { + ScopedObjectAccess soa(Thread::Current()); + // Inject in boot class path so that the compiler driver can see it. + class_linker->AppendToBootClassPath(soa.Self(), *dex_file.get()); + } + class_path.push_back(dex_file.get()); + } + + // Enable write for dex2dex. + for (const DexFile* dex_file : class_path) { + dex_file_locations.push_back(dex_file->GetLocation()); + if (dex_file->IsReadOnly()) { + dex_file->EnableWrite(); + } + } + { + // Create a generic tmp file, to be the base of the .art and .oat temporary files. + ScratchFile location; + for (int i = 0; i < static_cast<int>(class_path.size()); ++i) { + std::string cur_location = + android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i); + image_locations.push_back(ScratchFile(cur_location)); + } + } + std::vector<std::string> image_filenames; + for (ScratchFile& file : image_locations) { + std::string image_filename(GetSystemImageFilename(file.GetFilename().c_str(), kRuntimeISA)); + image_filenames.push_back(image_filename); + size_t pos = image_filename.rfind('/'); + CHECK_NE(pos, std::string::npos) << image_filename; + if (image_dir.empty()) { + image_dir = image_filename.substr(0, pos); + int mkdir_result = mkdir(image_dir.c_str(), 0700); + CHECK_EQ(0, mkdir_result) << image_dir; + } + image_files.push_back(ScratchFile(OS::CreateEmptyFile(image_filename.c_str()))); + } + + std::vector<std::string> oat_filenames; + std::vector<std::string> vdex_filenames; + for (const std::string& image_filename : image_filenames) { + std::string oat_filename = ReplaceFileExtension(image_filename, "oat"); + oat_files.push_back(ScratchFile(OS::CreateEmptyFile(oat_filename.c_str()))); + oat_filenames.push_back(oat_filename); + std::string vdex_filename = ReplaceFileExtension(image_filename, "vdex"); + vdex_files.push_back(ScratchFile(OS::CreateEmptyFile(vdex_filename.c_str()))); + vdex_filenames.push_back(vdex_filename); + } + + std::unordered_map<const DexFile*, size_t> dex_file_to_oat_index_map; + std::vector<const char*> oat_filename_vector; + for (const std::string& file : oat_filenames) { + oat_filename_vector.push_back(file.c_str()); + } + std::vector<const char*> image_filename_vector; + for (const std::string& file : image_filenames) { + image_filename_vector.push_back(file.c_str()); + } + size_t image_idx = 0; + for (const DexFile* dex_file : class_path) { + dex_file_to_oat_index_map.emplace(dex_file, image_idx); + ++image_idx; + } + // TODO: compile_pic should be a test argument. + std::unique_ptr<ImageWriter> writer(new ImageWriter(*driver, + kRequestedImageBase, + /*compile_pic*/false, + /*compile_app_image*/false, + storage_mode, + oat_filename_vector, + dex_file_to_oat_index_map)); + { + { + jobject class_loader = nullptr; + TimingLogger timings("ImageTest::WriteRead", false, false); + TimingLogger::ScopedTiming t("CompileAll", &timings); + driver->SetDexFilesForOatFile(class_path); + driver->CompileAll(class_loader, class_path, /* verifier_deps */ nullptr, &timings); + + t.NewTiming("WriteElf"); + SafeMap<std::string, std::string> key_value_store; + std::vector<const char*> dex_filename_vector; + for (size_t i = 0; i < class_path.size(); ++i) { + dex_filename_vector.push_back(""); + } + key_value_store.Put(OatHeader::kBootClassPathKey, + gc::space::ImageSpace::GetMultiImageBootClassPath( + dex_filename_vector, + oat_filename_vector, + image_filename_vector)); + + std::vector<std::unique_ptr<ElfWriter>> elf_writers; + std::vector<std::unique_ptr<OatWriter>> oat_writers; + for (ScratchFile& oat_file : oat_files) { + elf_writers.emplace_back(CreateElfWriterQuick(driver->GetInstructionSet(), + driver->GetInstructionSetFeatures(), + &driver->GetCompilerOptions(), + oat_file.GetFile())); + elf_writers.back()->Start(); + oat_writers.emplace_back(new OatWriter(/*compiling_boot_image*/true, + &timings, + /*profile_compilation_info*/nullptr)); + } + + std::vector<OutputStream*> rodata; + std::vector<std::unique_ptr<MemMap>> opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> opened_dex_files; + // Now that we have finalized key_value_store_, start writing the oat file. + for (size_t i = 0, size = oat_writers.size(); i != size; ++i) { + const DexFile* dex_file = class_path[i]; + rodata.push_back(elf_writers[i]->StartRoData()); + ArrayRef<const uint8_t> raw_dex_file( + reinterpret_cast<const uint8_t*>(&dex_file->GetHeader()), + dex_file->GetHeader().file_size_); + oat_writers[i]->AddRawDexFileSource(raw_dex_file, + dex_file->GetLocation().c_str(), + dex_file->GetLocationChecksum()); + + std::unique_ptr<MemMap> cur_opened_dex_files_map; + std::vector<std::unique_ptr<const DexFile>> cur_opened_dex_files; + bool dex_files_ok = oat_writers[i]->WriteAndOpenDexFiles( + kIsVdexEnabled ? vdex_files[i].GetFile() : oat_files[i].GetFile(), + rodata.back(), + driver->GetInstructionSet(), + driver->GetInstructionSetFeatures(), + &key_value_store, + /* verify */ false, // Dex files may be dex-to-dex-ed, don't verify. + /* update_input_vdex */ false, + &cur_opened_dex_files_map, + &cur_opened_dex_files); + ASSERT_TRUE(dex_files_ok); + + if (cur_opened_dex_files_map != nullptr) { + opened_dex_files_map.push_back(std::move(cur_opened_dex_files_map)); + for (std::unique_ptr<const DexFile>& cur_dex_file : cur_opened_dex_files) { + // dex_file_oat_index_map_.emplace(dex_file.get(), i); + opened_dex_files.push_back(std::move(cur_dex_file)); + } + } else { + ASSERT_TRUE(cur_opened_dex_files.empty()); + } + } + bool image_space_ok = writer->PrepareImageAddressSpace(); + ASSERT_TRUE(image_space_ok); + + if (kIsVdexEnabled) { + for (size_t i = 0, size = vdex_files.size(); i != size; ++i) { + std::unique_ptr<BufferedOutputStream> vdex_out( + MakeUnique<BufferedOutputStream>( + MakeUnique<FileOutputStream>(vdex_files[i].GetFile()))); + oat_writers[i]->WriteVerifierDeps(vdex_out.get(), nullptr); + oat_writers[i]->WriteChecksumsAndVdexHeader(vdex_out.get()); + } + } + + for (size_t i = 0, size = oat_files.size(); i != size; ++i) { + linker::MultiOatRelativePatcher patcher(driver->GetInstructionSet(), + driver->GetInstructionSetFeatures()); + OatWriter* const oat_writer = oat_writers[i].get(); + ElfWriter* const elf_writer = elf_writers[i].get(); + std::vector<const DexFile*> cur_dex_files(1u, class_path[i]); + oat_writer->Initialize(driver, writer.get(), cur_dex_files); + oat_writer->PrepareLayout(&patcher); + size_t rodata_size = oat_writer->GetOatHeader().GetExecutableOffset(); + size_t text_size = oat_writer->GetOatSize() - rodata_size; + elf_writer->PrepareDynamicSection(rodata_size, + text_size, + oat_writer->GetBssSize(), + oat_writer->GetBssRootsOffset()); + + writer->UpdateOatFileLayout(i, + elf_writer->GetLoadedSize(), + oat_writer->GetOatDataOffset(), + oat_writer->GetOatSize()); + + bool rodata_ok = oat_writer->WriteRodata(rodata[i]); + ASSERT_TRUE(rodata_ok); + elf_writer->EndRoData(rodata[i]); + + OutputStream* text = elf_writer->StartText(); + bool text_ok = oat_writer->WriteCode(text); + ASSERT_TRUE(text_ok); + elf_writer->EndText(text); + + bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(), 0u, 0u, 0u); + ASSERT_TRUE(header_ok); + + writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader()); + + elf_writer->WriteDynamicSection(); + elf_writer->WriteDebugInfo(oat_writer->GetMethodDebugInfo()); + + bool success = elf_writer->End(); + ASSERT_TRUE(success); + } + } + + bool success_image = writer->Write(kInvalidFd, + image_filename_vector, + oat_filename_vector); + ASSERT_TRUE(success_image); + + for (size_t i = 0, size = oat_filenames.size(); i != size; ++i) { + const char* oat_filename = oat_filenames[i].c_str(); + std::unique_ptr<File> oat_file(OS::OpenFileReadWrite(oat_filename)); + ASSERT_TRUE(oat_file != nullptr); + bool success_fixup = ElfWriter::Fixup(oat_file.get(), + writer->GetOatDataBegin(i)); + ASSERT_TRUE(success_fixup); + ASSERT_EQ(oat_file->FlushCloseOrErase(), 0) << "Could not flush and close oat file " + << oat_filename; + } + } +} + +inline void ImageTest::Compile(ImageHeader::StorageMode storage_mode, + CompilationHelper& helper, + const std::string& extra_dex, + const std::initializer_list<std::string>& image_classes) { + for (const std::string& image_class : image_classes) { + image_classes_.insert(image_class); + } + CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); + // Set inline filter values. + compiler_options_->SetInlineMaxCodeUnits(CompilerOptions::kDefaultInlineMaxCodeUnits); + image_classes_.clear(); + if (!extra_dex.empty()) { + helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); + } + helper.Compile(compiler_driver_.get(), storage_mode); + if (image_classes.begin() != image_classes.end()) { + // Make sure the class got initialized. + ScopedObjectAccess soa(Thread::Current()); + ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); + for (const std::string& image_class : image_classes) { + mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); + EXPECT_TRUE(klass != nullptr); + EXPECT_TRUE(klass->IsInitialized()); + } + } +} + +inline void ImageTest::TestWriteRead(ImageHeader::StorageMode storage_mode) { + CompilationHelper helper; + Compile(storage_mode, /*out*/ helper); + std::vector<uint64_t> image_file_sizes; + for (ScratchFile& image_file : helper.image_files) { + std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); + ASSERT_TRUE(file.get() != nullptr); + ImageHeader image_header; + ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); + ASSERT_TRUE(image_header.IsValid()); + const auto& bitmap_section = image_header.GetImageSection(ImageHeader::kSectionImageBitmap); + ASSERT_GE(bitmap_section.Offset(), sizeof(image_header)); + ASSERT_NE(0U, bitmap_section.Size()); + + gc::Heap* heap = Runtime::Current()->GetHeap(); + ASSERT_TRUE(heap->HaveContinuousSpaces()); + gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); + ASSERT_FALSE(space->IsImageSpace()); + ASSERT_TRUE(space != nullptr); + ASSERT_TRUE(space->IsMallocSpace()); + image_file_sizes.push_back(file->GetLength()); + } + + ASSERT_TRUE(compiler_driver_->GetImageClasses() != nullptr); + std::unordered_set<std::string> image_classes(*compiler_driver_->GetImageClasses()); + + // Need to delete the compiler since it has worker threads which are attached to runtime. + compiler_driver_.reset(); + + // Tear down old runtime before making a new one, clearing out misc state. + + // Remove the reservation of the memory for use to load the image. + // Need to do this before we reset the runtime. + UnreserveImageSpace(); + + helper.extra_dex_files.clear(); + runtime_.reset(); + java_lang_dex_file_ = nullptr; + + MemMap::Init(); + + RuntimeOptions options; + std::string image("-Ximage:"); + image.append(helper.image_locations[0].GetFilename()); + options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr))); + // By default the compiler this creates will not include patch information. + options.push_back(std::make_pair("-Xnorelocate", nullptr)); + + if (!Runtime::Create(options, false)) { + LOG(FATAL) << "Failed to create runtime"; + return; + } + runtime_.reset(Runtime::Current()); + // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, + // give it away now and then switch to a more managable ScopedObjectAccess. + Thread::Current()->TransitionFromRunnableToSuspended(kNative); + ScopedObjectAccess soa(Thread::Current()); + ASSERT_TRUE(runtime_.get() != nullptr); + class_linker_ = runtime_->GetClassLinker(); + + gc::Heap* heap = Runtime::Current()->GetHeap(); + ASSERT_TRUE(heap->HasBootImageSpace()); + ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace()); + + // We loaded the runtime with an explicit image, so it must exist. + ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size()); + for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) { + std::unique_ptr<const DexFile> dex( + LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str())); + ASSERT_TRUE(dex != nullptr); + uint64_t image_file_size = image_file_sizes[i]; + gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i]; + ASSERT_TRUE(image_space != nullptr); + if (storage_mode == ImageHeader::kStorageModeUncompressed) { + // Uncompressed, image should be smaller than file. + ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size); + } else if (image_file_size > 16 * KB) { + // Compressed, file should be smaller than image. Not really valid for small images. + ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize()); + } + + image_space->VerifyImageAllocations(); + uint8_t* image_begin = image_space->Begin(); + uint8_t* image_end = image_space->End(); + if (i == 0) { + // This check is only valid for image 0. + CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin)); + } + for (size_t j = 0; j < dex->NumClassDefs(); ++j) { + const DexFile::ClassDef& class_def = dex->GetClassDef(j); + const char* descriptor = dex->GetClassDescriptor(class_def); + mirror::Class* klass = class_linker_->FindSystemClass(soa.Self(), descriptor); + EXPECT_TRUE(klass != nullptr) << descriptor; + if (image_classes.find(descriptor) == image_classes.end()) { + EXPECT_TRUE(reinterpret_cast<uint8_t*>(klass) >= image_end || + reinterpret_cast<uint8_t*>(klass) < image_begin) << descriptor; + } else { + // Image classes should be located inside the image. + EXPECT_LT(image_begin, reinterpret_cast<uint8_t*>(klass)) << descriptor; + EXPECT_LT(reinterpret_cast<uint8_t*>(klass), image_end) << descriptor; + } + EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); + } + } +} + + +} // namespace art + +#endif // ART_COMPILER_IMAGE_TEST_H_ diff --git a/compiler/image_write_read_test.cc b/compiler/image_write_read_test.cc new file mode 100644 index 0000000000..32c0b06766 --- /dev/null +++ b/compiler/image_write_read_test.cc @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "image_test.h" + +namespace art { + +TEST_F(ImageTest, WriteReadUncompressed) { + TestWriteRead(ImageHeader::kStorageModeUncompressed); +} + +TEST_F(ImageTest, WriteReadLZ4) { + TestWriteRead(ImageHeader::kStorageModeLZ4); +} + +TEST_F(ImageTest, WriteReadLZ4HC) { + TestWriteRead(ImageHeader::kStorageModeLZ4HC); +} + +} // namespace art diff --git a/oatdump/Android.bp b/oatdump/Android.bp index f1fcf3dad8..1cd97c2b53 100644 --- a/oatdump/Android.bp +++ b/oatdump/Android.bp @@ -114,5 +114,8 @@ art_cc_test { defaults: [ "art_gtest_defaults", ], - srcs: ["oatdump_test.cc"], + srcs: [ + "oatdump_test.cc", + "oatdump_image_test.cc", + ], } diff --git a/oatdump/oatdump_image_test.cc b/oatdump/oatdump_image_test.cc new file mode 100644 index 0000000000..e9cc922d9b --- /dev/null +++ b/oatdump/oatdump_image_test.cc @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "oatdump_test.h" + +namespace art { + +// Disable tests on arm and mips as they are taking too long to run. b/27824283. +#if !defined(__arm__) && !defined(__mips__) +TEST_F(OatDumpTest, TestImage) { + std::string error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg; +} +TEST_F(OatDumpTest, TestImageStatic) { + TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); + std::string error_msg; + ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg; +} + +TEST_F(OatDumpTest, TestOatImage) { + std::string error_msg; + ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg; +} +TEST_F(OatDumpTest, TestOatImageStatic) { + TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); + std::string error_msg; + ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg; +} +#endif +} // namespace art diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc index c7c3ddd7cc..7260d7477b 100644 --- a/oatdump/oatdump_test.cc +++ b/oatdump/oatdump_test.cc @@ -14,235 +14,12 @@ * limitations under the License. */ -#include <sstream> -#include <string> -#include <vector> - -#include "android-base/strings.h" - -#include "common_runtime_test.h" - -#include "base/unix_file/fd_file.h" -#include "runtime/arch/instruction_set.h" -#include "runtime/exec_utils.h" -#include "runtime/gc/heap.h" -#include "runtime/gc/space/image_space.h" -#include "runtime/os.h" -#include "runtime/utils.h" -#include "utils.h" - -#include <sys/types.h> -#include <unistd.h> +#include "oatdump_test.h" namespace art { -class OatDumpTest : public CommonRuntimeTest { - protected: - virtual void SetUp() { - CommonRuntimeTest::SetUp(); - core_art_location_ = GetCoreArtLocation(); - core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA); - } - - // Linking flavor. - enum Flavor { - kDynamic, // oatdump(d) - kStatic, // oatdump(d)s - }; - - // Returns path to the oatdump binary. - std::string GetOatDumpFilePath(Flavor flavor) { - std::string root = GetTestAndroidRoot(); - root += "/bin/oatdump"; - if (kIsDebugBuild) { - root += "d"; - } - if (flavor == kStatic) { - root += "s"; - } - return root; - } - - enum Mode { - kModeOat, - kModeArt, - kModeSymbolize, - }; - - // Display style. - enum Display { - kListOnly, - kListAndCode - }; - - // Run the test with custom arguments. - bool Exec(Flavor flavor, - Mode mode, - const std::vector<std::string>& args, - Display display, - std::string* error_msg) { - std::string file_path = GetOatDumpFilePath(flavor); - - EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; - - // ScratchFile scratch; - std::vector<std::string> exec_argv = { file_path }; - std::vector<std::string> expected_prefixes; - if (mode == kModeSymbolize) { - exec_argv.push_back("--symbolize=" + core_oat_location_); - exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize"); - } else { - expected_prefixes.push_back("Dex file data for"); - expected_prefixes.push_back("Num string ids:"); - expected_prefixes.push_back("Num field ids:"); - expected_prefixes.push_back("Num method ids:"); - expected_prefixes.push_back("LOCATION:"); - expected_prefixes.push_back("MAGIC:"); - expected_prefixes.push_back("DEX FILE COUNT:"); - if (display == kListAndCode) { - // Code and dex code do not show up if list only. - expected_prefixes.push_back("DEX CODE:"); - expected_prefixes.push_back("CODE:"); - expected_prefixes.push_back("CodeInfoEncoding"); - expected_prefixes.push_back("CodeInfoInlineInfo"); - } - if (mode == kModeArt) { - exec_argv.push_back("--image=" + core_art_location_); - exec_argv.push_back("--instruction-set=" + std::string( - GetInstructionSetString(kRuntimeISA))); - expected_prefixes.push_back("IMAGE LOCATION:"); - expected_prefixes.push_back("IMAGE BEGIN:"); - expected_prefixes.push_back("kDexCaches:"); - } else { - CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat)); - exec_argv.push_back("--oat-file=" + core_oat_location_); - } - } - exec_argv.insert(exec_argv.end(), args.begin(), args.end()); - - bool result = true; - // We must set --android-root. - int link[2]; - if (pipe(link) == -1) { - *error_msg = strerror(errno); - return false; - } - - const pid_t pid = fork(); - if (pid == -1) { - *error_msg = strerror(errno); - return false; - } - - if (pid == 0) { - dup2(link[1], STDOUT_FILENO); - close(link[0]); - close(link[1]); - // change process groups, so we don't get reaped by ProcessManager - setpgid(0, 0); - // Use execv here rather than art::Exec to avoid blocking on waitpid here. - std::vector<char*> argv; - for (size_t i = 0; i < exec_argv.size(); ++i) { - argv.push_back(const_cast<char*>(exec_argv[i].c_str())); - } - argv.push_back(nullptr); - UNUSED(execv(argv[0], &argv[0])); - const std::string command_line(android::base::Join(exec_argv, ' ')); - PLOG(ERROR) << "Failed to execv(" << command_line << ")"; - // _exit to avoid atexit handlers in child. - _exit(1); - } else { - close(link[1]); - static const size_t kLineMax = 256; - char line[kLineMax] = {}; - size_t line_len = 0; - size_t total = 0; - std::vector<bool> found(expected_prefixes.size(), false); - while (true) { - while (true) { - size_t spaces = 0; - // Trim spaces at the start of the line. - for (; spaces < line_len && isspace(line[spaces]); ++spaces) {} - if (spaces > 0) { - line_len -= spaces; - memmove(&line[0], &line[spaces], line_len); - } - ssize_t bytes_read = - TEMP_FAILURE_RETRY(read(link[0], &line[line_len], kLineMax - line_len)); - if (bytes_read <= 0) { - break; - } - line_len += bytes_read; - total += bytes_read; - } - if (line_len == 0) { - break; - } - // Check contents. - for (size_t i = 0; i < expected_prefixes.size(); ++i) { - const std::string& expected = expected_prefixes[i]; - if (!found[i] && - line_len >= expected.length() && - memcmp(line, expected.c_str(), expected.length()) == 0) { - found[i] = true; - } - } - // Skip to next line. - size_t next_line = 0; - for (; next_line + 1 < line_len && line[next_line] != '\n'; ++next_line) {} - line_len -= next_line + 1; - memmove(&line[0], &line[next_line + 1], line_len); - } - if (mode == kModeSymbolize) { - EXPECT_EQ(total, 0u); - } else { - EXPECT_GT(total, 0u); - } - LOG(INFO) << "Processed bytes " << total; - close(link[0]); - int status = 0; - if (waitpid(pid, &status, 0) != -1) { - result = (status == 0); - } - - for (size_t i = 0; i < expected_prefixes.size(); ++i) { - if (!found[i]) { - LOG(ERROR) << "Did not find prefix " << expected_prefixes[i]; - result = false; - } - } - } - - return result; - } - - private: - std::string core_art_location_; - std::string core_oat_location_; -}; - // Disable tests on arm and mips as they are taking too long to run. b/27824283. #if !defined(__arm__) && !defined(__mips__) -TEST_F(OatDumpTest, TestImage) { - std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg; -} -TEST_F(OatDumpTest, TestImageStatic) { - TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); - std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeArt, {}, kListAndCode, &error_msg)) << error_msg; -} - -TEST_F(OatDumpTest, TestOatImage) { - std::string error_msg; - ASSERT_TRUE(Exec(kDynamic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg; -} -TEST_F(OatDumpTest, TestOatImageStatic) { - TEST_DISABLED_FOR_NON_STATIC_HOST_BUILDS(); - std::string error_msg; - ASSERT_TRUE(Exec(kStatic, kModeOat, {}, kListAndCode, &error_msg)) << error_msg; -} - TEST_F(OatDumpTest, TestNoDumpVmap) { std::string error_msg; ASSERT_TRUE(Exec(kDynamic, kModeArt, {"--no-dump:vmap"}, kListAndCode, &error_msg)) << error_msg; diff --git a/oatdump/oatdump_test.h b/oatdump/oatdump_test.h new file mode 100644 index 0000000000..48e9eb5964 --- /dev/null +++ b/oatdump/oatdump_test.h @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_OATDUMP_OATDUMP_TEST_H_ +#define ART_OATDUMP_OATDUMP_TEST_H_ + +#include <sstream> +#include <string> +#include <vector> + +#include "android-base/strings.h" + +#include "common_runtime_test.h" + +#include "base/unix_file/fd_file.h" +#include "runtime/arch/instruction_set.h" +#include "runtime/exec_utils.h" +#include "runtime/gc/heap.h" +#include "runtime/gc/space/image_space.h" +#include "runtime/os.h" +#include "runtime/utils.h" +#include "utils.h" + +#include <sys/types.h> +#include <unistd.h> + +namespace art { + +class OatDumpTest : public CommonRuntimeTest { + protected: + virtual void SetUp() { + CommonRuntimeTest::SetUp(); + core_art_location_ = GetCoreArtLocation(); + core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA); + } + + // Linking flavor. + enum Flavor { + kDynamic, // oatdump(d) + kStatic, // oatdump(d)s + }; + + // Returns path to the oatdump binary. + std::string GetOatDumpFilePath(Flavor flavor) { + std::string root = GetTestAndroidRoot(); + root += "/bin/oatdump"; + if (kIsDebugBuild) { + root += "d"; + } + if (flavor == kStatic) { + root += "s"; + } + return root; + } + + enum Mode { + kModeOat, + kModeArt, + kModeSymbolize, + }; + + // Display style. + enum Display { + kListOnly, + kListAndCode + }; + + // Run the test with custom arguments. + bool Exec(Flavor flavor, + Mode mode, + const std::vector<std::string>& args, + Display display, + std::string* error_msg) { + std::string file_path = GetOatDumpFilePath(flavor); + + EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path"; + + // ScratchFile scratch; + std::vector<std::string> exec_argv = { file_path }; + std::vector<std::string> expected_prefixes; + if (mode == kModeSymbolize) { + exec_argv.push_back("--symbolize=" + core_oat_location_); + exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize"); + } else { + expected_prefixes.push_back("Dex file data for"); + expected_prefixes.push_back("Num string ids:"); + expected_prefixes.push_back("Num field ids:"); + expected_prefixes.push_back("Num method ids:"); + expected_prefixes.push_back("LOCATION:"); + expected_prefixes.push_back("MAGIC:"); + expected_prefixes.push_back("DEX FILE COUNT:"); + if (display == kListAndCode) { + // Code and dex code do not show up if list only. + expected_prefixes.push_back("DEX CODE:"); + expected_prefixes.push_back("CODE:"); + expected_prefixes.push_back("CodeInfoEncoding"); + expected_prefixes.push_back("CodeInfoInlineInfo"); + } + if (mode == kModeArt) { + exec_argv.push_back("--image=" + core_art_location_); + exec_argv.push_back("--instruction-set=" + std::string( + GetInstructionSetString(kRuntimeISA))); + expected_prefixes.push_back("IMAGE LOCATION:"); + expected_prefixes.push_back("IMAGE BEGIN:"); + expected_prefixes.push_back("kDexCaches:"); + } else { + CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat)); + exec_argv.push_back("--oat-file=" + core_oat_location_); + } + } + exec_argv.insert(exec_argv.end(), args.begin(), args.end()); + + bool result = true; + // We must set --android-root. + int link[2]; + if (pipe(link) == -1) { + *error_msg = strerror(errno); + return false; + } + + const pid_t pid = fork(); + if (pid == -1) { + *error_msg = strerror(errno); + return false; + } + + if (pid == 0) { + dup2(link[1], STDOUT_FILENO); + close(link[0]); + close(link[1]); + // change process groups, so we don't get reaped by ProcessManager + setpgid(0, 0); + // Use execv here rather than art::Exec to avoid blocking on waitpid here. + std::vector<char*> argv; + for (size_t i = 0; i < exec_argv.size(); ++i) { + argv.push_back(const_cast<char*>(exec_argv[i].c_str())); + } + argv.push_back(nullptr); + UNUSED(execv(argv[0], &argv[0])); + const std::string command_line(android::base::Join(exec_argv, ' ')); + PLOG(ERROR) << "Failed to execv(" << command_line << ")"; + // _exit to avoid atexit handlers in child. + _exit(1); + } else { + close(link[1]); + static const size_t kLineMax = 256; + char line[kLineMax] = {}; + size_t line_len = 0; + size_t total = 0; + std::vector<bool> found(expected_prefixes.size(), false); + while (true) { + while (true) { + size_t spaces = 0; + // Trim spaces at the start of the line. + for (; spaces < line_len && isspace(line[spaces]); ++spaces) {} + if (spaces > 0) { + line_len -= spaces; + memmove(&line[0], &line[spaces], line_len); + } + ssize_t bytes_read = + TEMP_FAILURE_RETRY(read(link[0], &line[line_len], kLineMax - line_len)); + if (bytes_read <= 0) { + break; + } + line_len += bytes_read; + total += bytes_read; + } + if (line_len == 0) { + break; + } + // Check contents. + for (size_t i = 0; i < expected_prefixes.size(); ++i) { + const std::string& expected = expected_prefixes[i]; + if (!found[i] && + line_len >= expected.length() && + memcmp(line, expected.c_str(), expected.length()) == 0) { + found[i] = true; + } + } + // Skip to next line. + size_t next_line = 0; + for (; next_line + 1 < line_len && line[next_line] != '\n'; ++next_line) {} + line_len -= next_line + 1; + memmove(&line[0], &line[next_line + 1], line_len); + } + if (mode == kModeSymbolize) { + EXPECT_EQ(total, 0u); + } else { + EXPECT_GT(total, 0u); + } + LOG(INFO) << "Processed bytes " << total; + close(link[0]); + int status = 0; + if (waitpid(pid, &status, 0) != -1) { + result = (status == 0); + } + + for (size_t i = 0; i < expected_prefixes.size(); ++i) { + if (!found[i]) { + LOG(ERROR) << "Did not find prefix " << expected_prefixes[i]; + result = false; + } + } + } + + return result; + } + + private: + std::string core_art_location_; + std::string core_oat_location_; +}; + +} // namespace art + +#endif // ART_OATDUMP_OATDUMP_TEST_H_ |