Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add cgroup setter node #6977

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
21 changes: 21 additions & 0 deletions system/cgroup_setter/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.14)
project(cgroup_setter)

find_package(autoware_cmake REQUIRED)
find_package(yaml-cpp REQUIRED)
find_package(Boost REQUIRED COMPONENTS filesystem system)
include_directories(${Boost_INCLUDE_DIRS})

autoware_package()

ament_auto_add_executable(${PROJECT_NAME}
src/cgroup_setter.cpp
)

target_link_libraries(cgroup_setter yaml-cpp)
target_link_libraries(cgroup_setter ${Boost_LIBRARIES})

ament_auto_package(INSTALL_TO_SHARE
launch
config
)
95 changes: 95 additions & 0 deletions system/cgroup_setter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# cgroup_setter

## Purpose

This package set a PID to a custom cgroup.
The PID is found by `pgrep -f`.

## Inputs / Outputs

### Outputs

| Name | Type | Description |
| -------------- | ---------------------------------------- | ------------------- |
| `/diagnostics` | `diagnostic_msgs::msgs::DiagnosticArray` | Diagnostics outputs |

## Parameters

### Node Parameters

| Name | Type | Default Value | Explanation | Reconfigurable |
| ---------------------------- | ------ | ---------------------------------------------------- | -------------- | -------------- |
| `cgroup_setting_config_path` | string | `$(find-pkg-share cgroup_setter)/config/cgroup.yaml` | yaml file path | |

### YAML format for cgroup_setter

format

```yaml
base_path: /sys/fs/cgroup
settings:
- directory: xxx/xxx
search_word:
- xxxxx
- xxxxx
- directory: xxx/xxx
search_word:
- xxxxx
```

The following is an example of joining the PID from running `pgrep -f`
with the keyword `__node:=system_monitor_container` to a cgroup named `/sys/fs/cgroup/autoware/system_monitor`.

example

```yaml
base_path: /sys/fs/cgroup
settings:
- directory: autoware/system_monitor
search_word:
- __node:=system_monitor_container
```

#### Rules

- The value of settings must be a sequence.

example

```yaml
# NG
base_path: /sys/fs/cgroup
settings:
directory: autoware/system_monitor # - directory
search_word:
- __node:=system_monitor_container
```

- The value of search_word must be a sequence.

example

```yaml
# NG
base_path: /sys/fs/cgroup
settings:
- directory: autoware/system_monitor
search_word: __node:=system_monitor_container # ["__node:=system_monitor_container"] or - "__node:=system_monitor_container"
```

#### Notes

- Write permission is required for a custom cgroup, and /sys/fs/cgroup/cgroup.procs to attach a PID to the cgroup.
- A PID cannot be attached to a cgroup that has a small group.(only leaf cgroup)

## Assumptions / Known limits

TBD.

## Usage

### launch

```sh
ros2 launch cgroup_setter cgroup_setter.launch.xml
```
5 changes: 5 additions & 0 deletions system/cgroup_setter/config/cgroup.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
base_path: /sys/fs/cgroup
settings:
- directory: autoware/system_monitor
search_word:
- __node:=system_monitor_container
6 changes: 6 additions & 0 deletions system/cgroup_setter/launch/cgroup_setter.launch.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<launch>
<arg name="cgroup_setting_config_path" default="$(find-pkg-share cgroup_setter)/config/cgroup.yaml"/>
<node pkg="cgroup_setter" exec="cgroup_setter" name="cgroup_setter">
<param name="cgroup_setting_config_path" value="$(var cgroup_setting_config_path)"/>
</node>
</launch>
26 changes: 26 additions & 0 deletions system/cgroup_setter/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>cgroup_setter</name>
<version>0.1.0</version>
<description>set pid to a cgroup</description>
<maintainer email="[email protected]">TetsuKawa</maintainer>
<license>Apache License 2.0</license>

<buildtool_depend>ament_cmake_auto</buildtool_depend>
<buildtool_depend>autoware_cmake</buildtool_depend>

<depend>diagnostic_msgs</depend>
<depend>diagnostic_updater</depend>
<depend>libboost-filesystem-dev</depend>
<depend>rclcpp</depend>
<depend>rclcpp_components</depend>
<depend>yaml-cpp</depend>

<test_depend>ament_lint_auto</test_depend>
<test_depend>autoware_lint_common</test_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
224 changes: 224 additions & 0 deletions system/cgroup_setter/src/cgroup_setter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
// Copyright 2024 Autoware Foundation

Check warning on line 1 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Overall Code Complexity

This module has a mean cyclomatic complexity of 4.86 across 7 functions. The mean complexity threshold is 4. This file has many conditional statements (e.g. if, for, while) across its implementation, leading to lower code health. Avoid adding more conditionals.
//
// 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.

/**
* @file cgroup_setter.cpp
* @brief Cgroup setter class
*/

#include "cgroup_setter.hpp"

#include <boost/process.hpp>

#include <unistd.h>
#include <yaml-cpp/yaml.h>

#include <cstring>
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <vector>

namespace bp = boost::process;

