Refactor experiments to allow multidex analysis

Analyze multidex deduplication for debug infos when excluding the
header fields.

Bug: 77721545
Test: test-art-host

Change-Id: I247399aaecaf072cce1b3e035256db0f5e91e8c3
diff --git a/tools/dexanalyze/dexanalyze.cc b/tools/dexanalyze/dexanalyze.cc
index 7d7e5f2..7a9b8fb 100644
--- a/tools/dexanalyze/dexanalyze.cc
+++ b/tools/dexanalyze/dexanalyze.cc
@@ -116,12 +116,14 @@
       }
     }
 
-    bool ProcessDexFile(const DexFile& dex_file) {
+    bool ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
       for (std::unique_ptr<Experiment>& experiment : experiments_) {
-        experiment->ProcessDexFile(dex_file);
+        experiment->ProcessDexFiles(dex_files);
       }
-      total_size_ += dex_file.Size();
-      ++dex_count_;
+      for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+        total_size_ += dex_file->Size();
+      }
+      dex_count_ += dex_files.size();
       return true;
     }
 
@@ -169,18 +171,16 @@
         LOG(ERROR) << "OpenAll failed for " + filename << " with " << error_msg << std::endl;
         return kExitCodeFailedToOpenDex;
       }
-      for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
-        if (options.dump_per_input_dex_) {
-          Analysis current(&options);
-          if (!current.ProcessDexFile(*dex_file)) {
-            LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg;
-            return kExitCodeFailedToProcessDex;
-          }
-          LOG(INFO) << "Analysis for " << dex_file->GetLocation() << std::endl;
-          current.Dump(LOG_STREAM(INFO));
+      if (options.dump_per_input_dex_) {
+        Analysis current(&options);
+        if (!current.ProcessDexFiles(dex_files)) {
+          LOG(ERROR) << "Failed to process " << filename << " with error " << error_msg;
+          return kExitCodeFailedToProcessDex;
         }
-        cumulative.ProcessDexFile(*dex_file);
+        LOG(INFO) << "Analysis for " << filename << std::endl;
+        current.Dump(LOG_STREAM(INFO));
       }
+      cumulative.ProcessDexFiles(dex_files);
     }
     LOG(INFO) << "Cumulative analysis for " << cumulative.dex_count_ << " DEX files" << std::endl;
     cumulative.Dump(LOG_STREAM(INFO));
diff --git a/tools/dexanalyze/dexanalyze_experiments.cc b/tools/dexanalyze/dexanalyze_experiments.cc
index 1a3b89c..244f45b 100644
--- a/tools/dexanalyze/dexanalyze_experiments.cc
+++ b/tools/dexanalyze/dexanalyze_experiments.cc
@@ -75,86 +75,95 @@
   return len;
 }
 
