Read /proc/self/smaps in perfetto_hprof.

This will allow us to debug issues related to dexfiles being extracted
into RAM.

Test: flash aosp_blueline-userdebug and profile.
Bug: 150930222

This is a cherry-pick of 4b79ef49dce84b8e4c08d25b73411329fc6a0d55

Change-Id: Id3af447794c04f776e429c5f5688e762af265531
Merged-In: Id3af447794c04f776e429c5f5688e762af265531
diff --git a/perfetto_hprof/perfetto_hprof.cc b/perfetto_hprof/perfetto_hprof.cc
index c761d43..5626e5f 100644
--- a/perfetto_hprof/perfetto_hprof.cc
+++ b/perfetto_hprof/perfetto_hprof.cc
@@ -35,9 +35,11 @@
 #include "mirror/object-refvisitor-inl.h"
 #include "nativehelper/scoped_local_ref.h"
 #include "perfetto/profiling/normalize.h"
+#include "perfetto/profiling/parse_smaps.h"
 #include "perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "perfetto/trace/profiling/heap_graph.pbzero.h"
 #include "perfetto/trace/profiling/profile_common.pbzero.h"
+#include "perfetto/trace/profiling/smaps.pbzero.h"
 #include "perfetto/config/profiling/java_hprof_config.pbzero.h"
 #include "perfetto/protozero/packed_repeated_fields.h"
 #include "perfetto/tracing.h"
@@ -110,6 +112,34 @@
   }
 }
 
+bool StartsWith(const std::string& str, const std::string& prefix) {
+  return str.compare(0, prefix.length(), prefix) == 0;
+}
+
+// Sample entries that match one of the following
+// start with /system/
+// start with /vendor/
+// start with /data/app/
+// contains "extracted in memory from Y", where Y matches any of the above
+bool ShouldSampleSmapsEntry(const perfetto::profiling::SmapsEntry& e) {
+  if (StartsWith(e.pathname, "/system/") || StartsWith(e.pathname, "/vendor/") ||
+      StartsWith(e.pathname, "/data/app/")) {
+    return true;
+  }
+  if (StartsWith(e.pathname, "[anon:")) {
+    if (e.pathname.find("extracted in memory from /system/") != std::string::npos) {
+      return true;
+    }
+    if (e.pathname.find("extracted in memory from /vendor/") != std::string::npos) {
+      return true;
+    }
+    if (e.pathname.find("extracted in memory from /data/app/") != std::string::npos) {
+      return true;
+    }
+  }
+  return false;
+}
+
 constexpr size_t kMaxCmdlineSize = 512;
 
 class JavaHprofDataSource : public perfetto::DataSource<JavaHprofDataSource> {
@@ -122,6 +152,8 @@
         new perfetto::protos::pbzero::JavaHprofConfig::Decoder(
           args.config->java_hprof_config_raw()));
 
+    dump_smaps_ = cfg->dump_smaps();
+
     uint64_t self_pid = static_cast<uint64_t>(getpid());
     for (auto pid_it = cfg->pid(); pid_it; ++pid_it) {
       if (*pid_it == self_pid) {
@@ -169,6 +201,7 @@
     }
   }
 
+  bool dump_smaps() { return dump_smaps_; }
   bool enabled() { return enabled_; }
 
   void OnStart(const StartArgs&) override {
@@ -196,6 +229,7 @@
 
  private:
   bool enabled_ = false;
+  bool dump_smaps_ = false;
   static art::Thread* self_;
 };
 
@@ -357,6 +391,28 @@
   return result;
 }
 
+void DumpSmaps(JavaHprofDataSource::TraceContext* ctx) {
+  FILE* smaps = fopen("/proc/self/smaps", "r");
+  if (smaps != nullptr) {
+    auto trace_packet = ctx->NewTracePacket();
+    auto* smaps_packet = trace_packet->set_smaps_packet();
+    smaps_packet->set_pid(getpid());
+    perfetto::profiling::ParseSmaps(smaps,
+        [&smaps_packet](const perfetto::profiling::SmapsEntry& e) {
+      if (ShouldSampleSmapsEntry(e)) {
+        auto* smaps_entry = smaps_packet->add_entries();
+        smaps_entry->set_path(e.pathname);
+        smaps_entry->set_size_kb(e.size_kb);
+        smaps_entry->set_private_dirty_kb(e.private_dirty_kb);
+        smaps_entry->set_swap_kb(e.swap_kb);
+      }
+    });
+    fclose(smaps);
+  } else {
+    PLOG(ERROR) << "failed to open smaps";
+  }
+}
+
 void DumpPerfetto(art::Thread* self) {
   pid_t parent_pid = getpid();
   LOG(INFO) << "preparing to dump heap for " << parent_pid;
@@ -414,14 +470,19 @@
   JavaHprofDataSource::Trace(
       [parent_pid, timestamp](JavaHprofDataSource::TraceContext ctx)
           NO_THREAD_SAFETY_ANALYSIS {
+            bool dump_smaps;
             {
               auto ds = ctx.GetDataSourceLocked();
               if (!ds || !ds->enabled()) {
                 LOG(INFO) << "skipping irrelevant data source.";
                 return;
               }
+              dump_smaps = ds->dump_smaps();
             }
             LOG(INFO) << "dumping heap for " << parent_pid;
+            if (dump_smaps) {
+              DumpSmaps(&ctx);
+            }
             Writer writer(parent_pid, &ctx, timestamp);
             // Make sure that intern ID 0 (default proto value for a uint64_t) always maps to ""
             // (default proto value for a string).