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
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index 7ef24c7..9ffc414 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -333,9 +333,11 @@
                         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_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 @@
       }
     }
     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 @@
     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 @@
   };
 
   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 @@
              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 @@
     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 @@
       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 @@
   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 @@
       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 @@
                          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 @@
                                                   &remote_contents_,
                                                   &zygote_contents_,
                                                   boot_map_,
-                                                  image_header_);
+                                                  image_header_,
+                                                  dump_dirty_objects_);
 
     RemoteProcesses remotes;
     if (zygote_pid_only_) {
@@ -1364,6 +1398,7 @@
   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 @@
 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 @@
                                   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 @@
         *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 @@
         "  --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 @@
  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 @@
     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;
   }
 };