From a9e4ebbe4bc8325451bc1a56a1dbb60d3e89089a Mon Sep 17 00:00:00 2001 From: Arne Tarara Date: Fri, 22 Nov 2024 19:15:56 +0100 Subject: [PATCH 1/2] Container energy idle (#992) * Clarification on Turbo Boost and when it is off * Container energy and power incl. idle added [skip ci] --- frontend/js/helpers/config.js.example | 12 ++++++++++++ lib/hardware_info.py | 2 +- lib/phase_stats.py | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/js/helpers/config.js.example b/frontend/js/helpers/config.js.example index 7b61d6bc2..f6eb2f8c6 100644 --- a/frontend/js/helpers/config.js.example +++ b/frontend/js/helpers/config.js.example @@ -34,12 +34,24 @@ METRIC_MAPPINGS = { 'explanation': 'Container energy estimated via CPU-% share', }, + 'psu_energy_cgroup_slice': { + 'clean_name': 'Container Energy (+Idle)', + 'source': 'estimation', + 'explanation': 'Container energy estimated via CPU-% share (incl. idle)', + }, + 'psu_power_cgroup_container': { 'clean_name': 'Container Power', 'source': 'estimation', 'explanation': 'Container power estimated via CPU-% share', }, + 'psu_power_cgroup_slice': { + 'clean_name': 'Container Power (+Idle)', + 'source': 'estimation', + 'explanation': 'Container power estimated via CPU-% share incl. Idle', + }, + 'psu_carbon_dc_rapl_msr_machine': { 'clean_name': 'Machine CO₂', 'source': 'RAPL', diff --git a/lib/hardware_info.py b/lib/hardware_info.py index b46548de0..8df9ec9f6 100755 --- a/lib/hardware_info.py +++ b/lib/hardware_info.py @@ -55,7 +55,7 @@ # If another scaling driver is used the info will be in /sys/devices/system/cpu/cpufreq/boost # See https://wiki.archlinux.org/title/CPU_frequency_scaling # See further: https://www.kernel.org/doc/html/v5.17/admin-guide/pm/intel_pstate.html#user-space-interface-in-sysfs - [rfwr, 'Turbo Boost', '/sys/devices/system/cpu/intel_pstate/no_turbo', r'(?P.*)'], + [rfwr, 'Turbo Boost (1=off)', '/sys/devices/system/cpu/intel_pstate/no_turbo', r'(?P.*)'], [rfwr, 'Turbo Boost (Legacy non intel_pstate)', '/sys/devices/system/cpu/cpufreq/boost', r'(?P.*)'], [rfwr, 'Virtualization', '/proc/cpuinfo', r'(?Phypervisor)'], [rpwrs, 'SGX', f"{os.path.join(CURRENT_PATH, '../tools/sgx_enable')} -s", r'(?P.*)', re.IGNORECASE | re.DOTALL], diff --git a/lib/phase_stats.py b/lib/phase_stats.py index 44ea39699..7ac57870a 100644 --- a/lib/phase_stats.py +++ b/lib/phase_stats.py @@ -182,15 +182,17 @@ def build_and_store_phase_stats(run_id, sci=None): if machine_power_idle and cpu_utilization_machine and cpu_utilization_containers: surplus_power_runtime = machine_power_runtime - machine_power_idle surplus_energy_runtime = machine_energy_runtime - (machine_power_idle * decimal.Decimal(duration / 10**6)) + total_container_utilization = sum(cpu_utilization_containers.values()) if int(total_container_utilization) == 0: continue for detail_name, container_utilization in cpu_utilization_containers.items(): + csv_buffer.write(generate_csv_line(run_id, 'psu_energy_cgroup_slice', detail_name, f"{idx:03}_{phase['name']}", machine_energy_runtime * (container_utilization / total_container_utilization), 'TOTAL', None, None, 'mJ')) + csv_buffer.write(generate_csv_line(run_id, 'psu_power_cgroup_slice', detail_name, f"{idx:03}_{phase['name']}", machine_power_runtime * (container_utilization / total_container_utilization), 'TOTAL', None, None, 'mW')) csv_buffer.write(generate_csv_line(run_id, 'psu_energy_cgroup_container', detail_name, f"{idx:03}_{phase['name']}", surplus_energy_runtime * (container_utilization / total_container_utilization), 'TOTAL', None, None, 'mJ')) csv_buffer.write(generate_csv_line(run_id, 'psu_power_cgroup_container', detail_name, f"{idx:03}_{phase['name']}", surplus_power_runtime * (container_utilization / total_container_utilization), 'TOTAL', None, None, 'mW')) - csv_buffer.seek(0) # Reset buffer position to the beginning DB().copy_from( csv_buffer, From 6172f32ac79c6fc649350e16457f1fcf2454193a Mon Sep 17 00:00:00 2001 From: Arne Tarara Date: Sat, 23 Nov 2024 14:51:33 +0100 Subject: [PATCH 2/2] Codespaces merge into main (#993) * Create devcontainer.json * Adding feature for installing GMT directly * Update devcontainer.json * Update devcontainer.json - ubuntu 22.04 * Update devcontainer.json - just ubuntu * cgroup providers will now check cgroupv1 paths if cgroupv2 paths are not found * codespaces on ubuntu 22.04; gmt install on codespace container creation; splash screeen; readme link; * try install on post-create command to see if it helps with second run issue (etc/hosts gets deleted) * rewrite to etc/hosts on start * add api_url to on-start script * add api_url to on-start script - wrong var name * print_message -> echo * make port changes for codespaces setup script * set server_names_hash_bucket_size in nginx.conf * fix spacing in port setup * fix port name for metrics, removed accidental paste * added -t flag to codespace install script run * add examples directory to codespace * update codespace splash - note on port visibility * add splash open to on-start script * disable network io cgroup provider for codespace * install reqs into codespace * remove code splash screen line from codespace on-start * add common category to disabled providers in codespace * splash example is set to examples directory stress, not test stress * try starting splash screen with postStartCommand * remove splash start from postStartCommand - it couldn't find the right code application to open * added containerized/base split; added workflow to build github codespace container; added devcontainer config for containerized version * fix reference in contianerized devcontainer; removed duplicate docker install * use secrets.github_token * updated build and push action * fix path for dockerfile * try with checkout first, despite documentation * use same base image * remove trailing \ in mv command * workspace -> workspaces * corrected copy path for dockerfile * dockerfile: cd into gmt directory then install * turn off all but one psu providers * install with no-hosts flag * install docker compose * activate venv before installing requirements * add test image with nothing to find correct docker variables * try new way of installing docker * try last hail mary on installing from within dockerfile * try one last thing * try split containrization to avoid running docker in docker * corrected context for gunicorn build * try again with build context * fix path? * from instead of second pull * don't need to build gunicorn again * pull gunicorn image during codespace start, see how long that takes; run codespace-setup script on start * remove test config; add codespace-startup script to containerized * Speedy version for linux * Added no ipmi and no sensors flag * Added switches to entry statement * Updated splash message * Leveraging python cache even more * Slimming down container * Resureccting docker in docker and trimming splash screenh * Removing docker in docker. Selfinstall * Proper naming * Splash fix * Using codespaces default image * Further savings on example clone * New example applications folder * Remote updated for XGboost * Typo * Moving to container build again * Fixes for domain resolution in codespaces * Deactiving GPU; Added true if directory exists; Stopping containers before new boot * Updated splash message to use Bakery Demo Repo * being more explicit about the Wagtail repo * Updated idle to at least 10 s sleep * Removed the no sleep to show container idle power * Showing energy for every runtime phase * Updated Cloud Energy * Idle duration set to 10 to make container energy estimations more reliable * Clarification on Turbo Boost and when it is off * Improve Codespaces Setup (#833) * Fix repo path in example * minor * Add VS Code extensions * Add hint to make api port public to avoid CORS issues * Remove YAML VS Code extension * Open markdown files in preview mode by default * Ignore backuped Makefile in Git * Ensure ports are set to public after Codespace restart * Update git repository URL * Improve quickstart and add troubleshooting information --------- Co-authored-by: Arne Tarara * Disabling SSL for codespaces * Loading block.conf also in Codespaces * Updated XGBoost * Typos in the container providers+ * Activating ports correctly + enabling XGboost manually * Skipping lmsensors * Refactored get cgroup path to C lib * Compile errors and makefile additions * Makefile additions * Removing unused vars * Using new --dev-cache-build switch. Was renamed * Defautl host blocking must be disabled in GH Codespaces * Check for memory file must be different * Making codespaces work on main --------- Co-authored-by: dan-mm <33732895+dan-mm@users.noreply.github.com> Co-authored-by: dan-mm Co-authored-by: David --- .devcontainer/codespace-setup.sh | 36 +++++++++++ .devcontainer/devcontainer.json | 31 +++++++++ .devcontainer/on-start.sh | 17 +++++ .devcontainer/splash.md | 35 ++++++++++ .devcontainer/troubleshooting.md | 27 ++++++++ .../workflows/build-codespace-container.yml | 15 ++++- .gitignore | 10 +-- README.md | 2 + config.yml.example | 2 +- docker/nginx/nginx.conf | 3 +- install_linux.sh | 3 - install_mac.sh | 4 -- lib/c/Makefile | 5 ++ lib/c/detect_cgroup_path.c | 64 +++++++++++++++++++ lib/c/detect_cgroup_path.h | 6 ++ lib/install_shared.sh | 4 +- metric_providers/base.py | 4 -- .../cpu/time/cgroup/container/Makefile | 2 +- .../cpu/time/cgroup/container/provider.py | 3 +- .../cpu/time/cgroup/container/source.c | 39 +++-------- .../cpu/time/cgroup/system/provider.py | 6 +- .../cpu/utilization/cgroup/container/Makefile | 2 +- .../utilization/cgroup/container/provider.py | 3 +- .../cpu/utilization/cgroup/container/source.c | 39 ++++------- .../disk/io/cgroup/container/Makefile | 2 +- .../disk/io/cgroup/container/provider.py | 3 +- .../disk/io/cgroup/container/source.c | 39 ++++------- .../disk/io/procfs/system/provider.py | 4 +- .../memory/used/cgroup/container/Makefile | 2 +- .../memory/used/cgroup/container/provider.py | 3 +- .../memory/used/cgroup/container/source.c | 41 ++++-------- .../network/io/cgroup/container/Makefile | 2 +- .../network/io/cgroup/container/provider.py | 3 +- .../network/io/cgroup/container/source.c | 55 +++++----------- metric_providers/powermetrics/provider.py | 2 +- .../psu/energy/ac/xgboost/machine/model | 2 +- runner.py | 13 +--- tests/test-config.yml | 6 ++ tools/calibrate.py | 10 +-- 39 files changed, 331 insertions(+), 218 deletions(-) create mode 100755 .devcontainer/codespace-setup.sh create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/on-start.sh create mode 100644 .devcontainer/splash.md create mode 100644 .devcontainer/troubleshooting.md create mode 100644 lib/c/detect_cgroup_path.c create mode 100644 lib/c/detect_cgroup_path.h diff --git a/.devcontainer/codespace-setup.sh b/.devcontainer/codespace-setup.sh new file mode 100755 index 000000000..5314dd3d6 --- /dev/null +++ b/.devcontainer/codespace-setup.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -euxo pipefail + +# we have to rename this makefile as it doesn't compile in Codespaces +if [ -f /workspaces/green-metrics-tool/metric_providers/lm_sensors/Makefile ]; then + mv /workspaces/green-metrics-tool/metric_providers/lm_sensors/Makefile /workspaces/green-metrics-tool/metric_providers/lm_sensors/Makefile.bak + git update-index --assume-unchanged /workspaces/green-metrics-tool/metric_providers/lm_sensors/Makefile +fi + +/workspaces/green-metrics-tool/install_linux.sh -p testpw -a "https://${CODESPACE_NAME}-9142.app.github.dev" -m "https://${CODESPACE_NAME}-9143.app.github.dev" -t -i -s -l +source venv/bin/activate + +# Also add XGBoost, as we need it +python3 -m pip install -r /workspaces/green-metrics-tool/metric_providers/psu/energy/ac/xgboost/machine/model/requirements.txt + +# make edits to ports so we can use 9143 to access front end +sed -i 's/listen \[::\]:9142;/listen [::]:9143;/; s/listen 9142;/listen 9143;/' /workspaces/green-metrics-tool/docker/nginx/frontend.conf +sed -i 's/- 9142:9142/- 9142:9142\n - 9143:9143/' /workspaces/green-metrics-tool/docker/compose.yml +sed -i 's|- ./nginx/block.conf|#- ./nginx/block.conf|' /workspaces/green-metrics-tool/docker/compose.yml + +# activate XGBoost provider with sane values for GitHub Codespaces +sed -i 's/common:/common:\n psu.energy.ac.xgboost.machine.provider.PsuEnergyAcXgboostMachineProvider:\n resolution: 99\n CPUChips: 1\n HW_CPUFreq: 2800\n CPUCores: 32\n CPUThreads: 64\n TDP: 270\n HW_MemAmountGB: 256\n VHost_Ratio: 0.03125\n/' /workspaces/green-metrics-tool/config.yml + + +git clone https://github.com/green-coding-solutions/example-applications.git --depth=1 --single-branch /workspaces/green-metrics-tool/example-applications || true + +source venv/bin/activate + +docker compose -f /workspaces/green-metrics-tool/docker/compose.yml down + +docker compose -f /workspaces/green-metrics-tool/docker/compose.yml up -d + + +gh codespace ports visibility 9142:public -c $CODESPACE_NAME + +gh codespace ports visibility 9143:public -c $CODESPACE_NAME \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..9fa8d8350 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,31 @@ +{ + "postStartCommand": "/workspaces/green-metrics-tool/.devcontainer/on-start.sh", + "forwardPorts": [9142, 9143], + "portsAttributes": { + "9143": { + "label": "metrics page" + }, + "9142": { + "label": "api" + } + }, + "customizations": { + "codespaces": { + "openFiles": [ + ".devcontainer/splash.md" + ] + }, + "vscode": { + "settings": { + "workbench.editorAssociations": { + "*.md": "vscode.markdown.preview.editor" // Open markdown files in preview mode by default + } + }, + "extensions": + [ + "ms-python.python", + "ms-azuretools.vscode-docker" + ] + } + } +} diff --git a/.devcontainer/on-start.sh b/.devcontainer/on-start.sh new file mode 100755 index 000000000..8f0c1f449 --- /dev/null +++ b/.devcontainer/on-start.sh @@ -0,0 +1,17 @@ +#!/bin/bash +set -euo pipefail + +etc_hosts_line_1="127.0.0.1 green-coding-postgres-container" + +echo "Writing to /etc/hosts file..." + +# Entry 1 is needed for the local resolution of the containers through the jobs.py and runner.py +if ! sudo grep -Fxq "$etc_hosts_line_1" /etc/hosts; then + echo "$etc_hosts_line_1" | sudo tee -a /etc/hosts +else + echo "Entry was already present..." +fi + +# Ensure that after a restart of the Codespace the ports are set to public again +gh codespace ports visibility 9142:public -c $CODESPACE_NAME +gh codespace ports visibility 9143:public -c $CODESPACE_NAME diff --git a/.devcontainer/splash.md b/.devcontainer/splash.md new file mode 100644 index 000000000..8ff746a9b --- /dev/null +++ b/.devcontainer/splash.md @@ -0,0 +1,35 @@ +# Green Metrics Tool Codespaces Quickstart + +Thank you for trying out the Green Metrics Tool :-) + +Please run the following command in the terminal to set up everything! 🚀 + +```sh +bash .devcontainer/codespace-setup.sh +``` + +It will take about 3 minutes. + +Afterwards, load the python environment: + +```sh +source venv/bin/activate +``` + +Do your first measurement run like this: + +```sh +python3 runner.py --name "Simple Test" --uri "/workspaces/green-metrics-tool/example-applications/" --filename "stress/usage_scenario.yml" --skip-system-checks --dev-no-optimizations --dev-cache-build +``` + +Then, if you want to see a more representative repository, try running our Bakery Demo repository we did together with the Wagtail Community: + +```sh +python3 runner.py --uri https://github.com/green-coding-solutions/bakerydemo --branch gmt --skip-system-checks --dev-no-optimization --dev-cache-build --skip-unsafe --name "Bakery Demo Test" +``` + +To see the Metrics front end, go to your ports tab and follow the forwarding address for port 9143. + +Make sure the `api` port (9142) is public. If it's private, the metrics frontend will not be able to access the API due to CORS issues. + +If you are experiencing problems, see the file [.devcontainer/troubleshooting.md](./troubleshooting.md) for some common troubleshooting tips. diff --git a/.devcontainer/troubleshooting.md b/.devcontainer/troubleshooting.md new file mode 100644 index 000000000..0ffb3f62a --- /dev/null +++ b/.devcontainer/troubleshooting.md @@ -0,0 +1,27 @@ +# Troubleshooting + +## Frontend can't be open + +Make sure the ports 9142 (`api`) und 9143 (`metrics page`) are public. If they are private, the metrics frontend will not be able to access the API due to CORS issues. After a restart of the codespace the ports are set to private, so you have to change the visibility manually. + +You can use the following commands in the terminal to make the ports public: + +```sh +gh codespace ports visibility 9142:public -c $CODESPACE_NAME +gh codespace ports visibility 9143:public -c $CODESPACE_NAME +``` + +## Connection to server failed + +If you entcounter an error like + +```log +error connecting in 'pool-1': connection failed: connection to server at "127.0.0.1", port 9573 failed: Connection refused + Is the server running on that host and accepting TCP/IP connections? +``` + +then ensure that the Docker containers of GMT are running. + +```sh +docker compose -f docker/compose.yml up -d +``` diff --git a/.github/workflows/build-codespace-container.yml b/.github/workflows/build-codespace-container.yml index c108e6912..e8ee990df 100644 --- a/.github/workflows/build-codespace-container.yml +++ b/.github/workflows/build-codespace-container.yml @@ -20,6 +20,9 @@ jobs: gmt-api-token: ${{ secrets.GMT_API_TOKEN }} electricitymaps-api-token: ${{ secrets.ELECTRICITYMAPS_TOKEN }} + - name: Checkout repository + uses: actions/checkout@v4 + - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -31,13 +34,21 @@ jobs: with: registry: ghcr.io username: ${{ github.actor }} - password: ${{ inputs.github-token }} + password: ${{ secrets.GITHUB_TOKEN }} + + # - name: Build and push gunicorn container + # uses: docker/build-push-action@v5 + # with: + # context: ./docker + # file: ./docker/Dockerfile-gunicorn + # push: true + # tags: ghcr.io/green-coding-berlin/green-coding-gunicorn-container:latest - name: Build and push Docker container uses: docker/build-push-action@v6 with: context: . - file: /.devcontainer/containerized/Dockerfile # Path to the Dockerfile + file: ./.devcontainer/containerized/Dockerfile push: true tags: ghcr.io/green-coding-solutions/codespace-container:latest diff --git a/.gitignore b/.gitignore index 3102d8269..286f02e18 100644 --- a/.gitignore +++ b/.gitignore @@ -14,13 +14,13 @@ metric-provider-binary .vscode static-binary .pytest_cache -/docker/test-compose.yml -/tests/structure.sql -/tools/sgx_enable -/venv/ /lib/hardware_info_root.py /tools/cluster/cleanup.sh /node_modules/ /lib/c/parse_int.o /tools/backup -/manager-config.yml \ No newline at end of file +/manager-config.yml +/venv/ +Makefile.bak +/docker/test-compose.yml +/tests/structure.sql diff --git a/README.md b/README.md index 0ca9ec8ea..665b0f7a6 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Energy Used](https://api.green-coding.io/v1/ci/badge/get/?repo=green-coding-solutions/green-metrics-tool&branch=main&workflow=45267393)](https://metrics.green-coding.io/ci.html?repo=green-coding-solutions/green-metrics-tool&branch=main&workflow=45267393) (This is the energy cost of running our CI-Pipelines on Github. [Find out more about Eco-CI](https://www.green-coding.io/projects/eco-ci/)) +[![Try in Github Codespaces!](https://github.com/codespaces/badge.svg)](https://codespaces.new/green-coding-berlin/green-metrics-tool) + # Introduction The Green Metrics Tool is a developer tool indented for measuring the energy and CO2 consumption of software through a software life cycle analysis (SLCA). diff --git a/config.yml.example b/config.yml.example index ff89c0f81..753d0a0ac 100644 --- a/config.yml.example +++ b/config.yml.example @@ -78,7 +78,7 @@ machine: measurement: system_check_threshold: 3 # Can be 1=INFO, 2=WARN or 3=ERROR pre-test-sleep: 5 - idle-duration: 5 + idle-duration: 10 baseline-duration: 5 post-test-sleep: 5 phase-transition-time: 1 diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 08eba4724..e0ec8641b 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -12,6 +12,7 @@ events { http { + server_names_hash_bucket_size 128; include /etc/nginx/mime.types; default_type application/octet-stream; @@ -34,4 +35,4 @@ http { gzip_disable "MSIE [1-6]\."; include /etc/nginx/conf.d/*.conf; -} \ No newline at end of file +} diff --git a/install_linux.sh b/install_linux.sh index be03f6de9..888ca7a31 100755 --- a/install_linux.sh +++ b/install_linux.sh @@ -1,7 +1,6 @@ #!/bin/bash set -euo pipefail - if [[ $(uname) != "Linux" ]]; then echo "Error: This script can only be run on Linux." exit 1 @@ -88,5 +87,3 @@ if ! mount | grep -E '\s/tmp\s' | grep -Eq '\stmpfs\s' && [[ $ask_tmpfs == true fi finalize - - diff --git a/install_mac.sh b/install_mac.sh index aceb49274..7da34911d 100755 --- a/install_mac.sh +++ b/install_mac.sh @@ -34,8 +34,4 @@ echo "ALL ALL=(ALL) NOPASSWD:/usr/bin/powermetrics" | sudo tee /etc/sudoers.d/gr echo "ALL ALL=(ALL) NOPASSWD:/usr/bin/killall powermetrics" | sudo tee /etc/sudoers.d/green_coding_kill_powermetrics echo "ALL ALL=(ALL) NOPASSWD:/usr/bin/killall -9 powermetrics" | sudo tee /etc/sudoers.d/green_coding_kill_powermetrics_sigkill - - - - finalize \ No newline at end of file diff --git a/lib/c/Makefile b/lib/c/Makefile index 9cac1ff0a..9597e5ed0 100644 --- a/lib/c/Makefile +++ b/lib/c/Makefile @@ -1,4 +1,9 @@ CFLAGS = -O3 -Wall +all: parse_int.o detect_cgroup_path.o + parse_int.o: parse_int.c gcc -c $< $(CFLAGS) -o $@ + +detect_cgroup_path.o: detect_cgroup_path.c + gcc -c $< $(CFLAGS) -o $@ \ No newline at end of file diff --git a/lib/c/detect_cgroup_path.c b/lib/c/detect_cgroup_path.c new file mode 100644 index 000000000..91e02c564 --- /dev/null +++ b/lib/c/detect_cgroup_path.c @@ -0,0 +1,64 @@ +#include "detect_cgroup_path.h" + +#include +#include +#include +#include +#include + +char* detect_cgroup_path(const char* controller, int user_id, const char* id) { + char* path = malloc(PATH_MAX); + if (path == NULL) { + fprintf(stderr, "Could not allocate memory for detect_cgroup_path\n"); + exit(1); + } + + FILE* fd = NULL; + + // Try cgroups v2 with systemd slices (typically in rootless mode) + snprintf(path, PATH_MAX, + "/sys/fs/cgroup/user.slice/user-%d.slice/user@%d.service/user.slice/docker-%s.scope/%s", + user_id, user_id, id, controller); + fd = fopen(path, "r"); + if (fd != NULL) { + fclose(fd); + return path; + } + + // Try cgroups v2 with systemd but non-slice mountpoints (typically in non-rootless mode) + snprintf(path, PATH_MAX, + "/sys/fs/cgroup/system.slice/docker-%s.scope/%s", + id, controller); + fd = fopen(path, "r"); + if (fd != NULL) { + fclose(fd); + return path; + } + + // Try cgroups v2 without slice mountpoints (used in Github codespaces) + snprintf(path, PATH_MAX, + "/sys/fs/cgroup/docker/%s/%s", + id, controller); + fd = fopen(path, "r"); + if (fd != NULL) { + fclose(fd); + return path; + } + + // Try cgroups v2 without slice mountpoints and in subdir (used in Github actions) + snprintf(path, PATH_MAX, + "/sys/fs/cgroup/actions_job/%s/%s", + id, controller); + fd = fopen(path, "r"); + if (fd != NULL) { + fclose(fd); + return path; + } + + // If no valid path is found, free the allocated memory and error + free(path); + fprintf(stderr, "Error - Could not open container for reading: %s. Maybe the container is not running anymore? Errno: %d\n", id, errno); + exit(1); + +} + diff --git a/lib/c/detect_cgroup_path.h b/lib/c/detect_cgroup_path.h new file mode 100644 index 000000000..f6acb36e9 --- /dev/null +++ b/lib/c/detect_cgroup_path.h @@ -0,0 +1,6 @@ +#ifndef DETECT_CGROUP_PATH_H +#define DETECT_CGROUP_PATH_H + +char* detect_cgroup_path(const char* controller, int user_id, const char* id); + +#endif // DETECT_CGROUP_PATH_H \ No newline at end of file diff --git a/lib/install_shared.sh b/lib/install_shared.sh index c288c2662..b9252ca76 100644 --- a/lib/install_shared.sh +++ b/lib/install_shared.sh @@ -222,6 +222,9 @@ function build_binaries() { if [[ $(uname) == "Linux" ]] && [[ "$make_path" == *"/mach/"* ]]; then continue fi + if [[ "$make_path" == *"/lmsensors/"* ]] && [[ "${install_sensors}" == false ]]; then + continue + fi echo "Installing $subdir/metric-provider-binary ..." rm -f $subdir/metric-provider-binary 2> /dev/null make -C $subdir @@ -290,7 +293,6 @@ while getopts "p:a:m:nhtbisyrlc:k:" o; do ;; s) install_sensors=false - # currently unused ;; r) install_msr_tools=false diff --git a/metric_providers/base.py b/metric_providers/base.py index 42c1d7f72..fc89217f6 100644 --- a/metric_providers/base.py +++ b/metric_providers/base.py @@ -34,7 +34,6 @@ def __init__( self._sudo = sudo self._has_started = False self._disable_buffer = disable_buffer - self._rootless = None self._skip_check = skip_check self._tmp_folder = '/tmp/green-metrics-tool' @@ -159,9 +158,6 @@ def start_profiling(self, containers=None): call_string += ' -s ' call_string += ','.join(containers.keys()) - if self._rootless is True: - call_string += ' --rootless ' - call_string += f" > {self._filename}" if platform.system() == "Linux": diff --git a/metric_providers/cpu/time/cgroup/container/Makefile b/metric_providers/cpu/time/cgroup/container/Makefile index 9170c4109..45c04b7bd 100644 --- a/metric_providers/cpu/time/cgroup/container/Makefile +++ b/metric_providers/cpu/time/cgroup/container/Makefile @@ -1,4 +1,4 @@ CFLAGS = -O3 -Wall -I../../../../../lib/c metric-provider-binary: source.c - gcc ../../../../../lib/c/parse_int.o $< $(CFLAGS) -o $@ \ No newline at end of file + gcc ../../../../../lib/c/parse_int.o ../../../../../lib/c/detect_cgroup_path.o $< $(CFLAGS) -o $@ \ No newline at end of file diff --git a/metric_providers/cpu/time/cgroup/container/provider.py b/metric_providers/cpu/time/cgroup/container/provider.py index 5dc214e00..27ef3ada3 100644 --- a/metric_providers/cpu/time/cgroup/container/provider.py +++ b/metric_providers/cpu/time/cgroup/container/provider.py @@ -3,7 +3,7 @@ from metric_providers.base import BaseMetricProvider class CpuTimeCgroupContainerProvider(BaseMetricProvider): - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='cpu_time_cgroup_container', metrics={'time': int, 'value': int, 'container_id': str}, @@ -12,7 +12,6 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check=skip_check, ) - self._rootless = rootless def read_metrics(self, run_id, containers=None): df = super().read_metrics(run_id, containers) diff --git a/metric_providers/cpu/time/cgroup/container/source.c b/metric_providers/cpu/time/cgroup/container/source.c index 9908ec44c..bf9d53568 100644 --- a/metric_providers/cpu/time/cgroup/container/source.c +++ b/metric_providers/cpu/time/cgroup/container/source.c @@ -8,11 +8,12 @@ #include #include #include "parse_int.h" +#include "detect_cgroup_path.h" #define DOCKER_CONTAINER_ID_BUFFER 65 // Docker container ID size is 64 + 1 byte for NUL termination typedef struct container_t { // struct is a specification and this static makes no sense here - char path[PATH_MAX]; + char* path; char id[DOCKER_CONTAINER_ID_BUFFER]; } container_t; @@ -28,7 +29,7 @@ static long int read_cpu_cgroup(char* filename) { long int cpu_usage = -1; FILE* fd = fopen(filename, "r"); if ( fd == NULL) { - fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Are you using --rootless mode? Errno: %d\n", filename, errno); + fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Errno: %d\n", filename, errno); exit(1); } int match_result = fscanf(fd, "usage_usec %ld", &cpu_usage); @@ -51,7 +52,7 @@ static void output_stats(container_t *containers, int length) { usleep(msleep_time*1000); } -static int parse_containers(container_t** containers, char* containers_string, int rootless_mode) { +static int parse_containers(container_t** containers, char* containers_string) { if(containers_string == NULL) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); @@ -76,17 +77,7 @@ static int parse_containers(container_t** containers, char* containers_string, i strncpy((*containers)[length-1].id, id, DOCKER_CONTAINER_ID_BUFFER - 1); (*containers)[length-1].id[DOCKER_CONTAINER_ID_BUFFER - 1] = '\0'; - if(rootless_mode) { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/user.slice/user-%d.slice/user@%d.service/user.slice/docker-%s.scope/cpu.stat", - user_id, user_id, id); - } else { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/system.slice/docker-%s.scope/cpu.stat", - id); - } + (*containers)[length-1].path = detect_cgroup_path("cpu.stat", user_id, id); } if(length == 0) { @@ -96,15 +87,10 @@ static int parse_containers(container_t** containers, char* containers_string, i return length; } -static int check_system(int rootless_mode) { +static int check_system() { const char* check_path; - if(rootless_mode) { - check_path = "/sys/fs/cgroup/user.slice/cpu.stat"; - } else { - check_path = "/sys/fs/cgroup/system.slice/cpu.stat"; - } - + check_path = "/sys/fs/cgroup/cpu.stat"; FILE* fd = fopen(check_path, "r"); if (fd == NULL) { @@ -118,7 +104,6 @@ static int check_system(int rootless_mode) { int main(int argc, char **argv) { int c; - int rootless_mode = 0; // docker root is default char *containers_string = NULL; // Dynamic buffer to store optarg container_t *containers = NULL; int check_system_flag = 0; @@ -129,7 +114,6 @@ int main(int argc, char **argv) { static struct option long_options[] = { - {"rootless", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, {"interval", no_argument, NULL, 'i'}, {"containers", no_argument, NULL, 's'}, @@ -137,7 +121,7 @@ int main(int argc, char **argv) { {NULL, 0, NULL, 0} }; - while ((c = getopt_long(argc, argv, "ri:s:hc", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "i:s:hc", long_options, NULL)) != -1) { switch (c) { case 'h': printf("Usage: %s [-i msleep_time] [-h]\n\n",argv[0]); @@ -159,9 +143,6 @@ int main(int argc, char **argv) { case 'i': msleep_time = parse_int(optarg); break; - case 'r': - rootless_mode = 1; - break; case 's': containers_string = (char *)malloc(strlen(optarg) + 1); // Allocate memory if (!containers_string) { @@ -181,10 +162,10 @@ int main(int argc, char **argv) { } if(check_system_flag){ - exit(check_system(rootless_mode)); + exit(check_system()); } - int length = parse_containers(&containers, containers_string, rootless_mode); + int length = parse_containers(&containers, containers_string); while(1) { output_stats(containers, length); diff --git a/metric_providers/cpu/time/cgroup/system/provider.py b/metric_providers/cpu/time/cgroup/system/provider.py index 339c65295..fc97f7bae 100644 --- a/metric_providers/cpu/time/cgroup/system/provider.py +++ b/metric_providers/cpu/time/cgroup/system/provider.py @@ -3,10 +3,7 @@ from metric_providers.base import BaseMetricProvider class CpuTimeCgroupSystemProvider(BaseMetricProvider): - # disabling unused-argument because as a cgroup provider we always pass in rootless as a variable - # even though this provider does not need/care about it - #pylint: disable=unused-argument - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='cpu_time_cgroup_system', metrics={'time': int, 'value': int}, @@ -15,4 +12,3 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check=skip_check, ) - self._rootless = False #this provider does not need or take --rootless flag, despite being a cgroup provider diff --git a/metric_providers/cpu/utilization/cgroup/container/Makefile b/metric_providers/cpu/utilization/cgroup/container/Makefile index 9170c4109..45c04b7bd 100644 --- a/metric_providers/cpu/utilization/cgroup/container/Makefile +++ b/metric_providers/cpu/utilization/cgroup/container/Makefile @@ -1,4 +1,4 @@ CFLAGS = -O3 -Wall -I../../../../../lib/c metric-provider-binary: source.c - gcc ../../../../../lib/c/parse_int.o $< $(CFLAGS) -o $@ \ No newline at end of file + gcc ../../../../../lib/c/parse_int.o ../../../../../lib/c/detect_cgroup_path.o $< $(CFLAGS) -o $@ \ No newline at end of file diff --git a/metric_providers/cpu/utilization/cgroup/container/provider.py b/metric_providers/cpu/utilization/cgroup/container/provider.py index ea433ea45..66974505e 100644 --- a/metric_providers/cpu/utilization/cgroup/container/provider.py +++ b/metric_providers/cpu/utilization/cgroup/container/provider.py @@ -3,7 +3,7 @@ from metric_providers.base import BaseMetricProvider class CpuUtilizationCgroupContainerProvider(BaseMetricProvider): - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='cpu_utilization_cgroup_container', metrics={'time': int, 'value': int, 'container_id': str}, @@ -12,7 +12,6 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check = skip_check, ) - self._rootless = rootless def read_metrics(self, run_id, containers=None): df = super().read_metrics(run_id, containers) diff --git a/metric_providers/cpu/utilization/cgroup/container/source.c b/metric_providers/cpu/utilization/cgroup/container/source.c index 6c5c45d17..d1afce1e9 100644 --- a/metric_providers/cpu/utilization/cgroup/container/source.c +++ b/metric_providers/cpu/utilization/cgroup/container/source.c @@ -8,11 +8,12 @@ #include #include #include "parse_int.h" +#include "detect_cgroup_path.h" #define DOCKER_CONTAINER_ID_BUFFER 65 // Docker container ID size is 64 + 1 byte for NUL termination typedef struct container_t { // struct is a specification and this static makes no sense here - char path[PATH_MAX]; + char* path; char id[DOCKER_CONTAINER_ID_BUFFER]; } container_t; @@ -57,7 +58,7 @@ static long int get_cpu_stat(char* filename, int mode) { FILE* fd = fopen(filename, "r"); if ( fd == NULL) { - fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Are you using --rootless mode? Errno: %d\n", filename, errno); + fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Errno: %d\n", filename, errno); exit(1); } if(mode == 1) { @@ -127,7 +128,7 @@ static void output_stats(container_t* containers, int length) { } } -static int parse_containers(container_t** containers, char* containers_string, int rootless_mode) { +static int parse_containers(container_t** containers, char* containers_string) { if(containers_string == NULL) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); @@ -152,36 +153,23 @@ static int parse_containers(container_t** containers, char* containers_string, i strncpy((*containers)[length-1].id, id, DOCKER_CONTAINER_ID_BUFFER - 1); (*containers)[length-1].id[DOCKER_CONTAINER_ID_BUFFER - 1] = '\0'; - if(rootless_mode) { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/user.slice/user-%d.slice/user@%d.service/user.slice/docker-%s.scope/cpu.stat", - user_id, user_id, id); - } else { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/system.slice/docker-%s.scope/cpu.stat", - id); - } + (*containers)[length-1].path = detect_cgroup_path("cpu.stat", user_id, id); } if(length == 0) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); } + return length; } -static int check_system(int rootless_mode) { +static int check_system() { const char* file_path_cpu_stat; const char* file_path_proc_stat; int found_error = 0; - if(rootless_mode) { - file_path_cpu_stat = "/sys/fs/cgroup/user.slice/cpu.stat"; - } else { - file_path_cpu_stat = "/sys/fs/cgroup/system.slice/cpu.stat"; - } + file_path_cpu_stat = "/sys/fs/cgroup/cpu.stat"; file_path_proc_stat = "/proc/stat"; FILE* fd = fopen(file_path_cpu_stat, "r"); @@ -211,7 +199,6 @@ int main(int argc, char **argv) { int c; int check_system_flag = 0; - int rootless_mode = 0; // docker root is default char *containers_string = NULL; // Dynamic buffer to store optarg container_t *containers = NULL; @@ -221,7 +208,6 @@ int main(int argc, char **argv) { static struct option long_options[] = { - {"rootless", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, {"interval", no_argument, NULL, 'i'}, {"containers", no_argument, NULL, 's'}, @@ -229,7 +215,7 @@ int main(int argc, char **argv) { {NULL, 0, NULL, 0} }; - while ((c = getopt_long(argc, argv, "ri:s:hc", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "i:s:hc", long_options, NULL)) != -1) { switch (c) { case 'h': printf("Usage: %s [-i msleep_time] [-h]\n\n",argv[0]); @@ -251,9 +237,6 @@ int main(int argc, char **argv) { case 'i': msleep_time = parse_int(optarg); break; - case 'r': - rootless_mode = 1; - break; case 's': containers_string = (char *)malloc(strlen(optarg) + 1); // Allocate memory if (!containers_string) { @@ -273,10 +256,10 @@ int main(int argc, char **argv) { } if(check_system_flag){ - exit(check_system(rootless_mode)); + exit(check_system()); } - int length = parse_containers(&containers, containers_string, rootless_mode); + int length = parse_containers(&containers, containers_string); while(1) { output_stats(containers, length); diff --git a/metric_providers/disk/io/cgroup/container/Makefile b/metric_providers/disk/io/cgroup/container/Makefile index 9170c4109..45c04b7bd 100644 --- a/metric_providers/disk/io/cgroup/container/Makefile +++ b/metric_providers/disk/io/cgroup/container/Makefile @@ -1,4 +1,4 @@ CFLAGS = -O3 -Wall -I../../../../../lib/c metric-provider-binary: source.c - gcc ../../../../../lib/c/parse_int.o $< $(CFLAGS) -o $@ \ No newline at end of file + gcc ../../../../../lib/c/parse_int.o ../../../../../lib/c/detect_cgroup_path.o $< $(CFLAGS) -o $@ \ No newline at end of file diff --git a/metric_providers/disk/io/cgroup/container/provider.py b/metric_providers/disk/io/cgroup/container/provider.py index 1a150a294..2f2ba7eae 100644 --- a/metric_providers/disk/io/cgroup/container/provider.py +++ b/metric_providers/disk/io/cgroup/container/provider.py @@ -4,7 +4,7 @@ from metric_providers.base import BaseMetricProvider class DiskIoCgroupContainerProvider(BaseMetricProvider): - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='disk_io_cgroup_container', metrics={'time': int, 'read_bytes': int, 'written_bytes': int, 'container_id': str}, @@ -13,7 +13,6 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check=skip_check, ) - self._rootless = rootless def read_metrics(self, run_id, containers=None): df = super().read_metrics(run_id, containers) diff --git a/metric_providers/disk/io/cgroup/container/source.c b/metric_providers/disk/io/cgroup/container/source.c index 22c255b22..bcb6de4bb 100644 --- a/metric_providers/disk/io/cgroup/container/source.c +++ b/metric_providers/disk/io/cgroup/container/source.c @@ -7,11 +7,13 @@ #include #include #include "parse_int.h" +#include "detect_cgroup_path.h" + #define DOCKER_CONTAINER_ID_BUFFER 65 // Docker container ID size is 64 + 1 byte for NUL termination typedef struct container_t { // struct is a specification and this static makes no sense here - char path[PATH_MAX]; + char* path; char id[DOCKER_CONTAINER_ID_BUFFER]; } container_t; @@ -35,7 +37,7 @@ static disk_io_t get_disk_cgroup(char* filename) { FILE * fd = fopen(filename, "r"); if ( fd == NULL) { - fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Are you using --rootless mode? Errno: %d\n", filename, errno); + fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Errno: %d\n", filename, errno); exit(1); } @@ -67,7 +69,7 @@ static void output_stats(container_t *containers, int length) { usleep(msleep_time*1000); } -static int parse_containers(container_t** containers, char* containers_string, int rootless_mode) { +static int parse_containers(container_t** containers, char* containers_string) { if(containers_string == NULL) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); @@ -93,17 +95,7 @@ static int parse_containers(container_t** containers, char* containers_string, i strncpy((*containers)[length-1].id, id, DOCKER_CONTAINER_ID_BUFFER - 1); (*containers)[length-1].id[DOCKER_CONTAINER_ID_BUFFER - 1] = '\0'; - if(rootless_mode) { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/user.slice/user-%d.slice/user@%d.service/user.slice/docker-%s.scope/io.stat", - user_id, user_id, id); - } else { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/system.slice/docker-%s.scope/io.stat", - id); - } + (*containers)[length-1].path = detect_cgroup_path("io.stat", user_id, id); } if(length == 0) { @@ -113,14 +105,10 @@ static int parse_containers(container_t** containers, char* containers_string, i return length; } -static int check_system(int rootless_mode) { +static int check_system() { const char* check_path; - if(rootless_mode) { - check_path = "/sys/fs/cgroup/user.slice/io.stat"; - } else { - check_path = "/sys/fs/cgroup/system.slice/io.stat"; - } + check_path = "/sys/fs/cgroup/io.stat"; FILE* fd = fopen(check_path, "r"); @@ -136,7 +124,6 @@ int main(int argc, char **argv) { int c; int check_system_flag = 0; - int rootless_mode = 0; // docker root is default char *containers_string = NULL; // Dynamic buffer to store optarg container_t *containers = NULL; @@ -145,7 +132,6 @@ int main(int argc, char **argv) { static struct option long_options[] = { - {"rootless", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, {"interval", no_argument, NULL, 'i'}, {"containers", no_argument, NULL, 's'}, @@ -153,7 +139,7 @@ int main(int argc, char **argv) { {NULL, 0, NULL, 0} }; - while ((c = getopt_long(argc, argv, "ri:s:hc", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "i:s:hc", long_options, NULL)) != -1) { switch (c) { case 'h': printf("Usage: %s [-i msleep_time] [-h]\n\n",argv[0]); @@ -165,9 +151,6 @@ int main(int argc, char **argv) { case 'i': msleep_time = parse_int(optarg); break; - case 'r': - rootless_mode = 1; - break; case 's': containers_string = (char *)malloc(strlen(optarg) + 1); // Allocate memory if (!containers_string) { @@ -187,10 +170,10 @@ int main(int argc, char **argv) { } if(check_system_flag){ - exit(check_system(rootless_mode)); + exit(check_system()); } - int length = parse_containers(&containers, containers_string, rootless_mode); + int length = parse_containers(&containers, containers_string); while(1) { output_stats(containers, length); diff --git a/metric_providers/disk/io/procfs/system/provider.py b/metric_providers/disk/io/procfs/system/provider.py index d91652039..055d667ba 100644 --- a/metric_providers/disk/io/procfs/system/provider.py +++ b/metric_providers/disk/io/procfs/system/provider.py @@ -5,7 +5,7 @@ from metric_providers.base import BaseMetricProvider class DiskIoProcfsSystemProvider(BaseMetricProvider): - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='disk_io_procfs_system', metrics={'time': int, 'read_sectors': int, 'written_sectors': int, 'device': str}, @@ -14,8 +14,6 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check=skip_check, ) - self._rootless = rootless - def read_metrics(self, run_id, containers=None): df = super().read_metrics(run_id, containers) diff --git a/metric_providers/memory/used/cgroup/container/Makefile b/metric_providers/memory/used/cgroup/container/Makefile index b7807d0fc..2c8216689 100644 --- a/metric_providers/memory/used/cgroup/container/Makefile +++ b/metric_providers/memory/used/cgroup/container/Makefile @@ -1,4 +1,4 @@ CFLAGS = -O3 -Wall -I../../../../../lib/c metric-provider-binary: source.c - gcc ../../../../../lib/c/parse_int.o $< $(CFLAGS) -o $@ + gcc ../../../../../lib/c/parse_int.o ../../../../../lib/c/detect_cgroup_path.o $< $(CFLAGS) -o $@ diff --git a/metric_providers/memory/used/cgroup/container/provider.py b/metric_providers/memory/used/cgroup/container/provider.py index e9ebf0827..6db7c2ecc 100644 --- a/metric_providers/memory/used/cgroup/container/provider.py +++ b/metric_providers/memory/used/cgroup/container/provider.py @@ -3,7 +3,7 @@ from metric_providers.base import BaseMetricProvider class MemoryUsedCgroupContainerProvider(BaseMetricProvider): - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='memory_used_cgroup_container', metrics={'time': int, 'value': int, 'container_id': str}, @@ -12,7 +12,6 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check=skip_check, ) - self._rootless = rootless def read_metrics(self, run_id, containers=None): df = super().read_metrics(run_id, containers) diff --git a/metric_providers/memory/used/cgroup/container/source.c b/metric_providers/memory/used/cgroup/container/source.c index a67662384..8232102d8 100644 --- a/metric_providers/memory/used/cgroup/container/source.c +++ b/metric_providers/memory/used/cgroup/container/source.c @@ -7,11 +7,12 @@ #include #include #include "parse_int.h" +#include "detect_cgroup_path.h" #define DOCKER_CONTAINER_ID_BUFFER 65 // Docker container ID size is 64 + 1 byte for NUL termination typedef struct container_t { // struct is a specification and this static makes no sense here - char path[PATH_MAX]; + char* path; char id[DOCKER_CONTAINER_ID_BUFFER]; } container_t; @@ -27,7 +28,7 @@ static unsigned long long int get_memory_cgroup(char* filename) { FILE * fd = fopen(filename, "r"); if ( fd == NULL) { - fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Are you using --rootless mode? Errno: %d\n", filename, errno); + fprintf(stderr, "Error - Could not open path for reading: %s. Maybe the container is not running anymore? Errno: %d\n", filename, errno); exit(1); } @@ -59,7 +60,7 @@ static void output_stats(container_t *containers, int length) { usleep(msleep_time*1000); } -static int parse_containers(container_t** containers, char* containers_string, int rootless_mode) { +static int parse_containers(container_t** containers, char* containers_string) { if(containers_string == NULL) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); @@ -78,6 +79,7 @@ static int parse_containers(container_t** containers, char* containers_string, i //printf("Token: %s\n", id); length++; *containers = realloc(*containers, length * sizeof(container_t)); + if (!containers) { fprintf(stderr, "Could not allocate memory for containers string\n"); exit(1); @@ -85,17 +87,7 @@ static int parse_containers(container_t** containers, char* containers_string, i strncpy((*containers)[length-1].id, id, DOCKER_CONTAINER_ID_BUFFER - 1); (*containers)[length-1].id[DOCKER_CONTAINER_ID_BUFFER - 1] = '\0'; - if(rootless_mode) { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/user.slice/user-%d.slice/user@%d.service/user.slice/docker-%s.scope/memory.current", - user_id, user_id, id); - } else { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/system.slice/docker-%s.scope/memory.current", - id); - } + (*containers)[length-1].path = detect_cgroup_path("memory.current", user_id, id); } if(length == 0) { @@ -105,19 +97,15 @@ static int parse_containers(container_t** containers, char* containers_string, i return length; } -static int check_system(int rootless_mode) { +static int check_system() { const char* check_path; - if(rootless_mode) { - check_path = "/sys/fs/cgroup/user.slice/memory.current"; - } else { - check_path = "/sys/fs/cgroup/system.slice/memory.current"; - } + check_path = "/sys/fs/cgroup/memory.stat"; // note: the .current is only available in slices. if the memory.stat file is present, we expect the .current also in the slices FILE* fd = fopen(check_path, "r"); if (fd == NULL) { - fprintf(stderr, "Couldn't open memory.current file at %s\n", check_path); + fprintf(stderr, "Couldn't open memory.stat file at %s\n", check_path); exit(1); } fclose(fd); @@ -128,7 +116,6 @@ int main(int argc, char **argv) { int c; int check_system_flag = 0; - int rootless_mode = 0; // docker root is default char *containers_string = NULL; // Dynamic buffer to store optarg container_t *containers = NULL; @@ -137,7 +124,6 @@ int main(int argc, char **argv) { static struct option long_options[] = { - {"rootless", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, {"interval", no_argument, NULL, 'i'}, {"containers", no_argument, NULL, 's'}, @@ -145,7 +131,7 @@ int main(int argc, char **argv) { {NULL, 0, NULL, 0} }; - while ((c = getopt_long(argc, argv, "ri:s:hc", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "i:s:hc", long_options, NULL)) != -1) { switch (c) { case 'h': printf("Usage: %s [-i msleep_time] [-h]\n\n",argv[0]); @@ -157,9 +143,6 @@ int main(int argc, char **argv) { case 'i': msleep_time = parse_int(optarg); break; - case 'r': - rootless_mode = 1; - break; case 's': containers_string = (char *)malloc(strlen(optarg) + 1); // Allocate memory if (!containers_string) { @@ -179,10 +162,10 @@ int main(int argc, char **argv) { } if(check_system_flag){ - exit(check_system(rootless_mode)); + exit(check_system()); } - int length = parse_containers(&containers, containers_string, rootless_mode); + int length = parse_containers(&containers, containers_string); while(1) { output_stats(containers, length); diff --git a/metric_providers/network/io/cgroup/container/Makefile b/metric_providers/network/io/cgroup/container/Makefile index abd83a86e..bf7b2e7de 100644 --- a/metric_providers/network/io/cgroup/container/Makefile +++ b/metric_providers/network/io/cgroup/container/Makefile @@ -1,6 +1,6 @@ CFLAGS = -O3 -Wall -lc -I../../../../../lib/c metric-provider-binary: source.c - gcc ../../../../../lib/c/parse_int.o $< $(CFLAGS) -o $@ + gcc ../../../../../lib/c/parse_int.o ../../../../../lib/c/detect_cgroup_path.o $< $(CFLAGS) -o $@ sudo chown root $@ sudo chmod u+s $@ \ No newline at end of file diff --git a/metric_providers/network/io/cgroup/container/provider.py b/metric_providers/network/io/cgroup/container/provider.py index a3e398a5e..0419333e6 100644 --- a/metric_providers/network/io/cgroup/container/provider.py +++ b/metric_providers/network/io/cgroup/container/provider.py @@ -4,7 +4,7 @@ from metric_providers.base import BaseMetricProvider class NetworkIoCgroupContainerProvider(BaseMetricProvider): - def __init__(self, resolution, rootless=False, skip_check=False): + def __init__(self, resolution, skip_check=False): super().__init__( metric_name='network_io_cgroup_container', metrics={'time': int, 'received_bytes': int, 'transmitted_bytes': int, 'container_id': str}, @@ -13,7 +13,6 @@ def __init__(self, resolution, rootless=False, skip_check=False): current_dir=os.path.dirname(os.path.abspath(__file__)), skip_check=skip_check, ) - self._rootless = rootless def read_metrics(self, run_id, containers=None): df = super().read_metrics(run_id, containers) diff --git a/metric_providers/network/io/cgroup/container/source.c b/metric_providers/network/io/cgroup/container/source.c index 20a02df7a..cdb55e2c8 100644 --- a/metric_providers/network/io/cgroup/container/source.c +++ b/metric_providers/network/io/cgroup/container/source.c @@ -11,11 +11,12 @@ #include #include #include "parse_int.h" +#include "detect_cgroup_path.h" #define DOCKER_CONTAINER_ID_BUFFER 65 // Docker container ID size is 64 + 1 byte for NUL termination typedef struct container_t { // struct is a specification and this static makes no sense here - char path[PATH_MAX]; + char* path; char id[DOCKER_CONTAINER_ID_BUFFER]; unsigned int pid; } container_t; @@ -123,7 +124,7 @@ static void output_stats(container_t *containers, int length) { usleep(msleep_time*1000); } -static int parse_containers(container_t** containers, char* containers_string, int rootless_mode) { +static int parse_containers(container_t** containers, char* containers_string) { if(containers_string == NULL) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); @@ -136,7 +137,6 @@ static int parse_containers(container_t** containers, char* containers_string, i } char *id = strtok(containers_string,","); int length = 0; - int match_result = 0; for (; id != NULL; id = strtok(NULL, ",")) { //printf("Token: %s\n", id); @@ -149,49 +149,31 @@ static int parse_containers(container_t** containers, char* containers_string, i strncpy((*containers)[length-1].id, id, DOCKER_CONTAINER_ID_BUFFER - 1); (*containers)[length-1].id[DOCKER_CONTAINER_ID_BUFFER - 1] = '\0'; - if(rootless_mode) { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/user.slice/user-%d.slice/user@%d.service/user.slice/docker-%s.scope/cgroup.procs", - user_id, user_id, id); - } else { - snprintf((*containers)[length-1].path, - PATH_MAX, - "/sys/fs/cgroup/system.slice/docker-%s.scope/cgroup.procs", - id); - } - FILE* fd = fopen((*containers)[length-1].path, "r"); // check for general readability only once - if ( fd == NULL) { - fprintf(stderr, "Error - cgroup.procs file %s failed to open: errno: %d\n", (*containers)[length-1].path, errno); + (*containers)[length-1].path = detect_cgroup_path("cgroup.procs", user_id, id); + FILE* fd = fopen((*containers)[length-1].path, "r"); + if (fd != NULL) { + int match_result = fscanf(fd, "%u", &(*containers)[length-1].pid); + if (match_result != 1) { + fprintf(stderr, "Could not match container PID\n"); exit(1); + } + fclose(fd); } - match_result = fscanf(fd, "%u", &(*containers)[length-1].pid); - if (match_result != 1) { - fprintf(stderr, "Could not match container PID\n"); - exit(1); - } - fclose(fd); } if(length == 0) { fprintf(stderr, "Please supply at least one container id with -s XXXX\n"); exit(1); } - return length; } -static int check_system(int rootless_mode) { +static int check_system() { const char* file_path_cgroup_procs; const char file_path_proc_net_dev[] = "/proc/net/dev"; int found_error = 0; - if(rootless_mode) { - file_path_cgroup_procs = "/sys/fs/cgroup/user.slice/cgroup.procs"; - } else { - file_path_cgroup_procs = "/sys/fs/cgroup/system.slice/cgroup.procs"; - } - + file_path_cgroup_procs = "/sys/fs/cgroup/cgroup.procs"; FILE* fd = fopen(file_path_cgroup_procs, "r"); if (fd == NULL) { fprintf(stderr, "Couldn't open cgroup.procs file at %s\n", file_path_cgroup_procs); @@ -219,7 +201,6 @@ int main(int argc, char **argv) { int c; int check_system_flag = 0; - int rootless_mode = 0; // docker root is default char *containers_string = NULL; // Dynamic buffer to store optarg container_t *containers = NULL; @@ -228,7 +209,6 @@ int main(int argc, char **argv) { static struct option long_options[] = { - {"rootless", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, {"interval", no_argument, NULL, 'i'}, {"containers", no_argument, NULL, 's'}, @@ -236,7 +216,7 @@ int main(int argc, char **argv) { {NULL, 0, NULL, 0} }; - while ((c = getopt_long(argc, argv, "ri:s:hc", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "i:s:hc", long_options, NULL)) != -1) { switch (c) { case 'h': printf("Usage: %s [-i msleep_time] [-h]\n\n",argv[0]); @@ -248,9 +228,6 @@ int main(int argc, char **argv) { case 'i': msleep_time = parse_int(optarg); break; - case 'r': - rootless_mode = 1; - break; case 's': containers_string = (char *)malloc(strlen(optarg) + 1); // Allocate memory if (!containers_string) { @@ -270,10 +247,10 @@ int main(int argc, char **argv) { } if(check_system_flag){ - exit(check_system(rootless_mode)); + exit(check_system()); } - int length = parse_containers(&containers, containers_string, rootless_mode); + int length = parse_containers(&containers, containers_string); while(1) { output_stats(containers, length); diff --git a/metric_providers/powermetrics/provider.py b/metric_providers/powermetrics/provider.py index 3d8284c48..ee33d80d3 100644 --- a/metric_providers/powermetrics/provider.py +++ b/metric_providers/powermetrics/provider.py @@ -41,7 +41,7 @@ def __init__(self, resolution, skip_check=False): ] - def check_system(self): + def check_system(self, check_command="default", check_error_message=None, check_parallel_provider=True): # no call to super().check_system() as we have different logic of finding the process if self._pm_process_count > 0: raise MetricProviderConfigurationError('Another instance of powermetrics is already running on the system!\n' diff --git a/metric_providers/psu/energy/ac/xgboost/machine/model b/metric_providers/psu/energy/ac/xgboost/machine/model index 5b7cc582e..3d44005a8 160000 --- a/metric_providers/psu/energy/ac/xgboost/machine/model +++ b/metric_providers/psu/energy/ac/xgboost/machine/model @@ -1 +1 @@ -Subproject commit 5b7cc582e749ee826fe45379cb1dbe1190a2bacf +Subproject commit 3d44005a8fe48d8e43d145d64002797ec44ea516 diff --git a/runner.py b/runner.py index 56c08fbd6..543c08f0c 100755 --- a/runner.py +++ b/runner.py @@ -498,10 +498,7 @@ def import_metric_providers(self): print(TerminalColors.WARNING, arrows('No metric providers were configured in config.yml. Was this intentional?'), TerminalColors.ENDC) return - docker_ps = subprocess.run(["docker", "info"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, encoding='UTF-8', check=True) - rootless = False - if 'rootless' in docker_ps.stdout: - rootless = True + subprocess.run(["docker", "info"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, encoding='UTF-8', check=True) for metric_provider in metric_providers: # will iterate over keys module_path, class_name = metric_provider.rsplit('.', 1) @@ -511,13 +508,7 @@ def import_metric_providers(self): print(f"Importing {class_name} from {module_path}") module = importlib.import_module(module_path) - if rootless and '.cgroup.' in module_path and self._skip_system_checks: - metric_provider_obj = getattr(module, class_name)(**conf, rootless=True, skip_check=True) - print(f"Configuration is {conf}; rootless=true, skip_check=True") - elif rootless and '.cgroup.' in module_path: - metric_provider_obj = getattr(module, class_name)(**conf, rootless=True) - print(f"Configuration is {conf}; rootless=true") - elif self._skip_system_checks: + if self._skip_system_checks: metric_provider_obj = getattr(module, class_name)(**conf, skip_check=True) print(f"Configuration is {conf}; skip_check=true") else: diff --git a/tests/test-config.yml b/tests/test-config.yml index 46ab25090..3a8fb721b 100644 --- a/tests/test-config.yml +++ b/tests/test-config.yml @@ -78,6 +78,12 @@ measurement: resolution: 99 memory.used.procfs.system.provider.MemoryUsedProcfsSystemProvider: resolution: 99 + cpu.utilization.cgroup.container.provider.CpuUtilizationCgroupContainerProvider: + resolution: 99 + memory.used.cgroup.container.provider.MemoryUsedCgroupContainerProvider: + resolution: 99 + network.io.cgroup.container.provider.NetworkIoCgroupContainerProvider: + resolution: 99 macos: cpu.utilization.mach.system.provider.CpuUtilizationMachSystemProvider: resolution: 99 diff --git a/tools/calibrate.py b/tools/calibrate.py index f60d96ded..6a800e1a1 100755 --- a/tools/calibrate.py +++ b/tools/calibrate.py @@ -78,7 +78,7 @@ def check_temperature_increase(total_seconds, desc, temp_mean, temp_std, temp_pr -def load_metric_providers(mp, pt_providers, provider_interval_override=None, rootless=False): +def load_metric_providers(mp, pt_providers, provider_interval_override=None): global metric_providers metric_providers = [] # reset @@ -88,9 +88,6 @@ def load_metric_providers(mp, pt_providers, provider_interval_override=None, roo module_path = f"metric_providers.{module_path}" conf = mp[metric_provider] or {} - if rootless and '.cgroup.' in module_path: - conf['rootless'] = True - logging.info(f"Importing {class_name} from {module_path}") if provider_interval_override: @@ -254,10 +251,7 @@ def check_configured_provider_energy_overhead(mp, energy_provider_key, idle_time client = docker.from_env() - is_rootless = any('rootless' in option for option in client.info()['SecurityOptions']) - logging.debug(f"Rootless mode is {is_rootless}") - - load_metric_providers(mp, mp, None, rootless=is_rootless) + load_metric_providers(mp, mp, None) # We need to start at least one container that just idles so we can also run the container providers