traced_probes: Add kmem_activity trigger

kmem_activity gets triggered when there is detected kernel memory
activity -- kswapd, direct reclaim, compaction. This is used a hueristic
for memory pressure, which allows for signaling perfetto to capture
system memory state during periods of memory pressure.

Bug: 155928119
Test: adb root
      adb push test/configs/mm_events.cfg /data/local/tmp/
      adb shell 'cat /data/local/tmp/mm_events.cfg | perfetto --txt -c - -o /data/misc/perfetto-traces/trace'
      [In another shell] Synthesize memory pressure (e.g adb shell /data/local/tmp/lmkd_unit_test)
      adb pull /data/misc/perfetto-traces/trace
Change-Id: Id0a5dad2f6e557606801bad0a58ea180123f54fd
diff --git a/Android.bp b/Android.bp
index 1567a65..42cd999 100644
--- a/Android.bp
+++ b/Android.bp
@@ -8153,6 +8153,7 @@
 filegroup {
   name: "perfetto_src_traced_probes_probes_src",
   srcs: [
+    "src/traced/probes/kmem_activity_trigger.cc",
     "src/traced/probes/probes_producer.cc",
   ],
 }
diff --git a/BUILD b/BUILD
index 7695658..34851ea 100644
--- a/BUILD
+++ b/BUILD
@@ -1465,6 +1465,8 @@
 filegroup(
     name = "src_traced_probes_probes_src",
     srcs = [
+        "src/traced/probes/kmem_activity_trigger.cc",
+        "src/traced/probes/kmem_activity_trigger.h",
         "src/traced/probes/probes_producer.cc",
         "src/traced/probes/probes_producer.h",
     ],
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 9e09790..7fcdf42 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -67,6 +67,8 @@
   sources = [
     "probes_producer.cc",
     "probes_producer.h",
+    "kmem_activity_trigger.cc",
+    "kmem_activity_trigger.h",
   ]
 }
 
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index 5628cb7..7e10bc4 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -157,11 +157,15 @@
   // on Android. The permissions to these files are configured in
   // platform/framework/native/cmds/atrace/atrace.rc.
   for (size_t cpu = 0; cpu < NumberOfCpus(); cpu++) {
-    if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace"))
-      PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu);
+    ClearPerCpuTrace(cpu);
   }
 }
 
