filesystem: Migrate from probes_producer and add mount points

Bug: 73625480
Change-Id: I83146cfa6589af0bcf3301397e6f79b02f405fdc
diff --git a/Android.bp b/Android.bp
index 6c25b26..64d6a50 100644
--- a/Android.bp
+++ b/Android.bp
@@ -59,6 +59,9 @@
     "src/protozero/message_handle.cc",
     "src/protozero/proto_utils.cc",
     "src/protozero/scattered_stream_writer.cc",
+    "src/traced/probes/filesystem/fs_mount.cc",
+    "src/traced/probes/filesystem/inode_file_data_source.cc",
+    "src/traced/probes/filesystem/lru_inode_cache.cc",
     "src/traced/probes/probes.cc",
     "src/traced/probes/probes_producer.cc",
     "src/traced/probes/process_stats_data_source.cc",
@@ -276,6 +279,9 @@
     "src/protozero/message_handle.cc",
     "src/protozero/proto_utils.cc",
     "src/protozero/scattered_stream_writer.cc",
+    "src/traced/probes/filesystem/fs_mount.cc",
+    "src/traced/probes/filesystem/inode_file_data_source.cc",
+    "src/traced/probes/filesystem/lru_inode_cache.cc",
     "src/traced/probes/probes_producer.cc",
     "src/traced/probes/process_stats_data_source.cc",
     "src/tracing/core/chrome_config.cc",
@@ -3124,6 +3130,7 @@
     "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/filesystem/inode_file_data_source.cc",
     "src/traced/probes/filesystem/lru_inode_cache.cc",
     "src/traced/probes/filesystem/lru_inode_cache_unittest.cc",
     "src/traced/probes/probes_producer.cc",
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 51eb46d..26e1965 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -34,6 +34,7 @@
     "../../base",
     "../../process_stats:process_stats",
     "../../tracing:tracing",
