| Vladimir Marko | 1352f13 | 2017-04-28 15:28:29 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "image_test.h" |
| 18 | |
| 19 | namespace art { |
| Vladimir Marko | 7452797 | 2016-11-29 15:57:32 +0000 | [diff] [blame] | 20 | namespace linker { |
| Vladimir Marko | 1352f13 | 2017-04-28 15:28:29 +0100 | [diff] [blame] | 21 | |
| Vladimir Marko | f1ddc20 | 2019-10-18 12:35:14 +0100 | [diff] [blame] | 22 | class ImageWriteReadTest : public ImageTest { |
| 23 | protected: |
| 24 | void TestWriteRead(ImageHeader::StorageMode storage_mode, uint32_t max_image_block_size); |
| 25 | }; |
| 26 | |
| 27 | void ImageWriteReadTest::TestWriteRead(ImageHeader::StorageMode storage_mode, |
| 28 | uint32_t max_image_block_size) { |
| 29 | CompilationHelper helper; |
| 30 | Compile(storage_mode, max_image_block_size, /*out*/ helper); |
| 31 | std::vector<uint64_t> image_file_sizes; |
| 32 | for (ScratchFile& image_file : helper.image_files) { |
| 33 | std::unique_ptr<File> file(OS::OpenFileForReading(image_file.GetFilename().c_str())); |
| 34 | ASSERT_TRUE(file.get() != nullptr); |
| 35 | ImageHeader image_header; |
| 36 | ASSERT_EQ(file->ReadFully(&image_header, sizeof(image_header)), true); |
| 37 | ASSERT_TRUE(image_header.IsValid()); |
| 38 | const auto& bitmap_section = image_header.GetImageBitmapSection(); |
| 39 | ASSERT_GE(bitmap_section.Offset(), sizeof(image_header)); |
| 40 | ASSERT_NE(0U, bitmap_section.Size()); |
| 41 | |
| 42 | gc::Heap* heap = Runtime::Current()->GetHeap(); |
| 43 | ASSERT_TRUE(heap->HaveContinuousSpaces()); |
| 44 | gc::space::ContinuousSpace* space = heap->GetNonMovingSpace(); |
| 45 | ASSERT_FALSE(space->IsImageSpace()); |
| 46 | ASSERT_TRUE(space != nullptr); |
| 47 | ASSERT_TRUE(space->IsMallocSpace()); |
| 48 | image_file_sizes.push_back(file->GetLength()); |
| 49 | } |
| 50 | |
| 51 | // Need to delete the compiler since it has worker threads which are attached to runtime. |
| 52 | compiler_driver_.reset(); |
| 53 | |
| 54 | // Tear down old runtime before making a new one, clearing out misc state. |
| 55 | |
| 56 | // Remove the reservation of the memory for use to load the image. |
| 57 | // Need to do this before we reset the runtime. |
| 58 | UnreserveImageSpace(); |
| 59 | |
| 60 | helper.extra_dex_files.clear(); |
| 61 | runtime_.reset(); |
| 62 | java_lang_dex_file_ = nullptr; |
| 63 | |
| 64 | MemMap::Init(); |
| 65 | |
| 66 | RuntimeOptions options; |
| 67 | options.emplace_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()), nullptr); |
| 68 | options.emplace_back( |
| 69 | GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()), nullptr); |
| 70 | std::string image("-Ximage:"); |
| 71 | image.append(helper.image_locations[0].GetFilename()); |
| 72 | options.push_back(std::make_pair(image.c_str(), static_cast<void*>(nullptr))); |
| 73 | // By default the compiler this creates will not include patch information. |
| 74 | options.push_back(std::make_pair("-Xnorelocate", nullptr)); |
| 75 | |
| 76 | if (!Runtime::Create(options, false)) { |
| 77 | LOG(FATAL) << "Failed to create runtime"; |
| 78 | return; |
| 79 | } |
| 80 | runtime_.reset(Runtime::Current()); |
| 81 | // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, |
| 82 | // give it away now and then switch to a more managable ScopedObjectAccess. |
| 83 | Thread::Current()->TransitionFromRunnableToSuspended(kNative); |
| 84 | ScopedObjectAccess soa(Thread::Current()); |
| 85 | ASSERT_TRUE(runtime_.get() != nullptr); |
| 86 | class_linker_ = runtime_->GetClassLinker(); |
| 87 | |
| 88 | gc::Heap* heap = Runtime::Current()->GetHeap(); |
| 89 | ASSERT_TRUE(heap->HasBootImageSpace()); |
| 90 | ASSERT_TRUE(heap->GetNonMovingSpace()->IsMallocSpace()); |
| 91 | |
| 92 | // We loaded the runtime with an explicit image, so it must exist. |
| 93 | ASSERT_EQ(heap->GetBootImageSpaces().size(), image_file_sizes.size()); |
| 94 | const HashSet<std::string>& image_classes = compiler_options_->GetImageClasses(); |
| 95 | for (size_t i = 0; i < helper.dex_file_locations.size(); ++i) { |
| 96 | std::unique_ptr<const DexFile> dex( |
| 97 | LoadExpectSingleDexFile(helper.dex_file_locations[i].c_str())); |
| 98 | ASSERT_TRUE(dex != nullptr); |
| 99 | uint64_t image_file_size = image_file_sizes[i]; |
| 100 | gc::space::ImageSpace* image_space = heap->GetBootImageSpaces()[i]; |
| 101 | ASSERT_TRUE(image_space != nullptr); |
| 102 | if (storage_mode == ImageHeader::kStorageModeUncompressed) { |
| 103 | // Uncompressed, image should be smaller than file. |
| 104 | ASSERT_LE(image_space->GetImageHeader().GetImageSize(), image_file_size); |
| 105 | } else if (image_file_size > 16 * KB) { |
| 106 | // Compressed, file should be smaller than image. Not really valid for small images. |
| 107 | ASSERT_LE(image_file_size, image_space->GetImageHeader().GetImageSize()); |
| 108 | // TODO: Actually validate the blocks, this is hard since the blocks are not copied over for |
| 109 | // compressed images. Add kPageSize since image_size is rounded up to this. |
| 110 | ASSERT_GT(image_space->GetImageHeader().GetBlockCount() * max_image_block_size, |
| 111 | image_space->GetImageHeader().GetImageSize() - kPageSize); |
| 112 | } |
| 113 | |
| 114 | image_space->VerifyImageAllocations(); |
| 115 | uint8_t* image_begin = image_space->Begin(); |
| 116 | uint8_t* image_end = image_space->End(); |
| 117 | if (i == 0) { |
| 118 | // This check is only valid for image 0. |
| 119 | CHECK_EQ(kRequestedImageBase, reinterpret_cast<uintptr_t>(image_begin)); |
| 120 | } |
| 121 | for (size_t j = 0; j < dex->NumClassDefs(); ++j) { |
| 122 | const dex::ClassDef& class_def = dex->GetClassDef(j); |
| 123 | const char* descriptor = dex->GetClassDescriptor(class_def); |
| 124 | ObjPtr<mirror::Class> klass = class_linker_->FindSystemClass(soa.Self(), descriptor); |
| 125 | EXPECT_TRUE(klass != nullptr) << descriptor; |
| 126 | uint8_t* raw_klass = reinterpret_cast<uint8_t*>(klass.Ptr()); |
| 127 | if (image_classes.find(std::string_view(descriptor)) == image_classes.end()) { |
| 128 | EXPECT_TRUE(raw_klass >= image_end || raw_klass < image_begin) << descriptor; |
| 129 | } else { |
| 130 | // Image classes should be located inside the image. |
| 131 | EXPECT_LT(image_begin, raw_klass) << descriptor; |
| 132 | EXPECT_LT(raw_klass, image_end) << descriptor; |
| 133 | } |
| 134 | EXPECT_TRUE(Monitor::IsValidLockWord(klass->GetLockWord(false))); |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | TEST_F(ImageWriteReadTest, WriteReadUncompressed) { |
| Mathieu Chartier | 1a84296 | 2018-11-13 15:09:51 -0800 | [diff] [blame] | 140 | TestWriteRead(ImageHeader::kStorageModeUncompressed, |
| 141 | /*max_image_block_size=*/std::numeric_limits<uint32_t>::max()); |
| Vladimir Marko | 1352f13 | 2017-04-28 15:28:29 +0100 | [diff] [blame] | 142 | } |
| 143 | |
| Vladimir Marko | f1ddc20 | 2019-10-18 12:35:14 +0100 | [diff] [blame] | 144 | TEST_F(ImageWriteReadTest, WriteReadLZ4) { |
| Mathieu Chartier | 1a84296 | 2018-11-13 15:09:51 -0800 | [diff] [blame] | 145 | TestWriteRead(ImageHeader::kStorageModeLZ4, |
| 146 | /*max_image_block_size=*/std::numeric_limits<uint32_t>::max()); |
| Vladimir Marko | 1352f13 | 2017-04-28 15:28:29 +0100 | [diff] [blame] | 147 | } |
| 148 | |
| Vladimir Marko | f1ddc20 | 2019-10-18 12:35:14 +0100 | [diff] [blame] | 149 | TEST_F(ImageWriteReadTest, WriteReadLZ4HC) { |
| Mathieu Chartier | 1a84296 | 2018-11-13 15:09:51 -0800 | [diff] [blame] | 150 | TestWriteRead(ImageHeader::kStorageModeLZ4HC, |
| 151 | /*max_image_block_size=*/std::numeric_limits<uint32_t>::max()); |
| 152 | } |
| 153 | |
| 154 | |
| Vladimir Marko | f1ddc20 | 2019-10-18 12:35:14 +0100 | [diff] [blame] | 155 | TEST_F(ImageWriteReadTest, WriteReadLZ4HCKBBlock) { |
| Mathieu Chartier | 1a84296 | 2018-11-13 15:09:51 -0800 | [diff] [blame] | 156 | TestWriteRead(ImageHeader::kStorageModeLZ4HC, /*max_image_block_size=*/KB); |
| Vladimir Marko | 1352f13 | 2017-04-28 15:28:29 +0100 | [diff] [blame] | 157 | } |
| 158 | |
| Vladimir Marko | 7452797 | 2016-11-29 15:57:32 +0000 | [diff] [blame] | 159 | } // namespace linker |
| Vladimir Marko | 1352f13 | 2017-04-28 15:28:29 +0100 | [diff] [blame] | 160 | } // namespace art |