Merge "filesystem: Rate limit rate of scans."
diff --git a/src/traced/probes/filesystem/inode_file_data_source.cc b/src/traced/probes/filesystem/inode_file_data_source.cc
index fd308ac..1ecb400 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.cc
+++ b/src/traced/probes/filesystem/inode_file_data_source.cc
@@ -32,6 +32,10 @@
 
 namespace perfetto {
 
+namespace {
+uint64_t kScanIntervalMs = 10000;  // 10s
+}
+
 void ScanFilesDFS(
     const std::string& root_directory,
     const std::function<bool(BlockDeviceID block_device_id,
@@ -113,12 +117,14 @@
 }
 
 InodeFileDataSource::InodeFileDataSource(
+    base::TaskRunner* task_runner,
     TracingSessionID id,
     std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
         static_file_map,
     LRUInodeCache* cache,
     std::unique_ptr<TraceWriter> writer)
-    : session_id_(id),
+    : task_runner_(task_runner),
+      session_id_(id),
       static_file_map_(static_file_map),
       cache_(cache),
       writer_(std::move(writer)),
@@ -227,9 +233,9 @@
     PERFETTO_DLOG("Saw %zu block devices.", inode_file_maps.size());
 
   // Write a TracePacket with an InodeFileMap proto for each block device id
-  for (const auto& inode_file_map_data : inode_file_maps) {
+  for (auto& inode_file_map_data : inode_file_maps) {
     BlockDeviceID block_device_id = inode_file_map_data.first;
-    std::set<Inode> inode_numbers = inode_file_map_data.second;
+    std::set<Inode>& inode_numbers = inode_file_map_data.second;
     PERFETTO_DLOG("Saw %zu unique inode numbers.", inode_numbers.size());
 
     // New TracePacket for each InodeFileMap
@@ -250,13 +256,51 @@
     AddInodesFromStaticMap(block_device_id, &inode_numbers, inode_file_map);
     AddInodesFromLRUCache(block_device_id, &inode_numbers, inode_file_map);
     // TODO(azappone): Make root directory a mount point
-    std::string root_directory = "/data";
-    AddInodesFromFilesystemScan(root_directory, block_device_id, &inode_numbers,
-                                cache_, inode_file_map);
-    trace_packet->Finalize();
+    if (!inode_numbers.empty()) {
+      bool first_scan = missing_inodes_.empty();
+      missing_inodes_[block_device_id].insert(inode_numbers.cbegin(),
+                                              inode_numbers.cend());
+      if (first_scan) {
+        PERFETTO_DLOG("Posting to scan filesystem in %lu ms", kScanIntervalMs);
+        auto weak_this = GetWeakPtr();
+        task_runner_->PostDelayedTask(
+            [weak_this] {
+              if (!weak_this) {
+                PERFETTO_DLOG("Giving up filesystem scan.");
+                return;
+              }
+              weak_this.get()->FindMissingInodes();
+            },
+            kScanIntervalMs);
+      }
+    }
   }
 }
 
+void InodeFileDataSource::FindMissingInodes() {
+  for (auto& p : missing_inodes_) {
+    BlockDeviceID block_device_id = p.first;
+    std::set<Inode>& missing = p.second;
+
+    PERFETTO_DLOG("Scanning filesystem");
+    auto it = mount_points_.find(block_device_id);
+    if (it == mount_points_.end())
+      continue;
+
+    std::string root_directory = it->second;
+    // New TracePacket for each InodeFileMap
+    auto trace_packet = writer_->NewTracePacket();
+    auto inode_file_map = trace_packet->set_inode_file_map();
+    // Add block device id to InodeFileMap
+    inode_file_map->set_block_device_id(block_device_id);
+
+    AddInodesFromFilesystemScan(root_directory, block_device_id, &missing,
+                                cache_, inode_file_map);
+    PERFETTO_DLOG("Giving up on finding %lu inodes", missing.size());
+  }
+  missing_inodes_.clear();
+}
+
 base::WeakPtr<InodeFileDataSource> InodeFileDataSource::GetWeakPtr() const {
   return weak_factory_.GetWeakPtr();
 }
diff --git a/src/traced/probes/filesystem/inode_file_data_source.h b/src/traced/probes/filesystem/inode_file_data_source.h
index af881d4..3c5bff4 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.h
+++ b/src/traced/probes/filesystem/inode_file_data_source.h
@@ -25,6 +25,7 @@
 #include <string>
 #include <unordered_map>
 
+#include "perfetto/base/task_runner.h"
 #include "perfetto/base/weak_ptr.h"
 #include "perfetto/traced/data_source_types.h"
 #include "perfetto/tracing/core/basic_types.h"
@@ -59,6 +60,7 @@
 class InodeFileDataSource {
  public:
   InodeFileDataSource(
+      base::TaskRunner*,
       TracingSessionID,
       std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
           static_file_map,
@@ -91,12 +93,16 @@
                              InodeFileMap* destination);
 
  private:
+  void FindMissingInodes();
+
+  base::TaskRunner* task_runner_;
   const TracingSessionID session_id_;
   std::map<BlockDeviceID, std::unordered_map<Inode, InodeMapValue>>*
       static_file_map_;
   LRUInodeCache* cache_;
   std::multimap<BlockDeviceID, std::string> mount_points_;
   std::unique_ptr<TraceWriter> writer_;
+  std::map<BlockDeviceID, std::set<Inode>> missing_inodes_;
   base::WeakPtrFactory<InodeFileDataSource> weak_factory_;  // Keep last.
 };
 
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index b460971..42ae465 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -189,9 +189,9 @@
       static_cast<BufferID>(source_config.target_buffer()));
   if (system_inodes_.empty())
     CreateStaticDeviceToInodeMap("/system", &system_inodes_);
-  auto file_map_source =
-      std::unique_ptr<InodeFileDataSource>(new InodeFileDataSource(
-          session_id, &system_inodes_, &cache_, std::move(trace_writer)));
+  auto file_map_source = std::unique_ptr<InodeFileDataSource>(
+      new InodeFileDataSource(task_runner_, session_id, &system_inodes_,
+                              &cache_, std::move(trace_writer)));
   file_map_sources_.emplace(id, std::move(file_map_source));
   AddWatchdogsTimer(id, source_config);
 }