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).