-void AnalyzeDebugInfo::ProcessDexFile(const DexFile& dex_file) {
+void Experiment::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    ProcessDexFile(*dex_file);
+  }
+}
+
+void AnalyzeDebugInfo::ProcessDexFiles(
+    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
   std::set<const uint8_t*> seen;
   std::vector<size_t> counts(256, 0u);
   std::vector<size_t> opcode_counts(256, 0u);
   std::set<std::vector<uint8_t>> unique_non_header;
-  for (ClassAccessor accessor : dex_file.GetClasses()) {
-    for (const ClassAccessor::Method& method : accessor.GetMethods()) {
-      CodeItemDebugInfoAccessor code_item(dex_file, method.GetCodeItem(), method.GetIndex());
-      const uint8_t* debug_info = dex_file.GetDebugInfoStream(code_item.DebugInfoOffset());
-      if (debug_info != nullptr && seen.insert(debug_info).second) {
-        const uint8_t* stream = debug_info;
-        DecodeUnsignedLeb128(&stream);  // line_start
-        uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-        for (uint32_t i = 0; i < parameters_size; ++i) {
-          DecodeUnsignedLeb128P1(&stream);  // Parameter name.
-        }
-        bool done = false;
-        const uint8_t* after_header_start = stream;
-        while (!done) {
-          const uint8_t* const op_start = stream;
-          uint8_t opcode = *stream++;
-          ++opcode_counts[opcode];
-          ++total_opcode_bytes_;
-          switch (opcode) {
-            case DexFile::DBG_END_SEQUENCE:
-              ++total_end_seq_bytes_;
-              done = true;
-              break;
-            case DexFile::DBG_ADVANCE_PC:
-              DecodeUnsignedLeb128(&stream);  // addr_diff
-              total_advance_pc_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_ADVANCE_LINE:
-              DecodeSignedLeb128(&stream);  // line_diff
-              total_advance_line_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_START_LOCAL:
-              DecodeUnsignedLeb128(&stream);  // register_num
-              DecodeUnsignedLeb128P1(&stream);  // name_idx
-              DecodeUnsignedLeb128P1(&stream);  // type_idx
-              total_start_local_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_START_LOCAL_EXTENDED:
-              DecodeUnsignedLeb128(&stream);  // register_num
-              DecodeUnsignedLeb128P1(&stream);  // name_idx
-              DecodeUnsignedLeb128P1(&stream);  // type_idx
-              DecodeUnsignedLeb128P1(&stream);  // sig_idx
-              total_start_local_extended_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_END_LOCAL:
-              DecodeUnsignedLeb128(&stream);  // register_num
-              total_end_local_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_RESTART_LOCAL:
-              DecodeUnsignedLeb128(&stream);  // register_num
-              total_restart_local_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_SET_PROLOGUE_END:
-            case DexFile::DBG_SET_EPILOGUE_BEGIN:
-              total_epilogue_bytes_ += stream - op_start;
-              break;
-            case DexFile::DBG_SET_FILE: {
-              DecodeUnsignedLeb128P1(&stream);  // name_idx
-              total_set_file_bytes_ += stream - op_start;
-              break;
-            }
-            default: {
-              total_other_bytes_ += stream - op_start;
-              break;
+  for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+    for (ClassAccessor accessor : dex_file->GetClasses()) {
+      for (const ClassAccessor::Method& method : accessor.GetMethods()) {
+        CodeItemDebugInfoAccessor code_item(*dex_file, method.GetCodeItem(), method.GetIndex());
+        const uint8_t* debug_info = dex_file->GetDebugInfoStream(code_item.DebugInfoOffset());
+        if (debug_info != nullptr && seen.insert(debug_info).second) {
+          const uint8_t* stream = debug_info;
+          DecodeUnsignedLeb128(&stream);  // line_start
+          uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
+          for (uint32_t i = 0; i < parameters_size; ++i) {
+            DecodeUnsignedLeb128P1(&stream);  // Parameter name.
+          }
+          bool done = false;
+          const uint8_t* after_header_start = stream;
+          while (!done) {
+            const uint8_t* const op_start = stream;
+            uint8_t opcode = *stream++;
+            ++opcode_counts[opcode];
+            ++total_opcode_bytes_;
+            switch (opcode) {
+              case DexFile::DBG_END_SEQUENCE:
+                ++total_end_seq_bytes_;
+                done = true;
+                break;
+              case DexFile::DBG_ADVANCE_PC:
+                DecodeUnsignedLeb128(&stream);  // addr_diff
+                total_advance_pc_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_ADVANCE_LINE:
+                DecodeSignedLeb128(&stream);  // line_diff
+                total_advance_line_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_START_LOCAL:
+                DecodeUnsignedLeb128(&stream);  // register_num
+                DecodeUnsignedLeb128P1(&stream);  // name_idx
+                DecodeUnsignedLeb128P1(&stream);  // type_idx
+                total_start_local_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_START_LOCAL_EXTENDED:
+                DecodeUnsignedLeb128(&stream);  // register_num
+                DecodeUnsignedLeb128P1(&stream);  // name_idx
+                DecodeUnsignedLeb128P1(&stream);  // type_idx
+                DecodeUnsignedLeb128P1(&stream);  // sig_idx
+                total_start_local_extended_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_END_LOCAL:
+                DecodeUnsignedLeb128(&stream);  // register_num
+                total_end_local_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_RESTART_LOCAL:
+                DecodeUnsignedLeb128(&stream);  // register_num
+                total_restart_local_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_SET_PROLOGUE_END:
+              case DexFile::DBG_SET_EPILOGUE_BEGIN:
+                total_epilogue_bytes_ += stream - op_start;
+                break;
+              case DexFile::DBG_SET_FILE: {
+                DecodeUnsignedLeb128P1(&stream);  // name_idx
+                total_set_file_bytes_ += stream - op_start;
+                break;
+              }
+              default: {
+                total_other_bytes_ += stream - op_start;
+                break;
+              }
             }
           }
-        }
-        const size_t bytes = stream - debug_info;
-        total_bytes_ += bytes;
-        total_non_header_bytes_ += stream - after_header_start;
-        if (unique_non_header.insert(std::vector<uint8_t>(after_header_start, stream)).second) {
-          total_unique_non_header_bytes_ += stream - after_header_start;
-        }
-        for (size_t i = 0; i < bytes; ++i) {
-          ++counts[debug_info[i]];
+          const size_t bytes = stream - debug_info;
+          total_bytes_ += bytes;
+          total_non_header_bytes_ += stream - after_header_start;
+          if (unique_non_header.insert(std::vector<uint8_t>(after_header_start, stream)).second) {
+            total_unique_non_header_bytes_ += stream - after_header_start;
+          }
+          for (size_t i = 0; i < bytes; ++i) {
+            ++counts[debug_info[i]];
+          }
         }
       }
     }
diff --git a/tools/dexanalyze/dexanalyze_experiments.h b/tools/dexanalyze/dexanalyze_experiments.h
index a2621c8..2be53d6 100644
--- a/tools/dexanalyze/dexanalyze_experiments.h
+++ b/tools/dexanalyze/dexanalyze_experiments.h
@@ -18,7 +18,9 @@
 #define ART_TOOLS_DEXANALYZE_DEXANALYZE_EXPERIMENTS_H_
 
 #include <iosfwd>
+#include <memory>
 #include <set>
+#include <vector>
 
 namespace art {
 
@@ -30,7 +32,8 @@
 class Experiment {
  public:
   virtual ~Experiment() {}
-  virtual void ProcessDexFile(const DexFile& dex_file) = 0;
+  virtual void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+  virtual void ProcessDexFile(const DexFile&) {}
   virtual void Dump(std::ostream& os, uint64_t total_size) const = 0;
 };
 
@@ -54,7 +57,7 @@
 // Analyze debug info sizes.
 class AnalyzeDebugInfo  : public Experiment {
  public:
-  void ProcessDexFile(const DexFile& dex_file);
+  void ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files);
   void Dump(std::ostream& os, uint64_t total_size) const;
 
  private:
@@ -112,7 +115,7 @@
   size_t total_super_ = 0;
 };
 
-// Measure various code metrics including args per invoke-virtual, fill/spill move paterns.
+// Measure various code metrics including args per invoke-virtual, fill/spill move patterns.
 class CodeMetrics : public Experiment {
  public:
   void ProcessDexFile(const DexFile& dex_file);