CgroupSetter::CgroupSetter(const rclcpp::NodeOptions & options)
: Node("cgroup_setter_node", options), updater_(this)

Check warning on line 37 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L36-L37

Added lines #L36 - L37 were not covered by tests
{
try {
std::string yaml_path = this->declare_parameter<std::string>("cgroup_setting_config_path");
YAML::Node config = YAML::LoadFile(yaml_path);

Check warning on line 41 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L40-L41

Added lines #L40 - L41 were not covered by tests
if (config["base_path"]) {
base_path_ = config["base_path"].as<std::string>();

Check warning on line 43 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L43

Added line #L43 was not covered by tests
} else {
RCLCPP_ERROR(this->get_logger(), "base_path is not set in the config file.");
return;

Check warning on line 46 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L45-L46

Added lines #L45 - L46 were not covered by tests
}

if (!config["settings"]) {
RCLCPP_ERROR(this->get_logger(), "settings is not set in the config file.");
return;

Check warning on line 51 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L50-L51

Added lines #L50 - L51 were not covered by tests
}

for (auto setting : config["settings"]) {
if (!setting["directory"] || !setting["search_word"]) {
RCLCPP_ERROR(this->get_logger(), "directory or search_word is not set in the config file.");

Check warning on line 56 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L56

Added line #L56 was not covered by tests
return;
}

for (auto word : setting["search_word"]) {
std::pair<std::string, std::string> tmp_pair =
std::make_pair(setting["directory"].as<std::string>(), word.as<std::string>());
cgroup_map_[tmp_pair] = false;

Check warning on line 63 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L62-L63

Added lines #L62 - L63 were not covered by tests
}
}
} catch (const std::exception & e) {
RCLCPP_ERROR(this->get_logger(), "Failed to load the config file.");

Check warning on line 67 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L66-L67

Added lines #L66 - L67 were not covered by tests
return;
}

gethostname(hostname_, sizeof(hostname_));
updater_.setHardwareID(hostname_);
updater_.add("Cgroup Setting", this, &CgroupSetter::checkCgroup);

Check warning on line 73 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L71-L73

Added lines #L71 - L73 were not covered by tests

// Timer
using namespace std::literals::chrono_literals;
timer_ = rclcpp::create_timer(
this, this->get_clock(), 1s, std::bind(&CgroupSetter::checkProcessAndAddToCgroup, this));

Check warning on line 78 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L77-L78

Added lines #L77 - L78 were not covered by tests
}

void CgroupSetter::checkCgroup(diagnostic_updater::DiagnosticStatusWrapper & stat)

Check warning on line 81 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L81

Added line #L81 was not covered by tests
{
bool allOK = true;
for (auto & entry : cgroup_map_) {
if (entry.second) {
stat.add(entry.first.first + " " + entry.first.second, "OK");

Check warning on line 86 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L86

Added line #L86 was not covered by tests
} else {
allOK = false;
stat.add(entry.first.first + " " + entry.first.second, "NG");

Check warning on line 89 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L89

Added line #L89 was not covered by tests
}
}
if (allOK) {
timer_->cancel();
stat.summary(diagnostic_msgs::msg::DiagnosticStatus::OK, "All processes are added to cgroup.");

Check warning on line 94 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L93-L94

Added lines #L93 - L94 were not covered by tests
} else {
stat.summary(

Check warning on line 96 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L96

Added line #L96 was not covered by tests
diagnostic_msgs::msg::DiagnosticStatus::WARN, "Some processes are not added to cgroup.");
}
}

void CgroupSetter::checkProcessAndAddToCgroup()

Check warning on line 101 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L101

Added line #L101 was not covered by tests
{
for (auto & entry : cgroup_map_) {
if (entry.second) {
continue;

Check warning on line 105 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L105

Added line #L105 was not covered by tests
}
std::string word = entry.first.second;
std::string result = executeCommand(word);

Check warning on line 108 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L108

Added line #L108 was not covered by tests
if (!result.empty()) {
std::istringstream iss(result);

Check warning on line 110 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L110

Added line #L110 was not covered by tests
std::string pid;
bool allAdded = true;
while (std::getline(iss, pid, '\n')) {
if (!pid.empty() && addToCgroup(entry.first.first, pid)) {
if (checkPIDExists(base_path_ + "/" + entry.first.first + "/cgroup.procs", pid)) {
RCLCPP_INFO(

Check warning on line 116 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L116

Added line #L116 was not covered by tests
this->get_logger(), "Added all PIDs to cgroup. %s %s", entry.first.second.c_str(),
pid.c_str());
} else {
allAdded = false;
RCLCPP_ERROR(

Check warning on line 121 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L121

Added line #L121 was not covered by tests
this->get_logger(), "Failed to add PID %s to cgroup. %s %s", pid.c_str(),
entry.first.second.c_str(), result.c_str());
}
} else {
allAdded = false;
RCLCPP_ERROR(

Check warning on line 127 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L127

Added line #L127 was not covered by tests
this->get_logger(), "Failed to add PID %s to cgroup. %s %s", pid.c_str(),
entry.first.second.c_str(), result.c_str());
}
}
if (allAdded) {
entry.second = true;

Check warning on line 133 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L133

Added line #L133 was not covered by tests
}
} else {
RCLCPP_ERROR(

Check warning on line 136 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L135-L136

Added lines #L135 - L136 were not covered by tests
this->get_logger(), "Failed to get PID. %s %s", entry.first.second.c_str(), result.c_str());
}
}
}

