Extend profman to be able to dump profile content.

With the move to binary format it gets harder to debug profiles. This
adds a simple dumping mechanism to make profiles human readable.

Bug: 27600652

(cherry picked from commit 54196728c25421e72d0009cac4c3145a0da18a58)

Change-Id: I73148fe9e24f30089e9f0fb383e9f6f6c61a46e5
diff --git a/profman/profman.cc b/profman/profman.cc
index 7c9e449..3e632bc 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
+#include "errno.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/file.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <iostream>
 #include <string>
 #include <vector>
 
@@ -68,6 +70,9 @@
   UsageError("Command: %s", CommandLine().c_str());
   UsageError("Usage: profman [options]...");
   UsageError("");
+  UsageError("  --dump-info-for=<filename>: dumps the content of the profile file");
+  UsageError("      to standard output in a human readable form.");
+  UsageError("");
   UsageError("  --profile-file=<filename>: specify profiler output file to use for compilation.");
   UsageError("      Can be specified multiple time, in which case the data from the different");
   UsageError("      profiles will be aggregated.");
@@ -117,9 +122,11 @@
       const StringPiece option(argv[i]);
       const bool log_options = false;
       if (log_options) {
-        LOG(INFO) << "patchoat: option[" << i << "]=" << argv[i];
+        LOG(INFO) << "profman: option[" << i << "]=" << argv[i];
       }
-      if (option.starts_with("--profile-file=")) {
+      if (option.starts_with("--dump-info-for=")) {
+        dump_info_for_ = option.substr(strlen("--dump-info-for=")).ToString();
+      } else if (option.starts_with("--profile-file=")) {
         profile_files_.push_back(option.substr(strlen("--profile-file=")).ToString());
       } else if (option.starts_with("--profile-file-fd=")) {
         ParseFdForCollection(option, "--profile-file-fd", &profile_files_fd_);
@@ -132,13 +139,23 @@
       }
     }
 
-    if (profile_files_.empty() && profile_files_fd_.empty()) {
+    bool has_profiles = !profile_files_.empty() || !profile_files_fd_.empty();
+    bool has_reference_profile = !reference_profile_file_.empty() ||
+        (reference_profile_file_fd_ != -1);
+
+    if (!dump_info_for_.empty()) {
+      if (has_profiles || has_reference_profile) {
+        Usage("dump-info-for cannot be specified together with other options");
+      }
+      return;
+    }
+    if (!has_profiles) {
       Usage("No profile files specified.");
     }
     if (!profile_files_.empty() && !profile_files_fd_.empty()) {
       Usage("Profile files should not be specified with both --profile-file-fd and --profile-file");
     }
-    if (!reference_profile_file_.empty() && (reference_profile_file_fd_ != -1)) {
+    if (!has_reference_profile) {
       Usage("--reference-profile-file-fd should only be supplied with --profile-file-fd");
     }
     if (reference_profile_file_.empty() && (reference_profile_file_fd_ == -1)) {
@@ -160,6 +177,27 @@
     return result;
   }
 
+  int DumpProfileInfo() {
+    int fd = open(dump_info_for_.c_str(), O_RDWR);
+    if (fd < 0) {
+      std::cerr << "Cannot open " << dump_info_for_ << strerror(errno);
+      return -1;
+    }
+    ProfileCompilationInfo info;
+    if (!info.Load(fd)) {
+      std::cerr << "Cannot load profile info from " << dump_info_for_;
+      return -1;
+    }
+    std::string dump = info.DumpInfo(/*dex_files*/ nullptr);
+    info.Save(fd);
+    std::cout << dump << "\n";
+    return 0;
+  }
+
+  bool ShouldOnlyDumpProfile() {
+    return !dump_info_for_.empty();
+  }
+
  private:
   static void ParseFdForCollection(const StringPiece& option,
                                    const char* arg_name,
@@ -186,6 +224,7 @@
   std::string reference_profile_file_;
   int reference_profile_file_fd_;
   uint64_t start_ns_;
+  std::string dump_info_for_;
 };
 
 // See ProfileAssistant::ProcessingResult for return codes.
@@ -195,6 +234,9 @@
   // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
   profman.ParseArgs(argc, argv);
 
+  if (profman.ShouldOnlyDumpProfile()) {
+    return profman.DumpProfileInfo();
+  }
   // Process profile information and assess if we need to do a profile guided compilation.
   // This operation involves I/O.
   return profman.ProcessProfiles();
diff --git a/runtime/jit/offline_profiling_info.cc b/runtime/jit/offline_profiling_info.cc
index 4ecb926..fa71905 100644
--- a/runtime/jit/offline_profiling_info.cc
+++ b/runtime/jit/offline_profiling_info.cc
@@ -616,19 +616,29 @@
       std::string multidex_suffix = DexFile::GetMultiDexSuffix(location);
       os << (multidex_suffix.empty() ? kFirstDexFileKeySubstitute : multidex_suffix);
     }
-    for (const auto method_it : dex_data.method_set) {
-      if (dex_files != nullptr) {
-        const DexFile* dex_file = nullptr;
-        for (size_t i = 0; i < dex_files->size(); i++) {
-          if (location == (*dex_files)[i]->GetLocation()) {
-            dex_file = (*dex_files)[i];
-          }
-        }
-        if (dex_file != nullptr) {
-          os << "\n  " << PrettyMethod(method_it, *dex_file, true);
+    const DexFile* dex_file = nullptr;
+    if (dex_files != nullptr) {
+      for (size_t i = 0; i < dex_files->size(); i++) {
+        if (location == (*dex_files)[i]->GetLocation()) {
+          dex_file = (*dex_files)[i];
         }
       }
-      os << ", " << method_it;
+    }
+    os << "\n\tmethods: ";
+    for (const auto method_it : dex_data.method_set) {
+      if (dex_file != nullptr) {
+        os << "\n\t\t" << PrettyMethod(method_it, *dex_file, true);
+      } else {
+        os << method_it << ",";
+      }
+    }
+    os << "\n\tclasses: ";
+    for (const auto class_it : dex_data.class_set) {
+      if (dex_file != nullptr) {
+        os << "\n\t\t" << PrettyType(class_it, *dex_file);
+      } else {
+        os << class_it << ",";
+      }
     }
   }
   return os.str();