Add application to monitor filesystem utilization.
Change-Id: I2305ac657c7b2c528d31e27f44b1a7d5015c2492
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/aos/util/BUILD b/aos/util/BUILD
index 7bb4061..599d477 100644
--- a/aos/util/BUILD
+++ b/aos/util/BUILD
@@ -578,3 +578,25 @@
"//aos/testing:path",
],
)
+
+static_flatbuffer(
+ name = "filesystem_fbs",
+ srcs = ["filesystem.fbs"],
+)
+
+cc_static_flatbuffer(
+ name = "filesystem_schema",
+ function = "aos::util::FilesystemStatusSchema",
+ target = ":filesystem_fbs_reflection_out",
+)
+
+cc_binary(
+ name = "filesystem_monitor",
+ srcs = ["filesystem_monitor.cc"],
+ deps = [
+ ":filesystem_fbs",
+ "//aos:init",
+ "//aos/events:shm_event_loop",
+ "@com_google_absl//absl/strings",
+ ],
+)
diff --git a/aos/util/filesystem.fbs b/aos/util/filesystem.fbs
new file mode 100644
index 0000000..0571d15
--- /dev/null
+++ b/aos/util/filesystem.fbs
@@ -0,0 +1,23 @@
+namespace aos.util;
+
+table Filesystem {
+ // Mountpoint of the filesystem in question.
+ path: string (id: 0);
+ // Type (e.g., "ext4") of the filesystem.
+ type: string (id: 1);
+ // Total size of the filesystem, in bytes.
+ overall_space: uint64 (id: 2);
+ // Total free space on the filesystem, in bytes.
+ free_space: uint64 (id: 3);
+ // Total number of inodes on this filesystem.
+ overall_inodes: uint64 (id: 4);
+ // Total free inodes on this filesystem.
+ free_inodes: uint64 (id: 5);
+}
+
+// Table to track the current state of a compute platform's filesystem.
+table FilesystemStatus {
+ filesystems: [Filesystem] (id: 0);
+}
+
+root_type FilesystemStatus;
diff --git a/aos/util/filesystem_monitor.cc b/aos/util/filesystem_monitor.cc
new file mode 100644
index 0000000..4efb141
--- /dev/null
+++ b/aos/util/filesystem_monitor.cc
@@ -0,0 +1,140 @@
+#include <sys/statvfs.h>
+
+#include "absl/strings/str_split.h"
+#include "gflags/gflags.h"
+
+#include "aos/events/shm_event_loop.h"
+#include "aos/init.h"
+#include "aos/util/filesystem_generated.h"
+
+DEFINE_string(config, "aos_config.json", "File path of aos configuration");
+
+namespace aos::util {
+namespace {
+std::optional<std::string> ReadShortFile(std::string_view file_name) {
+ // Open as input and seek to end immediately.
+ std::ifstream file(std::string(file_name), std::ios_base::in);
+ if (!file.good()) {
+ VLOG(1) << "Can't read " << file_name;
+ return std::nullopt;
+ }
+ const size_t kMaxLineLength = 4096;
+ char buffer[kMaxLineLength];
+ file.read(buffer, kMaxLineLength);
+ if (!file.eof()) {
+ return std::nullopt;
+ }
+ return std::string(buffer, file.gcount());
+}
+} // namespace
+
+// Periodically sends out the Filesystems message with filesystem utilization
+// info.
+class FilesystemMonitor {
+ public:
+ FilesystemMonitor(aos::EventLoop *event_loop)
+ : event_loop_(event_loop),
+ sender_(event_loop_->MakeSender<FilesystemStatus>("/aos")) {
+ periodic_timer_ =
+ event_loop_->AddTimer([this]() { PublishFilesystemStatus(); });
+ event_loop_->OnRun([this]() {
+ periodic_timer_->Schedule(event_loop_->monotonic_now(),
+ std::chrono::seconds(5));
+ });
+ }
+
+ private:
+ void PublishFilesystemStatus() {
+ aos::Sender<FilesystemStatus>::Builder builder = sender_.MakeBuilder();
+
+ std::optional<std::string> contents = ReadShortFile("/proc/self/mountinfo");
+
+ CHECK(contents.has_value());
+
+ std::vector<flatbuffers::Offset<Filesystem>> filesystems;
+
+ // Iterate through /proc/self/mounts to find all the filesystems.
+ for (std::string_view line :
+ absl::StrSplit(std::string_view(contents->c_str(), contents->size()),
+ '\n', absl::SkipWhitespace())) {
+ // See https://www.kernel.org/doc/Documentation/filesystems/proc.txt for
+ // the format.
+ std::vector<std::string_view> elements =
+ absl::StrSplit(line, ' ', absl::SkipWhitespace());
+
+ // First thing after - is the filesystem type.
+ size_t i = 6;
+ while (elements[i] != "-") {
+ ++i;
+ CHECK_LT(i + 1, elements.size());
+ }
+
+ // Mount point is the 4th element.
+ std::string mount_point(elements[4]);
+ std::string_view type = elements[i + 1];
+
+ // Ignore filesystems without reasonable types.
+ if (type != "ext2" && type != "xfs" && type != "vfat" && type != "ext3" &&
+ type != "ext4" && type != "tmpfs" && type != "devtmpfs") {
+ continue;
+ }
+ VLOG(1) << mount_point << ", type " << type;
+
+ struct statvfs info;
+
+ PCHECK(statvfs(mount_point.c_str(), &info) == 0);
+
+ VLOG(1) << "overall size: " << info.f_frsize * info.f_blocks << ", free "
+ << info.f_bfree * info.f_bsize << ", inodes " << info.f_files
+ << ", free " << info.f_ffree;
+
+ flatbuffers::Offset<flatbuffers::String> path_offset =
+ builder.fbb()->CreateString(mount_point);
+ flatbuffers::Offset<flatbuffers::String> type_offset =
+ builder.fbb()->CreateString(type);
+ Filesystem::Builder filesystem_builder =
+ builder.MakeBuilder<Filesystem>();
+ filesystem_builder.add_path(path_offset);
+ filesystem_builder.add_type(type_offset);
+ filesystem_builder.add_overall_space(info.f_frsize * info.f_blocks);
+ filesystem_builder.add_free_space(info.f_bfree * info.f_bsize);
+ filesystem_builder.add_overall_inodes(info.f_files);
+ filesystem_builder.add_free_inodes(info.f_ffree);
+
+ filesystems.emplace_back(filesystem_builder.Finish());
+ }
+
+ flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<Filesystem>>>
+ filesystems_offset = builder.fbb()->CreateVector(filesystems);
+
+ FilesystemStatus::Builder filesystem_status_builder =
+ builder.MakeBuilder<FilesystemStatus>();
+
+ filesystem_status_builder.add_filesystems(filesystems_offset);
+
+ (void)builder.Send(filesystem_status_builder.Finish());
+ }
+
+ aos::EventLoop *event_loop_;
+
+ aos::Sender<FilesystemStatus> sender_;
+
+ aos::TimerHandler *periodic_timer_;
+};
+
+} // namespace aos::util
+
+int main(int argc, char **argv) {
+ aos::InitGoogle(&argc, &argv);
+
+ aos::FlatbufferDetachedBuffer<aos::Configuration> config =
+ aos::configuration::ReadConfig(FLAGS_config);
+
+ aos::ShmEventLoop shm_event_loop(&config.message());
+
+ aos::util::FilesystemMonitor filesystem_monitor(&shm_event_loop);
+
+ shm_event_loop.Run();
+
+ return 0;
+}
diff --git a/y2024/BUILD b/y2024/BUILD
index 212f70e..e3fbb72 100644
--- a/y2024/BUILD
+++ b/y2024/BUILD
@@ -124,6 +124,7 @@
"//y2024/localizer:status_fbs",
"//y2024/localizer:visualization_fbs",
"//aos/network:timestamp_fbs",
+ "//aos/util:filesystem_fbs",
"//aos/network:remote_message_fbs",
"//frc971/vision:calibration_fbs",
"//frc971/vision:target_map_fbs",
@@ -183,6 +184,7 @@
"//y2024/localizer:visualization_fbs",
"//frc971/vision:target_map_fbs",
"//frc971/vision:vision_fbs",
+ "//aos/util:filesystem_fbs",
"@com_github_foxglove_schemas//:schemas",
],
target_compatible_with = ["@platforms//os:linux"],
diff --git a/y2024/y2024_imu.json b/y2024/y2024_imu.json
index 3da3e11..5990242 100644
--- a/y2024/y2024_imu.json
+++ b/y2024/y2024_imu.json
@@ -2,6 +2,12 @@
"channels": [
{
"name": "/imu/aos",
+ "type": "aos.util.FilesystemStatus",
+ "source_node": "imu",
+ "frequency": 2
+ },
+ {
+ "name": "/imu/aos",
"type": "aos.JoystickState",
"source_node": "imu",
"frequency": 100,
diff --git a/y2024/y2024_orin1.json b/y2024/y2024_orin1.json
index 099a06a..01dae9e 100644
--- a/y2024/y2024_orin1.json
+++ b/y2024/y2024_orin1.json
@@ -2,6 +2,12 @@
"channels": [
{
"name": "/orin1/aos",
+ "type": "aos.util.FilesystemStatus",
+ "source_node": "orin1",
+ "frequency": 2
+ },
+ {
+ "name": "/orin1/aos",
"type": "aos.timing.Report",
"source_node": "orin1",
"frequency": 50,