Check warning on line 140 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Method

CgroupSetter::checkProcessAndAddToCgroup has a cyclomatic complexity of 10, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

Check warning on line 140 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Bumpy Road Ahead

CgroupSetter::checkProcessAndAddToCgroup has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

Check warning on line 140 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Deep, Nested Complexity

CgroupSetter::checkProcessAndAddToCgroup has a nested complexity depth of 5, threshold = 4. This function contains deeply nested logic such as if statements and/or loops. The deeper the nesting, the lower the code health.

std::string CgroupSetter::executeCommand(const std::string & search_word)

Check warning on line 142 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L142

Added line #L142 was not covered by tests
{
int out_fd[2];
if (pipe2(out_fd, O_CLOEXEC) != 0) {
RCLCPP_ERROR(this->get_logger(), "pipe2 error");
return "";

Check warning on line 147 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L146-L147

Added lines #L146 - L147 were not covered by tests
}
bp::pipe out_pipe{out_fd[0], out_fd[1]};
bp::ipstream is_out{std::move(out_pipe)};

Check warning on line 150 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L149-L150

Added lines #L149 - L150 were not covered by tests

int err_fd[2];
if (pipe2(err_fd, O_CLOEXEC) != 0) {
RCLCPP_ERROR(this->get_logger(), "pipe2 error");
return "";

Check warning on line 155 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L154-L155

Added lines #L154 - L155 were not covered by tests
}
bp::pipe err_pipe{err_fd[0], err_fd[1]};
bp::ipstream is_err{std::move(err_pipe)};
auto cmd = bp::search_path("pgrep");

Check warning on line 159 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L157-L159

Added lines #L157 - L159 were not covered by tests
std::vector<std::string> args;
args.push_back("-f");
args.push_back(search_word);
bp::child c(cmd, bp::args = args, bp::std_out > is_out, bp::std_err > is_err);
c.wait();

Check warning on line 164 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L161-L164

Added lines #L161 - L164 were not covered by tests
if (c.exit_code() != 0) {
std::ostringstream os;
is_err >> os.rdbuf();
RCLCPP_ERROR(this->get_logger(), os.str().c_str());
return "";
} else {
std::ostringstream os;
os << is_out.rdbuf();

Check warning on line 172 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L166-L172

Added lines #L166 - L172 were not covered by tests
std::string output = os.str();
return output;
}
}

bool CgroupSetter::checkPIDExists(const std::string & filePath, const std::string & pid)

Check warning on line 178 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L178

Added line #L178 was not covered by tests
{
std::ifstream file(filePath);

Check warning on line 180 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L180

Added line #L180 was not covered by tests
if (!file.is_open()) {
RCLCPP_ERROR(this->get_logger(), "Failed to open %s", filePath.c_str());
return false;

Check warning on line 183 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L182-L183

Added lines #L182 - L183 were not covered by tests
}

std::string line;
while (std::getline(file, line)) {
if (line == pid) {
return true;
}
}
return false;
}

bool CgroupSetter::addToCgroup(const std::string & cgroupPath, const std::string & pid)

Check warning on line 195 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L195

Added line #L195 was not covered by tests
{
std::string cgroupProcFile = base_path_ + "/" + cgroupPath + "/cgroup.procs";
std::ofstream ofs(cgroupProcFile, std::ofstream::app);

Check warning on line 198 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L197-L198

Added lines #L197 - L198 were not covered by tests
if (!ofs) {
std::cerr << "Failed to open " << cgroupProcFile << std::endl;
ofs.close();

Check warning on line 201 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L201

Added line #L201 was not covered by tests
return false;
}
ofs << pid;
if (!ofs) {
std::cerr << "Failed to write to " << cgroupProcFile << std::endl;
ofs.close();

Check warning on line 207 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L207

Added line #L207 was not covered by tests
return false;
}
ofs.close();

Check warning on line 210 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L210

Added line #L210 was not covered by tests
return true;
}

int main(int argc, char ** argv)

Check warning on line 214 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L214

Added line #L214 was not covered by tests
{
rclcpp::init(argc, argv);
rclcpp::executors::SingleThreadedExecutor executor;
auto options = rclcpp::NodeOptions();

Check warning on line 218 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L216-L218

Added lines #L216 - L218 were not covered by tests
auto node = std::make_shared<CgroupSetter>(options);
executor.add_node(node);
executor.spin();
executor.remove_node(node);
rclcpp::shutdown();

Check warning on line 223 in system/cgroup_setter/src/cgroup_setter.cpp

View check run for this annotation

Codecov / codecov/patch

system/cgroup_setter/src/cgroup_setter.cpp#L220-L223

Added lines #L220 - L223 were not covered by tests
}
Loading
Loading