summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Hao <jeffhao@google.com> 2017-07-27 18:19:38 -0700
committer Jeff Hao <jeffhao@google.com> 2017-08-02 14:35:48 -0700
commitc23b0c0409a19b0a0dc6d04abec0faf00ce1f141 (patch)
tree09da5c365ce3d7f2f6cfc68b76eb920f7b5eee34
parentd335e7b4d034bc8b69efde641779c6e1cd7bba42 (diff)
Group dirty images objects together in the image.
Adds --dirty-image-objects switch to dex2oat to pass to image writer, which takes in a list of dirty objects dumped by the new imgdump switch --dump-dirty-objects. Currently that list of dirty objects contains classes with dirty static fields. Bins these classes into kBinKnownDirty, which are image objects that are known to be dirty. Measured fewer dirty pages at runtime via showmap: systemui private dirty memory (kB): 492 -> 352 in boot-framework.art 204 -> 192 in boot.art 96 -> 88 in boot-core-libart.art systemserver private dirty memory (kB): 412 -> 304 in boot-framework.art 148 -> 132 in boot.art 100 -> 96 in boot-core-libart.art Bug: 62554875 Test: mm test-art-host Change-Id: If8293da07a97a2051c33890faa9b5d3b283b8e6d
-rw-r--r--compiler/image_test.h3
-rw-r--r--compiler/image_writer.cc13
-rw-r--r--compiler/image_writer.h7
-rw-r--r--dex2oat/dex2oat.cc37
-rw-r--r--dex2oat/dex2oat_image_test.cc9
-rw-r--r--imgdiag/imgdiag.cc74
6 files changed, 116 insertions, 27 deletions
diff --git a/compiler/image_test.h b/compiler/image_test.h
index 57d0987982..daa4b11967 100644
--- a/compiler/image_test.h
+++ b/compiler/image_test.h
@@ -214,7 +214,8 @@ inline void CompilationHelper::Compile(CompilerDriver* driver,
/*compile_app_image*/false,
storage_mode,
oat_filename_vector,
- dex_file_to_oat_index_map));
+ dex_file_to_oat_index_map,
+ /*dirty_image_objects*/nullptr));
{
{
jobject class_loader = nullptr;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f4e8a89e92..9e4971ce75 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -579,7 +579,12 @@ void ImageWriter::AssignImageBinSlot(mirror::Object* object, size_t oat_index) {
}
}
- if (klass->GetStatus() == Class::kStatusInitialized) {
+ // Move known dirty objects into their own sections. This includes:
+ // - classes with dirty static fields.
+ if (dirty_image_objects_ != nullptr &&
+ dirty_image_objects_->find(klass->PrettyDescriptor()) != dirty_image_objects_->end()) {
+ bin = kBinKnownDirty;
+ } else if (klass->GetStatus() == Class::kStatusInitialized) {
bin = kBinClassInitialized;
// If the class's static fields are all final, put it into a separate bin
@@ -2774,7 +2779,8 @@ ImageWriter::ImageWriter(
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
- const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map)
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects)
: compiler_driver_(compiler_driver),
global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
image_objects_offset_begin_(0),
@@ -2786,7 +2792,8 @@ ImageWriter::ImageWriter(
clean_methods_(0u),
image_storage_mode_(image_storage_mode),
oat_filenames_(oat_filenames),
- dex_file_oat_index_map_(dex_file_oat_index_map) {
+ dex_file_oat_index_map_(dex_file_oat_index_map),
+ dirty_image_objects_(dirty_image_objects) {
CHECK_NE(image_begin, 0U);
std::fill_n(image_methods_, arraysize(image_methods_), nullptr);
CHECK_EQ(compile_app_image, !Runtime::Current()->GetHeap()->GetBootImageSpaces().empty())
diff --git a/compiler/image_writer.h b/compiler/image_writer.h
index 34bbbad75d..866e2042f7 100644
--- a/compiler/image_writer.h
+++ b/compiler/image_writer.h
@@ -75,7 +75,8 @@ class ImageWriter FINAL {
bool compile_app_image,
ImageHeader::StorageMode image_storage_mode,
const std::vector<const char*>& oat_filenames,
- const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map);
+ const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+ const std::unordered_set<std::string>* dirty_image_objects);
bool PrepareImageAddressSpace();
@@ -159,6 +160,7 @@ class ImageWriter FINAL {
// Classify different kinds of bins that objects end up getting packed into during image writing.
// Ordered from dirtiest to cleanest (until ArtMethods).
enum Bin {
+ kBinKnownDirty, // Known dirty objects from --dirty-image-objects list
kBinMiscDirty, // Dex caches, object locks, etc...
kBinClassVerified, // Class verified, but initializers haven't been run
// Unknown mix of clean/dirty:
@@ -599,6 +601,9 @@ class ImageWriter FINAL {
// Map of dex files to the indexes of oat files that they were compiled into.
const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map_;
+ // Set of objects known to be dirty in the image. Can be nullptr if there are none.
+ const std::unordered_set<std::string>* dirty_image_objects_;
+
class ComputeLazyFieldsForClassesVisitor;
class FixupClassVisitor;
class FixupRootVisitor;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 0826fa1488..d57bf6cab3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -408,17 +408,17 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError("");
UsageError(" --class-loader-context=<string spec>: a string specifying the intended");
UsageError(" runtime loading context for the compiled dex files.");
- UsageError(" ");
+ UsageError("");
UsageError(" It describes how the class loader chain should be built in order to ensure");
UsageError(" classes are resolved during dex2aot as they would be resolved at runtime.");
UsageError(" This spec will be encoded in the oat file. If at runtime the dex file is");
UsageError(" loaded in a different context, the oat file will be rejected.");
- UsageError(" ");
+ UsageError("");
UsageError(" The chain is interpreted in the natural 'parent order', meaning that class");
UsageError(" loader 'i+1' will be the parent of class loader 'i'.");
UsageError(" The compilation sources will be appended to the classpath of the first class");
UsageError(" loader.");
- UsageError(" ");
+ UsageError("");
UsageError(" E.g. if the context is 'PCL[lib1.dex];DLC[lib2.dex]' and ");
UsageError(" --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath ");
UsageError(" 'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with ");
@@ -428,9 +428,12 @@ NO_RETURN static void Usage(const char* fmt, ...) {
UsageError(" with --dex-file are found in the classpath. The source dex files will be");
UsageError(" removed from any class loader's classpath possibly resulting in empty");
UsageError(" class loaders.");
- UsageError(" ");
+ UsageError("");
UsageError(" Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
UsageError("");
+ UsageError(" --dirty-image-objects=<directory-path>: list of known dirty objects in the image.");
+ UsageError(" The image writer will group them together.");
+ UsageError("");
std::cerr << "See log for usage error information\n";
exit(EXIT_FAILURE);
}
@@ -1307,6 +1310,8 @@ class Dex2Oat FINAL {
if (class_loader_context_== nullptr) {
Usage("Option --class-loader-context has an incorrect format: %s", option.data());
}
+ } else if (option.starts_with("--dirty-image-objects=")) {
+ dirty_image_objects_filename_ = option.substr(strlen("--dirty-image-objects=")).data();
} else if (!compiler_options_->ParseCompilerOption(option, Usage)) {
Usage("Unknown argument %s", option.data());
}
@@ -1508,7 +1513,8 @@ class Dex2Oat FINAL {
dex2oat::ReturnCode Setup() {
TimingLogger::ScopedTiming t("dex2oat Setup", timings_);
- if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) {
+ if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods() ||
+ !PrepareDirtyObjects()) {
return dex2oat::ReturnCode::kOther;
}
@@ -2002,7 +2008,8 @@ class Dex2Oat FINAL {
IsAppImage(),
image_storage_mode_,
oat_filenames_,
- dex_file_oat_index_map_));
+ dex_file_oat_index_map_,
+ dirty_image_objects_.get()));
// We need to prepare method offsets in the image address space for direct method patching.
TimingLogger::ScopedTiming t2("dex2oat Prepare image address space", timings_);
@@ -2428,6 +2435,22 @@ class Dex2Oat FINAL {
return true;
}
+ bool PrepareDirtyObjects() {
+ if (dirty_image_objects_filename_ != nullptr) {
+ dirty_image_objects_.reset(ReadCommentedInputFromFile<std::unordered_set<std::string>>(
+ dirty_image_objects_filename_,
+ nullptr));
+ if (dirty_image_objects_ == nullptr) {
+ LOG(ERROR) << "Failed to create list of dirty objects from '"
+ << dirty_image_objects_filename_ << "'";
+ return false;
+ }
+ } else {
+ dirty_image_objects_.reset(nullptr);
+ }
+ return true;
+ }
+
void PruneNonExistentDexFiles() {
DCHECK_EQ(dex_filenames_.size(), dex_locations_.size());
size_t kept = 0u;
@@ -2845,9 +2868,11 @@ class Dex2Oat FINAL {
const char* compiled_methods_zip_filename_;
const char* compiled_methods_filename_;
const char* passes_to_run_filename_;
+ const char* dirty_image_objects_filename_;
std::unique_ptr<std::unordered_set<std::string>> image_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_classes_;
std::unique_ptr<std::unordered_set<std::string>> compiled_methods_;
+ std::unique_ptr<std::unordered_set<std::string>> dirty_image_objects_;
std::unique_ptr<std::vector<std::string>> passes_to_run_;
bool multi_image_;
bool is_host_;
diff --git a/dex2oat/dex2oat_image_test.cc b/dex2oat/dex2oat_image_test.cc
index 95fb16dfd7..46c5f581b6 100644
--- a/dex2oat/dex2oat_image_test.cc
+++ b/dex2oat/dex2oat_image_test.cc
@@ -340,6 +340,15 @@ TEST_F(Dex2oatImageTest, TestModesAndFilters) {
// EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
EXPECT_GE(profile_sizes.vdex_size / kRatio, compiled_methods_sizes.vdex_size);
}
+ // Test dirty image objects.
+ {
+ ScratchFile classes;
+ GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
+ image_classes_sizes = CompileImageAndGetSizes(
+ {"--dirty-image-objects=" + classes.GetFilename()});
+ classes.Close();
+ std::cout << "Dirty image object sizes " << image_classes_sizes << std::endl;
+ }
}
} // namespace art
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 7ef24c700e..9ffc4149ab 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -333,9 +333,11 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
std::vector<uint8_t>* remote_contents,
std::vector<uint8_t>* zygote_contents,
const backtrace_map_t& boot_map,
- const ImageHeader& image_header) :
- RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
- os_(*os) { }
+ const ImageHeader& image_header,
+ bool dump_dirty_objects)
+ : RegionCommon<mirror::Object>(os, remote_contents, zygote_contents, boot_map, image_header),
+ os_(*os),
+ dump_dirty_objects_(dump_dirty_objects) { }
void CheckEntrySanity(const uint8_t* current) const
REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -396,7 +398,10 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
class_data_[klass].AddDirtyObject(entry, entry_remote);
}
- void DiffEntryContents(mirror::Object* entry, uint8_t* remote_bytes, const uint8_t* base_ptr)
+ void DiffEntryContents(mirror::Object* entry,
+ uint8_t* remote_bytes,
+ const uint8_t* base_ptr,
+ bool log_dirty_objects)
REQUIRES_SHARED(Locks::mutator_lock_) {
const char* tabs = " ";
// Attempt to find fields for all dirty bytes.
@@ -453,6 +458,9 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
}
}
if (!dirty_static_fields.empty()) {
+ if (dump_dirty_objects_ && log_dirty_objects) {
+ dirty_objects_.insert(entry);
+ }
os_ << tabs << "Dirty static fields " << dirty_static_fields.size() << "\n";
for (ArtField* field : dirty_static_fields) {
os_ << tabs << ArtField::PrettyField(field)
@@ -463,6 +471,14 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
os_ << "\n";
}
+ void DumpDirtyObjects() REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (mirror::Object* obj : dirty_objects_) {
+ if (obj->IsClass()) {
+ os_ << "Private dirty object: " << obj->AsClass()->PrettyDescriptor() << "\n";
+ }
+ }
+ }
+
void DumpDirtyEntries() REQUIRES_SHARED(Locks::mutator_lock_) {
// vector of pairs (size_t count, Class*)
auto dirty_object_class_values =
@@ -592,6 +608,8 @@ class RegionSpecializedBase<mirror::Object> : public RegionCommon<mirror::Object
};
std::ostream& os_;
+ bool dump_dirty_objects_;
+ std::unordered_set<mirror::Object*> dirty_objects_;
std::map<mirror::Class*, ClassData> class_data_;
DISALLOW_COPY_AND_ASSIGN(RegionSpecializedBase);
@@ -720,9 +738,15 @@ class RegionData : public RegionSpecializedBase<T> {
std::vector<uint8_t>* remote_contents,
std::vector<uint8_t>* zygote_contents,
const backtrace_map_t& boot_map,
- const ImageHeader& image_header) :
- RegionSpecializedBase<T>(os, remote_contents, zygote_contents, boot_map, image_header),
- os_(*os) {
+ const ImageHeader& image_header,
+ bool dump_dirty_objects)
+ : RegionSpecializedBase<T>(os,
+ remote_contents,
+ zygote_contents,
+ boot_map,
+ image_header,
+ dump_dirty_objects),
+ os_(*os) {
CHECK(remote_contents != nullptr);
CHECK(zygote_contents != nullptr);
}
@@ -773,7 +797,8 @@ class RegionData : public RegionSpecializedBase<T> {
DiffDirtyEntries(ProcessType::kRemote,
begin_image_ptr,
RegionCommon<T>::remote_contents_,
- base_ptr);
+ base_ptr,
+ /*log_dirty_objects*/true);
// Print shared dirty after since it's less important.
if (RegionCommon<T>::GetZygoteDirtyEntryCount() != 0) {
// We only reach this point if both pids were specified. Furthermore,
@@ -784,8 +809,10 @@ class RegionData : public RegionSpecializedBase<T> {
DiffDirtyEntries(ProcessType::kZygote,
begin_image_ptr,
RegionCommon<T>::zygote_contents_,
- begin_image_ptr);
+ begin_image_ptr,
+ /*log_dirty_objects*/false);
}
+ RegionSpecializedBase<T>::DumpDirtyObjects();
RegionSpecializedBase<T>::DumpDirtyEntries();
RegionSpecializedBase<T>::DumpFalseDirtyEntries();
RegionSpecializedBase<T>::DumpCleanEntries();
@@ -797,7 +824,8 @@ class RegionData : public RegionSpecializedBase<T> {
void DiffDirtyEntries(ProcessType process_type,
const uint8_t* begin_image_ptr,
std::vector<uint8_t>* contents,
- const uint8_t* base_ptr)
+ const uint8_t* base_ptr,
+ bool log_dirty_objects)
REQUIRES_SHARED(Locks::mutator_lock_) {
os_ << RegionCommon<T>::dirty_entries_.size() << "\n";
const std::set<T*>& entries =
@@ -808,7 +836,10 @@ class RegionData : public RegionSpecializedBase<T> {
uint8_t* entry_bytes = reinterpret_cast<uint8_t*>(entry);
ptrdiff_t offset = entry_bytes - begin_image_ptr;
uint8_t* remote_bytes = &(*contents)[offset];
- RegionSpecializedBase<T>::DiffEntryContents(entry, remote_bytes, &base_ptr[offset]);
+ RegionSpecializedBase<T>::DiffEntryContents(entry,
+ remote_bytes,
+ &base_ptr[offset],
+ log_dirty_objects);
}
}
@@ -872,12 +903,14 @@ class ImgDiagDumper {
const ImageHeader& image_header,
const std::string& image_location,
pid_t image_diff_pid,
- pid_t zygote_diff_pid)
+ pid_t zygote_diff_pid,
+ bool dump_dirty_objects)
: os_(os),
image_header_(image_header),
image_location_(image_location),
image_diff_pid_(image_diff_pid),
zygote_diff_pid_(zygote_diff_pid),
+ dump_dirty_objects_(dump_dirty_objects),
zygote_pid_only_(false) {}
bool Init() {
@@ -1207,7 +1240,8 @@ class ImgDiagDumper {
&remote_contents_,
&zygote_contents_,
boot_map_,
- image_header_);
+ image_header_,
+ dump_dirty_objects_);
RemoteProcesses remotes;
if (zygote_pid_only_) {
@@ -1364,6 +1398,7 @@ class ImgDiagDumper {
const std::string image_location_;
pid_t image_diff_pid_; // Dump image diff against boot.art if pid is non-negative
pid_t zygote_diff_pid_; // Dump image diff against zygote boot.art if pid is non-negative
+ bool dump_dirty_objects_; // Adds dumping of objects that are dirty.
bool zygote_pid_only_; // The user only specified a pid for the zygote.
// BacktraceMap used for finding the memory mapping of the image file.
@@ -1391,7 +1426,8 @@ class ImgDiagDumper {
static int DumpImage(Runtime* runtime,
std::ostream* os,
pid_t image_diff_pid,
- pid_t zygote_diff_pid) {
+ pid_t zygote_diff_pid,
+ bool dump_dirty_objects) {
ScopedObjectAccess soa(Thread::Current());
gc::Heap* heap = runtime->GetHeap();
std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
@@ -1407,7 +1443,8 @@ static int DumpImage(Runtime* runtime,
image_header,
image_space->GetImageLocation(),
image_diff_pid,
- zygote_diff_pid);
+ zygote_diff_pid,
+ dump_dirty_objects);
if (!img_diag_dumper.Init()) {
return EXIT_FAILURE;
}
@@ -1445,6 +1482,8 @@ struct ImgDiagArgs : public CmdlineArgs {
*error_msg = "Zygote diff pid out of range";
return kParseError;
}
+ } else if (option == "--dump-dirty-objects") {
+ dump_dirty_objects_ = true;
} else {
return kParseUnknownArgument;
}
@@ -1497,6 +1536,7 @@ struct ImgDiagArgs : public CmdlineArgs {
" --zygote-diff-pid=<pid>: provide the PID of the zygote whose boot.art you want to diff "
"against.\n"
" Example: --zygote-diff-pid=$(pid zygote)\n"
+ " --dump-dirty-objects: additionally output dirty objects of interest.\n"
"\n";
return usage;
@@ -1505,6 +1545,7 @@ struct ImgDiagArgs : public CmdlineArgs {
public:
pid_t image_diff_pid_ = -1;
pid_t zygote_diff_pid_ = -1;
+ bool dump_dirty_objects_ = false;
};
struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
@@ -1514,7 +1555,8 @@ struct ImgDiagMain : public CmdlineMain<ImgDiagArgs> {
return DumpImage(runtime,
args_->os_,
args_->image_diff_pid_,
- args_->zygote_diff_pid_) == EXIT_SUCCESS;
+ args_->zygote_diff_pid_,
+ args_->dump_dirty_objects_) == EXIT_SUCCESS;
}
};