+    "filesystem",
   ]
   sources = [
     "probes_producer.cc",
diff --git a/src/traced/probes/filesystem/BUILD.gn b/src/traced/probes/filesystem/BUILD.gn
index 40aebc7..32a0361 100644
--- a/src/traced/probes/filesystem/BUILD.gn
+++ b/src/traced/probes/filesystem/BUILD.gn
@@ -13,12 +13,19 @@
 # limitations under the License.
 
 source_set("filesystem") {
+  public_deps = [
+    "../../../../protos/perfetto/trace/filesystem:zero",
+  ]
   deps = [
     "../../../../gn:default_deps",
+    "../../../base",
+    "../../../ftrace_reader",
   ]
   sources = [
     "fs_mount.cc",
     "fs_mount.h",
+    "inode_file_data_source.cc",
+    "inode_file_data_source.h",
     "lru_inode_cache.cc",
     "lru_inode_cache.h",
   ]
diff --git a/src/traced/probes/filesystem/inode_file_data_source.cc b/src/traced/probes/filesystem/inode_file_data_source.cc
new file mode 100644
index 0000000..b1a6aaa
--- /dev/null
+++ b/src/traced/probes/filesystem/inode_file_data_source.cc
@@ -0,0 +1,127 @@
+/*
+ * 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 "src/traced/probes/filesystem/inode_file_data_source.h"
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <queue>
+
+#include "include/perfetto/ftrace_reader/ftrace_controller.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/tracing/core/trace_packet.h"
+
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+using BlockDeviceID = decltype(stat::st_dev);
+
+void CreateDeviceToInodeMap(
+    const std::string& root_directory,
+    std::map<BlockDeviceID, std::map<Inode, InodeMapValue>>* block_device_map) {
+  std::queue<std::string> queue;
+  queue.push(root_directory);
+  while (!queue.empty()) {
+    struct dirent* entry;
+    std::string filepath = queue.front();
+    queue.pop();
+    DIR* dir = opendir(filepath.c_str());
+    filepath += "/";
+    if (dir == nullptr)
+      continue;
+    while ((entry = readdir(dir)) != nullptr) {
+      std::string filename = entry->d_name;
+      if (filename == "." || filename == "..")
+        continue;
+      Inode inode_number = entry->d_ino;
+      struct stat buf;
+      if (lstat(filepath.c_str(), &buf) != 0)
+        continue;
+      BlockDeviceID block_device_id = buf.st_dev;
+      std::map<Inode, InodeMapValue>& inode_map =
+          (*block_device_map)[block_device_id];
+      // Default
+      protos::pbzero::InodeFileMap_Entry_Type type =
+          protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
+      // Readdir and stat not guaranteed to have directory info for all systems
+      if (entry->d_type == DT_DIR || S_ISDIR(buf.st_mode)) {
+        // Continue iterating through files if current entry is a directory
+        queue.push(filepath + filename);
+        type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
+      } else if (entry->d_type == DT_REG || S_ISREG(buf.st_mode)) {
+        type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
+      }
+      inode_map[inode_number].SetType(type);
+      inode_map[inode_number].AddPath(filepath + filename);
+    }
+    closedir(dir);
+  }
+}
+
+InodeFileDataSource::InodeFileDataSource(
+    std::map<BlockDeviceID, std::map<Inode, InodeMapValue>>* file_system_inodes,
+    std::unique_ptr<TraceWriter> writer)
+    : file_system_inodes_(file_system_inodes), writer_(std::move(writer)) {}
+
+void InodeFileDataSource::WriteInodes(const FtraceMetadata& metadata) {
+  PERFETTO_DLOG("Write Inodes start");
+
+  if (mount_points_.empty()) {
+    mount_points_ = ParseMounts();
+  }
+  // Group inodes from FtraceMetadata by block device
+  auto inodes = metadata.inodes;
+  std::map<BlockDeviceID, std::set<Inode>> inode_file_maps;
+  for (const auto& inode : inodes) {
+    BlockDeviceID block_device_id = inode.first;
+    Inode inode_number = inode.second;
+    inode_file_maps[block_device_id].emplace(inode_number);
+  }
+  // Write a TracePacket with an InodeFileMap proto for each block device id
+  for (const auto& inode_file_map_data : inode_file_maps) {
+    auto trace_packet = writer_->NewTracePacket();
+    auto inode_file_map = trace_packet->set_inode_file_map();
+    // Add block device id
+    BlockDeviceID block_device_id = inode_file_map_data.first;
+    inode_file_map->set_block_device_id(block_device_id);
+    // Add mount points
+    auto range = mount_points_.equal_range(block_device_id);
+    for (std::multimap<BlockDeviceID, std::string>::iterator it = range.first;
+         it != range.second; ++it) {
+      inode_file_map->add_mount_points(it->second.c_str());
+    }
+    // Add entries for each inode number
+    std::set<Inode> inode_numbers = inode_file_map_data.second;
+    for (const auto& inode_number : inode_numbers) {
+      auto* entry = inode_file_map->add_entries();
+      entry->set_inode_number(inode_number);
+      auto block_device_map = file_system_inodes_->find(block_device_id);
+      if (block_device_map != file_system_inodes_->end()) {
+        auto inode_map = block_device_map->second.find(inode_number);
+        if (inode_map != block_device_map->second.end()) {
+          entry->set_type(inode_map->second.type());
+          for (const auto& path : inode_map->second.paths())
+            entry->add_paths(path.c_str());
+        }
+      }
+    }
+    trace_packet->Finalize();
+  }
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
new file mode 100644
index 0000000..faa02f2
--- /dev/null
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -0,0 +1,74 @@
+/*
+ * 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_FILESYSTEM_INODE_FILE_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_FILESYSTEM_INODE_FILE_DATA_SOURCE_H_
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <map>
+#include <set>
+#include <string>
+
+#include "perfetto/ftrace_reader/ftrace_controller.h"
+#include "perfetto/tracing/core/basic_types.h"
+#include "perfetto/tracing/core/trace_writer.h"
+#include "src/traced/probes/filesystem/fs_mount.h"
+
+#include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
+
+namespace perfetto {
+
+using Inode = uint64_t;
+using InodeFileMap = protos::pbzero::InodeFileMap;
+
+class InodeMapValue {
+ public:
+  protos::pbzero::InodeFileMap_Entry_Type type() const { return entry_type_; }
+  std::set<std::string> paths() const { return paths_; }
+  void SetType(protos::pbzero::InodeFileMap_Entry_Type entry_type) {
+    entry_type_ = entry_type;
+  }
+  void SetPaths(std::set<std::string> paths) { paths_ = paths; }
+  void AddPath(std::string path) { paths_.emplace(path); }
+
+ private:
+  protos::pbzero::InodeFileMap_Entry_Type entry_type_;
+  std::set<std::string> paths_;
+};
+
+void CreateDeviceToInodeMap(
+    const std::string& root_directory,
+    std::map<BlockDeviceID, std::map<Inode, InodeMapValue>>* block_device_map);
+
+class InodeFileDataSource {
+ public:
+  explicit InodeFileDataSource(
+      std::map<BlockDeviceID, std::map<Inode, InodeMapValue>>*
+          file_system_inodes,
+      std::unique_ptr<TraceWriter> writer);
+
+  void WriteInodes(const FtraceMetadata& metadata);
+
+ private:
+  std::map<BlockDeviceID, std::map<Inode, InodeMapValue>>* file_system_inodes_;
+  std::multimap<BlockDeviceID, std::string> mount_points_;
+  std::unique_ptr<TraceWriter> writer_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_FILESYSTEM_INODE_FILE_DATA_SOURCE_H_
diff --git a/src/traced/probes/filesystem/inode_file_data_source_unittest.cc b/src/traced/probes/filesystem/inode_file_data_source_unittest.cc
new file mode 100644
index 0000000..63d9fe8
--- /dev/null
+++ b/src/traced/probes/filesystem/inode_file_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 "src/traced/probes/filesystem/inode_file_data_source.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace {
+
+// TODO(azappone): Add tests
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index 0c1025d..27064fd 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -28,6 +28,7 @@
 #include "perfetto/tracing/core/ftrace_config.h"
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/trace_packet.h"
+#include "src/traced/probes/filesystem/inode_file_data_source.h"
 
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
@@ -93,7 +94,7 @@
   if (source_config.name() == kFtraceSourceName) {
     CreateFtraceDataSourceInstance(id, source_config);
   } else if (source_config.name() == kInodeFileMapSourceName) {
-    CreateInodeFileMapDataSourceInstance(id, source_config);
+    CreateInodeFileDataSourceInstance(id, source_config);
   } else if (source_config.name() == kProcessStatsSourceName) {
     CreateProcessStatsDataSourceInstance(id, source_config);
   } else {
@@ -152,16 +153,17 @@
   AddWatchdogsTimer(id, source_config);
 }
 
-void ProbesProducer::CreateInodeFileMapDataSourceInstance(
+void ProbesProducer::CreateInodeFileDataSourceInstance(
     DataSourceInstanceID id,
     const DataSourceConfig& source_config) {
   PERFETTO_LOG("Inode file map start (id=%" PRIu64 ", target_buf=%" PRIu32 ")",
                id, source_config.target_buffer());
   auto trace_writer = endpoint_->CreateTraceWriter(
       static_cast<BufferID>(source_config.target_buffer()));
-  CreateDeviceToInodeMap("/system/", &system_inodes_);
-  auto file_map_source = std::unique_ptr<InodeFileMapDataSource>(
-      new InodeFileMapDataSource(&system_inodes_, std::move(trace_writer)));
+  if (system_inodes_.empty())
+    CreateDeviceToInodeMap("/system/", &system_inodes_);
+  auto file_map_source = std::unique_ptr<InodeFileDataSource>(
+      new InodeFileDataSource(&system_inodes_, std::move(trace_writer)));
   file_map_sources_.emplace(id, std::move(file_map_source));
   AddWatchdogsTimer(id, source_config);
 }
@@ -179,50 +181,6 @@
   it_and_inserted.first->second->WriteAllProcesses();
 }
 
-// static
-void ProbesProducer::CreateDeviceToInodeMap(
-    const std::string& root_directory,
-    std::map<uint32_t, InodeMap>* block_device_map) {
-  // Return immediately if we've already filled in the system map
-  if (!block_device_map->empty())
-    return;
-  std::queue<std::string> queue;
-  queue.push(root_directory);
-  while (!queue.empty()) {
-    struct dirent* entry;
-    std::string filepath = queue.front();
-    queue.pop();
-    DIR* dir = opendir(filepath.c_str());
-    filepath += "/";
-    if (dir == nullptr)
-      continue;
-    while ((entry = readdir(dir)) != nullptr) {
-      std::string filename = entry->d_name;
-      if (filename == "." || filename == "..")
-        continue;
-      uint64_t inode_number = entry->d_ino;
-      struct stat buf;
-      if (lstat(filepath.c_str(), &buf) != 0)
-        continue;
-      uint32_t block_device_id = buf.st_dev;
-      InodeMap& inode_map = (*block_device_map)[block_device_id];
-      // Default
-      Type type = protos::pbzero::InodeFileMap_Entry_Type_UNKNOWN;
-      // Readdir and stat not guaranteed to have directory info for all systems
-      if (entry->d_type == DT_DIR || S_ISDIR(buf.st_mode)) {
-        // Continue iterating through files if current entry is a directory
-        queue.push(filepath + filename);
-        type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
-      } else if (entry->d_type == DT_REG || S_ISREG(buf.st_mode)) {
-        type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
-      }
-      inode_map[inode_number].first = type;
-      inode_map[inode_number].second.emplace(filepath + filename);
-    }
-    closedir(dir);
-  }
-}
-
 void ProbesProducer::TearDownDataSourceInstance(DataSourceInstanceID id) {
   PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
   // |id| could be the id of any of the datasources we handle:
@@ -292,39 +250,8 @@
 }
 
 void ProbesProducer::SinkDelegate::OnInodes(
-    const std::vector<std::pair<uint64_t, uint32_t>>& inodes) {
-  PERFETTO_DLOG("Saw FtraceBundle with %zu inodes.", inodes.size());
-}
-
-ProbesProducer::InodeFileMapDataSource::InodeFileMapDataSource(
-    std::map<uint32_t, InodeMap>* file_system_inodes,
-    std::unique_ptr<TraceWriter> writer)
-    : file_system_inodes_(file_system_inodes), writer_(std::move(writer)) {}
-
-ProbesProducer::InodeFileMapDataSource::~InodeFileMapDataSource() = default;
-
-void ProbesProducer::InodeFileMapDataSource::WriteInodes(
-    const FtraceMetadata& metadata) {
-  auto trace_packet = writer_->NewTracePacket();
-  auto inode_file_map = trace_packet->set_inode_file_map();
-  // TODO(azappone): Get mount_points & add to the proto
-  auto inodes = metadata.inodes;
-  for (const auto& inode : inodes) {
-    uint32_t block_device_id = inode.first;
-    uint64_t inode_number = inode.second;
-    auto* entry = inode_file_map->add_entries();
-    entry->set_inode_number(inode_number);
-    auto block_device_map = file_system_inodes_->find(block_device_id);
-    if (block_device_map != file_system_inodes_->end()) {
-      auto inode_map = block_device_map->second.find(inode_number);
-      if (inode_map != block_device_map->second.end()) {
-        entry->set_type(inode_map->second.first);
-        for (const auto& path : inode_map->second.second)
-          entry->add_paths(path.c_str());
-      }
-    }
-  }
-  trace_packet->Finalize();
+    const std::vector<std::pair<Inode, uint32_t>>& inodes) {
+  PERFETTO_LOG("Saw FtraceBundle with %zu inodes.", inodes.size());
 }
 
 }  // namespace perfetto
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index e323e9d..5a4d7e3 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -26,6 +26,7 @@
 #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/filesystem/inode_file_data_source.h"
 #include "src/traced/probes/process_stats_data_source.h"
 
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
@@ -52,19 +53,14 @@
   void CreateProcessStatsDataSourceInstance(
       DataSourceInstanceID id,
       const DataSourceConfig& source_config);
-  void CreateInodeFileMapDataSourceInstance(
-      DataSourceInstanceID id,
-      const DataSourceConfig& source_config);
+  void CreateInodeFileDataSourceInstance(DataSourceInstanceID id,
+                                         const DataSourceConfig& source_config);
 
   void OnMetadata(const FtraceMetadata& metadata);
 
  private:
   using FtraceBundleHandle =
       protozero::MessageHandle<protos::pbzero::FtraceEventBundle>;
-  using Type = protos::pbzero::InodeFileMap_Entry_Type;
-  using InodeMap = std::map<uint64_t,
-                            std::pair<protos::pbzero::InodeFileMap_Entry_Type,
-                                      std::set<std::string>>>;
 
   class SinkDelegate : public FtraceSink::Delegate {
    public:
@@ -79,7 +75,7 @@
                           const FtraceMetadata& metadata) override;
 
     void set_sink(std::unique_ptr<FtraceSink> sink) { sink_ = std::move(sink); }
-    void OnInodes(const std::vector<std::pair<uint64_t, uint32_t>>& inodes);
+    void OnInodes(const std::vector<std::pair<Inode, uint32_t>>& inodes);
 
    private:
     base::TaskRunner* task_runner_;
@@ -93,20 +89,6 @@
     base::WeakPtrFactory<SinkDelegate> weak_factory_;
   };
 
-  class InodeFileMapDataSource {
-   public:
-    explicit InodeFileMapDataSource(
-        std::map<uint32_t, InodeMap>* file_system_inodes,
-        std::unique_ptr<TraceWriter> writer);
-    ~InodeFileMapDataSource();
-
-    void WriteInodes(const FtraceMetadata& metadata);
-
-   private:
-    std::map<uint32_t, InodeMap>* file_system_inodes_;
-    std::unique_ptr<TraceWriter> writer_;
-  };
-
   enum State {
     kNotStarted = 0,
     kNotConnected,
@@ -122,9 +104,6 @@
   void IncreaseConnectionBackoff();
   void AddWatchdogsTimer(DataSourceInstanceID id,
                          const DataSourceConfig& source_config);
-  static void CreateDeviceToInodeMap(
-      const std::string& root_directory,
-      std::map<uint32_t, InodeMap>* block_device_map);
 
   State state_ = kNotStarted;
   base::TaskRunner* task_runner_;
@@ -137,9 +116,9 @@
       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>>
+  std::map<DataSourceInstanceID, std::unique_ptr<InodeFileDataSource>>
       file_map_sources_;
-  std::map<uint32_t, InodeMap> system_inodes_;
+  std::map<BlockDeviceID, std::map<Inode, InodeMapValue>> system_inodes_;
 };
 
 }  // namespace perfetto