Add stateful filesystem scanner.
This will be used to interrupt long scans.
Bug: 74584014
Change-Id: I80206a3dd6cce265fc957532c855f07f08120a7d
diff --git a/Android.bp b/Android.bp
index b9c1996..953276c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -63,6 +63,7 @@
"src/protozero/proto_utils.cc",
"src/protozero/scattered_stream_null_delegate.cc",
"src/protozero/scattered_stream_writer.cc",
+ "src/traced/probes/filesystem/file_scanner.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",
@@ -301,6 +302,7 @@
"src/protozero/proto_utils.cc",
"src/protozero/scattered_stream_null_delegate.cc",
"src/protozero/scattered_stream_writer.cc",
+ "src/traced/probes/filesystem/file_scanner.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",
@@ -3340,6 +3342,7 @@
"src/protozero/scattered_stream_writer_unittest.cc",
"src/protozero/test/fake_scattered_buffer.cc",
"src/protozero/test/protozero_conformance_unittest.cc",
+ "src/traced/probes/filesystem/file_scanner.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",
diff --git a/src/traced/probes/filesystem/BUILD.gn b/src/traced/probes/filesystem/BUILD.gn
index ce481d6..6beb973 100644
--- a/src/traced/probes/filesystem/BUILD.gn
+++ b/src/traced/probes/filesystem/BUILD.gn
@@ -22,6 +22,8 @@
"../../../base",
]
sources = [
+ "file_scanner.cc",
+ "file_scanner.h",
"fs_mount.cc",
"fs_mount.h",
"inode_file_data_source.cc",
diff --git a/src/traced/probes/filesystem/file_scanner.cc b/src/traced/probes/filesystem/file_scanner.cc
new file mode 100644
index 0000000..3fb9cfe
--- /dev/null
+++ b/src/traced/probes/filesystem/file_scanner.cc
@@ -0,0 +1,141 @@
+/*
+ * 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/file_scanner.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "src/traced/probes/filesystem/inode_file_data_source.h"
+
+namespace perfetto {
+namespace {
+
+std::string JoinPaths(const std::string& one, const std::string& other) {
+ std::string result;
+ result.reserve(one.size() + other.size() + 1);
+ result += one;
+ if (!result.empty() && result.back() != '/')
+ result += '/';
+ result += other;
+ return result;
+}
+
+} // namespace
+
+FileScanner::FileScanner(std::vector<std::string> root_directories,
+ Delegate* delegate,
+ uint64_t scan_interval_ms,
+ uint64_t scan_steps)
+ : delegate_(delegate),
+ scan_interval_ms_(scan_interval_ms),
+ scan_steps_(scan_steps),
+ queue_(std::move(root_directories)),
+ weak_factory_(this) {}
+
+void FileScanner::Scan(base::TaskRunner* task_runner) {
+ Steps(scan_steps_);
+ if (Done())
+ return delegate_->OnInodeScanDone();
+ auto weak_this = weak_factory_.GetWeakPtr();
+ task_runner->PostDelayedTask(
+ [weak_this, task_runner] {
+ if (!weak_this)
+ return;
+ weak_this->Scan(task_runner);
+ },
+ scan_interval_ms_);
+}
+
+void FileScanner::NextDirectory() {
+ std::string directory = std::move(queue_.back());
+ queue_.pop_back();
+ current_dir_handle_.reset(opendir(directory.c_str()));
+ if (!current_dir_handle_) {
+ PERFETTO_DPLOG("opendir %s", directory.c_str());
+ current_directory_.clear();
+ return;
+ }
+ current_directory_ = std::move(directory);
+
+ struct stat buf;
+ if (fstat(dirfd(current_dir_handle_.get()), &buf) != 0) {
+ PERFETTO_DPLOG("fstat %s", current_directory_.c_str());
+ current_dir_handle_.reset();
+ current_directory_.clear();
+ return;
+ }
+
+ if (S_ISLNK(buf.st_mode)) {
+ current_dir_handle_.reset();
+ current_directory_.clear();
+ return;
+ }
+ current_block_device_id_ = buf.st_dev;
+}
+
+void FileScanner::Step() {
+ if (!current_dir_handle_) {
+ if (queue_.empty())
+ return;
+ NextDirectory();
+ }
+
+ if (!current_dir_handle_)
+ return;
+
+ struct dirent* entry = readdir(current_dir_handle_.get());
+ if (entry == nullptr) {
+ current_dir_handle_.reset();
+ return;
+ }
+
+ std::string filename = entry->d_name;
+ if (filename == "." || filename == "..")
+ return;
+
+ std::string filepath = JoinPaths(current_directory_, filename);
+
+ 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) {
+ // Continue iterating through files if current entry is a directory
+ queue_.emplace_back(filepath);
+ type = protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY;
+ } else if (entry->d_type == DT_REG) {
+ type = protos::pbzero::InodeFileMap_Entry_Type_FILE;
+ }
+
+ if (!delegate_->OnInodeFound(current_block_device_id_, entry->d_ino, filepath,
+ type)) {
+ queue_.clear();
+ current_dir_handle_.reset();
+ }
+}
+
+void FileScanner::Steps(uint64_t n) {
+ for (uint64_t i = 0; i < n && !Done(); ++i)
+ Step();
+}
+
+bool FileScanner::Done() {
+ return !current_dir_handle_ && queue_.empty();
+}
+
+} // namespace perfetto
diff --git a/src/traced/probes/filesystem/file_scanner.h b/src/traced/probes/filesystem/file_scanner.h
new file mode 100644
index 0000000..24475a4
--- /dev/null
+++ b/src/traced/probes/filesystem/file_scanner.h
@@ -0,0 +1,71 @@
+/*
+ * 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_FILE_SCANNER_H_
+#define SRC_TRACED_PROBES_FILESYSTEM_FILE_SCANNER_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/weak_ptr.h"
+#include "perfetto/traced/data_source_types.h"
+
+namespace perfetto {
+
+class FileScanner {
+ public:
+ class Delegate {
+ public:
+ virtual bool OnInodeFound(BlockDeviceID,
+ Inode,
+ const std::string&,
+ protos::pbzero::InodeFileMap_Entry_Type) = 0;
+ virtual void OnInodeScanDone() = 0;
+ virtual ~Delegate() {}
+ };
+
+ FileScanner(std::vector<std::string> root_directories,
+ Delegate* delegate,
+ uint64_t scan_interval_ms,
+ uint64_t scan_steps);
+
+ FileScanner(const FileScanner&) = delete;
+ FileScanner& operator=(const FileScanner&) = delete;
+
+ void Scan(base::TaskRunner* task_runner);
+
+ private:
+ void NextDirectory();
+ void Step();
+ void Steps(uint64_t n);
+ bool Done();
+
+ Delegate* delegate_;
+ const uint64_t scan_interval_ms_;
+ const uint64_t scan_steps_;
+
+ std::vector<std::string> queue_;
+ base::ScopedDir current_dir_handle_;
+ std::string current_directory_;
+ BlockDeviceID current_block_device_id_;
+ base::WeakPtrFactory<FileScanner> weak_factory_; // Keep last.
+};
+
+} // namespace perfetto
+
+#endif // SRC_TRACED_PROBES_FILESYSTEM_FILE_SCANNER_H_
diff --git a/src/traced/probes/filesystem/inode_file_data_source.cc b/src/traced/probes/filesystem/inode_file_data_source.cc
index 0a75dc0..b0086cf 100644
--- a/src/traced/probes/filesystem/inode_file_data_source.cc
+++ b/src/traced/probes/filesystem/inode_file_data_source.cc
@@ -29,9 +29,9 @@
#include "perfetto/tracing/core/trace_writer.h"
#include "perfetto/trace/trace_packet.pbzero.h"
+#include "src/traced/probes/filesystem/file_scanner.h"
namespace perfetto {
-
namespace {
const int kScanIntervalMs = 10000; // 10s
}