+void FtraceProcfs::ClearPerCpuTrace(size_t cpu) {
+  if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace"))
+    PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu);
+}
+
 bool FtraceProcfs::WriteTraceMarker(const std::string& str) {
   std::string path = root_ + "trace_marker";
   return WriteToFile(path, str);
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index f2ea712..e8b292f 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -68,6 +68,9 @@
   // Clears the trace buffers for all CPUs. Blocks until this is done.
   void ClearTrace();
 
+  // Clears the trace buffer for cpu. Blocks until this is done.
+  void ClearPerCpuTrace(size_t cpu);
+
   // Writes the string |str| as an event into the trace buffer.
   bool WriteTraceMarker(const std::string& str);
 
diff --git a/src/traced/probes/kmem_activity_trigger.cc b/src/traced/probes/kmem_activity_trigger.cc
new file mode 100644
index 0000000..7b55313
--- /dev/null
+++ b/src/traced/probes/kmem_activity_trigger.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/traced/probes/kmem_activity_trigger.h"
+
+#include <unistd.h>
+
+#include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "src/traced/probes/probes_producer.h"
+
+namespace perfetto {
+
+namespace {
+constexpr base::TimeSeconds kTriggerInterval{60};  // 1 min
+constexpr size_t kPerCpuTraceBufferSizeInPages = 1;
+constexpr char kTriggerName[] = "kmem_activity";
+}  // namespace
+
+void KmemActivityTriggerThread::InitializeOnThread() {
+  // Create kmem activity FtraceProcfs
+  size_t index = 0;
+  while (!ftrace_procfs_ && FtraceController::kTracingPaths[index]) {
+    std::string root = FtraceController::kTracingPaths[index++] +
+                       std::string("instances/mm_events/");
+    ftrace_procfs_ = FtraceProcfs::Create(root);
+  }
+  if (!ftrace_procfs_) {
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+    PERFETTO_LOG(
+        "mm_events ftrace instance not found. Triggering of traces on memory "
+        "pressure will not be available on this device.");
+#endif
+    return;
+  }
+
+  ftrace_procfs_->SetCpuBufferSizeInPages(kPerCpuTraceBufferSizeInPages);
+
+  // Enable mm trace events
+  ftrace_procfs_->EnableEvent("vmscan", "mm_vmscan_kswapd_wake");
+  ftrace_procfs_->EnableEvent("vmscan", "mm_vmscan_direct_reclaim_begin");
+  ftrace_procfs_->EnableEvent("compaction", "mm_compaction_begin");
+
+  ftrace_procfs_->EnableTracing();
+
+  size_t num_cpus = ftrace_procfs_->NumberOfCpus();
+  for (size_t cpu = 0; cpu < num_cpus; cpu++) {
+    trace_pipe_fds_.emplace_back(ftrace_procfs_->OpenPipeForCpu(cpu));
+
+    if (!trace_pipe_fds_.back()) {
+      PERFETTO_PLOG("Failed to open trace_pipe_raw for cpu %zu", cpu);
+      trace_pipe_fds_.pop_back();
+      continue;
+    }
+
+    int watch_fd = trace_pipe_fds_.back().get();
+    thread_.AddFileDescriptorWatch(watch_fd, [this, cpu]() {
+      base::TimeSeconds now = base::GetBootTimeS();
+      base::TimeSeconds elapsed = std::chrono::duration_cast<base::TimeSeconds>(
+          now - last_trigger_time_);
+      ProbesProducer* probes_producer = ProbesProducer::GetInstance();
+      if (probes_producer &&
+          (elapsed > kTriggerInterval || last_trigger_time_.count() == 0)) {
+        probes_producer->ActivateTrigger(kTriggerName);
+        last_trigger_time_ = now;
+      }
+      ftrace_procfs_->ClearPerCpuTrace(cpu);
+    });
+  }
+}
+
+KmemActivityTriggerThread::KmemActivityTriggerThread()
+    : thread_(base::ThreadTaskRunner::CreateAndStart()) {
+  thread_.PostTask([this]() { InitializeOnThread(); });
+}
+
+KmemActivityTriggerThread::~KmemActivityTriggerThread() {
+  thread_.PostTask([this]() {
+    ftrace_procfs_->DisableTracing();
+    ftrace_procfs_->ClearTrace();
+
+    for (const base::ScopedFile& fd : trace_pipe_fds_) {
+      thread_.RemoveFileDescriptorWatch(fd.get());
+    }
+
+    trace_pipe_fds_.clear();
+
+    thread_.get()->Quit();
+  });
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/kmem_activity_trigger.h b/src/traced/probes/kmem_activity_trigger.h
new file mode 100644
index 0000000..b15d347
--- /dev/null
+++ b/src/traced/probes/kmem_activity_trigger.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACED_PROBES_KMEM_ACTIVITY_TRIGGER_H_
+#define SRC_TRACED_PROBES_KMEM_ACTIVITY_TRIGGER_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/base/time.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/thread_task_runner.h"
+
+namespace perfetto {
+
+class FtraceProcfs;
+
+class KmemActivityTriggerThread {
+ public:
+  KmemActivityTriggerThread();
+  ~KmemActivityTriggerThread();
+
+ private:
+  void InitializeOnThread();
+
+  base::ThreadTaskRunner thread_;
+  std::unique_ptr<FtraceProcfs> ftrace_procfs_;
+  std::vector<base::ScopedFile> trace_pipe_fds_;
+  base::TimeSeconds last_trigger_time_{0};
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_KMEM_ACTIVITY_TRIGGER_H_
diff --git a/src/traced/probes/probes.cc b/src/traced/probes/probes.cc
index 9d83f45..39f6be8 100644
--- a/src/traced/probes/probes.cc
+++ b/src/traced/probes/probes.cc
@@ -27,6 +27,7 @@
 #include "perfetto/ext/tracing/ipc/default_socket.h"
 
 #include "src/traced/probes/ftrace/ftrace_procfs.h"
+#include "src/traced/probes/kmem_activity_trigger.h"
 #include "src/traced/probes/probes_producer.h"
 
 namespace perfetto {
@@ -85,6 +86,10 @@
   base::UnixTaskRunner task_runner;
   ProbesProducer producer;
   producer.ConnectWithRetries(GetProducerSocket(), &task_runner);
+
+  // Start the thread that polls mm_event instance and triggers
+  KmemActivityTriggerThread kmem_activity_trigger;
+
   task_runner.Run();
   return 0;
 }
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 58389b8..cfa893d 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -86,8 +86,19 @@
 //                    +--------------+
 //
 
-ProbesProducer::ProbesProducer() : weak_factory_(this) {}
+ProbesProducer* ProbesProducer::instance_ = nullptr;
+
+ProbesProducer* ProbesProducer::GetInstance() {
+  return instance_;
+}
+
+ProbesProducer::ProbesProducer() : weak_factory_(this) {
+  PERFETTO_CHECK(instance_ == nullptr);
+  instance_ = this;
+}
+
 ProbesProducer::~ProbesProducer() {
+  instance_ = nullptr;
   // The ftrace data sources must be deleted before the ftrace controller.
   data_sources_.clear();
   ftrace_.reset();
@@ -493,7 +504,7 @@
       if (ps->on_demand_dumps_enabled())
         ps_data_source = ps;
     }
-  }    // for (session_data_sources_)
+  }  // for (session_data_sources_)
 }
 
 void ProbesProducer::ConnectWithRetries(const char* socket_name,
@@ -526,4 +537,11 @@
   connection_backoff_ms_ = kInitialConnectionBackoffMs;
 }
 
+void ProbesProducer::ActivateTrigger(std::string trigger) {
+  task_runner_->PostTask([this, trigger]() {
+    if (endpoint_)
+      endpoint_->ActivateTriggers({trigger});
+  });
+}
+
 }  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index 39d6425..b879a01 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -44,6 +44,8 @@
   ProbesProducer();
   ~ProbesProducer() override;
 
+  static ProbesProducer* GetInstance();
+
   // Producer Impl:
   void OnConnect() override;
   void OnDisconnect() override;
@@ -93,8 +95,11 @@
   std::unique_ptr<ProbesDataSource> CreateInitialDisplayStateDataSource(
       TracingSessionID session_id,
       const DataSourceConfig& config);
+  void ActivateTrigger(std::string trigger);
 
  private:
+  static ProbesProducer* instance_;
+
   enum State {
     kNotStarted = 0,
     kNotConnected,