diff --git a/system/autoware_component_monitor/CMakeLists.txt b/system/autoware_component_monitor/CMakeLists.txt
new file mode 100644
index 0000000000000..674b079a90563
--- /dev/null
+++ b/system/autoware_component_monitor/CMakeLists.txt
@@ -0,0 +1,31 @@
+cmake_minimum_required(VERSION 3.8)
+project(autoware_component_monitor)
+
+find_package(autoware_cmake REQUIRED)
+autoware_package()
+
+find_package(Boost REQUIRED COMPONENTS
+ filesystem
+)
+
+ament_auto_add_library(${PROJECT_NAME} SHARED
+ src/component_monitor_node.cpp
+)
+target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
+
+rclcpp_components_register_node(${PROJECT_NAME}
+ PLUGIN "autoware::component_monitor::ComponentMonitor"
+ EXECUTABLE ${PROJECT_NAME}_node
+)
+
+if(BUILD_TESTING)
+ ament_add_ros_isolated_gtest(test_unit_conversions test/test_unit_conversions.cpp)
+ target_link_libraries(test_unit_conversions ${PROJECT_NAME})
+ target_include_directories(test_unit_conversions PRIVATE src)
+endif()
+
+ament_auto_package(
+ INSTALL_TO_SHARE
+ config
+ launch
+)
diff --git a/system/autoware_component_monitor/README.md b/system/autoware_component_monitor/README.md
new file mode 100644
index 0000000000000..c255c420c048e
--- /dev/null
+++ b/system/autoware_component_monitor/README.md
@@ -0,0 +1,84 @@
+# autoware_component_monitor
+
+The `autoware_component_monitor` package allows monitoring system usage of component containers.
+The composable node inside the package is attached to a component container, and it publishes CPU and memory usage of
+the container.
+
+## Inputs / Outputs
+
+### Input
+
+None.
+
+### Output
+
+| Name | Type | Description |
+| -------------------------- | -------------------------------------------------- | ---------------------- |
+| `~/component_system_usage` | `autoware_internal_msgs::msg::ResourceUsageReport` | CPU, Memory usage etc. |
+
+## Parameters
+
+### Core Parameters
+
+{{ json_to_markdown("system/autoware_component_monitor/schema/component_monitor.schema.json") }}
+
+## How to use
+
+Add it as a composable node in your launch file:
+
+```xml
+
+
+
+
+ ...
+
+
+
+
+
+
+
+ ...
+
+
+```
+
+### Quick testing
+
+You can test the package by running the following command:
+
+```bash
+ros2 component load autoware_component_monitor autoware::component_monitor::ComponentMonitor -p publish_rate:=10.0 --node-namespace
+
+# Example usage
+ros2 component load /pointcloud_container autoware_component_monitor autoware::component_monitor::ComponentMonitor -p publish_rate:=10.0 --node-namespace /pointcloud_container
+```
+
+## How it works
+
+The package uses the `top` command under the hood.
+`top -b -n 1 -E k -p PID` command is run at 10 Hz to get the system usage of the process.
+
+- `-b` activates the batch mode. By default, `top` doesn't exit and prints to stdout periodically. Batch mode allows
+ exiting the program.
+- `-n` number of times should `top` prints the system usage in batch mode.
+- `-p` specifies the PID of the process to monitor.
+- `-E k` changes the memory unit in the summary section to KiB.
+
+Here is a sample output:
+
+```text
+top - 13:57:26 up 3:14, 1 user, load average: 1,09, 1,10, 1,04
+Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
+%Cpu(s): 0,0 us, 0,8 sy, 0,0 ni, 99,2 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
+KiB Mem : 65532208 total, 35117428 free, 17669824 used, 12744956 buff/cache
+KiB Swap: 39062524 total, 39062524 free, 0 used. 45520816 avail Mem
+
+ PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
+ 3352 meb 20 0 2905940 1,2g 39292 S 0,0 2,0 23:24.01 awesome
+```
+
+We get 5th, 8th fields from the last line, which are RES, %CPU respectively.
diff --git a/system/autoware_component_monitor/config/component_monitor.param.yaml b/system/autoware_component_monitor/config/component_monitor.param.yaml
new file mode 100644
index 0000000000000..62cf278921460
--- /dev/null
+++ b/system/autoware_component_monitor/config/component_monitor.param.yaml
@@ -0,0 +1,3 @@
+/**:
+ ros__parameters:
+ publish_rate: 5.0 # Hz
diff --git a/system/autoware_component_monitor/launch/component_monitor.launch.xml b/system/autoware_component_monitor/launch/component_monitor.launch.xml
new file mode 100644
index 0000000000000..1b2a77eddab93
--- /dev/null
+++ b/system/autoware_component_monitor/launch/component_monitor.launch.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/system/autoware_component_monitor/package.xml b/system/autoware_component_monitor/package.xml
new file mode 100644
index 0000000000000..640e1f2dc2517
--- /dev/null
+++ b/system/autoware_component_monitor/package.xml
@@ -0,0 +1,25 @@
+
+
+
+ autoware_component_monitor
+ 0.0.0
+ A ROS 2 package to monitor system usage of component containers.
+ Mehmet Emin Başoğlu
+ Apache-2.0
+
+ ament_cmake_auto
+ autoware_cmake
+
+ autoware_internal_msgs
+ libboost-filesystem-dev
+ rclcpp
+ rclcpp_components
+
+ ament_cmake_ros
+ ament_lint_auto
+ autoware_lint_common
+
+
+ ament_cmake
+
+
diff --git a/system/autoware_component_monitor/schema/component_monitor.schema.json b/system/autoware_component_monitor/schema/component_monitor.schema.json
new file mode 100644
index 0000000000000..f0edf5add718e
--- /dev/null
+++ b/system/autoware_component_monitor/schema/component_monitor.schema.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Parameters for the Component Monitor node",
+ "type": "object",
+ "definitions": {
+ "component_monitor": {
+ "type": "object",
+ "properties": {
+ "publish_rate": {
+ "type": "number",
+ "default": "5.0",
+ "description": "Publish rate in Hz"
+ }
+ },
+ "required": ["publish_rate"]
+ }
+ },
+ "properties": {
+ "/**": {
+ "type": "object",
+ "properties": {
+ "ros__parameters": {
+ "$ref": "#/definitions/component_monitor"
+ }
+ },
+ "required": ["ros__parameters"]
+ }
+ },
+ "required": ["/**"]
+}
diff --git a/system/autoware_component_monitor/src/component_monitor_node.cpp b/system/autoware_component_monitor/src/component_monitor_node.cpp
new file mode 100644
index 0000000000000..3c5d6b6667725
--- /dev/null
+++ b/system/autoware_component_monitor/src/component_monitor_node.cpp
@@ -0,0 +1,177 @@
+// Copyright 2024 The Autoware Foundation
+//
+// 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 "component_monitor_node.hpp"
+
+#include "unit_conversions.hpp"
+
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace autoware::component_monitor
+{
+ComponentMonitor::ComponentMonitor(const rclcpp::NodeOptions & node_options)
+: Node("component_monitor", node_options), publish_rate_(declare_parameter("publish_rate"))
+{
+ usage_pub_ =
+ create_publisher("~/component_system_usage", rclcpp::SensorDataQoS());
+
+ // Make sure top ins installed and is in path
+ const auto path_top = boost::process::search_path("top");
+ if (path_top.empty()) {
+ RCLCPP_ERROR_STREAM(get_logger(), "Couldn't find 'top' in path.");
+ rclcpp::shutdown();
+ }
+
+ // Get the PID of the current process
+ int pid = getpid();
+
+ environment_ = boost::this_process::environment();
+ environment_["LC_NUMERIC"] = "en_US.UTF-8";
+
+ on_timer_tick_wrapped_ = std::bind(&ComponentMonitor::on_timer_tick, this, pid);
+
+ timer_ = rclcpp::create_timer(
+ this, get_clock(), rclcpp::Rate(publish_rate_).period(), on_timer_tick_wrapped_);
+}
+
+void ComponentMonitor::on_timer_tick(const int pid) const
+{
+ if (usage_pub_->get_subscription_count() == 0) return;
+
+ try {
+ auto usage_msg = pid_to_report(pid);
+ usage_msg.header.stamp = this->now();
+ usage_msg.pid = pid;
+ usage_pub_->publish(usage_msg);
+ } catch (std::exception & e) {
+ RCLCPP_ERROR(get_logger(), "%s", e.what());
+ } catch (...) {
+ RCLCPP_ERROR(get_logger(), "An unknown error occurred.");
+ }
+}
+
+ComponentMonitor::ResourceUsageReport ComponentMonitor::pid_to_report(const pid_t & pid) const
+{
+ const auto std_out = run_system_command("top -b -n 1 -E k -p " + std::to_string(pid));
+
+ const auto fields = parse_lines_into_words(std_out);
+
+ ResourceUsageReport report;
+ report.cpu_cores_utilized = std::stof(fields.back().at(8)) / 100.0f;
+ report.total_memory_bytes = unit_conversions::kib_to_bytes(std::stoul(fields.at(3).at(3)));
+ report.free_memory_bytes = unit_conversions::kib_to_bytes(std::stoul(fields.at(3).at(5)));
+ report.process_memory_bytes = parse_memory_res(fields.back().at(5));
+
+ return report;
+}
+
+std::stringstream ComponentMonitor::run_system_command(const std::string & cmd) const
+{
+ int out_fd[2];
+ if (pipe2(out_fd, O_CLOEXEC) != 0) {
+ RCLCPP_ERROR_STREAM(get_logger(), "Error setting flags on out_fd");
+ }
+ boost::process::pipe out_pipe{out_fd[0], out_fd[1]};
+ boost::process::ipstream is_out{std::move(out_pipe)};
+
+ int err_fd[2];
+ if (pipe2(err_fd, O_CLOEXEC) != 0) {
+ RCLCPP_ERROR_STREAM(get_logger(), "Error setting flags on err_fd");
+ }
+ boost::process::pipe err_pipe{err_fd[0], err_fd[1]};
+ boost::process::ipstream is_err{std::move(err_pipe)};
+
+ boost::process::child c(
+ cmd, environment_, boost::process::std_out > is_out, boost::process::std_err > is_err);
+ c.wait();
+
+ if (c.exit_code() != 0) {
+ std::ostringstream os;
+ is_err >> os.rdbuf();
+ RCLCPP_ERROR_STREAM(get_logger(), "Error running command: " << os.str());
+ }
+
+ std::stringstream sstream;
+ sstream << is_out.rdbuf();
+ return sstream;
+}
+
+ComponentMonitor::VecVecStr ComponentMonitor::parse_lines_into_words(
+ const std::stringstream & std_out)
+{
+ VecVecStr fields;
+ std::string line;
+ std::istringstream input{std_out.str()};
+
+ while (std::getline(input, line)) {
+ std::istringstream iss_line{line};
+ std::string word;
+ std::vector words;
+
+ while (iss_line >> word) {
+ words.push_back(word);
+ }
+
+ fields.push_back(words);
+ }
+
+ return fields;
+}
+
+std::uint64_t ComponentMonitor::parse_memory_res(const std::string & mem_res)
+{
+ // example 1: 12.3g
+ // example 2: 123 (without suffix, just bytes)
+ static const std::unordered_map> unit_map{
+ {'k', unit_conversions::kib_to_bytes}, {'m', unit_conversions::mib_to_bytes},
+ {'g', unit_conversions::gib_to_bytes}, {'t', unit_conversions::tib_to_bytes},
+ {'p', unit_conversions::pib_to_bytes}, {'e', unit_conversions::eib_to_bytes}};
+
+ if (std::isdigit(mem_res.back())) {
+ return std::stoull(mem_res); // Handle plain bytes without any suffix
+ }
+
+ // Extract the numeric part and the unit suffix
+ double value = std::stod(mem_res.substr(0, mem_res.size() - 1));
+ char suffix = mem_res.back();
+
+ // Find the appropriate function from the map
+ auto it = unit_map.find(suffix);
+ if (it != unit_map.end()) {
+ const auto & conversion_function = it->second;
+ return conversion_function(value);
+ }
+
+ // Throw an exception or handle the error as needed if the suffix is not recognized
+ throw std::runtime_error("Unsupported unit suffix: " + std::string(1, suffix));
+}
+
+} // namespace autoware::component_monitor
+
+#include
+RCLCPP_COMPONENTS_REGISTER_NODE(autoware::component_monitor::ComponentMonitor)
diff --git a/system/autoware_component_monitor/src/component_monitor_node.hpp b/system/autoware_component_monitor/src/component_monitor_node.hpp
new file mode 100644
index 0000000000000..70a486eb8209b
--- /dev/null
+++ b/system/autoware_component_monitor/src/component_monitor_node.hpp
@@ -0,0 +1,102 @@
+// Copyright 2024 The Autoware Foundation
+//
+// 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 COMPONENT_MONITOR_NODE_HPP_
+#define COMPONENT_MONITOR_NODE_HPP_
+
+#include
+
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+namespace autoware::component_monitor
+{
+class ComponentMonitor : public rclcpp::Node
+{
+public:
+ explicit ComponentMonitor(const rclcpp::NodeOptions & node_options);
+
+private:
+ using ResourceUsageReport = autoware_internal_msgs::msg::ResourceUsageReport;
+ using VecVecStr = std::vector>;
+
+ const double publish_rate_;
+
+ std::function on_timer_tick_wrapped_;
+
+ rclcpp::Publisher::SharedPtr usage_pub_;
+ rclcpp::TimerBase::SharedPtr timer_;
+
+ boost::process::native_environment environment_;
+
+ void on_timer_tick(int pid) const;
+
+ /**
+ * @brief Get system usage of the component.
+ *
+ * @details The output of top -b -n 1 -E k -p PID` is like below:
+ *
+ * top - 13:57:26 up 3:14, 1 user, load average: 1,09, 1,10, 1,04
+ * Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
+ * %Cpu(s): 0,0 us, 0,8 sy, 0,0 ni, 99,2 id, 0,0 wa, 0,0 hi, 0,0 si, 0,0 st
+ * KiB Mem : 65532208 total, 35117428 free, 17669824 used, 12744956 buff/cache
+ * KiB Swap: 39062524 total, 39062524 free, 0 used. 45520816 avail Mem
+ *
+ * PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
+ * 3352 meb 20 0 2905940 1,2g 39292 S 0,0 2,0 23:24.01 awesome
+ *
+ * We get 5th and 8th fields, which are RES, %CPU, respectively.
+ */
+ ResourceUsageReport pid_to_report(const pid_t & pid) const;
+
+ /**
+ * @brief Run a terminal command and return the standard output.
+ *
+ * @param cmd The terminal command to run
+ * @return The standard output of the command
+ */
+ std::stringstream run_system_command(const std::string & cmd) const;
+
+ /**
+ * @brief Parses text from a stringstream into separated words by line.
+ *
+ * @param std_out Reference to the input stringstream.
+ * @return Nested vector with each inner vector containing words from one line.
+ */
+ static VecVecStr parse_lines_into_words(const std::stringstream & std_out);
+
+ /**
+ * @brief Parses a memory resource string and converts it to bytes.
+ *
+ * This function handles memory size strings with suffixes to denote
+ * the units (e.g., "k" for kibibytes, "m" for mebibytes, etc.).
+ * If the string has no suffix, it is interpreted as plain bytes.
+ *
+ * @param mem_res A string representing the memory resource with a unit suffix or just bytes.
+ * @return uint64_t The memory size in bytes.
+ *
+ * @exception std::runtime_error Thrown if the suffix is not recognized.
+ */
+ static std::uint64_t parse_memory_res(const std::string & mem_res);
+};
+
+} // namespace autoware::component_monitor
+
+#endif // COMPONENT_MONITOR_NODE_HPP_
diff --git a/system/autoware_component_monitor/src/unit_conversions.hpp b/system/autoware_component_monitor/src/unit_conversions.hpp
new file mode 100644
index 0000000000000..c8f3fa02da519
--- /dev/null
+++ b/system/autoware_component_monitor/src/unit_conversions.hpp
@@ -0,0 +1,68 @@
+// Copyright 2024 The Autoware Foundation
+//
+// 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 UNIT_CONVERSIONS_HPP_
+#define UNIT_CONVERSIONS_HPP_
+
+#include
+#include
+
+namespace autoware::component_monitor::unit_conversions
+{
+template
+std::uint64_t kib_to_bytes(T kibibytes)
+{
+ static_assert(std::is_arithmetic::value, "Template parameter must be a numeric type");
+ return static_cast(kibibytes * 1024);
+}
+
+template
+std::uint64_t mib_to_bytes(T mebibytes)
+{
+ static_assert(std::is_arithmetic::value, "Template parameter must be a numeric type");
+ return static_cast(mebibytes * 1024 * 1024);
+}
+
+template
+std::uint64_t gib_to_bytes(T gibibytes)
+{
+ static_assert(std::is_arithmetic::value, "Template parameter must be a numeric type");
+ return static_cast(gibibytes * 1024ULL * 1024ULL * 1024ULL);
+}
+
+template
+std::uint64_t tib_to_bytes(T tebibytes)
+{
+ static_assert(std::is_arithmetic::value, "Template parameter must be a numeric type");
+ return static_cast(tebibytes * 1024ULL * 1024ULL * 1024ULL * 1024ULL);
+}
+
+template
+std::uint64_t pib_to_bytes(T pebibytes)
+{
+ static_assert(std::is_arithmetic::value, "Template parameter must be a numeric type");
+ return static_cast(pebibytes * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL);
+}
+
+template
+std::uint64_t eib_to_bytes(T exbibytes)
+{
+ static_assert(std::is_arithmetic::value, "Template parameter must be a numeric type");
+ return static_cast(
+ exbibytes * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL * 1024ULL);
+}
+
+} // namespace autoware::component_monitor::unit_conversions
+
+#endif // UNIT_CONVERSIONS_HPP_
diff --git a/system/autoware_component_monitor/test/test_unit_conversions.cpp b/system/autoware_component_monitor/test/test_unit_conversions.cpp
new file mode 100644
index 0000000000000..e8104ff31b80e
--- /dev/null
+++ b/system/autoware_component_monitor/test/test_unit_conversions.cpp
@@ -0,0 +1,57 @@
+// Copyright 2024 The Autoware Foundation
+//
+// 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 "unit_conversions.hpp"
+
+#include
+
+namespace autoware::component_monitor::unit_conversions
+{
+TEST(UnitConversions, kib_to_bytes)
+{
+ EXPECT_EQ(kib_to_bytes(1), 1024U);
+ EXPECT_EQ(kib_to_bytes(0), 0U);
+ EXPECT_EQ(kib_to_bytes(10), 10240U);
+}
+TEST(UnitConversions, mib_to_bytes)
+{
+ EXPECT_EQ(mib_to_bytes(1), 1048576U);
+ EXPECT_EQ(mib_to_bytes(0), 0U);
+ EXPECT_EQ(mib_to_bytes(10), 10485760U);
+}
+TEST(UnitConversions, gib_to_bytes)
+{
+ EXPECT_EQ(gib_to_bytes(1), 1073741824U);
+ EXPECT_EQ(gib_to_bytes(0), 0U);
+ EXPECT_EQ(gib_to_bytes(10), 10737418240U);
+}
+TEST(UnitConversions, tib_to_bytes)
+{
+ EXPECT_EQ(tib_to_bytes(1), 1099511627776U);
+ EXPECT_EQ(tib_to_bytes(0), 0U);
+ EXPECT_EQ(tib_to_bytes(10), 10995116277760U);
+}
+TEST(UnitConversions, pib_to_bytes)
+{
+ EXPECT_EQ(pib_to_bytes(1), 1125899906842624U);
+ EXPECT_EQ(pib_to_bytes(0), 0U);
+ EXPECT_EQ(pib_to_bytes(10), 11258999068426240U);
+}
+TEST(UnitConversions, eib_to_bytes)
+{
+ EXPECT_EQ(eib_to_bytes(1), 1152921504606846976U);
+ EXPECT_EQ(eib_to_bytes(0), 0U);
+ EXPECT_EQ(eib_to_bytes(10), 11529215046068469760U);
+}
+} // namespace autoware::component_monitor::unit_conversions