traced_probes: Add ProcessStatsDataSource

ProcessStatsDataSource should be able to write metadata for processes
which are created while a trace is running. To do this it needs to live
as long as the trace does so this change creates a
ProcessStatsDataSource to mirror the Sink and InodeFileMapDataSource
that exist for the ftrace and inode datasources.

This also pulls ProcessStatsDataSource and associated logic into its
own class: ProbesProducer is getting too big. Finally it adds the
boilerplate for testing ProcessStatsDataSource - but no actual tests.
There is some additional work required to add tests (e.g. creating a
fake TraceWriter impl) and that shouldn't block progress on
ProcessStatsDataSource.

Bug: 73058765
Change-Id: I2bf68166711289df081edd4ee3375b6fd77f4bb0
diff --git a/Android.bp b/Android.bp
index 15f4e94..aad645f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,6 +61,7 @@
     "src/protozero/scattered_stream_writer.cc",
     "src/traced/probes/probes.cc",
     "src/traced/probes/probes_producer.cc",
+    "src/traced/probes/process_stats_data_source.cc",
     "src/traced/service/service.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
@@ -276,6 +277,7 @@
     "src/protozero/proto_utils.cc",
     "src/protozero/scattered_stream_writer.cc",
     "src/traced/probes/probes_producer.cc",
+    "src/traced/probes/process_stats_data_source.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
     "src/tracing/core/data_source_config.cc",
@@ -3108,6 +3110,8 @@
     "src/ipc/test/ipc_integrationtest.cc",
     "src/ipc/unix_socket.cc",
     "src/ipc/unix_socket_unittest.cc",
+    "src/process_stats/file_utils.cc",
+    "src/process_stats/procfs_utils.cc",
     "src/protozero/message.cc",
     "src/protozero/message_handle.cc",
     "src/protozero/message_handle_unittest.cc",
@@ -3120,6 +3124,9 @@
     "src/protozero/test/protozero_conformance_unittest.cc",
     "src/traced/probes/filesystem/fs_mount.cc",
     "src/traced/probes/filesystem/fs_mount_unittest.cc",
+    "src/traced/probes/probes_producer.cc",
+    "src/traced/probes/process_stats_data_source.cc",
+    "src/traced/probes/process_stats_data_source_unittest.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
     "src/tracing/core/data_source_config.cc",
diff --git a/BUILD.gn b/BUILD.gn
index 73b166b..92cb14f 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -66,6 +66,7 @@
   if (is_linux || is_android) {
     deps += [
       "src/ftrace_reader:ftrace_reader_unittests",
+      "src/traced/probes:probes_unittests",
       "tools/ftrace_proto_gen:ftrace_proto_gen_unittests",
     ]
   }
diff --git a/include/perfetto/tracing/core/trace_writer.h b/include/perfetto/tracing/core/trace_writer.h
index d29930d..85140ee 100644
--- a/include/perfetto/tracing/core/trace_writer.h
+++ b/include/perfetto/tracing/core/trace_writer.h
@@ -18,6 +18,7 @@
 #define INCLUDE_PERFETTO_TRACING_CORE_TRACE_WRITER_H_
 
 #include "perfetto/protozero/message_handle.h"
+#include "perfetto/tracing/core/basic_types.h"
 
 namespace perfetto {
 
diff --git a/src/ftrace_reader/ftrace_config_muxer_unittest.cc b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
index 44c63a0..9a88bae 100644
--- a/src/ftrace_reader/ftrace_config_muxer_unittest.cc
+++ b/src/ftrace_reader/ftrace_config_muxer_unittest.cc
@@ -110,7 +110,7 @@
       new ProtoTranslationTable(events, std::move(common_fields)));
 }
 
