summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dex2oat/linker/image_writer.cc7
-rw-r--r--runtime/image.h10
-rw-r--r--runtime/runtime_image.cc220
3 files changed, 77 insertions, 160 deletions
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index e7967bc08e..b5105f762e 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -417,7 +417,8 @@ bool ImageWriter::Write(int image_fd,
if (i == 0u) {
primary_image_file = std::move(image_file);
} else {
- if (!image_file.WriteHeaderAndClose(image_filename, image_header)) {
+ if (!image_file.WriteHeaderAndClose(image_filename, image_header, &error_msg)) {
+ LOG(ERROR) << error_msg;
return false;
}
// Update the primary image checksum with the secondary image checksum.
@@ -426,7 +427,9 @@ bool ImageWriter::Write(int image_fd,
}
}
DCHECK(primary_image_file != nullptr);
- if (!primary_image_file.WriteHeaderAndClose(image_filenames[0], primary_header)) {
+ std::string error_msg;
+ if (!primary_image_file.WriteHeaderAndClose(image_filenames[0], primary_header, &error_msg)) {
+ LOG(ERROR) << error_msg;
return false;
}
diff --git a/runtime/image.h b/runtime/image.h
index 4d98aaec62..324cd3c6d7 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -553,10 +553,13 @@ class ImageFileGuard {
return image_file_.get();
}
- bool WriteHeaderAndClose(const std::string& image_filename, const ImageHeader* image_header) {
+ bool WriteHeaderAndClose(const std::string& image_filename,
+ const ImageHeader* image_header,
+ std::string* error_msg) {
// The header is uncompressed since it contains whether the image is compressed or not.
if (!image_file_->PwriteFully(image_header, sizeof(ImageHeader), 0)) {
- PLOG(ERROR) << "Failed to write image file header " << image_filename;
+ *error_msg = "Failed to write image file header "
+ + image_filename + ": " + std::string(strerror(errno));
return false;
}
@@ -564,7 +567,8 @@ class ImageFileGuard {
// to do that whether the FlushCloseOrErase() succeeds or fails.
std::unique_ptr<File> image_file = std::move(image_file_);
if (image_file->FlushCloseOrErase() != 0) {
- PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
+ *error_msg = "Failed to flush and close image file "
+ + image_filename + ": " + std::string(strerror(errno));
return false;
}
diff --git a/runtime/runtime_image.cc b/runtime/runtime_image.cc
index ccc3ba8574..e3dea632ce 100644
--- a/runtime/runtime_image.cc
+++ b/runtime/runtime_image.cc
@@ -157,36 +157,56 @@ class RuntimeImageHelper {
return true;
}
- const std::vector<uint8_t>& GetObjects() const {
- return objects_;
- }
+ void FillData(std::vector<uint8_t>& data) {
+ // Note we don't put the header, we only have it reserved in `data` as
+ // Image::WriteData expects the object section to contain the image header.
+ auto compute_dest = [&](const ImageSection& section) {
+ return data.data() + section.Offset();
+ };
- const std::vector<uint8_t>& GetArtMethods() const {
- return art_methods_;
- }
+ auto objects_section = header_.GetImageSection(ImageHeader::kSectionObjects);
+ memcpy(compute_dest(objects_section) + sizeof(ImageHeader), objects_.data(), objects_.size());
+ std::vector<uint8_t>().swap(objects_);
- const std::vector<uint8_t>& GetArtFields() const {
- return art_fields_;
- }
+ auto fields_section = header_.GetImageSection(ImageHeader::kSectionArtFields);
+ memcpy(compute_dest(fields_section), art_fields_.data(), fields_section.Size());
+ std::vector<uint8_t>().swap(art_fields_);
- const std::vector<uint8_t>& GetImTables() const {
- return im_tables_;
- }
+ auto methods_section = header_.GetImageSection(ImageHeader::kSectionArtMethods);
+ memcpy(compute_dest(methods_section), art_methods_.data(), methods_section.Size());
+ std::vector<uint8_t>().swap(art_methods_);
- const std::vector<uint8_t>& GetMetadata() const {
- return metadata_;
- }
+ auto im_tables_section = header_.GetImageSection(ImageHeader::kSectionImTables);
+ memcpy(compute_dest(im_tables_section), im_tables_.data(), im_tables_section.Size());
+ std::vector<uint8_t>().swap(im_tables_);
- const std::vector<uint8_t>& GetDexCacheArrays() const {
- return dex_cache_arrays_;
- }
+ auto intern_section = header_.GetImageSection(ImageHeader::kSectionInternedStrings);
+ intern_table_.WriteToMemory(compute_dest(intern_section));
- const std::vector<AppImageReferenceOffsetInfo>& GetStringReferenceOffsets() const {
- return string_reference_offsets_;
+ auto class_table_section = header_.GetImageSection(ImageHeader::kSectionClassTable);
+ class_table_.WriteToMemory(compute_dest(class_table_section));
+
+ auto string_offsets_section =
+ header_.GetImageSection(ImageHeader::kSectionStringReferenceOffsets);
+ memcpy(compute_dest(string_offsets_section),
+ string_reference_offsets_.data(),
+ string_offsets_section.Size());
+ std::vector<AppImageReferenceOffsetInfo>().swap(string_reference_offsets_);
+
+ auto dex_cache_section = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays);
+ memcpy(compute_dest(dex_cache_section), dex_cache_arrays_.data(), dex_cache_section.Size());
+ std::vector<uint8_t>().swap(dex_cache_arrays_);
+
+ auto metadata_section = header_.GetImageSection(ImageHeader::kSectionMetadata);
+ memcpy(compute_dest(metadata_section), metadata_.data(), metadata_section.Size());
+ std::vector<uint8_t>().swap(metadata_);
+
+ DCHECK_EQ(metadata_section.Offset() + metadata_section.Size(), data.size());
}
- const ImageHeader& GetHeader() const {
- return header_;
+
+ ImageHeader* GetHeader() {
+ return &header_;
}
const gc::accounting::ContinuousSpaceBitmap& GetImageBitmap() const {
@@ -197,14 +217,6 @@ class RuntimeImageHelper {
return dex_location_;
}
- void GenerateInternData(std::vector<uint8_t>& data) const {
- intern_table_.WriteToMemory(data.data());
- }
-
- void GenerateClassTableData(std::vector<uint8_t>& data) const {
- class_table_.WriteToMemory(data.data());
- }
-
private:
bool IsInBootImage(const void* obj) const {
return reinterpret_cast<uintptr_t>(obj) - boot_image_begin_ < boot_image_size_;
@@ -1484,138 +1496,36 @@ bool RuntimeImage::WriteImageToDisk(std::string* error_msg) {
// We first generate the app image in a temporary file, which we will then
// move to `path`.
const std::string temp_path = ReplaceFileExtension(path, std::to_string(getpid()) + ".tmp");
- std::unique_ptr<File> out(OS::CreateEmptyFileWriteOnly(temp_path.c_str()));
- if (out == nullptr) {
- *error_msg = "Could not open " + temp_path + " for writing";
- return false;
- }
+ ImageFileGuard image_file;
+ image_file.reset(OS::CreateEmptyFileWriteOnly(temp_path.c_str()));
- // Write objects. The header is written at the end in case we get killed.
- if (out->Write(reinterpret_cast<const char*>(image.GetObjects().data()),
- image.GetObjects().size(),
- sizeof(ImageHeader)) != static_cast<int64_t>(image.GetObjects().size())) {
- *error_msg = "Could not write image data to " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
-
- {
- // Write fields.
- auto fields_section = image.GetHeader().GetImageSection(ImageHeader::kSectionArtFields);
- if (out->Write(reinterpret_cast<const char*>(image.GetArtFields().data()),
- fields_section.Size(),
- fields_section.Offset()) != fields_section.Size()) {
- *error_msg = "Could not write fields section " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
- }
-
- {
- // Write methods.
- auto methods_section = image.GetHeader().GetImageSection(ImageHeader::kSectionArtMethods);
- if (out->Write(reinterpret_cast<const char*>(image.GetArtMethods().data()),
- methods_section.Size(),
- methods_section.Offset()) != methods_section.Size()) {
- *error_msg = "Could not write methods section " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
- }
-
- {
- // Write im tables.
- auto im_tables_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImTables);
- if (out->Write(reinterpret_cast<const char*>(image.GetImTables().data()),
- im_tables_section.Size(),
- im_tables_section.Offset()) != im_tables_section.Size()) {
- *error_msg = "Could not write ImTable section " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
- }
-
- {
- // Write intern string set.
- auto intern_section = image.GetHeader().GetImageSection(ImageHeader::kSectionInternedStrings);
- std::vector<uint8_t> intern_data(intern_section.Size());
- image.GenerateInternData(intern_data);
- if (out->Write(reinterpret_cast<const char*>(intern_data.data()),
- intern_section.Size(),
- intern_section.Offset()) != intern_section.Size()) {
- *error_msg = "Could not write intern section " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
- }
-
- {
- // Write class table.
- auto class_table_section = image.GetHeader().GetImageSection(ImageHeader::kSectionClassTable);
- std::vector<uint8_t> class_table_data(class_table_section.Size());
- image.GenerateClassTableData(class_table_data);
- if (out->Write(reinterpret_cast<const char*>(class_table_data.data()),
- class_table_section.Size(),
- class_table_section.Offset()) != class_table_section.Size()) {
- *error_msg = "Could not write class table section " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
- }
-
- // Write bitmap.
- auto bitmap_section = image.GetHeader().GetImageSection(ImageHeader::kSectionImageBitmap);
- if (out->Write(reinterpret_cast<const char*>(image.GetImageBitmap().Begin()),
- bitmap_section.Size(),
- bitmap_section.Offset()) != bitmap_section.Size()) {
- *error_msg = "Could not write image bitmap " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
-
- // Write metadata section.
- auto metadata_section = image.GetHeader().GetImageSection(ImageHeader::kSectionMetadata);
- if (out->Write(reinterpret_cast<const char*>(image.GetMetadata().data()),
- metadata_section.Size(),
- metadata_section.Offset()) != metadata_section.Size()) {
- *error_msg = "Could not write metadata " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
-
- // Write string offset array section.
- auto string_offsets_section =
- image.GetHeader().GetImageSection(ImageHeader::kSectionStringReferenceOffsets);
- if (out->Write(reinterpret_cast<const char*>(image.GetStringReferenceOffsets().data()),
- string_offsets_section.Size(),
- string_offsets_section.Offset()) != string_offsets_section.Size()) {
- *error_msg = "Could not write string reference offsets " + temp_path;
- out->Erase(/*unlink=*/true);
- return false;
- }
-
- // Write dex cache array section.
- auto dex_cache_section = image.GetHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays);
- if (out->Write(reinterpret_cast<const char*>(image.GetDexCacheArrays().data()),
- dex_cache_section.Size(),
- dex_cache_section.Offset()) != dex_cache_section.Size()) {
- *error_msg = "Could not write dex cache arrays " + temp_path;
- out->Erase(/*unlink=*/true);
+ if (image_file == nullptr) {
+ *error_msg = "Could not open " + temp_path + " for writing";
return false;
}
- // Now write header.
- if (out->Write(reinterpret_cast<const char*>(&image.GetHeader()), sizeof(ImageHeader), 0u) !=
- sizeof(ImageHeader)) {
- *error_msg = "Could not write image header to " + temp_path;
- out->Erase(/*unlink=*/true);
+ std::vector<uint8_t> full_data(image.GetHeader()->GetImageSize());
+ image.FillData(full_data);
+
+ // Specify default block size of 512K to enable parallel image decompression.
+ static constexpr size_t kMaxImageBlockSize = 524288;
+ // Use LZ4 as good compromise between CPU time and compression. LZ4HC
+ // empirically takes 10x more time compressing.
+ static constexpr ImageHeader::StorageMode kImageStorageMode = ImageHeader::kStorageModeLZ4;
+ // Note: no need to update the checksum of the runtime app image: we have no
+ // use for it, and computing it takes CPU time.
+ if (!image.GetHeader()->WriteData(
+ image_file,
+ full_data.data(),
+ reinterpret_cast<const uint8_t*>(image.GetImageBitmap().Begin()),
+ kImageStorageMode,
+ kMaxImageBlockSize,
+ /* update_checksum= */ false,
+ error_msg)) {
return false;
}
- if (out->FlushClose() != 0) {
- *error_msg = "Could not flush and close " + temp_path;
- // Unlink directly: we cannot use `out` as we may have closed it.
- unlink(temp_path.c_str());
+ if (!image_file.WriteHeaderAndClose(temp_path, image.GetHeader(), error_msg)) {
return false;
}