Add oatdump support for app images
Example usage on host:
oatdumpd --app-oat=art/plus32.odex --app-image=art/plus32.art
--image=art/oats/system@framework@boot.art --instruction-set=arm
TODO: Add to oatdump test.
Bug: 27408512
Bug: 22858531
(cherry picked from commit bcb6a72569a1401b36a3ad3b6aa4d13e29966cf0)
Change-Id: I9d1aa7eaa16795e5fbabc6974d245849e16b1d03
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 79fde5e..9ac18e8 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -291,6 +291,8 @@
bool list_methods,
bool dump_header_only,
const char* export_dex_location,
+ const char* app_image,
+ const char* app_oat,
uint32_t addr2instr)
: dump_raw_mapping_table_(dump_raw_mapping_table),
dump_raw_gc_map_(dump_raw_gc_map),
@@ -304,6 +306,8 @@
list_methods_(list_methods),
dump_header_only_(dump_header_only),
export_dex_location_(export_dex_location),
+ app_image_(app_image),
+ app_oat_(app_oat),
addr2instr_(addr2instr),
class_loader_(nullptr) {}
@@ -319,6 +323,8 @@
const bool list_methods_;
const bool dump_header_only_;
const char* const export_dex_location_;
+ const char* const app_image_;
+ const char* const app_oat_;
uint32_t addr2instr_;
Handle<mirror::ClassLoader>* class_loader_;
};
@@ -1444,8 +1450,10 @@
class ImageDumper {
public:
- ImageDumper(std::ostream* os, gc::space::ImageSpace& image_space,
- const ImageHeader& image_header, OatDumperOptions* oat_dumper_options)
+ ImageDumper(std::ostream* os,
+ gc::space::ImageSpace& image_space,
+ const ImageHeader& image_header,
+ OatDumperOptions* oat_dumper_options)
: os_(os),
vios_(os),
indent1_(&vios_),
@@ -1543,16 +1551,23 @@
os << "OAT LOCATION: " << oat_location;
os << "\n";
std::string error_msg;
- const OatFile* oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(
- oat_location);
+ const OatFile* oat_file = image_space_.GetOatFile();
if (oat_file == nullptr) {
- oat_file = OatFile::Open(oat_location, oat_location,
- nullptr, nullptr, false, nullptr,
+ oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location);
+ }
+ if (oat_file == nullptr) {
+ oat_file = OatFile::Open(oat_location,
+ oat_location,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
&error_msg);
- if (oat_file == nullptr) {
- os << "NOT FOUND: " << error_msg << "\n";
- return false;
- }
+ }
+ if (oat_file == nullptr) {
+ os << "OAT FILE NOT FOUND: " << error_msg << "\n";
+ return EXIT_FAILURE;
}
os << "\n";
@@ -1603,21 +1618,27 @@
// TODO: Dump fields.
// Dump methods after.
const auto& methods_section = image_header_.GetMethodsSection();
- const size_t pointer_size =
- InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet());
DumpArtMethodVisitor visitor(this);
- methods_section.VisitPackedArtMethods(&visitor, image_space_.Begin(), pointer_size);
+ methods_section.VisitPackedArtMethods(&visitor,
+ image_space_.Begin(),
+ image_header_.GetPointerSize());
// Dump the large objects separately.
heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
indent_os << "\n";
}
os << "STATS:\n" << std::flush;
std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str()));
- if (file.get() == nullptr) {
+ size_t data_size = image_header_.GetDataSize(); // stored size in file.
+ if (file == nullptr) {
LOG(WARNING) << "Failed to find image in " << image_filename;
- }
- if (file.get() != nullptr) {
+ } else {
stats_.file_bytes = file->GetLength();
+ // If the image is compressed, adjust to decompressed size.
+ size_t uncompressed_size = image_header_.GetImageSize() - sizeof(ImageHeader);
+ if (image_header_.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+ DCHECK_EQ(uncompressed_size, data_size) << "Sizes should match for uncompressed image";
+ }
+ stats_.file_bytes += uncompressed_size - data_size;
}
size_t header_bytes = sizeof(ImageHeader);
const auto& object_section = image_header_.GetImageSection(ImageHeader::kSectionObjects);
@@ -1664,10 +1685,10 @@
uint32_t end_intern = intern_section.Offset() + intern_section.Size();
stats_.alignment_bytes += class_table_section.Offset() - end_intern;
- // Add space between class table and bitmap. Expect the bitmap to be page-aligned.
- uint32_t end_ctable = class_table_section.Offset() + class_table_section.Size();
+ // Add space between end of image data and bitmap. Expect the bitmap to be page-aligned.
+ const size_t bitmap_offset = sizeof(ImageHeader) + data_size;
CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
- stats_.alignment_bytes += bitmap_section.Offset() - end_ctable;
+ stats_.alignment_bytes += RoundUp(bitmap_offset, kPageSize) - bitmap_offset;
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
@@ -1691,7 +1712,7 @@
virtual void Visit(ArtMethod* method) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
std::ostream& indent_os = image_dumper_->vios_.Stream();
indent_os << method << " " << " ArtMethod: " << PrettyMethod(method) << "\n";
- image_dumper_->DumpMethod(method, image_dumper_, indent_os);
+ image_dumper_->DumpMethod(method, indent_os);
indent_os << "\n";
}
@@ -1784,10 +1805,9 @@
return image_space_.Contains(object);
}
- const void* GetQuickOatCodeBegin(ArtMethod* m)
- SHARED_REQUIRES(Locks::mutator_lock_) {
+ const void* GetQuickOatCodeBegin(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) {
const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
- InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()));
+ image_header_.GetPointerSize());
if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) {
quick_code = oat_dumper_->GetQuickOatCode(m);
}
@@ -1846,8 +1866,7 @@
}
ScopedIndentation indent1(&state->vios_);
DumpFields(os, obj, obj_class);
- const auto image_pointer_size =
- InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet());
+ const size_t image_pointer_size = state->image_header_.GetPointerSize();
if (obj->IsObjectArray()) {
auto* obj_array = obj->AsObjectArray<mirror::Object>();
for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) {
@@ -1892,7 +1911,9 @@
ScopedIndentation indent2(&state->vios_);
auto* resolved_methods = dex_cache->GetResolvedMethods();
for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
- auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size);
+ auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods,
+ i,
+ image_pointer_size);
size_t run = 0;
for (size_t j = i + 1;
j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods,
@@ -1954,13 +1975,11 @@
state->stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
}
- void DumpMethod(ArtMethod* method, ImageDumper* state, std::ostream& indent_os)
+ void DumpMethod(ArtMethod* method, std::ostream& indent_os)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(method != nullptr);
- const auto image_pointer_size =
- InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet());
- const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method);
- const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method);
+ const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
+ const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
if (method->IsNative()) {
@@ -1969,13 +1988,13 @@
DCHECK(method_header->GetMappingTable() == nullptr) << PrettyMethod(method);
}
bool first_occurrence;
- uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
- state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
+ uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
+ ComputeOatSize(quick_oat_code_begin, &first_occurrence);
if (first_occurrence) {
- state->stats_.native_to_managed_code_bytes += quick_oat_code_size;
+ stats_.native_to_managed_code_bytes += quick_oat_code_size;
}
- if (quick_oat_code_begin !=
- method->GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size)) {
+ if (quick_oat_code_begin != method->GetEntryPointFromQuickCompiledCodePtrSize(
+ image_header_.GetPointerSize())) {
indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
@@ -1984,46 +2003,44 @@
} else {
const DexFile::CodeItem* code_item = method->GetCodeItem();
size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
- state->stats_.dex_instruction_bytes += dex_instruction_bytes;
+ stats_.dex_instruction_bytes += dex_instruction_bytes;
bool first_occurrence;
- size_t gc_map_bytes = state->ComputeOatSize(
- method_header->GetNativeGcMap(), &first_occurrence);
+ size_t gc_map_bytes = ComputeOatSize(method_header->GetNativeGcMap(), &first_occurrence);
if (first_occurrence) {
- state->stats_.gc_map_bytes += gc_map_bytes;
+ stats_.gc_map_bytes += gc_map_bytes;
}
- size_t pc_mapping_table_bytes = state->ComputeOatSize(
+ size_t pc_mapping_table_bytes = ComputeOatSize(
method_header->GetMappingTable(), &first_occurrence);
if (first_occurrence) {
- state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
+ stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
}
size_t vmap_table_bytes = 0u;
if (!method_header->IsOptimized()) {
// Method compiled with the optimizing compiler have no vmap table.
- vmap_table_bytes = state->ComputeOatSize(
- method_header->GetVmapTable(), &first_occurrence);
+ vmap_table_bytes = ComputeOatSize(method_header->GetVmapTable(), &first_occurrence);
if (first_occurrence) {
- state->stats_.vmap_table_bytes += vmap_table_bytes;
+ stats_.vmap_table_bytes += vmap_table_bytes;
}
}
- uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
- state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
+ uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
+ ComputeOatSize(quick_oat_code_begin, &first_occurrence);
if (first_occurrence) {
- state->stats_.managed_code_bytes += quick_oat_code_size;
+ stats_.managed_code_bytes += quick_oat_code_size;
if (method->IsConstructor()) {
if (method->IsStatic()) {
- state->stats_.class_initializer_code_bytes += quick_oat_code_size;
+ stats_.class_initializer_code_bytes += quick_oat_code_size;
} else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
- state->stats_.large_initializer_code_bytes += quick_oat_code_size;
+ stats_.large_initializer_code_bytes += quick_oat_code_size;
}
} else if (dex_instruction_bytes > kLargeMethodDexBytes) {
- state->stats_.large_method_code_bytes += quick_oat_code_size;
+ stats_.large_method_code_bytes += quick_oat_code_size;
}
}
- state->stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
+ stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
uint32_t method_access_flags = method->GetAccessFlags();
@@ -2033,11 +2050,11 @@
method_access_flags);
size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
- vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_pointer_size);
+ vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_header_.GetPointerSize());
double expansion =
static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes);
- state->stats_.ComputeOutliers(total_size, expansion, method);
+ stats_.ComputeOutliers(total_size, expansion, method);
}
}
@@ -2372,26 +2389,75 @@
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
-static int DumpImage(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
+static int DumpImage(gc::space::ImageSpace* image_space,
+ OatDumperOptions* options,
+ std::ostream* os) SHARED_REQUIRES(Locks::mutator_lock_) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ if (!image_header.IsValid()) {
+ fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ return EXIT_FAILURE;
+ }
+ ImageDumper image_dumper(os, *image_space, image_header, options);
+ if (!image_dumper.Dump()) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
// Dumping the image, no explicit class loader.
ScopedNullHandle<mirror::ClassLoader> null_class_loader;
options->class_loader_ = &null_class_loader;
ScopedObjectAccess soa(Thread::Current());
- gc::Heap* heap = runtime->GetHeap();
- std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
- CHECK(!image_spaces.empty());
- for (gc::space::ImageSpace* image_space : image_spaces) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- if (!image_header.IsValid()) {
- fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ if (options->app_image_ != nullptr) {
+ if (options->app_oat_ == nullptr) {
+ LOG(ERROR) << "Can not dump app image without app oat file";
return EXIT_FAILURE;
}
-
- ImageDumper image_dumper(os, *image_space, image_header, options);
- if (!image_dumper.Dump()) {
+ // We can't know if the app image is 32 bits yet, but it contains pointers into the oat file.
+ // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file
+ // pointers into 32 bit pointer sized ArtMethods.
+ std::string error_msg;
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(options->app_oat_,
+ options->app_oat_,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/true,
+ nullptr,
+ &error_msg));
+ if (oat_file == nullptr) {
+ LOG(ERROR) << "Failed to open oat file " << options->app_oat_ << " with error " << error_msg;
return EXIT_FAILURE;
}
+ std::unique_ptr<gc::space::ImageSpace> space(
+ gc::space::ImageSpace::CreateFromAppImage(options->app_image_, oat_file.get(), &error_msg));
+ if (space == nullptr) {
+ LOG(ERROR) << "Failed to open app image " << options->app_image_ << " with error "
+ << error_msg;
+ }
+ // Open dex files for the image.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (!runtime->GetClassLinker()->OpenImageDexFiles(space.get(), &dex_files, &error_msg)) {
+ LOG(ERROR) << "Failed to open app image dex files " << options->app_image_ << " with error "
+ << error_msg;
+ }
+ // Dump the actual image.
+ int result = DumpImage(space.get(), options, os);
+ if (result != EXIT_SUCCESS) {
+ return result;
+ }
+ // Fall through to dump the boot images.
+ }
+
+ gc::Heap* heap = runtime->GetHeap();
+ CHECK(heap->HasBootImageSpace()) << "No image spaces";
+ for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
+ int result = DumpImage(image_space, options, os);
+ if (result != EXIT_SUCCESS) {
+ return result;
+ }
}
return EXIT_SUCCESS;
}
@@ -2447,8 +2513,14 @@
static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
std::ostream* os) {
std::string error_msg;
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- nullptr, &error_msg);
+ OatFile* oat_file = OatFile::Open(oat_filename,
+ oat_filename,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2463,8 +2535,14 @@
static int SymbolizeOat(const char* oat_filename, std::string& output_name, bool no_bits) {
std::string error_msg;
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- nullptr, &error_msg);
+ OatFile* oat_file = OatFile::Open(oat_filename,
+ oat_filename,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2537,6 +2615,10 @@
*error_msg = "Address conversion failed";
return kParseError;
}
+ } else if (option.starts_with("--app-image=")) {
+ app_image_ = option.substr(strlen("--app-image=")).data();
+ } else if (option.starts_with("--app-oat=")) {
+ app_oat_ = option.substr(strlen("--app-oat=")).data();
} else {
return kParseUnknownArgument;
}
@@ -2582,6 +2664,13 @@
"\n"
" --image=<file.art>: specifies an input image location.\n"
" Example: --image=/system/framework/boot.art\n"
+ "\n"
+ " --app-image=<file.art>: specifies an input app image. Must also have a specified\n"
+ " boot image and app oat file.\n"
+ " Example: --app-image=app.art\n"
+ "\n"
+ " --app-oat=<file.odex>: specifies an input app oat.\n"
+ " Example: --app-oat=app.odex\n"
"\n";
usage += Base::GetUsage();
@@ -2655,6 +2744,8 @@
bool dump_header_only_ = false;
uint32_t addr2instr_ = 0;
const char* export_dex_location_ = nullptr;
+ const char* app_image_ = nullptr;
+ const char* app_oat_ = nullptr;
};
struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
@@ -2664,7 +2755,7 @@
// If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
bool absolute_addresses = (args_->oat_filename_ == nullptr);
- oat_dumper_options_ = std::unique_ptr<OatDumperOptions>(new OatDumperOptions(
+ oat_dumper_options_.reset(new OatDumperOptions(
args_->dump_raw_mapping_table_,
args_->dump_raw_gc_map_,
args_->dump_vmap_,
@@ -2677,6 +2768,8 @@
args_->list_methods_,
args_->dump_header_only_,
args_->export_dex_location_,
+ args_->app_image_,
+ args_->app_oat_,
args_->addr2instr_));
return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
@@ -2714,7 +2807,7 @@
args_->os_) == EXIT_SUCCESS;
}
- return DumpImage(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
+ return DumpImages(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
}
std::unique_ptr<OatDumperOptions> oat_dumper_options_;