-TEST(FtraceConfigMuxer, ComputeCpuBufferSizeInPages) {
+TEST(FtraceConfigMuxerTest, ComputeCpuBufferSizeInPages) {
   // No buffer size given: good default (128 pages = 512kb).
   EXPECT_EQ(ComputeCpuBufferSizeInPages(0), 128u);
   // Buffer size given way too big: good default.
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 2e4ea03..51eb46d 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -38,5 +38,19 @@
   sources = [
     "probes_producer.cc",
     "probes_producer.h",
+    "process_stats_data_source.cc",
+    "process_stats_data_source.h",
+  ]
+}
+
+source_set("probes_unittests") {
+  testonly = true
+  deps = [
+    ":probes_src",
+    "../../../gn:default_deps",
+    "../../../gn:gtest_deps",
+  ]
+  sources = [
+    "process_stats_data_source_unittest.cc",
   ]
 }
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 7074f56..0c1025d 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -29,12 +29,8 @@
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/trace_packet.h"
 
-#include "src/process_stats/file_utils.h"
-#include "src/process_stats/procfs_utils.h"
-
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
-#include "perfetto/trace/ps/process_tree.pbzero.h"
 #include "perfetto/trace/trace_packet.pbzero.h"
 
 namespace perfetto {
@@ -174,34 +170,13 @@
     DataSourceInstanceID id,
     const DataSourceConfig& source_config) {
   PERFETTO_DCHECK(process_stats_sources_.count(id) == 0);
-  process_stats_sources_.insert(id);
   auto trace_writer = endpoint_->CreateTraceWriter(
       static_cast<BufferID>(source_config.target_buffer()));
-  procfs_utils::ProcessMap processes;
-  auto trace_packet = trace_writer->NewTracePacket();
-  protos::pbzero::ProcessTree* process_tree = trace_packet->set_process_tree();
-
-  file_utils::ForEachPidInProcPath(
-      "/proc", [&processes, &process_tree](int pid) {
-        if (!processes.count(pid)) {
-          if (procfs_utils::ReadTgid(pid) != pid)
-            return;
-          processes[pid] = procfs_utils::ReadProcessInfo(pid);
-        }
-        ProcessInfo* process = processes[pid].get();
-        procfs_utils::ReadProcessThreads(process);
-        auto* process_writer = process_tree->add_processes();
-        process_writer->set_pid(process->pid);
-        process_writer->set_ppid(process->ppid);
-        for (const auto& field : process->cmdline)
-          process_writer->add_cmdline(field.c_str());
-        for (auto& thread : process->threads) {
-          auto* thread_writer = process_writer->add_threads();
-          thread_writer->set_tid(thread.second.tid);
-          thread_writer->set_name(thread.second.name);
-        }
-      });
-  trace_packet->Finalize();
+  auto source = std::unique_ptr<ProcessStatsDataSource>(
+      new ProcessStatsDataSource(std::move(trace_writer)));
+  auto it_and_inserted = process_stats_sources_.emplace(id, std::move(source));
+  PERFETTO_DCHECK(it_and_inserted.second);
+  it_and_inserted.first->second->WriteAllProcesses();
 }
 
 // static
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index 1c7f73a..e323e9d 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#ifndef SRC_TRACED_PROBES_PROBES_PRODUCER_H_
+#define SRC_TRACED_PROBES_PROBES_PRODUCER_H_
+
 #include <map>
 #include <memory>
 #include <utility>
@@ -23,12 +26,10 @@
 #include "perfetto/tracing/core/producer.h"
 #include "perfetto/tracing/core/trace_writer.h"
 #include "perfetto/tracing/ipc/producer_ipc_client.h"
+#include "src/traced/probes/process_stats_data_source.h"
 
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
 
-#ifndef SRC_TRACED_PROBES_PROBES_PRODUCER_H_
-#define SRC_TRACED_PROBES_PROBES_PRODUCER_H_
-
 namespace perfetto {
 
 class ProbesProducer : public Producer {
@@ -113,6 +114,9 @@
     kConnected,
   };
 
+  ProbesProducer(const ProbesProducer&) = delete;
+  ProbesProducer& operator=(const ProbesProducer&) = delete;
+
   void Connect();
   void ResetConnectionBackoff();
   void IncreaseConnectionBackoff();
@@ -129,13 +133,15 @@
   bool ftrace_creation_failed_ = false;
   uint64_t connection_backoff_ms_ = 0;
   const char* socket_name_ = nullptr;
-  std::set<DataSourceInstanceID> process_stats_sources_;
+  std::map<DataSourceInstanceID, std::unique_ptr<ProcessStatsDataSource>>
+      process_stats_sources_;
   std::map<DataSourceInstanceID, std::unique_ptr<SinkDelegate>> delegates_;
   std::map<DataSourceInstanceID, base::Watchdog::Timer> watchdogs_;
   std::map<DataSourceInstanceID, std::unique_ptr<InodeFileMapDataSource>>
       file_map_sources_;
   std::map<uint32_t, InodeMap> system_inodes_;
 };
+
 }  // namespace perfetto
 
 #endif  // SRC_TRACED_PROBES_PROBES_PRODUCER_H_
diff --git a/src/traced/probes/process_stats_data_source.cc b/src/traced/probes/process_stats_data_source.cc
new file mode 100644
index 0000000..49803fc
--- /dev/null
+++ b/src/traced/probes/process_stats_data_source.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 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 "process_stats_data_source.h"
+
+#include <utility>
+
+#include "perfetto/trace/ps/process_tree.pbzero.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+#include "perfetto/tracing/core/trace_packet.h"
+#include "src/process_stats/file_utils.h"
+#include "src/process_stats/procfs_utils.h"
+
+namespace perfetto {
+
+ProcessStatsDataSource::ProcessStatsDataSource(
+    std::unique_ptr<TraceWriter> writer)
+    : writer_(std::move(writer)) {}
+
+ProcessStatsDataSource::~ProcessStatsDataSource() = default;
+
+void ProcessStatsDataSource::WriteAllProcesses() {
+  procfs_utils::ProcessMap processes;
+  auto trace_packet = writer_->NewTracePacket();
+  protos::pbzero::ProcessTree* process_tree = trace_packet->set_process_tree();
+
+  file_utils::ForEachPidInProcPath(
+      "/proc", [&processes, process_tree](int pid) {
+        // ForEachPid will list all processes and threads. Here we want to
+        // iterate first only by processes (for which pid == thread group id)
+        if (!processes.count(pid)) {
+          if (procfs_utils::ReadTgid(pid) != pid)
+            return;
+          processes[pid] = procfs_utils::ReadProcessInfo(pid);
+        }
+        ProcessInfo* process = processes[pid].get();
+        procfs_utils::ReadProcessThreads(process);
+        auto* process_writer = process_tree->add_processes();
+        process_writer->set_pid(process->pid);
+        process_writer->set_ppid(process->ppid);
+        for (const auto& field : process->cmdline)
+          process_writer->add_cmdline(field.c_str());
+        for (auto& thread : process->threads) {
+          auto* thread_writer = process_writer->add_threads();
+          thread_writer->set_tid(thread.second.tid);
+          thread_writer->set_name(thread.second.name);
+        }
+      });
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/process_stats_data_source.h b/src/traced/probes/process_stats_data_source.h
new file mode 100644
index 0000000..b7327a2
--- /dev/null
+++ b/src/traced/probes/process_stats_data_source.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2018 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_PROCESS_STATS_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_PROCESS_STATS_DATA_SOURCE_H_
+
+#include <memory>
+
+#include "perfetto/tracing/core/trace_writer.h"
+
+namespace perfetto {
+
+class ProcessStatsDataSource {
+ public:
+  explicit ProcessStatsDataSource(std::unique_ptr<TraceWriter> writer);
+  ~ProcessStatsDataSource();
+
+  void WriteAllProcesses();
+
+ private:
+  ProcessStatsDataSource(const ProcessStatsDataSource&) = delete;
+  ProcessStatsDataSource& operator=(const ProcessStatsDataSource&) = delete;
+
+  std::unique_ptr<TraceWriter> writer_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_PROCESS_STATS_DATA_SOURCE_H_
diff --git a/src/traced/probes/process_stats_data_source_unittest.cc b/src/traced/probes/process_stats_data_source_unittest.cc
new file mode 100644
index 0000000..dd7296d
--- /dev/null
+++ b/src/traced/probes/process_stats_data_source_unittest.cc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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 "process_stats_data_source.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace {
+
+// TODO(hjd): Add tests.
+
+}  // namespace
+}  // namespace perfetto