├── .clang-format ├── .clang-tidy ├── .dockerignore ├── .github ├── chglog │ ├── CHANGELOG.tpl.md │ └── config.yml ├── dependabot.yml └── workflows │ ├── build.yml │ ├── codeql-analysis.yml │ └── release.yml ├── .gitignore ├── CMakeLists.txt ├── CODEOWNERS ├── Dockerfile ├── LICENSE ├── README.md ├── cmake └── FindLibPFM.cmake ├── docker └── entrypoint.sh └── src ├── config.c ├── config.h ├── config_cli.c ├── config_cli.h ├── config_json.c ├── config_json.h ├── events.c ├── events.h ├── hwinfo.c ├── hwinfo.h ├── payload.c ├── payload.h ├── perf.c ├── perf.h ├── pmu.c ├── pmu.h ├── report.c ├── report.h ├── sensor.c ├── storage.c ├── storage.h ├── storage_csv.c ├── storage_csv.h ├── storage_mongodb.c ├── storage_mongodb.h ├── storage_null.c ├── storage_null.h ├── storage_socket.c ├── storage_socket.h ├── target.c ├── target.h ├── target_docker.c ├── target_docker.h ├── target_kubernetes.c ├── target_kubernetes.h ├── util.c ├── util.h └── version.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Webkit 3 | Language: Cpp 4 | ColumnLimit: 140 5 | SortIncludes: false 6 | AlignAfterOpenBracket: Align 7 | PointerAlignment: Right 8 | BinPackParameters: true 9 | BinPackArguments: true 10 | BreakStringLiterals: false 11 | AlwaysBreakAfterDefinitionReturnType: TopLevel 12 | AlwaysBreakAfterReturnType: TopLevel 13 | AlwaysBreakBeforeMultilineStrings: false 14 | SpaceAfterCStyleCast: true 15 | Cpp11BracedListStyle: true 16 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: '-*,bugprone-*,cert-*,clang-analyzer-*,misc-*,performance-*,portability-*,-bugprone-easily-swappable-parameters,-bugprone-unused-return-value,-cert-err33-c,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,-misc-include-cleaner' 3 | WarningsAsErrors: '*' 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # CMake common build paths: 2 | build/ 3 | localbuild/ 4 | -------------------------------------------------------------------------------- /.github/chglog/CHANGELOG.tpl.md: -------------------------------------------------------------------------------- 1 | {{ range .Versions }} 2 | 3 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }}) 4 | 5 | {{ range .CommitGroups -}} 6 | ### {{ .Title }} 7 | 8 | {{ range .Commits -}} 9 | * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ upperFirst .Subject }} 10 | {{ end }} 11 | {{ end -}} 12 | 13 | {{- if .RevertCommits -}} 14 | ### Reverts 15 | 16 | {{ range .RevertCommits -}} 17 | * {{ .Revert.Header }} 18 | {{ end }} 19 | {{ end -}} 20 | 21 | {{- if .NoteGroups -}} 22 | {{ range .NoteGroups -}} 23 | ### {{ .Title }} 24 | 25 | {{ range .Notes }} 26 | {{ upperFirst .Body }} 27 | {{ end }} 28 | {{ end -}} 29 | {{ end -}} 30 | {{ end -}} 31 | -------------------------------------------------------------------------------- /.github/chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/powerapi-ng/hwpc-sensor 6 | options: 7 | commits: 8 | filters: 9 | Type: 10 | - feat 11 | - fix 12 | - perf 13 | - refactor 14 | - style 15 | - docs 16 | - test 17 | - build 18 | - ci 19 | - chore 20 | commit_groups: 21 | title_maps: 22 | feat: Features 23 | fix: Bug Fixes 24 | perf: Performance Improvements 25 | refactor: Code Refactoring 26 | docs: Documentation 27 | test: Tests 28 | build: Build System 29 | ci: Continuous Integration 30 | chore: Miscellaneous Chores 31 | header: 32 | pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\/\\s]*)\\))?\\:\\s(.*)$" 33 | pattern_maps: 34 | - Type 35 | - Scope 36 | - Subject 37 | notes: 38 | keywords: 39 | - BREAKING CHANGE 40 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | 4 | - package-ecosystem: "github-actions" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | labels: 9 | - "dependencies" 10 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | BUILD_TYPE: Debug 11 | 12 | jobs: 13 | cmake-build: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | compiler: ["gcc", "clang"] 18 | 19 | steps: 20 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 21 | 22 | - name: Install dependencies 23 | run: | 24 | sudo apt-get update 25 | sudo apt-get install -y libczmq-dev libpfm4-dev libjson-c-dev libmongoc-dev 26 | 27 | - name: Configure CMake 28 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=${{matrix.compiler}} -DCMAKE_C_CLANG_TIDY=clang-tidy 29 | 30 | - name: Build 31 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 32 | 33 | container-build: 34 | runs-on: ubuntu-latest 35 | 36 | steps: 37 | - name: Setup Docker buildx 38 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 39 | 40 | - name: Build image 41 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 42 | with: 43 | push: false 44 | provenance: false 45 | load: true 46 | tags: localbuild/hwpc-sensor:sha-${{ github.sha }} -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | language: [ 'cpp' ] 21 | 22 | steps: 23 | - name: Checkout repository 24 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 25 | 26 | - name: Install dependencies 27 | run: | 28 | sudo apt-get update 29 | sudo apt-get install -y libczmq-dev libpfm4-dev libjson-c-dev libmongoc-dev 30 | 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 33 | with: 34 | languages: ${{ matrix.language }} 35 | queries: security-and-quality 36 | 37 | - name: Autobuild 38 | uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 39 | 40 | - name: Perform CodeQL Analysis 41 | uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: [ 'v*.*.*' ] 6 | 7 | jobs: 8 | 9 | docker-image: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | contents: read 13 | packages: write 14 | 15 | steps: 16 | - name: Setup Docker buildx 17 | uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 18 | 19 | - name: Log in to Docker Hub registry 20 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 21 | with: 22 | registry: docker.io 23 | username: ${{ vars.DOCKER_HUB_USERNAME }} 24 | password: ${{ secrets.DOCKER_HUB_TOKEN }} 25 | 26 | - name: Log in to GitHub Container Registry 27 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 28 | with: 29 | registry: ghcr.io 30 | username: ${{ github.actor }} 31 | password: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | - name: Extract Docker metadata 34 | id: meta 35 | uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 36 | with: 37 | images: | 38 | docker.io/powerapi/hwpc-sensor 39 | ghcr.io/powerapi-ng/hwpc-sensor 40 | tags: | 41 | type=semver,pattern={{version}} 42 | 43 | - name: Build and push Docker image 44 | uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 45 | id: build-and-push 46 | with: 47 | push: true 48 | provenance: false 49 | tags: ${{ steps.meta.outputs.tags }} 50 | labels: ${{ steps.meta.outputs.labels }} 51 | build-args: | 52 | BUILD_TYPE=Release 53 | 54 | github-release: 55 | name: Publish GitHub release 56 | runs-on: ubuntu-latest 57 | needs: docker-image 58 | permissions: 59 | contents: write 60 | env: 61 | CHGLOG_VERSION: "0.15.4" 62 | 63 | steps: 64 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 65 | with: 66 | fetch-depth: 0 67 | 68 | - name: Generate version changelog 69 | run: | 70 | set -euo pipefail 71 | export BASE_URL="https://github.com/git-chglog/git-chglog/releases/download" 72 | export FILENAME="git-chglog_${CHGLOG_VERSION}_linux_amd64.tar.gz" 73 | curl -fsSL "${BASE_URL}/v${CHGLOG_VERSION}/${FILENAME}" |sudo tar xz --no-same-owner -C /usr/local/bin git-chglog 74 | git-chglog --config .github/chglog/config.yml --output CHANGELOG.md "${GITHUB_REF_NAME}" 75 | 76 | - name: Create GitHub release 77 | uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2 78 | with: 79 | body_path: CHANGELOG.md 80 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Build dirs 55 | build/ 56 | localbuild/ 57 | dockerbuild/ 58 | 59 | # CMake 60 | compile_commands.json 61 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | project(hwpc-sensor LANGUAGES CXX) 3 | 4 | option(WITH_MONGODB "Build with support for MongoDB storage module" ON) 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 7 | 8 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 9 | 10 | add_compile_options(-Werror -Wall -Wextra -Wformat=2 -fstack-protector-strong -D_FORTIFY_SOURCE=2 -fPIE) 11 | add_link_options(-pie -Wl,-z,relro,-z,now -Wl,-z,noexecstack -Wl,-z,defs -Wl,--as-needed) 12 | 13 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 14 | # These warnings need to be suppressed temporarily, as the only fix is to rewrite the code in C++ 15 | add_compile_options(-Wno-deprecated -Wno-c99-designator -Wno-vla-cxx-extension) 16 | endif() 17 | 18 | if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 19 | add_compile_options(-Og -fsanitize=address,undefined -fno-omit-frame-pointer) 20 | add_link_options(-fsanitize=address,undefined) 21 | endif() 22 | 23 | set(SENSOR_SOURCES 24 | src/config_cli.c 25 | src/config_json.c 26 | src/config.c 27 | src/util.c 28 | src/target.c 29 | src/target_docker.c 30 | src/target_kubernetes.c 31 | src/pmu.c 32 | src/events.c 33 | src/hwinfo.c 34 | src/payload.c 35 | src/report.c 36 | src/perf.c 37 | src/storage.c 38 | src/storage_null.c 39 | src/storage_csv.c 40 | src/storage_socket.c 41 | src/sensor.c 42 | ) 43 | 44 | find_package(LibPFM REQUIRED) 45 | find_package(PkgConfig) 46 | pkg_check_modules(CZMQ REQUIRED libczmq) 47 | pkg_check_modules(JSONC REQUIRED json-c) 48 | 49 | if(WITH_MONGODB) 50 | pkg_check_modules(MONGOC REQUIRED libmongoc-1.0) 51 | list(APPEND SENSOR_SOURCES src/storage_mongodb.c) 52 | add_compile_definitions(HAVE_MONGODB) 53 | endif() 54 | 55 | if(DEFINED ENV{GIT_TAG} AND DEFINED ENV{GIT_REV}) 56 | add_compile_definitions(VERSION_GIT_TAG="$ENV{GIT_TAG}" VERSION_GIT_REV="$ENV{GIT_REV}") 57 | endif() 58 | 59 | add_executable(hwpc-sensor "${SENSOR_SOURCES}") 60 | 61 | foreach(src ${SENSOR_SOURCES}) 62 | set_source_files_properties(${src} PROPERTIES LANGUAGE CXX) 63 | endforeach() 64 | 65 | target_compile_features(hwpc-sensor PUBLIC cxx_std_20) 66 | set_target_properties(hwpc-sensor PROPERTIES CXX_EXTENSIONS OFF LINKER_LANGUAGE CXX) 67 | 68 | target_include_directories(hwpc-sensor SYSTEM PRIVATE "${LIBPFM_INCLUDE_DIRS}" "${CZMQ_INCLUDE_DIRS}" "${JSONC_INCLUDE_DIRS}" "${MONGOC_INCLUDE_DIRS}") 69 | target_link_libraries(hwpc-sensor "${LIBPFM_LIBRARIES}" "${CZMQ_LIBRARIES}" "${JSONC_LIBRARIES}" "${MONGOC_LIBRARIES}") 70 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default owner for everything in the repo. 2 | * @powerapi-ng/oss-maintainers -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # sensor builder image (build tools + development dependencies): 2 | FROM ubuntu:24.04 as sensor-builder 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ARG BUILD_TYPE=Debug 5 | ARG MONGODB_SUPPORT=ON 6 | RUN apt update && \ 7 | apt install -y build-essential git clang-tidy cmake pkg-config libczmq-dev libpfm4-dev libjson-c-dev libsystemd-dev uuid-dev && \ 8 | echo "${MONGODB_SUPPORT}" |grep -iq "on" && apt install -y libmongoc-dev || true 9 | COPY . /usr/src/hwpc-sensor 10 | RUN cd /usr/src/hwpc-sensor && \ 11 | GIT_TAG=$(git describe --tags --dirty 2>/dev/null || echo "unknown") \ 12 | GIT_REV=$(git rev-parse HEAD 2>/dev/null || echo "unknown") \ 13 | cmake -B build -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" -DCMAKE_C_CLANG_TIDY="clang-tidy" -DWITH_MONGODB="${MONGODB_SUPPORT}" && \ 14 | cmake --build build --parallel $(getconf _NPROCESSORS_ONLN) 15 | 16 | # sensor runner image (only runtime depedencies): 17 | FROM ubuntu:24.04 as sensor-runner 18 | ENV DEBIAN_FRONTEND=noninteractive 19 | ARG BUILD_TYPE=Debug 20 | ARG MONGODB_SUPPORT=ON 21 | ARG FILE_CAPABILITY=CAP_SYS_ADMIN 22 | RUN useradd -d /opt/powerapi -m powerapi && \ 23 | apt update && \ 24 | apt install -y libczmq4 libpfm4 libjson-c5 libcap2-bin && \ 25 | echo "${MONGODB_SUPPORT}" |grep -iq "on" && apt install -y libmongoc-1.0-0 || true && \ 26 | echo "${BUILD_TYPE}" |grep -iq "debug" && apt install -y libasan8 libubsan1 || true && \ 27 | rm -rf /var/lib/apt/lists/* 28 | COPY --from=sensor-builder /usr/src/hwpc-sensor/build/hwpc-sensor /usr/bin/hwpc-sensor 29 | RUN setcap "${FILE_CAPABILITY}+ep" /usr/bin/hwpc-sensor && \ 30 | setcap -v "${FILE_CAPABILITY}+ep" /usr/bin/hwpc-sensor 31 | COPY docker/entrypoint.sh /entrypoint.sh 32 | ENTRYPOINT ["/entrypoint.sh"] 33 | CMD ["--help"] 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, INRIA 4 | Copyright (c) 2018, University of Lille 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hardware Performance Counters (HwPC) Sensor 2 | This project is a lightweight sensor that monitor the Hardware Performance Counters of the cgroups (i.e. containers, systemd units...) running on a Linux system. 3 | 4 | # About 5 | This open-source project is maintained by the [PowerAPI consortium](https://github.com/powerapi-ng). 6 | To report bugs, submit feature ideas, or post feedback, please use the [Issues section](https://github.com/powerapi-ng/hwpc-sensor/issues) of this repo. 7 | 8 | The documentation is available [on the PowerAPI website](http://powerapi.org). 9 | 10 | # License 11 | This project is licensed under the [BSD-3-Clause License](https://opensource.org/licenses/BSD-3-Clause). -------------------------------------------------------------------------------- /cmake/FindLibPFM.cmake: -------------------------------------------------------------------------------- 1 | # CMake module for libpfm4 2 | # 3 | # The following variables will be set after configuration: 4 | # LIBPFM_FOUND 5 | # LIBPFM_LIBRARIES 6 | # LIBPFM_INCLUDE_DIRS 7 | # LIBPFM_VERSION 8 | 9 | include(FeatureSummary) 10 | include(FindPackageHandleStandardArgs) 11 | 12 | find_path(LIBPFM_INCLUDE_DIR NAMES perfmon/pfmlib.h) 13 | find_library(LIBPFM_LIBRARY NAMES pfm) 14 | 15 | # Follow symbolic link and try to extract library version from the .so file. 16 | get_filename_component(LIBPFM_LIBRARY_REALPATH "${LIBPFM_LIBRARY}" REALPATH) 17 | string(REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)$" LIBPFM_VERSION ${LIBPFM_LIBRARY_REALPATH}) 18 | 19 | find_package_handle_standard_args(LibPFM REQUIRED_VARS LIBPFM_LIBRARY LIBPFM_INCLUDE_DIR VERSION_VAR LIBPFM_VERSION) 20 | 21 | if (LIBPFM_FOUND) 22 | set(LIBPFM_LIBRARIES "pfm") 23 | set(LIBPFM_INCLUDE_DIRS "${LIBPFM_INCLUDE_DIR}") 24 | mark_as_advanced(LIBPFM_LIBRARIES LIBPFM_INCLUDE_DIRS LIBPFM_VERSION) 25 | endif() 26 | -------------------------------------------------------------------------------- /docker/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Check if the capabilities associated to the executable are available from the current Bounding set. 4 | # The Bounding set is used because the Permitted/Inheritable/Effective sets are cleared when we switch to an unprivileged user in the image. 5 | # 6 | REQUIRED_CAP=$(/sbin/getcap /usr/bin/hwpc-sensor |sed -e 's/.*\(cap_.*\)[+=].*$/\1/') 7 | REQUIRED_CAP_AVAILABLE=$([ -n "${REQUIRED_CAP}" ] && /sbin/capsh --print |grep "Bounding set =" |grep -iqv "${REQUIRED_CAP}" ; echo $?) 8 | if [ "${REQUIRED_CAP_AVAILABLE}" -eq 0 ]; then 9 | echo >&2 "ERROR: This program requires the '${REQUIRED_CAP^^}' capability to work." 10 | exit 1 11 | fi 12 | 13 | exec /usr/bin/hwpc-sensor "$@" 14 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "config.h" 42 | #include "events.h" 43 | #include "storage.h" 44 | 45 | 46 | struct config * 47 | config_create(void) 48 | { 49 | struct config *config = (struct config *) malloc(sizeof(struct config)); 50 | 51 | if (!config) 52 | return NULL; 53 | 54 | /* sensor default config */ 55 | config->sensor.verbose = 0; 56 | config->sensor.frequency = 1000; 57 | snprintf(config->sensor.cgroup_basepath, PATH_MAX, "%s", "/sys/fs/cgroup"); 58 | gethostname(config->sensor.name, HOST_NAME_MAX); 59 | 60 | /* storage default config */ 61 | config->storage.type = STORAGE_UNKNOWN; 62 | memset(&config->storage, 0, sizeof(struct config_storage)); 63 | 64 | /* events default config */ 65 | config->events.system = zhashx_new(); 66 | zhashx_set_duplicator(config->events.system, (zhashx_duplicator_fn *) events_group_dup); 67 | zhashx_set_destructor(config->events.system, (zhashx_destructor_fn *) events_group_destroy); 68 | 69 | config->events.containers = zhashx_new(); 70 | zhashx_set_duplicator(config->events.containers, (zhashx_duplicator_fn *) events_group_dup); 71 | zhashx_set_destructor(config->events.containers, (zhashx_destructor_fn *) events_group_destroy); 72 | 73 | return config; 74 | } 75 | 76 | static int 77 | check_cgroup_basepath(const char *cgroup_basepath) 78 | { 79 | struct statfs sfs; 80 | 81 | if (statfs(cgroup_basepath, &sfs)) { 82 | zsys_error("config: Failed to get cgroup basepath (%s) information: %s", cgroup_basepath, strerror(errno)); 83 | return -1; 84 | } 85 | 86 | if (sfs.f_type == CGROUP_SUPER_MAGIC) { 87 | return 0; 88 | } 89 | 90 | if (sfs.f_type == CGROUP2_SUPER_MAGIC) { 91 | return 0; 92 | } 93 | 94 | if (sfs.f_type == TMPFS_MAGIC) { 95 | zsys_warning("config: You are probably using a unified cgroupv2 basepath on a machine using the legacy cgroupv1 hierarchy!"); 96 | } 97 | 98 | zsys_error("config: Invalid cgroup basepath: %s", cgroup_basepath); 99 | return -1; 100 | } 101 | 102 | static int 103 | is_events_group_empty(zhashx_t *events_groups) 104 | { 105 | struct events_group *events_group = NULL; 106 | 107 | for (events_group = (struct events_group *) zhashx_first(events_groups); events_group; events_group = (struct events_group *) zhashx_next(events_groups)) { 108 | if (zlistx_size(events_group->events) == 0) { 109 | zsys_error("config: Events group '%s' is empty", events_group->name); 110 | return -1; 111 | } 112 | } 113 | 114 | return 0; 115 | } 116 | 117 | int 118 | config_validate(struct config *config) 119 | { 120 | const struct config_sensor *sensor = &config->sensor; 121 | const struct config_storage *storage = &config->storage; 122 | const struct config_events *events = &config->events; 123 | 124 | if (!strlen(sensor->name)) { 125 | zsys_error("config: You must provide a sensor name"); 126 | return -1; 127 | } 128 | 129 | if (!strlen(sensor->cgroup_basepath)) { 130 | zsys_error("config: You must provide a cgroup basepath"); 131 | return -1; 132 | } 133 | 134 | if (check_cgroup_basepath(sensor->cgroup_basepath)) { 135 | return -1; 136 | } 137 | 138 | if (zhashx_size(events->system) == 0 && zhashx_size(events->containers) == 0) { 139 | zsys_error("config: You must provide event(s) to monitor"); 140 | return -1; 141 | } 142 | 143 | if (is_events_group_empty(events->system) || is_events_group_empty(events->containers)) { 144 | return -1; 145 | } 146 | 147 | if (storage->type == STORAGE_CSV && !strlen(storage->csv.outdir)) { 148 | zsys_error("config: CSV storage module requires the 'outdir' parameter to be set"); 149 | return -1; 150 | } 151 | 152 | if (storage->type == STORAGE_SOCKET && (!strlen(storage->socket.hostname) || !strlen(storage->socket.port))) { 153 | zsys_error("config: Socket storage module requires the 'host' and 'port' parameters to be set"); 154 | return -1; 155 | } 156 | 157 | #ifdef HAVE_MONGODB 158 | if (storage->type == STORAGE_MONGODB && (!strlen(storage->mongodb.uri) || !strlen(storage->mongodb.database) || !strlen(storage->mongodb.collection))) { 159 | zsys_error("config: MongoDB storage module requires the 'uri', 'database' and 'collection' parameters to be set"); 160 | return -1; 161 | } 162 | #endif 163 | 164 | return 0; 165 | } 166 | 167 | void 168 | config_destroy(struct config *config) 169 | { 170 | if (!config) 171 | return; 172 | 173 | zhashx_destroy(&config->events.containers); 174 | zhashx_destroy(&config->events.system); 175 | 176 | free(config); 177 | } 178 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef CONFIG_H 33 | #define CONFIG_H 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | #include "events.h" 40 | #include "storage.h" 41 | 42 | /* 43 | * config_sensor stores sensor specific config. 44 | */ 45 | struct config_sensor 46 | { 47 | unsigned int verbose; 48 | unsigned int frequency; 49 | char cgroup_basepath[PATH_MAX]; 50 | char name[HOST_NAME_MAX]; 51 | }; 52 | 53 | /* 54 | * config_storage stores storage specific config. 55 | */ 56 | struct config_storage 57 | { 58 | enum storage_type type; 59 | union { 60 | struct { 61 | char outdir[PATH_MAX]; 62 | } csv; 63 | 64 | struct { 65 | char hostname[HOST_NAME_MAX]; 66 | char port[NI_MAXSERV]; 67 | } socket; 68 | 69 | #ifdef HAVE_MONGODB 70 | struct { 71 | char uri[PATH_MAX]; 72 | char database[NAME_MAX]; 73 | char collection[NAME_MAX]; 74 | } mongodb; 75 | #endif 76 | }; 77 | }; 78 | 79 | /* 80 | * config_events stores events specific config. 81 | */ 82 | struct config_events 83 | { 84 | zhashx_t *system; /* char *group_name -> struct events_group *group */ 85 | zhashx_t *containers; /* char *group_name -> struct events_group *group */ 86 | }; 87 | 88 | /* 89 | * config stores the application configuration. 90 | */ 91 | struct config 92 | { 93 | struct config_sensor sensor; 94 | struct config_storage storage; 95 | struct config_events events; 96 | }; 97 | 98 | /* 99 | * config_create allocate the required resources and setup the default config. 100 | */ 101 | struct config *config_create(void); 102 | 103 | /* 104 | * config_validate check the validity of the given config. 105 | */ 106 | int config_validate(struct config *config); 107 | 108 | /* 109 | * config_destroy free the allocated memory for the storage of the global config. 110 | */ 111 | void config_destroy(struct config *config); 112 | 113 | #endif /* CONFIG_H */ 114 | -------------------------------------------------------------------------------- /src/config_cli.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Inria 3 | * Copyright (c) 2024, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "config_cli.h" 39 | #include "config_json.h" 40 | #include "util.h" 41 | 42 | 43 | const char short_opts[] = "x:vf:p:n:s:c:e:or:U:D:C:P:"; 44 | static struct option long_opts[] = { 45 | {"config-file", required_argument, 0, 'x'}, 46 | {NULL, 0, NULL, 0} 47 | }; 48 | 49 | static int 50 | setup_config_from_file(struct config *config, char *filepath) 51 | { 52 | char *file_extension = NULL; 53 | 54 | errno = 0; 55 | if (access(filepath, R_OK) == -1) { 56 | zsys_error("config: cli: Unable to access configuration file: %s", strerror(errno)); 57 | return -1; 58 | } 59 | 60 | file_extension = strrchr(filepath, '.'); 61 | if (!file_extension) { 62 | zsys_error("config: cli: Missing extension to configuration file"); 63 | return -1; 64 | } 65 | 66 | if (strcasecmp(file_extension, ".json") == 0) { 67 | return config_setup_from_json_file(config, filepath); 68 | } 69 | 70 | zsys_error("config: cli: Unsupported configuration file format: %s", file_extension); 71 | return -1; 72 | } 73 | 74 | static int 75 | setup_cgroup_basepath(struct config *config, const char *cgroup_basepath) 76 | { 77 | if (snprintf(config->sensor.cgroup_basepath, PATH_MAX, "%s", cgroup_basepath) >= PATH_MAX) { 78 | zsys_error("config: cli: Cgroup basepath is too long"); 79 | return -1; 80 | } 81 | 82 | return 0; 83 | } 84 | 85 | static int 86 | setup_sensor_name(struct config *config, const char *sensor_name) 87 | { 88 | if (snprintf(config->sensor.name, HOST_NAME_MAX, "%s", sensor_name) >= HOST_NAME_MAX) { 89 | zsys_error("config: cli: Sensor name is too long"); 90 | return -1; 91 | } 92 | 93 | return 0; 94 | } 95 | 96 | static int 97 | setup_frequency(struct config *config, const char *value_str) 98 | { 99 | unsigned int frequency; 100 | 101 | if (str_to_uint(value_str, &frequency)) { 102 | zsys_error("config: cli: Frequency value is invalid"); 103 | return -1; 104 | } 105 | 106 | config->sensor.frequency = frequency; 107 | return 0; 108 | } 109 | 110 | static int 111 | setup_global_events_group(struct config *config, const char *group_name) 112 | { 113 | struct events_group *events_group = NULL; 114 | 115 | events_group = events_group_create(group_name); 116 | if (!events_group) { 117 | zsys_error("config: cli: Failed to create '%s' global events group"); 118 | return -1; 119 | } 120 | 121 | zhashx_insert(config->events.system, group_name, events_group); 122 | events_group_destroy(&events_group); /* The events group are duplicated on insert */ 123 | return 0; 124 | } 125 | 126 | static int 127 | setup_cgroups_events_group(struct config *config, const char *group_name) 128 | { 129 | struct events_group *events_group = NULL; 130 | 131 | events_group = events_group_create(group_name); 132 | if (!events_group) { 133 | zsys_error("config: cli: Failed to create '%s' cgroups events group", group_name); 134 | return -1; 135 | } 136 | 137 | zhashx_insert(config->events.containers, group_name, events_group); 138 | events_group_destroy(&events_group); /* The events group are duplicated on insert */ 139 | return 0; 140 | } 141 | 142 | static int 143 | setup_cgroups_events_group_type(struct events_group *events_group, enum events_group_monitoring_type type) 144 | { 145 | if (!events_group) { 146 | zsys_error("config: cli: No events group defined before setting type"); 147 | return -1; 148 | } 149 | 150 | events_group->type = type; 151 | return 0; 152 | } 153 | 154 | static int 155 | append_event_to_events_group(struct events_group *events_group, const char *event_name) 156 | { 157 | if (!events_group) { 158 | zsys_error("config: cli: No events group defined for event: %s", event_name); 159 | return -1; 160 | } 161 | 162 | if (events_group_append_event(events_group, event_name)) { 163 | zsys_error("config: cli: Failed to add event '%s' to group '%s'", optarg, events_group->name); 164 | return -1; 165 | } 166 | return 0; 167 | } 168 | 169 | static int 170 | setup_storage_module(struct config *config, const char *module_name) 171 | { 172 | enum storage_type type; 173 | 174 | type = storage_module_get_type(module_name); 175 | if (type == STORAGE_UNKNOWN) { 176 | zsys_error("config: cli: Storage module '%s' is invalid or disabled at compile time", module_name); 177 | return -1; 178 | } 179 | 180 | config->storage.type = type; 181 | return 0; 182 | } 183 | 184 | static int 185 | setup_storage_csv_parameters(struct config *config, int opt, const char *value) 186 | { 187 | switch (opt) 188 | { 189 | case 'U': /* Output directory path */ 190 | if (snprintf(config->storage.csv.outdir, PATH_MAX, "%s", value) >= PATH_MAX) { 191 | zsys_error("config: cli: CSV output directory path is too long"); 192 | return -1; 193 | } 194 | break; 195 | 196 | default: 197 | return -1; 198 | } 199 | 200 | return 0; 201 | } 202 | 203 | static int 204 | setup_storage_socket_parameters(struct config *config, int opt, const char *value) 205 | { 206 | switch (opt) 207 | { 208 | case 'U': /* Destination IP/hostname */ 209 | if (snprintf(config->storage.socket.hostname, HOST_NAME_MAX, "%s", value) >= HOST_NAME_MAX) { 210 | zsys_error("config: cli: Socket output host is too long"); 211 | return -1; 212 | } 213 | break; 214 | 215 | case 'P': /* Destination port number */ 216 | if (snprintf(config->storage.socket.port, NI_MAXSERV, "%s", value) >= NI_MAXSERV) { 217 | zsys_error("config: cli: Socket output port is too long"); 218 | return -1; 219 | } 220 | break; 221 | 222 | default: 223 | return -1; 224 | } 225 | 226 | return 0; 227 | } 228 | 229 | #ifdef HAVE_MONGODB 230 | static int 231 | setup_storage_mongodb_parameters(struct config *config, int opt, const char *value) 232 | { 233 | switch (opt) 234 | { 235 | case 'U': /* MongoDB URI (mongodb://x) */ 236 | if (snprintf(config->storage.mongodb.uri, PATH_MAX, "%s", value) >= PATH_MAX) { 237 | zsys_error("config: cli: MongoDB URI is too long"); 238 | return -1; 239 | } 240 | break; 241 | 242 | case 'D': /* MongoDB Database name */ 243 | if (snprintf(config->storage.mongodb.database, NAME_MAX, "%s", value) >= NAME_MAX) { 244 | zsys_error("config: cli: MongoDB database name is too long"); 245 | return -1; 246 | } 247 | break; 248 | 249 | case 'C': /* MongoDB Collection name */ 250 | if (snprintf(config->storage.mongodb.collection, NAME_MAX, "%s", value) >= NAME_MAX) { 251 | zsys_error("config: cli: MongoDB collection name is too long"); 252 | return -1; 253 | } 254 | break; 255 | 256 | default: 257 | return -1; 258 | } 259 | 260 | return 0; 261 | } 262 | #endif 263 | 264 | static int 265 | setup_storage_parameters(struct config *config, int opt, const char *value) 266 | { 267 | switch (config->storage.type) 268 | { 269 | case STORAGE_NULL: 270 | return 0; /* Ignore parameters */ 271 | 272 | case STORAGE_CSV: 273 | return setup_storage_csv_parameters(config, opt, value); 274 | 275 | case STORAGE_SOCKET: 276 | return setup_storage_socket_parameters(config, opt, value); 277 | 278 | #ifdef HAVE_MONGODB 279 | case STORAGE_MONGODB: 280 | return setup_storage_mongodb_parameters(config, opt, value); 281 | #endif 282 | 283 | default: 284 | return -1; 285 | } 286 | } 287 | 288 | int 289 | config_setup_from_cli(int argc, char **argv, struct config *config) 290 | { 291 | int option_index = 0; 292 | int opt = -1; 293 | struct events_group *current_events_group = NULL; 294 | 295 | opterr = 0; /* Disable getopt error messages */ 296 | 297 | while ((opt = getopt_long(argc, argv, short_opts, long_opts, &option_index)) != -1) { 298 | switch (opt) 299 | { 300 | case 'x': 301 | if (setup_config_from_file(config, optarg)) { 302 | return -1; 303 | } 304 | break; 305 | 306 | case 'v': 307 | config->sensor.verbose++; 308 | break; 309 | 310 | case 'p': 311 | if (setup_cgroup_basepath(config, optarg)) { 312 | return -1; 313 | } 314 | break; 315 | 316 | case 'n': 317 | if (setup_sensor_name(config, optarg)) { 318 | return -1; 319 | } 320 | break; 321 | 322 | case 'f': 323 | if (setup_frequency(config, optarg)) { 324 | return -1; 325 | } 326 | break; 327 | 328 | case 's': 329 | if (setup_global_events_group(config, optarg)) { 330 | return -1; 331 | } 332 | current_events_group = (struct events_group *) zhashx_lookup(config->events.system, optarg); 333 | break; 334 | 335 | case 'c': 336 | if (setup_cgroups_events_group(config, optarg)) { 337 | return -1; 338 | } 339 | current_events_group = (struct events_group *) zhashx_lookup(config->events.containers, optarg); 340 | break; 341 | 342 | case 'o': 343 | if (setup_cgroups_events_group_type(current_events_group, MONITOR_ONE_CPU_PER_SOCKET)) { 344 | return -1; 345 | } 346 | break; 347 | 348 | case 'e': 349 | if (append_event_to_events_group(current_events_group, optarg)) { 350 | return -1; 351 | } 352 | break; 353 | 354 | case 'r': 355 | if (setup_storage_module(config, optarg)) { 356 | return -1; 357 | } 358 | break; 359 | 360 | case 'U': 361 | case 'D': 362 | case 'C': 363 | case 'P': 364 | if (setup_storage_parameters(config, opt, optarg)) { 365 | return -1; 366 | } 367 | break; 368 | 369 | default: 370 | zsys_error("config: Argument '%c' is unknown", optopt); 371 | return -1; 372 | } 373 | } 374 | 375 | return 0; 376 | } 377 | -------------------------------------------------------------------------------- /src/config_cli.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Inria 3 | * Copyright (c) 2024, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef CONFIG_CLI_H 33 | #define CONFIG_CLI_H 34 | 35 | #include "config.h" 36 | 37 | /* 38 | * config_setup_from_cli setup the given global config from the command line arguments. 39 | */ 40 | int config_setup_from_cli(int argc, char **argv, struct config *config); 41 | 42 | #endif /* CONFIG_CLI_H */ 43 | -------------------------------------------------------------------------------- /src/config_json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Inria 3 | * Copyright (c) 2024, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "config_json.h" 38 | #include "util.h" 39 | 40 | /* 41 | * JSON_FILE_BUFFER_SIZE is the size of the buffer where the content of the json config file will be stored. 42 | */ 43 | #define JSON_FILE_BUFFER_SIZE 4096 44 | 45 | 46 | static int 47 | setup_verbose(struct config *config, json_object *verbose_obj) 48 | { 49 | int verbose; 50 | 51 | errno = 0; 52 | verbose = json_object_get_int(verbose_obj); 53 | if (errno != 0 || verbose < 0) { 54 | zsys_error("config: json: Verbose value is invalid (boolean or positive integer expected)"); 55 | return -1; 56 | } 57 | 58 | config->sensor.verbose = (unsigned int) verbose; 59 | return 0; 60 | } 61 | 62 | static int 63 | setup_cgroup_basepath(struct config *config, json_object *cgroup_basepath_obj) 64 | { 65 | const char *cgroup_basepath = NULL; 66 | 67 | cgroup_basepath = json_object_get_string(cgroup_basepath_obj); 68 | if (snprintf(config->sensor.cgroup_basepath, PATH_MAX, "%s", cgroup_basepath) >= PATH_MAX) { 69 | zsys_error("config: json: Cgroup basepath is too long"); 70 | return -1; 71 | } 72 | 73 | return 0; 74 | } 75 | 76 | static int 77 | setup_sensor_name(struct config *config, json_object *sensor_name_obj) 78 | { 79 | const char *sensor_name = NULL; 80 | 81 | sensor_name = json_object_get_string(sensor_name_obj); 82 | if (snprintf(config->sensor.name, HOST_NAME_MAX, "%s", sensor_name) >= HOST_NAME_MAX) { 83 | zsys_error("config: json: Sensor name is too long"); 84 | return -1; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | static int 91 | setup_frequency(struct config *config, json_object *frequency_obj) 92 | { 93 | int frequency; 94 | 95 | errno = 0; 96 | frequency = json_object_get_int(frequency_obj); 97 | if (errno != 0 || frequency < 0) { 98 | zsys_error("config: json: Frequency value is invalid (positive integer expected)"); 99 | return -1; 100 | } 101 | 102 | config->sensor.frequency = (unsigned int) frequency; 103 | return 0; 104 | } 105 | 106 | static int 107 | setup_storage_type(struct config *config, json_object *storage) 108 | { 109 | json_object *storage_type_obj = NULL; 110 | const char *storage_module_name = NULL; 111 | enum storage_type type; 112 | 113 | if (!json_object_object_get_ex(storage, "type", &storage_type_obj)) { 114 | zsys_error("config: json: The storage module 'type' field is required"); 115 | return -1; 116 | } 117 | 118 | storage_module_name = json_object_get_string(storage_type_obj); 119 | type = storage_module_get_type(storage_module_name); 120 | if (type == STORAGE_UNKNOWN) { 121 | zsys_error("config: json: Storage module '%s' is invalid or disabled at compile time", storage_module_name); 122 | return -1; 123 | } 124 | 125 | config->storage.type = type; 126 | return 0; 127 | } 128 | 129 | static int 130 | setup_storage_null_parameters(struct config *config __attribute__((unused)), json_object *storage_obj) 131 | { 132 | json_object_object_foreach(storage_obj, key, value) { 133 | if (!strcasecmp(key, "type")) { 134 | continue; 135 | } 136 | else { 137 | zsys_error("config: json: Invalid parameter '%s' for Null storage module", key); 138 | return -1; 139 | } 140 | } 141 | 142 | return 0; 143 | } 144 | 145 | static int 146 | setup_storage_csv_parameters(struct config *config, json_object *storage_obj) 147 | { 148 | const char *output_dir = NULL; 149 | 150 | json_object_object_foreach(storage_obj, key, value) { 151 | if (!strcasecmp(key, "type")) { 152 | continue; /* This field have already been processed */ 153 | } 154 | if (!strcasecmp(key, "directory") || !strcasecmp(key, "outdir")) { 155 | output_dir = json_object_get_string(value); 156 | if (snprintf(config->storage.csv.outdir, PATH_MAX, "%s", output_dir) >= PATH_MAX) { 157 | zsys_error("config: json: CSV output directory path is too long"); 158 | return -1; 159 | } 160 | } 161 | else { 162 | zsys_error("config: json: Invalid parameter '%s' for CSV storage module", key); 163 | return -1; 164 | } 165 | } 166 | 167 | return 0; 168 | } 169 | 170 | static int 171 | setup_storage_socket_parameters(struct config *config, json_object *storage_obj) 172 | { 173 | const char *host = NULL; 174 | const char *port = NULL; 175 | 176 | json_object_object_foreach(storage_obj, key, value) { 177 | if (!strcasecmp(key, "type")) { 178 | continue; /* This field have already been processed */ 179 | } 180 | else if (!strcasecmp(key, "uri") || !strcasecmp(key, "host")) { 181 | host = json_object_get_string(value); 182 | if (snprintf(config->storage.socket.hostname, HOST_NAME_MAX, "%s", host) >= HOST_NAME_MAX) { 183 | zsys_error("config: json: Socket output host is too long"); 184 | return -1; 185 | } 186 | } 187 | else if (!strcasecmp(key, "port")) { 188 | port = json_object_get_string(value); 189 | if (snprintf(config->storage.socket.port, NI_MAXSERV, "%s", port) >= NI_MAXSERV) { 190 | zsys_error("config: json: Socket output port is too long"); 191 | return -1; 192 | } 193 | } 194 | else { 195 | zsys_error("config: json: Invalid parameter '%s' for Socket storage module", key); 196 | return -1; 197 | } 198 | } 199 | 200 | return 0; 201 | } 202 | 203 | #ifdef HAVE_MONGODB 204 | static int 205 | setup_storage_mongodb_parameters(struct config *config, json_object *storage_obj) 206 | { 207 | const char *uri = NULL; 208 | const char *database = NULL; 209 | const char *collection = NULL; 210 | 211 | json_object_object_foreach(storage_obj, key, value) { 212 | if (!strcasecmp(key, "type")) { 213 | continue; /* This field have already been processed */ 214 | } 215 | else if (!strcasecmp(key, "uri")) { 216 | uri = json_object_get_string(value); 217 | if (snprintf(config->storage.mongodb.uri, PATH_MAX, "%s", uri) >= PATH_MAX) { 218 | zsys_error("config: json: MongoDB URI is too long"); 219 | return -1; 220 | } 221 | } 222 | else if (!strcasecmp(key, "database")) { 223 | database = json_object_get_string(value); 224 | if (snprintf(config->storage.mongodb.database, NAME_MAX, "%s", database) >= NAME_MAX) { 225 | zsys_error("config: json: MongoDB database name is too long"); 226 | return -1; 227 | } 228 | } 229 | else if (!strcasecmp(key, "collection")) { 230 | collection = json_object_get_string(value); 231 | if (snprintf(config->storage.mongodb.collection, NAME_MAX, "%s", collection) >= NAME_MAX) { 232 | zsys_error("config: json: MongoDB collection name is too long"); 233 | return -1; 234 | } 235 | } 236 | else { 237 | zsys_error("config: json: Invalid parameter '%s' for MongoDB storage module", key); 238 | return -1; 239 | } 240 | } 241 | 242 | return 0; 243 | } 244 | #endif 245 | 246 | static int 247 | handle_storage_parameters(struct config *config, json_object *storage_obj) 248 | { 249 | /* 250 | * Each storage module is configured with its own set of fields. 251 | * It is therefore required to know the storage type before processing any field. 252 | */ 253 | if (setup_storage_type(config, storage_obj)) { 254 | return -1; 255 | } 256 | 257 | switch (config->storage.type) 258 | { 259 | case STORAGE_NULL: 260 | return setup_storage_null_parameters(config, storage_obj); 261 | 262 | case STORAGE_CSV: 263 | return setup_storage_csv_parameters(config, storage_obj); 264 | 265 | case STORAGE_SOCKET: 266 | return setup_storage_socket_parameters(config, storage_obj); 267 | 268 | #ifdef HAVE_MONGODB 269 | case STORAGE_MONGODB: 270 | return setup_storage_mongodb_parameters(config, storage_obj); 271 | #endif 272 | 273 | default: 274 | return -1; 275 | } 276 | } 277 | 278 | static int 279 | setup_perf_events_group_events(struct events_group *events_group, json_object *events_group_obj) 280 | { 281 | const char *event_name = NULL; 282 | 283 | if (!json_object_is_type(events_group_obj, json_type_array)) { 284 | zsys_error("config: json: Invalid 'events' field type for group '%s'", events_group->name); 285 | return -1; 286 | } 287 | 288 | for (size_t i = 0; i < json_object_array_length(events_group_obj); i++) { 289 | event_name = json_object_get_string(json_object_array_get_idx(events_group_obj, i)); 290 | if (events_group_append_event(events_group, event_name)) { 291 | zsys_error("config: json: Failed to add event '%s' to group '%s'", event_name, events_group->name); 292 | return -1; 293 | } 294 | } 295 | 296 | return 0; 297 | } 298 | 299 | static int 300 | setup_perf_events_group_mode(struct events_group *events_group, json_object *mode_obj) 301 | { 302 | const char *mode = NULL; 303 | 304 | mode = json_object_get_string(mode_obj); 305 | if (!strcasecmp(mode, "MONITOR_ONE_CPU_PER_SOCKET") || !strcasecmp(mode, "ONE_CPU_PER_SOCKET")) { 306 | events_group->type = MONITOR_ONE_CPU_PER_SOCKET; 307 | return 0; 308 | } 309 | if (!strcasecmp(mode, "MONITOR_ALL_CPU_PER_SOCKET") || !strcasecmp(mode, "ALL_CPU_PER_SOCKET")) { 310 | events_group->type = MONITOR_ALL_CPU_PER_SOCKET; 311 | return 0; 312 | } 313 | 314 | zsys_error("config: json: Invalid monitoring mode '%s' for events group '%s'", mode, events_group->name); 315 | return -1; 316 | } 317 | 318 | static int 319 | handle_perf_events_group_parameters(const char *events_group_name, json_object *events_group_obj, zhashx_t *events_groups) 320 | { 321 | int ret = -1; 322 | struct events_group *events_group = NULL; 323 | 324 | events_group = events_group_create(events_group_name); 325 | if (!events_group) { 326 | zsys_error("config: json: Failed to create '%s' events group", events_group_name); 327 | return -1; 328 | } 329 | 330 | json_object_object_foreach(events_group_obj, key, value) { 331 | if (!strcasecmp(key, "events")) { 332 | if (setup_perf_events_group_events(events_group, value)) { 333 | goto cleanup; 334 | } 335 | } 336 | else if (!strcasecmp(key, "monitoring_type") || !strcasecmp(key, "mode")) { 337 | if (setup_perf_events_group_mode(events_group, value)) { 338 | goto cleanup; 339 | } 340 | } 341 | else { 342 | zsys_error("config: json: Invalid parameter '%s' for '%s' events group", key, events_group); 343 | goto cleanup; 344 | } 345 | } 346 | 347 | ret = 0; 348 | zhashx_insert(events_groups, events_group_name, events_group); 349 | 350 | cleanup: 351 | events_group_destroy(&events_group); /* The events group are duplicated on insert */ 352 | return ret; 353 | } 354 | 355 | static int 356 | handle_perf_events_groups(json_object *events_groups_obj, zhashx_t *events_groups) 357 | { 358 | json_object_object_foreach(events_groups_obj, key, value) { 359 | if (handle_perf_events_group_parameters(key, value, events_groups)) { 360 | return -1; 361 | } 362 | } 363 | 364 | return 0; 365 | } 366 | 367 | static int 368 | process_json_fields(struct config *config, json_object *root) 369 | { 370 | json_object_object_foreach(root, key, value) { 371 | if (!strcasecmp(key, "verbose")) { 372 | if (setup_verbose(config, value)) { 373 | return -1; 374 | } 375 | } 376 | else if (!strcasecmp(key, "name") || !strcasecmp(key, "sensor-name")) { 377 | if (setup_sensor_name(config, value)) { 378 | return -1; 379 | } 380 | } 381 | else if (!strcasecmp(key, "cgroup_basepath") || !strcasecmp(key, "cgroup-basepath")) { 382 | if (setup_cgroup_basepath(config, value)) { 383 | return -1; 384 | } 385 | } 386 | else if (!strcasecmp(key, "frequency")) { 387 | if (setup_frequency(config, value)) { 388 | return -1; 389 | } 390 | } 391 | else if (!strcasecmp(key, "output") || !strcasecmp(key, "storage")) { 392 | if (handle_storage_parameters(config, value)) { 393 | return -1; 394 | } 395 | } 396 | else if (!strcasecmp(key, "system") || !strcasecmp(key, "global")) { 397 | if (handle_perf_events_groups(value, config->events.system)) { 398 | return -1; 399 | } 400 | } 401 | else if (!strcasecmp(key, "container") || !strcasecmp(key, "cgroups")) { 402 | if (handle_perf_events_groups(value, config->events.containers)) { 403 | return -1; 404 | } 405 | } 406 | else { 407 | zsys_error("config: json: Unknown parameter: '%s'", key); 408 | return -1; 409 | } 410 | } 411 | 412 | return 0; 413 | } 414 | 415 | static int 416 | read_file_content(int fd, char *buffer, size_t buffer_size) 417 | { 418 | struct stat sb; 419 | ssize_t read_bytes = 0; 420 | 421 | if (fstat(fd, &sb) == -1) { 422 | zsys_error("config: json: Failed to get configuration file information: %s", strerror(errno)); 423 | return -1; 424 | } 425 | 426 | if (!S_ISREG(sb.st_mode)) { 427 | zsys_error("config: json: Configuration file is not a regular file"); 428 | return -1; 429 | } 430 | 431 | if (sb.st_size == 0) { 432 | zsys_error("config: json: Configuration file is empty"); 433 | return -1; 434 | } 435 | 436 | if (sb.st_size >= (off_t) buffer_size) { 437 | zsys_error("config: json: Configuration file size is too big (current: %lu KB, max: %lu KB)", sb.st_size / 1024, buffer_size / 1024); 438 | return -1; 439 | } 440 | 441 | read_bytes = read(fd, buffer, buffer_size - 1); 442 | if (read_bytes == -1) { 443 | zsys_error("config: json: Failed to read the configuration file: %s", strerror(errno)); 444 | return -1; 445 | } 446 | 447 | buffer[read_bytes] = '\0'; 448 | return 0; 449 | } 450 | 451 | static void 452 | compute_current_position_from_offset(const char *str, size_t target_offset, size_t *line, size_t *column) 453 | { 454 | *line = 1; 455 | *column = 1; 456 | for (size_t current_offset = 0; current_offset < target_offset; current_offset++) 457 | { 458 | if (str[current_offset] == '\n') { 459 | (*line)++; 460 | *column = 1; 461 | } 462 | else { 463 | (*column)++; 464 | } 465 | } 466 | } 467 | 468 | static int 469 | parse_json_configuration_file_from_fd(int fd, json_object **obj) 470 | { 471 | int ret = -1; 472 | char buffer[JSON_FILE_BUFFER_SIZE] = {}; 473 | json_tokener *tok = NULL; 474 | enum json_tokener_error jerr; 475 | size_t line, column; 476 | 477 | if (read_file_content(fd, buffer, JSON_FILE_BUFFER_SIZE)) { 478 | goto error_read_content; 479 | } 480 | 481 | tok = json_tokener_new(); 482 | if (!tok) { 483 | zsys_error("config: json: Failed to initialize json tokener"); 484 | goto error_init_tokener; 485 | } 486 | 487 | *obj = json_tokener_parse_ex(tok, buffer, (int) strlen(buffer) + 1); 488 | jerr = json_tokener_get_error(tok); 489 | 490 | if (jerr != json_tokener_success) { 491 | compute_current_position_from_offset(buffer, json_tokener_get_parse_end(tok), &line, &column); 492 | zsys_error("config: json: Failed to parse json: %s (line: %lu, column: %lu)", json_tokener_error_desc(jerr), line, column); 493 | goto error_tokener_parse; 494 | } 495 | 496 | ret = 0; 497 | 498 | error_tokener_parse: 499 | json_tokener_free(tok); 500 | error_read_content: 501 | error_init_tokener: 502 | return ret; 503 | } 504 | 505 | int 506 | config_setup_from_json_file(struct config *config, const char *filepath) 507 | { 508 | int ret = -1; 509 | int fd = -1; 510 | json_object *root = NULL; 511 | 512 | fd = open(filepath, O_RDONLY); 513 | if (fd < 0) { 514 | zsys_error("config: json: Failed to open configuration file: %s", strerror(errno)); 515 | goto error_open_file; 516 | } 517 | 518 | if (parse_json_configuration_file_from_fd(fd, &root)) { 519 | goto error_parse_file; 520 | } 521 | 522 | if (process_json_fields(config, root)) { 523 | zsys_error("config: json: Failed to process the given configuration file"); 524 | goto error_process_fields; 525 | } 526 | 527 | ret = 0; 528 | 529 | error_parse_file: 530 | error_process_fields: 531 | close(fd); 532 | json_object_put(root); 533 | error_open_file: 534 | return ret; 535 | } 536 | -------------------------------------------------------------------------------- /src/config_json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2024, Inria 3 | * Copyright (c) 2024, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef CONFIG_JSON_H 33 | #define CONFIG_JSON_H 34 | 35 | #include "config.h" 36 | 37 | /* 38 | * config_setup_from_json_file setup the given global config from the a json file. 39 | */ 40 | int config_setup_from_json_file(struct config *config, const char *filepath); 41 | 42 | #endif /* CONFIG_JSON_H */ 43 | -------------------------------------------------------------------------------- /src/events.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "events.h" 37 | #include "util.h" 38 | 39 | 40 | static int 41 | setup_msr_perf_event_attr_type(struct perf_event_attr *attr) 42 | { 43 | const char pmu_type_path[PATH_MAX] = "/sys/devices/msr/type"; 44 | FILE *f = NULL; 45 | char buffer[16] = {0}; /* uint32 expected */ 46 | unsigned int pmu_type = 0; 47 | int ret = -1; 48 | 49 | f = fopen(pmu_type_path, "r"); 50 | if (f) { 51 | if (fgets(buffer, sizeof(buffer), f)) { 52 | if (!str_to_uint(buffer, &pmu_type)) { 53 | attr->type = pmu_type; 54 | ret = 0; 55 | } 56 | } 57 | 58 | fclose(f); 59 | } 60 | 61 | return ret; 62 | } 63 | 64 | static int 65 | setup_msr_perf_event_attr_config(const char *event_name, struct perf_event_attr *attr) 66 | { 67 | /* events config from: https://github.com/torvalds/linux/blob/master/arch/x86/events/msr.c */ 68 | const char *msr_events_name[] = {"tsc", "aperf", "mperf", "pperf", "smi", "ptsc", "irperf", "cpu_thermal_margin"}; 69 | const uint64_t msr_events_config[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; 70 | const size_t num_msr_events = sizeof(msr_events_name) / sizeof(msr_events_name[0]); 71 | char event_path[PATH_MAX] = {0}; 72 | 73 | for (size_t i = 0; i < num_msr_events; i++) { 74 | if (!strcasecmp(event_name, msr_events_name[i])) { 75 | snprintf(event_path, PATH_MAX, "/sys/devices/msr/events/%s", msr_events_name[i]); 76 | if (!access(event_path, F_OK)) { 77 | attr->config = msr_events_config[i]; 78 | return 0; 79 | } 80 | } 81 | } 82 | 83 | return -1; 84 | } 85 | 86 | static int 87 | get_msr_pmu_event_encoding(const char *event_name, struct perf_event_attr *attr) 88 | { 89 | attr->size = sizeof(struct perf_event_attr); 90 | attr->disabled = 1; 91 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_GROUP; 92 | 93 | if (setup_msr_perf_event_attr_type(attr)) return -1; 94 | if (setup_msr_perf_event_attr_config(event_name, attr)) return -1; 95 | 96 | return 0; 97 | } 98 | 99 | static int 100 | setup_perf_event_attr(const char *event_name, struct perf_event_attr *attr) 101 | { 102 | pfm_perf_encode_arg_t arg = {}; 103 | 104 | attr->size = sizeof(struct perf_event_attr); 105 | attr->disabled = 1; 106 | attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_GROUP; 107 | 108 | arg.size = sizeof(pfm_perf_encode_arg_t); 109 | arg.attr = attr; 110 | if (pfm_get_os_event_encoding(event_name, PFM_PLM0 | PFM_PLM3, PFM_OS_PERF_EVENT_EXT, &arg) != PFM_SUCCESS) { 111 | /* fallback to MSR PMU event encoding if libpfm fails */ 112 | if (get_msr_pmu_event_encoding(event_name, attr)) { 113 | return -1; 114 | } 115 | } 116 | 117 | return 0; 118 | } 119 | 120 | struct event_config * 121 | event_config_create(const char *event_name) 122 | { 123 | struct perf_event_attr attr = {}; 124 | struct event_config *config = NULL; 125 | 126 | if (!setup_perf_event_attr(event_name, &attr)) { 127 | config = (struct event_config *) malloc(sizeof(struct event_config)); 128 | if (config) { 129 | snprintf(config->name, NAME_MAX, "%s", event_name); 130 | config->attr = attr; 131 | } 132 | } 133 | 134 | return config; 135 | } 136 | 137 | struct event_config * 138 | event_config_dup(struct event_config *config) 139 | { 140 | struct event_config *copy = NULL; 141 | 142 | if (config) { 143 | copy = (struct event_config *) malloc(sizeof(struct event_config)); 144 | if (copy) { 145 | snprintf(copy->name, NAME_MAX, "%s", config->name); 146 | copy->attr = config->attr; 147 | } 148 | } 149 | 150 | return copy; 151 | } 152 | 153 | void 154 | event_config_destroy(struct event_config **config) 155 | { 156 | if (!*config) 157 | return; 158 | 159 | free(*config); 160 | } 161 | 162 | struct events_group * 163 | events_group_create(const char *name) 164 | { 165 | struct events_group *group = (struct events_group *) malloc(sizeof(struct events_group)); 166 | 167 | if (group) { 168 | snprintf(group->name, NAME_MAX, "%s", name); 169 | group->type = MONITOR_ALL_CPU_PER_SOCKET; /* by default, monitor all cpu of the available socket(s) */ 170 | 171 | group->events = zlistx_new(); 172 | zlistx_set_duplicator(group->events, (zlistx_duplicator_fn *) event_config_dup); 173 | zlistx_set_destructor(group->events, (zlistx_destructor_fn *) event_config_destroy); 174 | } 175 | 176 | return group; 177 | } 178 | 179 | struct events_group * 180 | events_group_dup(struct events_group *group) 181 | { 182 | struct events_group *copy = NULL; 183 | 184 | if (group) { 185 | copy = (struct events_group *) malloc(sizeof(struct events_group)); 186 | if (copy) { 187 | snprintf(copy->name, NAME_MAX, "%s", group->name); 188 | copy->type = group->type; 189 | copy->events = zlistx_dup(group->events); 190 | } 191 | } 192 | 193 | return copy; 194 | } 195 | 196 | int 197 | events_group_append_event(struct events_group *group, const char *event_name) 198 | { 199 | int ret = -1; 200 | struct event_config *event = NULL; 201 | 202 | if (group) { 203 | event = event_config_create(event_name); 204 | if (event) { 205 | zlistx_add_end(group->events, event); 206 | free(event); 207 | ret = 0; 208 | } 209 | } 210 | 211 | return ret; 212 | } 213 | 214 | void 215 | events_group_destroy(struct events_group **group) 216 | { 217 | if (*group) { 218 | zlistx_destroy(&(*group)->events); 219 | free(*group); 220 | } 221 | } 222 | 223 | -------------------------------------------------------------------------------- /src/events.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef EVENTS_H 33 | #define EVENTS_H 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | /* 40 | * events_group_monitoring_type stores the possible monitoring type of an events group. 41 | */ 42 | enum events_group_monitoring_type 43 | { 44 | MONITOR_ALL_CPU_PER_SOCKET, 45 | MONITOR_ONE_CPU_PER_SOCKET 46 | }; 47 | 48 | /* 49 | * event_config is the event configuration container. 50 | */ 51 | struct event_config 52 | { 53 | char name[NAME_MAX]; 54 | struct perf_event_attr attr; 55 | }; 56 | 57 | /* 58 | * events_group is the events group container. 59 | */ 60 | struct events_group 61 | { 62 | char name[NAME_MAX]; 63 | enum events_group_monitoring_type type; 64 | zlistx_t *events; /* struct event_config *event */ 65 | }; 66 | 67 | /* 68 | * event_config_create allocate the required resources for the event config container. 69 | */ 70 | struct event_config *event_config_create(const char *event_name); 71 | 72 | /* 73 | * event_config_dup duplicate the given event config container. 74 | */ 75 | struct event_config *event_config_dup(struct event_config *config); 76 | 77 | /* 78 | * event_config_destroy free the allocated resources of the event config container. 79 | */ 80 | void event_config_destroy(struct event_config **config); 81 | 82 | /* 83 | * events_group_create allocate the required resources for the events group container. 84 | */ 85 | struct events_group *events_group_create(const char *name); 86 | 87 | /* 88 | * events_group_dup duplicate the given events group container. 89 | */ 90 | struct events_group *events_group_dup(struct events_group *group); 91 | 92 | /* 93 | * events_group_append_event get the event attributes from its name (if available) and store it into the events group container. 94 | */ 95 | int events_group_append_event(struct events_group *group, const char *event_name); 96 | 97 | /* 98 | * events_group_destroy free the allocated resources of the events group container. 99 | */ 100 | void events_group_destroy(struct events_group **group); 101 | 102 | #endif /* EVENTS_H */ 103 | 104 | -------------------------------------------------------------------------------- /src/hwinfo.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "hwinfo.h" 37 | #include "util.h" 38 | 39 | /* 40 | * SYSFS_CPU_PATH stores the path leading to the available CPUs for the system. 41 | */ 42 | #define SYSFS_CPU_PATH "/sys/bus/cpu/devices" 43 | 44 | /* 45 | * CPU_ID_REGEX is the regex used to extract the CPU id from its name. 46 | * CPU_ID_REGEX_EXPECTED_MATCHES is the number of expected matches from the regex. (num groups + 1) 47 | */ 48 | #define CPU_ID_REGEX "^cpu([0-9]+)$" 49 | #define CPU_ID_REGEX_EXPECTED_MATCHES 2 50 | 51 | 52 | static struct hwinfo_pkg * 53 | hwinfo_pkg_create(void) 54 | { 55 | struct hwinfo_pkg *pkg = (struct hwinfo_pkg *) malloc(sizeof(struct hwinfo_pkg)); 56 | 57 | if (!pkg) 58 | return NULL; 59 | 60 | pkg->cpus_id = zlistx_new(); 61 | zlistx_set_duplicator(pkg->cpus_id, (zlistx_duplicator_fn *) strdup); 62 | zlistx_set_destructor(pkg->cpus_id, (zlistx_destructor_fn *) ptrfree); 63 | 64 | return pkg; 65 | } 66 | 67 | static struct hwinfo_pkg * 68 | hwinfo_pkg_dup(struct hwinfo_pkg *pkg) 69 | { 70 | struct hwinfo_pkg *pkgcpy = (struct hwinfo_pkg *) malloc(sizeof(struct hwinfo_pkg)); 71 | 72 | if (!pkgcpy) 73 | return NULL; 74 | 75 | pkgcpy->cpus_id = zlistx_dup(pkg->cpus_id); 76 | 77 | return pkgcpy; 78 | } 79 | 80 | static void 81 | hwinfo_pkg_destroy(struct hwinfo_pkg **pkg_ptr) 82 | { 83 | if (!*pkg_ptr) 84 | return; 85 | 86 | zlistx_destroy(&(*pkg_ptr)->cpus_id); 87 | free(*pkg_ptr); 88 | *pkg_ptr = NULL; 89 | } 90 | 91 | static int 92 | get_cpu_online_status(const char *cpu_dir) 93 | { 94 | int status = 1; 95 | char path[PATH_MAX] = {}; 96 | FILE *f = NULL; 97 | char buffer[2]; /* boolean expected */ 98 | 99 | snprintf(path, PATH_MAX, "%s/%s/online", SYSFS_CPU_PATH, cpu_dir); 100 | 101 | f = fopen(path, "r"); 102 | if (f) { 103 | if (fgets(buffer, sizeof(buffer), f)) { 104 | if (buffer[0] == '0') 105 | status = 0; 106 | } 107 | fclose(f); 108 | } 109 | 110 | /* 111 | * Certain systems cannot disable some CPUs and the "online" file will not be available. 112 | * In this case, we report the cpu as online. 113 | */ 114 | return status; 115 | } 116 | 117 | static char * 118 | get_package_id(const char *cpu_dir) 119 | { 120 | FILE *f = NULL; 121 | char path[PATH_MAX] = {}; 122 | char buffer[24]; /* log10(ULLONG_MAX) */ 123 | char *id = NULL; 124 | 125 | snprintf(path, PATH_MAX, "%s/%s/topology/physical_package_id", SYSFS_CPU_PATH, cpu_dir); 126 | 127 | f = fopen(path, "r"); 128 | if (f) { 129 | if (fgets(buffer, sizeof(buffer), f)) { 130 | id = strndup(buffer, strlen(buffer) - 1); 131 | } 132 | fclose(f); 133 | } 134 | 135 | return id; 136 | } 137 | 138 | static char * 139 | parse_cpu_id_from_name(const char *str) 140 | { 141 | regex_t re = {}; 142 | regmatch_t matches[CPU_ID_REGEX_EXPECTED_MATCHES]; 143 | char *id = NULL; 144 | 145 | if (!regcomp(&re, CPU_ID_REGEX, REG_EXTENDED)) { 146 | if (!regexec(&re, str, CPU_ID_REGEX_EXPECTED_MATCHES, matches, 0)) { 147 | id = strndup(str + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); 148 | } 149 | regfree(&re); 150 | } 151 | 152 | return id; 153 | } 154 | 155 | static int 156 | do_packages_detection(struct hwinfo *hwinfo) 157 | { 158 | int ret = -1; 159 | DIR *dir = NULL; 160 | struct dirent *entry = NULL; 161 | int cpu_online; 162 | char *cpu_id = NULL; 163 | char *pkg_id = NULL; 164 | struct hwinfo_pkg *pkg = NULL; 165 | 166 | dir = opendir(SYSFS_CPU_PATH); 167 | if (!dir) { 168 | zsys_error("hwinfo: failed to open sysfs cpu directory"); 169 | return ret; 170 | } 171 | 172 | /* extract information from online cpus */ 173 | for (entry = readdir(dir); entry; entry = readdir(dir)) { 174 | if ((entry->d_type & DT_LNK) && (entry->d_name[0] != '.')) { 175 | cpu_online = get_cpu_online_status(entry->d_name); 176 | if (!cpu_online) { 177 | zsys_info("hwinfo: %s is offline and will be ignored", entry->d_name); 178 | continue; 179 | } 180 | 181 | cpu_id = parse_cpu_id_from_name(entry->d_name); 182 | if (!cpu_id) { 183 | zsys_error("hwinfo: failed to parse cpu id for %s", entry->d_name); 184 | goto cleanup; 185 | } 186 | 187 | pkg_id = get_package_id(entry->d_name); 188 | if (!pkg_id) { 189 | zsys_error("hwinfo: failed to parse package id for %s", entry->d_name); 190 | goto cleanup; 191 | } 192 | 193 | /* get cpu pkg or create it if never encountered */ 194 | pkg = (struct hwinfo_pkg *) zhashx_lookup(hwinfo->pkgs, pkg_id); 195 | if (!pkg) { 196 | pkg = hwinfo_pkg_create(); 197 | if (!pkg) { 198 | zsys_error("hwinfo: failed to allocate package info struct"); 199 | goto cleanup; 200 | } 201 | 202 | zhashx_insert(hwinfo->pkgs, pkg_id, pkg); 203 | hwinfo_pkg_destroy(&pkg); 204 | pkg = (struct hwinfo_pkg *) zhashx_lookup(hwinfo->pkgs, pkg_id); /* get the copy the pkg done by zhashx_insert */ 205 | } 206 | 207 | zlistx_add_end(pkg->cpus_id, cpu_id); 208 | 209 | free(cpu_id); 210 | cpu_id = NULL; 211 | free(pkg_id); 212 | pkg_id = NULL; 213 | } 214 | } 215 | 216 | ret = 0; 217 | 218 | cleanup: 219 | free(cpu_id); 220 | free(pkg_id); 221 | closedir(dir); 222 | return ret; 223 | } 224 | 225 | int 226 | hwinfo_detect(struct hwinfo *hwinfo) 227 | { 228 | if (do_packages_detection(hwinfo)) { 229 | return -1; 230 | } 231 | 232 | return 0; 233 | } 234 | 235 | struct hwinfo * 236 | hwinfo_create(void) 237 | { 238 | struct hwinfo *hw = (struct hwinfo *) malloc(sizeof(struct hwinfo)); 239 | 240 | if (!hw) 241 | return NULL; 242 | 243 | hw->pkgs = zhashx_new(); 244 | zhashx_set_duplicator(hw->pkgs, (zhashx_duplicator_fn *) hwinfo_pkg_dup); 245 | zhashx_set_destructor(hw->pkgs, (zlistx_destructor_fn *) hwinfo_pkg_destroy); 246 | 247 | return hw; 248 | } 249 | 250 | struct hwinfo * 251 | hwinfo_dup(struct hwinfo *hwinfo) 252 | { 253 | struct hwinfo *hwinfocpy = (struct hwinfo *) malloc(sizeof(struct hwinfo)); 254 | 255 | if (!hwinfocpy) 256 | return NULL; 257 | 258 | hwinfocpy->pkgs = zhashx_dup(hwinfo->pkgs); 259 | 260 | return hwinfocpy; 261 | } 262 | 263 | void 264 | hwinfo_destroy(struct hwinfo *hwinfo) 265 | { 266 | if (!hwinfo) 267 | return; 268 | 269 | zhashx_destroy(&hwinfo->pkgs); 270 | free(hwinfo); 271 | } 272 | 273 | -------------------------------------------------------------------------------- /src/hwinfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef HWINFO_H 33 | #define HWINFO_H 34 | 35 | #include 36 | #include 37 | 38 | 39 | /* 40 | * hwinfo_pkg stores information about the package. 41 | */ 42 | struct hwinfo_pkg 43 | { 44 | zlistx_t *cpus_id; /* char *cpu_id */ 45 | }; 46 | 47 | /* 48 | * hwinfo stores information about the machine hardware. 49 | */ 50 | struct hwinfo 51 | { 52 | zhashx_t *pkgs; /* char *pkg_id -> struct hwinfo_pkg *pkg */ 53 | }; 54 | 55 | /* 56 | * hwinfo_create allocate the needed ressources. 57 | */ 58 | struct hwinfo *hwinfo_create(void); 59 | 60 | /* 61 | * hwinfo_detect discover and store the machine hardware topology. 62 | */ 63 | int hwinfo_detect(struct hwinfo *hwinfo); 64 | 65 | /* 66 | * hwinfo_dup duplicate the hwinfo struct and its members. 67 | */ 68 | struct hwinfo *hwinfo_dup(struct hwinfo *hwinfo); 69 | 70 | /* 71 | * hwinfo_destroy free the allocated memory to store the machine topology. 72 | */ 73 | void hwinfo_destroy(struct hwinfo *hwinfo); 74 | 75 | #endif /* HWINFO_H */ 76 | 77 | -------------------------------------------------------------------------------- /src/payload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #include "util.h" 36 | #include "payload.h" 37 | 38 | struct payload_cpu_data * 39 | payload_cpu_data_create(void) 40 | { 41 | struct payload_cpu_data *data = (struct payload_cpu_data *) malloc(sizeof(struct payload_cpu_data)); 42 | 43 | if (!data) 44 | return NULL; 45 | 46 | data->events = zhashx_new(); 47 | zhashx_set_duplicator(data->events, (zhashx_duplicator_fn *) uint64ptrdup); 48 | zhashx_set_destructor(data->events, (zhashx_destructor_fn *) ptrfree); 49 | 50 | return data; 51 | } 52 | 53 | void 54 | payload_cpu_data_destroy(struct payload_cpu_data **data_ptr) 55 | { 56 | if (!*data_ptr) 57 | return; 58 | 59 | zhashx_destroy(&(*data_ptr)->events); 60 | free(*data_ptr); 61 | *data_ptr = NULL; 62 | } 63 | 64 | struct payload_pkg_data * 65 | payload_pkg_data_create(void) 66 | { 67 | struct payload_pkg_data *data = (struct payload_pkg_data *) malloc(sizeof(struct payload_pkg_data)); 68 | 69 | if (!data) 70 | return NULL; 71 | 72 | data->cpus = zhashx_new(); 73 | zhashx_set_destructor(data->cpus, (zhashx_destructor_fn *) payload_cpu_data_destroy); 74 | 75 | return data; 76 | } 77 | 78 | void 79 | payload_pkg_data_destroy(struct payload_pkg_data **data_ptr) 80 | { 81 | if (!*data_ptr) 82 | return; 83 | 84 | zhashx_destroy(&(*data_ptr)->cpus); 85 | free(*data_ptr); 86 | *data_ptr = NULL; 87 | } 88 | 89 | struct payload_group_data * 90 | payload_group_data_create(void) 91 | { 92 | struct payload_group_data *data = (struct payload_group_data *) malloc(sizeof(struct payload_group_data)); 93 | 94 | if (!data) 95 | return NULL; 96 | 97 | data->pkgs = zhashx_new(); 98 | zhashx_set_destructor(data->pkgs, (zhashx_destructor_fn *) payload_pkg_data_destroy); 99 | 100 | return data; 101 | } 102 | 103 | void 104 | payload_group_data_destroy(struct payload_group_data **data_ptr) 105 | { 106 | if (!*data_ptr) 107 | return; 108 | 109 | zhashx_destroy(&(*data_ptr)->pkgs); 110 | free(*data_ptr); 111 | *data_ptr = NULL; 112 | } 113 | 114 | struct payload * 115 | payload_create(uint64_t timestamp, const char *target_name) 116 | { 117 | struct payload *payload = (struct payload *) malloc(sizeof(struct payload)); 118 | 119 | if (!payload) 120 | return NULL; 121 | 122 | payload->timestamp = timestamp; 123 | payload->target_name = strdup(target_name); 124 | payload->groups = zhashx_new(); 125 | zhashx_set_destructor(payload->groups, (zhashx_destructor_fn *) payload_group_data_destroy); 126 | 127 | return payload; 128 | } 129 | 130 | void 131 | payload_destroy(struct payload *payload) 132 | { 133 | if (!payload) 134 | return; 135 | 136 | free(payload->target_name); 137 | zhashx_destroy(&payload->groups); 138 | free(payload); 139 | } 140 | 141 | -------------------------------------------------------------------------------- /src/payload.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef PAYLOAD_H 33 | #define PAYLOAD_H 34 | 35 | #include 36 | 37 | /* 38 | * payload_cpu_data stores the events values of a cpu. 39 | */ 40 | struct payload_cpu_data 41 | { 42 | zhashx_t *events; /* char *event_name -> uint64_t *event_value */ 43 | }; 44 | 45 | /* 46 | * payload_pkg_data stores the payloads for a cpu package. 47 | */ 48 | struct payload_pkg_data 49 | { 50 | zhashx_t *cpus; /* char *cpu_id -> struct payload_cpu_data *cpu_data */ 51 | }; 52 | 53 | /* 54 | * payload_group_data stores the payloads for an events group. 55 | */ 56 | struct payload_group_data 57 | { 58 | zhashx_t *pkgs; /* char *pkg_id -> struct payload_pkg_data *pkg_data */ 59 | }; 60 | 61 | /* 62 | * payload stores the data collected by the monitoring module for the reporting module. 63 | */ 64 | struct payload 65 | { 66 | uint64_t timestamp; 67 | char *target_name; 68 | zhashx_t *groups; /* char *group_name -> struct payload_group_data *group_data */ 69 | }; 70 | 71 | /* 72 | * payload_create allocate the required resources of a monitoring payload. 73 | */ 74 | struct payload *payload_create(uint64_t timestamp, const char *target_name); 75 | 76 | /* 77 | * payload_destroy free the allocated resources of the monitoring payload. 78 | */ 79 | void payload_destroy(struct payload *payload); 80 | 81 | /* 82 | * payload_group_data_create allocate the resources of an events group data container. 83 | */ 84 | struct payload_group_data *payload_group_data_create(void); 85 | 86 | /* 87 | * payload_group_data_destroy free the allocated resources of the events group data container. 88 | */ 89 | void payload_group_data_destroy(struct payload_group_data **data_ptr); 90 | 91 | /* 92 | * payload_pkg_data_create allocate the resources of a package data container. 93 | */ 94 | struct payload_pkg_data *payload_pkg_data_create(void); 95 | 96 | /* 97 | * payload_pkg_data_destroy free the allocated resources of the package data container. 98 | */ 99 | void payload_pkg_data_destroy(struct payload_pkg_data **data_ptr); 100 | 101 | /* 102 | * payload_cpu_data_create allocate the resources of a cpu data container. 103 | */ 104 | struct payload_cpu_data *payload_cpu_data_create(void); 105 | 106 | /* 107 | * payload_cpu_data_destroy free the allocated resources of the cpu data container. 108 | */ 109 | void payload_cpu_data_destroy(struct payload_cpu_data **data_ptr); 110 | 111 | #endif /* PAYLOAD_H */ 112 | 113 | -------------------------------------------------------------------------------- /src/perf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef PERF_H 33 | #define PERF_H 34 | 35 | #include 36 | #include "hwinfo.h" 37 | #include "events.h" 38 | 39 | /* 40 | * perf_config stores the configuration of a perf actor. 41 | */ 42 | struct perf_config 43 | { 44 | struct hwinfo *hwinfo; 45 | zhashx_t *events_groups; /* char *group_name -> struct events_group *group_config */ 46 | struct target *target; 47 | }; 48 | 49 | /* 50 | * perf_group_cpu_context stores the context of an events group for a specific cpu. 51 | */ 52 | struct perf_group_cpu_context 53 | { 54 | zlistx_t *perf_fds; /* int *fd */ 55 | }; 56 | 57 | /* 58 | * perf_group_pkg_context stores the context of an events group for a specific package. 59 | */ 60 | struct perf_group_pkg_context 61 | { 62 | zhashx_t *cpus_ctx; /* char *cpu_id -> struct perf_group_cpu_context *cpu_ctx */ 63 | }; 64 | 65 | /* 66 | * perf_group_context stores the context of an events group. 67 | */ 68 | struct perf_group_context 69 | { 70 | struct events_group *config; 71 | zhashx_t *pkgs_ctx; /* char *pkg_id -> struct perf_group_pkg_context *pkg_ctx */ 72 | }; 73 | 74 | /* 75 | * perf_context stores the context of a perf actor. 76 | */ 77 | struct perf_context 78 | { 79 | struct perf_config *config; 80 | const char *target_name; 81 | bool terminated; 82 | zsock_t *pipe; 83 | zsock_t *ticker; 84 | zpoller_t *poller; 85 | zsock_t *reporting; 86 | int cgroup_fd; 87 | zhashx_t *groups_ctx; /* char *group_name -> struct perf_group_context *group_ctx */ 88 | }; 89 | 90 | /* 91 | * perf_counter_value stores the counter value. 92 | */ 93 | struct perf_counter_value { 94 | uint64_t value; 95 | }; 96 | 97 | /* 98 | * perf_cpu_report stores the events counter value. 99 | */ 100 | struct perf_read_format { 101 | uint64_t nr; 102 | uint64_t time_enabled; /* PERF_FORMAT_TOTAL_TIME_ENABLED flag */ 103 | uint64_t time_running; /* PERF_FORMAT_TOTAL_TIME_RUNNING flag */ 104 | struct perf_counter_value values[]; 105 | }; 106 | 107 | /* 108 | * perf_config_create allocate and configure a perf configuration structure. 109 | */ 110 | struct perf_config *perf_config_create(struct hwinfo *hwinfo, zhashx_t *events_groups, struct target *target); 111 | 112 | /* 113 | * perf_config_destroy free the resources allocated for the perf configuration structure. 114 | */ 115 | void perf_config_destroy(struct perf_config *config); 116 | 117 | /* 118 | * perf_monitoring_actor handle the monitoring of a cgroup using perf_event. 119 | */ 120 | void perf_monitoring_actor(zsock_t *pipe, void *args); 121 | 122 | /* 123 | * perf_try_event_open try to open a global counting event using the perf_event_open syscall. 124 | * This is used to check if the perf_event_open syscall is working and the current process is allowed to use it. 125 | */ 126 | int perf_try_global_counting_event_open(void); 127 | 128 | #endif /* PERF_H */ 129 | 130 | -------------------------------------------------------------------------------- /src/pmu.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #include "pmu.h" 36 | #include "util.h" 37 | 38 | int 39 | pmu_initialize(void) 40 | { 41 | if (pfm_initialize() != PFM_SUCCESS) { 42 | return -1; 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | void 49 | pmu_deinitialize(void) 50 | { 51 | pfm_terminate(); 52 | } 53 | 54 | struct pmu_info * 55 | pmu_info_create(void) 56 | { 57 | struct pmu_info *pmu = (struct pmu_info *) malloc(sizeof(struct pmu_info)); 58 | return pmu; 59 | } 60 | 61 | struct pmu_info * 62 | pmu_info_dup(struct pmu_info *pmu) 63 | { 64 | struct pmu_info *copy = NULL; 65 | 66 | if (pmu) { 67 | copy = (struct pmu_info *) malloc(sizeof(struct pmu_info)); 68 | if (copy) { 69 | *copy = *pmu; 70 | } 71 | } 72 | 73 | return copy; 74 | } 75 | 76 | void 77 | pmu_info_destroy(struct pmu_info **pmu) 78 | { 79 | if (!*pmu) 80 | return; 81 | 82 | free(*pmu); 83 | } 84 | 85 | struct pmu_topology * 86 | pmu_topology_create(void) 87 | { 88 | struct pmu_topology *topology = (struct pmu_topology *) malloc(sizeof(struct pmu_topology)); 89 | 90 | if (!topology) 91 | return NULL; 92 | 93 | topology->pmus = zlistx_new(); 94 | zlistx_set_duplicator(topology->pmus, (zlistx_duplicator_fn *) pmu_info_dup); 95 | zlistx_set_destructor(topology->pmus, (zlistx_destructor_fn *) pmu_info_destroy); 96 | 97 | return topology; 98 | } 99 | 100 | void 101 | pmu_topology_destroy(struct pmu_topology *topology) 102 | { 103 | if (!topology) 104 | return; 105 | 106 | zlistx_destroy(&topology->pmus); 107 | free(topology); 108 | } 109 | 110 | int 111 | pmu_topology_detect(struct pmu_topology *topology) 112 | { 113 | pfm_pmu_t pmu = {}; 114 | pfm_pmu_info_t pmu_info = {}; 115 | 116 | for (pmu = PFM_PMU_NONE; pmu < PFM_PMU_MAX; pmu = static_cast(pmu + 1)) { 117 | if (pfm_get_pmu_info(pmu, &pmu_info) != PFM_SUCCESS) 118 | continue; 119 | 120 | /* filter to only present pmu */ 121 | if (pmu_info.is_present) { 122 | /* rewrite type for unknown PMU */ 123 | if (pmu_info.type >= PFM_PMU_TYPE_MAX) 124 | pmu_info.type = PFM_PMU_TYPE_UNKNOWN; 125 | 126 | zlistx_add_end(topology->pmus, &pmu_info); 127 | } 128 | } 129 | 130 | return 0; 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/pmu.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef PMU_H 33 | #define PMU_H 34 | 35 | #include 36 | #include 37 | 38 | /* 39 | * pmu_info is the container for the pmu information. 40 | */ 41 | struct pmu_info 42 | { 43 | pfm_pmu_info_t info; 44 | }; 45 | 46 | /* 47 | * pmu_topology is the container of the supported pmu by the machine. 48 | */ 49 | struct pmu_topology 50 | { 51 | zlistx_t *pmus; /* struct pmu_info *info */ 52 | }; 53 | 54 | /* 55 | * pmu_initialize allocate the resources needed to use the PMUs. 56 | */ 57 | int pmu_initialize(void); 58 | 59 | /* 60 | * pmu_deinitialize free the allocated resources needed to use the PMUs. 61 | */ 62 | void pmu_deinitialize(void); 63 | 64 | /* 65 | * pmu_info_create allocate the resources needed for a pmu info container. 66 | */ 67 | struct pmu_info *pmu_info_create(void); 68 | 69 | /* 70 | * pmu_info_dup duplicate the given pmu info. 71 | */ 72 | struct pmu_info *pmu_info_dup(struct pmu_info *pmu); 73 | 74 | /* 75 | * pmu_info_destroy free the allocated resources for the pmu info container. 76 | */ 77 | void pmu_info_destroy(struct pmu_info **pmu); 78 | 79 | /* 80 | * pmu_topology_create allocate the resource needed for a pmu topology container. 81 | */ 82 | struct pmu_topology *pmu_topology_create(void); 83 | 84 | /* 85 | * pmu_topology_destroy free the allocated resource of the pmu topology container. 86 | */ 87 | void pmu_topology_destroy(struct pmu_topology *topology); 88 | 89 | /* 90 | * pmu_topology_detect populate the topology container with the available PMUs. 91 | */ 92 | int pmu_topology_detect(struct pmu_topology *topology); 93 | 94 | #endif /* PMU_H */ 95 | 96 | -------------------------------------------------------------------------------- /src/report.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #include "report.h" 36 | #include "util.h" 37 | #include "hwinfo.h" 38 | #include "events.h" 39 | #include "payload.h" 40 | #include "perf.h" 41 | #include "storage.h" 42 | 43 | struct report_config * 44 | report_config_create(struct storage_module *storage_module) 45 | { 46 | struct report_config *config = (struct report_config *) malloc(sizeof(struct report_config)); 47 | 48 | if (!config) 49 | return NULL; 50 | 51 | config->storage = storage_module; 52 | 53 | return config; 54 | } 55 | 56 | void 57 | report_config_destroy(struct report_config *config) 58 | { 59 | if (!config) 60 | return; 61 | 62 | free(config); 63 | } 64 | 65 | static struct report_context * 66 | report_context_create(struct report_config *config, zsock_t *pipe) 67 | { 68 | struct report_context *ctx = (struct report_context *) malloc(sizeof(struct report_context)); 69 | 70 | if (!ctx) 71 | return NULL; 72 | 73 | ctx->terminated = false; 74 | ctx->pipe = pipe; 75 | ctx->reporting = zsock_new_pull("inproc://reporting"); 76 | ctx->poller = zpoller_new(ctx->pipe, ctx->reporting, NULL); 77 | ctx->config = config; 78 | 79 | return ctx; 80 | } 81 | 82 | static void 83 | report_context_destroy(struct report_context *ctx) 84 | { 85 | if (!ctx) 86 | return; 87 | 88 | zpoller_destroy(&ctx->poller); 89 | zsock_destroy(&ctx->reporting); 90 | free(ctx); 91 | } 92 | 93 | static void 94 | handle_pipe(struct report_context *ctx) 95 | { 96 | char *command = zstr_recv(ctx->pipe); 97 | 98 | if (streq(command, "$TERM")) { 99 | ctx->terminated = true; 100 | zsys_info("reporting: bye!"); 101 | } 102 | else { 103 | zsys_error("reporting: invalid pipe command: %s", command); 104 | } 105 | 106 | zstr_free(&command); 107 | } 108 | 109 | static void 110 | handle_reporting(struct report_context *ctx) 111 | { 112 | struct payload *payload = NULL; 113 | 114 | zsock_recv(ctx->reporting, "p", &payload); 115 | 116 | if (!payload) 117 | return; 118 | 119 | if (storage_module_store_report(ctx->config->storage, payload)) { 120 | zsys_error("report: failed to store the report for timestamp=%lu", payload->timestamp); 121 | } 122 | 123 | payload_destroy(payload); 124 | } 125 | 126 | void 127 | reporting_actor(zsock_t *pipe, void *args) 128 | { 129 | struct report_context *ctx = report_context_create((struct report_config *) args, pipe); 130 | zsock_t *which = NULL; 131 | 132 | if (!ctx) { 133 | zsys_error("reporting: cannot create context"); 134 | return; 135 | } 136 | 137 | zsock_signal(pipe, 0); 138 | 139 | while (!ctx->terminated) { 140 | which = (zsock_t *) zpoller_wait(ctx->poller, -1); 141 | 142 | if (zpoller_terminated(ctx->poller)) { 143 | break; 144 | } 145 | 146 | if (which == ctx->pipe) { 147 | handle_pipe(ctx); 148 | } 149 | else if (which == ctx->reporting) { 150 | handle_reporting(ctx); 151 | } 152 | } 153 | 154 | report_context_destroy(ctx); 155 | } 156 | 157 | -------------------------------------------------------------------------------- /src/report.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef REPORT_H 33 | #define REPORT_H 34 | 35 | #include 36 | #include 37 | 38 | /* 39 | * report_config stores the reporting module configuration. 40 | */ 41 | struct report_config 42 | { 43 | struct storage_module *storage; 44 | }; 45 | 46 | /* 47 | * report_context stores the reporting module execution context. 48 | */ 49 | struct report_context 50 | { 51 | struct report_config *config; 52 | bool terminated; 53 | zsock_t *pipe; 54 | zsock_t *reporting; 55 | zpoller_t *poller; 56 | }; 57 | 58 | /* 59 | * report_config_create allocate the resource of a report configuration structure. 60 | */ 61 | struct report_config *report_config_create(struct storage_module *storage_module); 62 | 63 | /* 64 | * report_config_destroy free the allocated resource of the report configuration structure. 65 | */ 66 | void report_config_destroy(struct report_config *config); 67 | 68 | /* 69 | * reporting_actor is the reporting actor entrypoint. 70 | */ 71 | void reporting_actor(zsock_t *pipe, void *args); 72 | 73 | #endif /* REPORT_H */ 74 | 75 | -------------------------------------------------------------------------------- /src/sensor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "version.h" 42 | #include "config.h" 43 | #include "config_cli.h" 44 | #include "pmu.h" 45 | #include "events.h" 46 | #include "hwinfo.h" 47 | #include "perf.h" 48 | #include "report.h" 49 | #include "target.h" 50 | #include "storage.h" 51 | #include "storage_null.h" 52 | #include "storage_csv.h" 53 | #include "storage_socket.h" 54 | 55 | #ifdef HAVE_MONGODB 56 | #include "storage_mongodb.h" 57 | #endif 58 | 59 | static struct storage_module * 60 | setup_storage_module(struct config *config) 61 | { 62 | switch (config->storage.type) 63 | { 64 | case STORAGE_NULL: 65 | return storage_null_create(config); 66 | case STORAGE_CSV: 67 | return storage_csv_create(config); 68 | case STORAGE_SOCKET: 69 | return storage_socket_create(config); 70 | #ifdef HAVE_MONGODB 71 | case STORAGE_MONGODB: 72 | return storage_mongodb_create(config); 73 | #endif 74 | default: 75 | return NULL; 76 | } 77 | } 78 | 79 | static void 80 | sync_cgroups_running_monitored(struct hwinfo *hwinfo, zhashx_t *container_events_groups, const char *cgroup_basepath, zhashx_t *container_monitoring_actors) 81 | { 82 | zhashx_t *running_targets = NULL; /* char *cgroup_path -> struct target *target */ 83 | zactor_t *perf_monitor = NULL; 84 | const char *cgroup_path = NULL; 85 | struct target *target = NULL; 86 | struct perf_config *monitor_config = NULL; 87 | 88 | /* to store running cgroups name and absolute path */ 89 | running_targets = zhashx_new(); 90 | 91 | /* get running (and identifiable) container(s) */ 92 | if (target_discover_running(cgroup_basepath, TARGET_TYPE_EVERYTHING, running_targets)) { 93 | zsys_error("sensor: error when retrieving the running targets."); 94 | goto out; 95 | } 96 | 97 | /* stop monitoring dead container(s) */ 98 | for (perf_monitor = (zactor_t *) zhashx_first(container_monitoring_actors); perf_monitor; perf_monitor = (zactor_t *) zhashx_next(container_monitoring_actors)) { 99 | cgroup_path = (const char *) zhashx_cursor(container_monitoring_actors); 100 | target = (struct target *) zhashx_lookup(running_targets, cgroup_path); 101 | if (!target) { 102 | zhashx_freefn(running_targets, cgroup_path, (zhashx_free_fn *) target_destroy); 103 | zhashx_delete(container_monitoring_actors, cgroup_path); 104 | } 105 | } 106 | 107 | /* start monitoring new container(s) */ 108 | for (target = (struct target *) zhashx_first(running_targets); target; target = (struct target *) zhashx_next(running_targets)) { 109 | cgroup_path = (const char *) zhashx_cursor(running_targets); 110 | if (!zhashx_lookup(container_monitoring_actors, cgroup_path)) { 111 | monitor_config = perf_config_create(hwinfo, container_events_groups, target); 112 | perf_monitor = zactor_new(perf_monitoring_actor, monitor_config); 113 | zhashx_insert(container_monitoring_actors, cgroup_path, perf_monitor); 114 | } else { 115 | zhashx_freefn(running_targets, cgroup_path, (zhashx_free_fn *) target_destroy); 116 | } 117 | } 118 | 119 | out: 120 | zhashx_destroy(&running_targets); 121 | } 122 | 123 | int 124 | main(int argc, char **argv) 125 | { 126 | int ret = 1; 127 | struct config *config = NULL; 128 | struct utsname kernel_info; 129 | struct pmu_topology *sys_pmu_topology = NULL; 130 | struct pmu_info *pmu = NULL; 131 | struct hwinfo *hwinfo = NULL; 132 | struct storage_module *storage = NULL; 133 | struct report_config reporting_conf = {}; 134 | zactor_t *reporting = NULL; 135 | zhashx_t *cgroups_running = NULL; /* char *cgroup_name -> char *cgroup_absolute_path */ 136 | zhashx_t *container_monitoring_actors = NULL; /* char *actor_name -> zactor_t *actor */ 137 | zsock_t *ticker = NULL; 138 | struct target *system_target = NULL; 139 | struct perf_config *system_monitor_config = NULL; 140 | zactor_t *system_perf_monitor = NULL; 141 | 142 | signal(SIGPIPE, SIG_IGN); 143 | 144 | if (!zsys_init()) { 145 | fprintf(stderr, "czmq: failed to initialize zsys context\n"); 146 | return ret; 147 | } 148 | 149 | /* disable limit of maximum czmq sockets */ 150 | zsys_set_max_sockets(0); 151 | 152 | /* show build information */ 153 | zsys_info("build: version %s (rev: %s)", VERSION_GIT_TAG, VERSION_GIT_REV); 154 | 155 | /* show Kernel information */ 156 | if (uname(&kernel_info)) { 157 | zsys_error("uname: failed to get Kernel information"); 158 | goto cleanup; 159 | } 160 | zsys_info("uname: %s %s %s %s", kernel_info.sysname, kernel_info.release, kernel_info.version, kernel_info.machine); 161 | 162 | /* check if perf_event is working */ 163 | if (perf_try_global_counting_event_open()) { 164 | zsys_error("perf: error while testing the perf_event support"); 165 | goto cleanup; 166 | } 167 | 168 | /* initialize the PMU module */ 169 | if (pmu_initialize()) { 170 | zsys_error("pmu: cannot initialize the pmu module"); 171 | goto cleanup; 172 | } 173 | 174 | /* detect pmu topology */ 175 | sys_pmu_topology = pmu_topology_create(); 176 | if (!sys_pmu_topology) { 177 | zsys_error("pmu: cannot allocate pmu topology memory"); 178 | goto cleanup; 179 | } 180 | if (pmu_topology_detect(sys_pmu_topology)) { 181 | zsys_error("pmu: cannot detect system PMU topology"); 182 | goto cleanup; 183 | } 184 | for (pmu = (struct pmu_info *) zlistx_first(sys_pmu_topology->pmus); pmu; pmu = (struct pmu_info *) zlistx_next(sys_pmu_topology->pmus)) { 185 | zsys_info("pmu: found %s '%s' having %d events, %d counters (%d general, %d fixed)", 186 | pmu->info.name, 187 | pmu->info.desc, 188 | pmu->info.nevents, 189 | pmu->info.num_cntrs + pmu->info.num_fixed_cntrs, 190 | pmu->info.num_cntrs, 191 | pmu->info.num_fixed_cntrs); 192 | } 193 | 194 | /* detect machine hardware */ 195 | hwinfo = hwinfo_create(); 196 | if (!hwinfo) { 197 | zsys_error("hwinfo: error while creating hardware information container"); 198 | goto cleanup; 199 | } 200 | if (hwinfo_detect(hwinfo)) { 201 | zsys_error("hwinfo: error while detecting hardware information"); 202 | goto cleanup; 203 | } 204 | 205 | /* get application config */ 206 | config = config_create(); 207 | if (!config) { 208 | zsys_error("config: failed to create config container"); 209 | goto cleanup; 210 | } 211 | if (config_setup_from_cli(argc, argv, config)) { 212 | zsys_error("config: failed to parse the provided command-line arguments"); 213 | goto cleanup; 214 | } 215 | if (config_validate(config)) { 216 | zsys_error("config: failed to validate config"); 217 | goto cleanup; 218 | } 219 | 220 | /* setup storage module */ 221 | storage = setup_storage_module(config); 222 | if (!storage) { 223 | zsys_error("sensor: failed to create '%s' storage module", storage_types_name[config->storage.type]); 224 | goto cleanup; 225 | } 226 | if (storage_module_initialize(storage)) { 227 | zsys_error("sensor: failed to initialize storage module"); 228 | goto cleanup; 229 | } 230 | if (storage_module_ping(storage)) { 231 | zsys_error("sensor: failed to ping storage module"); 232 | storage_module_deinitialize(storage); 233 | goto cleanup; 234 | } 235 | 236 | zsys_info("sensor: configuration is valid, starting monitoring..."); 237 | 238 | /* start reporting actor */ 239 | reporting_conf = (struct report_config){ 240 | .storage = storage 241 | }; 242 | reporting = zactor_new(reporting_actor, &reporting_conf); 243 | 244 | /* create ticker publisher socket */ 245 | ticker = zsock_new_pub("inproc://ticker"); 246 | 247 | /* start system monitoring actor only when needed */ 248 | if (zhashx_size(config->events.system)) { 249 | system_target = target_create(TARGET_TYPE_ALL, NULL, NULL); 250 | system_monitor_config = perf_config_create(hwinfo, config->events.system, system_target); 251 | system_perf_monitor = zactor_new(perf_monitoring_actor, system_monitor_config); 252 | } 253 | 254 | /* monitor running containers */ 255 | container_monitoring_actors = zhashx_new(); 256 | zhashx_set_destructor(container_monitoring_actors, (zhashx_destructor_fn *) zactor_destroy); 257 | while (!zsys_interrupted) { 258 | /* monitor containers only when needed */ 259 | if (zhashx_size(config->events.containers)) { 260 | sync_cgroups_running_monitored(hwinfo, config->events.containers, config->sensor.cgroup_basepath, container_monitoring_actors); 261 | } 262 | 263 | /* send clock tick to monitoring actors */ 264 | zsock_send(ticker, "s8", "CLOCK_TICK", zclock_time()); 265 | 266 | zclock_sleep((int)config->sensor.frequency); 267 | } 268 | 269 | /* clean storage module ressources */ 270 | storage_module_deinitialize(storage); 271 | 272 | ret = 0; 273 | 274 | cleanup: 275 | zhashx_destroy(&cgroups_running); 276 | zhashx_destroy(&container_monitoring_actors); 277 | zactor_destroy(&system_perf_monitor); 278 | zactor_destroy(&reporting); 279 | storage_module_destroy(storage); 280 | zsock_destroy(&ticker); 281 | config_destroy(config); 282 | pmu_topology_destroy(sys_pmu_topology); 283 | pmu_deinitialize(); 284 | hwinfo_destroy(hwinfo); 285 | zsys_shutdown(); 286 | return ret; 287 | } 288 | -------------------------------------------------------------------------------- /src/storage.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #include "storage.h" 36 | 37 | const char *storage_types_name[] = { 38 | [STORAGE_UNKNOWN] = "unknown", 39 | [STORAGE_NULL] = "null", 40 | [STORAGE_CSV] = "csv", 41 | [STORAGE_SOCKET] = "socket", 42 | #ifdef HAVE_MONGODB 43 | [STORAGE_MONGODB] = "mongodb", 44 | #endif 45 | }; 46 | 47 | enum storage_type 48 | storage_module_get_type(const char *type_name) 49 | { 50 | if (strcasecmp(type_name, storage_types_name[STORAGE_NULL]) == 0) { 51 | return STORAGE_NULL; 52 | } 53 | 54 | if (strcasecmp(type_name, storage_types_name[STORAGE_CSV]) == 0) { 55 | return STORAGE_CSV; 56 | } 57 | 58 | if (strcasecmp(type_name, storage_types_name[STORAGE_SOCKET]) == 0) { 59 | return STORAGE_SOCKET; 60 | } 61 | 62 | #ifdef HAVE_MONGODB 63 | if (strcasecmp(type_name, storage_types_name[STORAGE_MONGODB]) == 0) { 64 | return STORAGE_MONGODB; 65 | } 66 | #endif 67 | 68 | return STORAGE_UNKNOWN; 69 | } 70 | 71 | int 72 | storage_module_initialize(struct storage_module *module) 73 | { 74 | return (*module->initialize)(module); 75 | } 76 | 77 | int 78 | storage_module_ping(struct storage_module *module) 79 | { 80 | return (*module->ping)(module); 81 | } 82 | 83 | int 84 | storage_module_store_report(struct storage_module *module, struct payload *payload) 85 | { 86 | return (*module->store_report)(module, payload); 87 | } 88 | 89 | int 90 | storage_module_deinitialize(struct storage_module *module) 91 | { 92 | return (*module->deinitialize)(module); 93 | } 94 | 95 | void 96 | storage_module_destroy(struct storage_module *module) 97 | { 98 | if (!module) 99 | return; 100 | 101 | (*module->destroy)(module); 102 | free(module); 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/storage.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef STORAGE_H 33 | #define STORAGE_H 34 | 35 | #include "payload.h" 36 | #include "report.h" 37 | 38 | /* 39 | * storage_type enumeration allows to select a storage type to generate. 40 | */ 41 | enum storage_type 42 | { 43 | STORAGE_UNKNOWN, 44 | STORAGE_NULL, 45 | STORAGE_CSV, 46 | STORAGE_SOCKET, 47 | #ifdef HAVE_MONGODB 48 | STORAGE_MONGODB, 49 | #endif 50 | }; 51 | 52 | /* 53 | * storage_types_name stores the name (as string) of the supported storage types. 54 | */ 55 | extern const char *storage_types_name[]; 56 | 57 | /* 58 | * storage_module is a generic interface for storage modules. 59 | */ 60 | struct storage_module 61 | { 62 | enum storage_type type; 63 | void *context; 64 | bool is_initialized; 65 | int (*initialize)(struct storage_module *self); 66 | int (*ping)(struct storage_module *self); 67 | int (*store_report)(struct storage_module *self, struct payload *payload); 68 | int (*deinitialize)(struct storage_module *self); 69 | void (*destroy)(struct storage_module *self); 70 | }; 71 | 72 | /* 73 | * storage_module_get_type returns the type of the given storage module name. 74 | */ 75 | enum storage_type storage_module_get_type(const char *type_name); 76 | 77 | /* 78 | * storage_module_initialize initialize the storage module. 79 | */ 80 | int storage_module_initialize(struct storage_module *module); 81 | 82 | /* 83 | * storage_module_ping test if the storage module is working. 84 | */ 85 | int storage_module_ping(struct storage_module *module); 86 | 87 | /* 88 | * storage_module_store_report store a report using the storage module. 89 | */ 90 | int storage_module_store_report(struct storage_module *module, struct payload *payload); 91 | 92 | /* 93 | * storage_module_deinitialize deinitialize the storage module. 94 | */ 95 | int storage_module_deinitialize(struct storage_module *module); 96 | 97 | /* 98 | * storage_module_destroy free the allocated ressources for the storage module. 99 | */ 100 | void storage_module_destroy(struct storage_module *module); 101 | 102 | #endif /* STORAGE_H */ 103 | 104 | -------------------------------------------------------------------------------- /src/storage_csv.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "storage.h" 38 | #include "storage_csv.h" 39 | #include "config.h" 40 | 41 | static void 42 | group_fd_destroy(FILE **fd_ptr) 43 | { 44 | if (!*fd_ptr) 45 | return; 46 | 47 | fflush(*fd_ptr); 48 | fsync(fileno(*fd_ptr)); 49 | fclose(*fd_ptr); 50 | *fd_ptr = NULL; 51 | } 52 | 53 | static struct csv_context * 54 | csv_context_create(const char *sensor_name, const char *output_dir) 55 | { 56 | struct csv_context *ctx = (struct csv_context *) malloc(sizeof(struct csv_context)); 57 | 58 | if (!ctx) 59 | return NULL; 60 | 61 | ctx->config.output_dir = output_dir; 62 | ctx->config.sensor_name = sensor_name; 63 | 64 | ctx->groups_fd = zhashx_new(); 65 | zhashx_set_destructor(ctx->groups_fd, (zhashx_destructor_fn *) group_fd_destroy); 66 | 67 | ctx->groups_events = zhashx_new(); 68 | zhashx_set_destructor(ctx->groups_events, (zhashx_destructor_fn *) zlistx_destroy); 69 | 70 | return ctx; 71 | } 72 | 73 | static void 74 | csv_context_destroy(struct csv_context *ctx) 75 | { 76 | if (!ctx) 77 | return; 78 | 79 | zhashx_destroy(&ctx->groups_fd); 80 | zhashx_destroy(&ctx->groups_events); 81 | free(ctx); 82 | } 83 | 84 | static int 85 | csv_initialize(struct storage_module *module) 86 | { 87 | struct csv_context *ctx = (struct csv_context *) module->context; 88 | struct stat outdir_stat = {}; 89 | 90 | /* create output directory */ 91 | errno = 0; 92 | if (mkdir(ctx->config.output_dir, 0755) == -1) { 93 | /* ignore if directory already exists */ 94 | if (errno != EEXIST) { 95 | zsys_error("csv: failed to create output directory: %s", strerror(errno)); 96 | return -1; 97 | } 98 | } 99 | 100 | /* check if directory exists, above EEXIST check DO NOT guarantee that path is a directory */ 101 | errno = 0; 102 | if (stat(ctx->config.output_dir, &outdir_stat) == -1) { 103 | zsys_error("csv: failed to check output dir: %s", strerror(errno)); 104 | return -1; 105 | } 106 | if (!S_ISDIR(outdir_stat.st_mode)) { 107 | zsys_error("csv: output path already exists and is not a directory"); 108 | return -1; 109 | } 110 | 111 | /* check if we can write into the output directory */ 112 | errno = 0; 113 | if (access(ctx->config.output_dir, W_OK)) { 114 | zsys_error("csv: output path is not writable: %s", strerror(errno)); 115 | return -1; 116 | } 117 | 118 | module->is_initialized = true; 119 | return 0; 120 | } 121 | 122 | static int 123 | csv_ping(struct storage_module *module __attribute__ ((unused))) 124 | { 125 | /* ping is not needed because the relevant checks are done when initializing the module */ 126 | return 0; 127 | } 128 | 129 | static int 130 | write_group_header(struct csv_context *ctx, const char *group, FILE *fd, zhashx_t *events) 131 | { 132 | char buffer[CSV_LINE_BUFFER_SIZE] = {}; 133 | int pos = 0; 134 | zlistx_t *events_name = NULL; 135 | const char *event_name = NULL; 136 | 137 | events_name = zhashx_keys(events); 138 | if (!events_name) 139 | return -1; 140 | 141 | /* sort events by name */ 142 | zlistx_set_comparator(events_name, (zlistx_comparator_fn *) strcmp); 143 | zlistx_sort(events_name); 144 | 145 | /* write static elements to buffer */ 146 | pos += snprintf(buffer, CSV_LINE_BUFFER_SIZE, "timestamp,sensor,target,socket,cpu"); 147 | 148 | /* append dynamic elements (events) to buffer */ 149 | for (event_name = (const char * ) zlistx_first(events_name); event_name; event_name = (const char * ) zlistx_next(events_name)) { 150 | pos += snprintf(buffer + pos, CSV_LINE_BUFFER_SIZE - pos, ",%s", event_name); 151 | if (pos >= CSV_LINE_BUFFER_SIZE) 152 | goto error_buffer_too_small; 153 | } 154 | 155 | if (fprintf(fd, "%s\n", buffer) < 0) 156 | goto error_failed_write; 157 | 158 | /* force writing to the disk */ 159 | fflush(fd); 160 | 161 | /* store events name in the order written in header */ 162 | zhashx_insert(ctx->groups_events, group, events_name); 163 | 164 | return 0; 165 | 166 | error_failed_write: 167 | error_buffer_too_small: 168 | zlistx_destroy(&events_name); 169 | return -1; 170 | } 171 | 172 | static int 173 | open_group_outfile(struct csv_context *ctx, const char *group_name) 174 | { 175 | char path[PATH_MAX] = {}; 176 | int fd = -1; 177 | FILE *file = NULL; 178 | 179 | if (snprintf(path, PATH_MAX, "%s/%s.csv", ctx->config.output_dir, group_name) >= PATH_MAX) { 180 | zsys_error("csv: the destination path for output file of group %s is too long", group_name); 181 | return -1; 182 | } 183 | 184 | errno = 0; 185 | fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644); 186 | if (fd == -1) { 187 | zsys_error("csv: failed to open output file for group %s: %s", group_name, strerror(errno)); 188 | return -1; 189 | } 190 | 191 | errno = 0; 192 | file = fdopen(fd, "w"); 193 | if (!file) { 194 | zsys_error("csv: failed to associate a stream to output file of group %s: %s", group_name, strerror(errno)); 195 | close(fd); 196 | return -1; 197 | } 198 | 199 | zhashx_insert(ctx->groups_fd, group_name, file); 200 | return 0; 201 | } 202 | 203 | static int 204 | write_events_value(struct csv_context *ctx, const char *group, FILE *fd, uint64_t timestamp, const char *target, const char *socket, const char *cpu, zhashx_t *events) 205 | { 206 | zlistx_t *events_name = NULL; 207 | char buffer[CSV_LINE_BUFFER_SIZE] = {}; 208 | int pos = 0; 209 | const char *event_name = NULL; 210 | const uint64_t *event_value = NULL; 211 | 212 | /* get events name in the order of csv header */ 213 | events_name = (zlistx_t *) zhashx_lookup(ctx->groups_events, group); 214 | if (!events_name) 215 | return -1; 216 | 217 | /* write static elements to buffer */ 218 | pos += snprintf(buffer, CSV_LINE_BUFFER_SIZE, "%" PRIu64 ",%s,%s,%s,%s", timestamp, ctx->config.sensor_name, target, socket, cpu); 219 | 220 | /* write dynamic elements (events) to buffer */ 221 | for (event_name = (const char *) zlistx_first(events_name); event_name; event_name = (const char * ) zlistx_next(events_name)) { 222 | event_value = (uint64_t *) zhashx_lookup(events, event_name); 223 | if (!event_value) 224 | return -1; 225 | 226 | pos += snprintf(buffer + pos, CSV_LINE_BUFFER_SIZE - pos, ",%" PRIu64, *event_value); 227 | if (pos >= CSV_LINE_BUFFER_SIZE) 228 | return -1; 229 | } 230 | 231 | if (fprintf(fd, "%s\n", buffer) < 0) 232 | return -1; 233 | 234 | return 0; 235 | } 236 | 237 | static int 238 | csv_store_report(struct storage_module *module, struct payload *payload) 239 | { 240 | struct csv_context *ctx = (struct csv_context *) module->context; 241 | struct payload_group_data *group_data = NULL; 242 | const char *group_name = NULL; 243 | FILE *group_fd = NULL; 244 | bool write_header = false; 245 | struct payload_pkg_data *pkg_data = NULL; 246 | const char *pkg_id = NULL; 247 | struct payload_cpu_data *cpu_data = NULL; 248 | const char *cpu_id = NULL; 249 | 250 | /* 251 | * write report into csv file as following: 252 | * timestamp,sensor,target,socket,cpu,INSTRUCTIONS_RETIRED,LLC_MISSES 253 | * 1538327257673,grvingt-64,system,0,56,5996,108 254 | */ 255 | for (group_data = (struct payload_group_data *) zhashx_first(payload->groups); group_data; group_data = (struct payload_group_data *) zhashx_next(payload->groups)) { 256 | group_name = (const char *) zhashx_cursor(payload->groups); 257 | group_fd = (FILE *) zhashx_lookup(ctx->groups_fd, group_name); 258 | if (!group_fd) { 259 | if (open_group_outfile(ctx, group_name)) 260 | return -1; 261 | 262 | group_fd = (FILE *) zhashx_lookup(ctx->groups_fd, group_name); 263 | write_header = true; 264 | } 265 | 266 | for (pkg_data = (struct payload_pkg_data *) zhashx_first(group_data->pkgs); pkg_data; pkg_data = (struct payload_pkg_data *) zhashx_next(group_data->pkgs)) { 267 | pkg_id = (const char *) zhashx_cursor(group_data->pkgs); 268 | 269 | for (cpu_data = (struct payload_cpu_data *) zhashx_first(pkg_data->cpus); cpu_data; cpu_data = (struct payload_cpu_data *) zhashx_next(pkg_data->cpus)) { 270 | cpu_id = (const char *) zhashx_cursor(pkg_data->cpus); 271 | 272 | if (write_header) { 273 | if (write_group_header(ctx, group_name, group_fd, cpu_data->events)) { 274 | zsys_error("csv: failed to write header to file for group=%s", group_name); 275 | return -1; 276 | } 277 | write_header = false; 278 | } 279 | if (write_events_value(ctx, group_name, group_fd, payload->timestamp, payload->target_name, pkg_id, cpu_id, cpu_data->events)) { 280 | zsys_error("csv: failed to write report to file for group=%s timestamp=%" PRIu64, group_name, payload->timestamp); 281 | return -1; 282 | } 283 | } 284 | } 285 | } 286 | 287 | return 0; 288 | } 289 | 290 | static int 291 | csv_deinitialize(struct storage_module *module) 292 | { 293 | module->is_initialized = false; 294 | return 0; 295 | } 296 | 297 | static void 298 | csv_destroy(struct storage_module *module) 299 | { 300 | if (!module) 301 | return; 302 | 303 | csv_context_destroy((csv_context *) module->context); 304 | } 305 | 306 | struct storage_module * 307 | storage_csv_create(struct config *config) 308 | { 309 | struct storage_module *module = NULL; 310 | struct csv_context *ctx = NULL; 311 | 312 | module = (struct storage_module *) malloc(sizeof(struct storage_module)); 313 | if (!module) 314 | goto error; 315 | 316 | ctx = csv_context_create(config->sensor.name, config->storage.csv.outdir); 317 | if (!ctx) 318 | goto error; 319 | 320 | module->type = STORAGE_CSV; 321 | module->context = ctx; 322 | module->is_initialized = false; 323 | module->initialize = csv_initialize; 324 | module->ping = csv_ping; 325 | module->store_report = csv_store_report; 326 | module->deinitialize = csv_deinitialize; 327 | module->destroy = csv_destroy; 328 | 329 | return module; 330 | 331 | error: 332 | csv_context_destroy(ctx); 333 | free(module); 334 | return NULL; 335 | } 336 | 337 | -------------------------------------------------------------------------------- /src/storage_csv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef STORAGE_CSV_H 33 | #define STORAGE_CSV_H 34 | 35 | #include 36 | 37 | #include "config.h" 38 | 39 | /* 40 | * CSV_LINE_BUFFER_SIZE stores the maximum length of a line in a group csv output file. 41 | */ 42 | #define CSV_LINE_BUFFER_SIZE 1024 43 | 44 | /* 45 | * csv_config stores the required information for the module. 46 | */ 47 | struct csv_config 48 | { 49 | const char *sensor_name; 50 | const char *output_dir; 51 | }; 52 | 53 | /* 54 | * csv_context stores the context of the module. 55 | */ 56 | struct csv_context 57 | { 58 | struct csv_config config; 59 | zhashx_t *groups_fd; /* char *group_name -> FILE *fd */ 60 | zhashx_t *groups_events; /* char *group_name -> zlistx_t *group_events */ 61 | }; 62 | 63 | /* 64 | * storage_csv_create creates and configure a csv storage module.. 65 | */ 66 | struct storage_module *storage_csv_create(struct config *config); 67 | 68 | #endif /* CSV_H */ 69 | 70 | -------------------------------------------------------------------------------- /src/storage_mongodb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | 34 | #include "report.h" 35 | #include "storage_mongodb.h" 36 | #include "perf.h" 37 | 38 | static struct mongodb_context * 39 | mongodb_context_create(const char *sensor_name, const char *uri, const char *database, const char *collection) 40 | { 41 | struct mongodb_context *ctx = (struct mongodb_context *) malloc(sizeof(struct mongodb_context)); 42 | 43 | if (!ctx) 44 | return NULL; 45 | 46 | ctx->config.sensor_name = sensor_name; 47 | ctx->config.uri = uri; 48 | ctx->config.database_name = database; 49 | ctx->config.collection_name = collection; 50 | 51 | ctx->client = NULL; 52 | ctx->collection = NULL; 53 | 54 | return ctx; 55 | } 56 | 57 | static void 58 | mongodb_context_destroy(struct mongodb_context *ctx) 59 | { 60 | if (!ctx) 61 | return; 62 | 63 | free(ctx); 64 | } 65 | 66 | static int 67 | mongodb_initialize(struct storage_module *module) 68 | { 69 | struct mongodb_context *ctx = (struct mongodb_context *) module->context; 70 | bson_error_t error; 71 | 72 | if (module->is_initialized) 73 | return -1; 74 | 75 | mongoc_init(); 76 | 77 | ctx->uri = mongoc_uri_new_with_error(ctx->config.uri, &error); 78 | if (!ctx->uri) { 79 | zsys_error("mongodb: failed to parse uri: %s", error.message); 80 | goto error; 81 | } 82 | 83 | ctx->client = mongoc_client_new_from_uri(ctx->uri); 84 | if (!ctx->client) { 85 | zsys_error("mongodb: failed to create client"); 86 | goto error; 87 | } 88 | 89 | ctx->collection = mongoc_client_get_collection(ctx->client, ctx->config.database_name, ctx->config.collection_name); 90 | /* collection is automatically created if non-existent */ 91 | 92 | module->is_initialized = true; 93 | return 0; 94 | 95 | error: 96 | mongoc_uri_destroy(ctx->uri); 97 | mongoc_client_destroy(ctx->client); 98 | return -1; 99 | } 100 | 101 | static int 102 | mongodb_ping(struct storage_module *module) 103 | { 104 | struct mongodb_context *ctx = (struct mongodb_context *) module->context; 105 | int ret = 0; 106 | bson_t *ping_cmd = BCON_NEW("ping", BCON_INT32(1)); 107 | bson_error_t error; 108 | 109 | if (!mongoc_client_command_simple(ctx->client, "admin", ping_cmd, NULL, NULL, &error)) { 110 | zsys_error("mongodb: failed to ping mongodb server: %s", error.message); 111 | ret = -1; 112 | } 113 | 114 | bson_destroy(ping_cmd); 115 | return ret; 116 | } 117 | 118 | static int 119 | mongodb_store_report(struct storage_module *module, struct payload *payload) 120 | { 121 | struct mongodb_context *ctx = (struct mongodb_context *) module->context; 122 | bson_t document = BSON_INITIALIZER; 123 | bson_t doc_groups; 124 | struct payload_group_data *group_data = NULL; 125 | const char *group_name = NULL; 126 | bson_t doc_group; 127 | struct payload_pkg_data *pkg_data = NULL; 128 | const char *pkg_id = NULL; 129 | bson_t doc_pkg; 130 | struct payload_cpu_data *cpu_data = NULL; 131 | const char *cpu_id = NULL; 132 | bson_t doc_cpu; 133 | const char *event_name = NULL; 134 | uint64_t *event_value = NULL; 135 | bson_error_t error; 136 | int ret = 0; 137 | 138 | /* 139 | * construct mongodb document as following: 140 | * { 141 | * "timestamp": 1529868713854, 142 | * "sensor": "test.cluster.lan", 143 | * "target": "example", 144 | * "groups": { 145 | * "group_name": { 146 | * "pkg_id": { 147 | * "cpu_id": { 148 | * "time_enabled": 12345, 149 | * "time_running": 12345, 150 | * "event_name": 123456789.0, 151 | * more events... 152 | * }, 153 | * more cpus... 154 | * }, 155 | * more pkgs... 156 | * }, 157 | * more groups... 158 | * } 159 | * } 160 | */ 161 | BSON_APPEND_DATE_TIME(&document, "timestamp", payload->timestamp); 162 | BSON_APPEND_UTF8(&document, "sensor", ctx->config.sensor_name); 163 | BSON_APPEND_UTF8(&document, "target", payload->target_name); 164 | 165 | BSON_APPEND_DOCUMENT_BEGIN(&document, "groups", &doc_groups); 166 | for (group_data = (struct payload_group_data *) zhashx_first(payload->groups); group_data; group_data = (struct payload_group_data *) zhashx_next(payload->groups)) { 167 | group_name = (const char *) zhashx_cursor(payload->groups); 168 | BSON_APPEND_DOCUMENT_BEGIN(&doc_groups, group_name, &doc_group); 169 | 170 | for (pkg_data = (struct payload_pkg_data *) zhashx_first(group_data->pkgs); pkg_data; pkg_data = (struct payload_pkg_data *) zhashx_next(group_data->pkgs)) { 171 | pkg_id = (const char * ) zhashx_cursor(group_data->pkgs); 172 | BSON_APPEND_DOCUMENT_BEGIN(&doc_group, pkg_id, &doc_pkg); 173 | 174 | for (cpu_data = (struct payload_cpu_data *) zhashx_first(pkg_data->cpus); cpu_data; cpu_data = (struct payload_cpu_data *) zhashx_next(pkg_data->cpus)) { 175 | cpu_id = (const char *) zhashx_cursor(pkg_data->cpus); 176 | BSON_APPEND_DOCUMENT_BEGIN(&doc_pkg, cpu_id, &doc_cpu); 177 | 178 | for (event_value = (uint64_t *) zhashx_first(cpu_data->events); event_value; event_value = (uint64_t *) zhashx_next(cpu_data->events)) { 179 | event_name = (const char *) zhashx_cursor(cpu_data->events); 180 | BSON_APPEND_DOUBLE(&doc_cpu, event_name, *event_value); 181 | } 182 | 183 | bson_append_document_end(&doc_pkg, &doc_cpu); 184 | } 185 | 186 | bson_append_document_end(&doc_group, &doc_pkg); 187 | } 188 | 189 | bson_append_document_end(&doc_groups, &doc_group); 190 | } 191 | bson_append_document_end(&document, &doc_groups); 192 | 193 | /* insert document into collection */ 194 | if (!mongoc_collection_insert_one(ctx->collection, &document, NULL, NULL, &error)) { 195 | zsys_error("mongodb: failed insert timestamp=%lu target=%s: %s", payload->timestamp, payload->target_name, error.message); 196 | ret = -1; 197 | } 198 | 199 | bson_destroy(&document); 200 | return ret; 201 | } 202 | 203 | static int 204 | mongodb_deinitialize(struct storage_module *module __attribute__ ((unused))) 205 | { 206 | struct mongodb_context *ctx = (struct mongodb_context *) module->context; 207 | 208 | if (!module->is_initialized) 209 | return -1; 210 | 211 | mongoc_collection_destroy(ctx->collection); 212 | mongoc_client_destroy(ctx->client); 213 | mongoc_uri_destroy(ctx->uri); 214 | mongoc_cleanup(); 215 | module->is_initialized = false; 216 | return 0; 217 | } 218 | 219 | static void 220 | mongodb_destroy(struct storage_module *module) 221 | { 222 | if (!module) 223 | return; 224 | 225 | mongodb_context_destroy((struct mongodb_context *) module->context); 226 | } 227 | 228 | struct storage_module * 229 | storage_mongodb_create(struct config *config) 230 | { 231 | struct storage_module *module = NULL; 232 | struct mongodb_context *ctx = NULL; 233 | 234 | module = (struct storage_module *) malloc(sizeof(struct storage_module)); 235 | if (!module) 236 | goto error; 237 | 238 | ctx = mongodb_context_create(config->sensor.name, config->storage.mongodb.uri, config->storage.mongodb.database, config->storage.mongodb.collection); 239 | if (!ctx) 240 | goto error; 241 | 242 | module->type = STORAGE_MONGODB; 243 | module->context = ctx; 244 | module->is_initialized = false; 245 | module->initialize = mongodb_initialize; 246 | module->ping = mongodb_ping; 247 | module->store_report = mongodb_store_report; 248 | module->deinitialize = mongodb_deinitialize; 249 | module->destroy = mongodb_destroy; 250 | 251 | return module; 252 | 253 | error: 254 | mongodb_context_destroy(ctx); 255 | free(module); 256 | return NULL; 257 | } 258 | 259 | -------------------------------------------------------------------------------- /src/storage_mongodb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef STORAGE_MONGODB_H 33 | #define STORAGE_MONGODB_H 34 | 35 | #include 36 | 37 | #include "storage.h" 38 | #include "config.h" 39 | 40 | 41 | /* 42 | * mongodb_config stores the required information for the module. 43 | */ 44 | struct mongodb_config 45 | { 46 | const char *sensor_name; 47 | const char *uri; 48 | const char *database_name; 49 | const char *collection_name; 50 | }; 51 | 52 | /* 53 | * mongodb_context stores the context of the module. 54 | */ 55 | struct mongodb_context 56 | { 57 | struct mongodb_config config; 58 | mongoc_uri_t *uri; 59 | mongoc_client_t *client; 60 | mongoc_collection_t *collection; 61 | }; 62 | 63 | /* 64 | * storage_mongodb_create creates and configure a mongodb storage module. 65 | */ 66 | struct storage_module *storage_mongodb_create(struct config *config); 67 | 68 | #endif /* STORAGE_MONGODB_H */ 69 | 70 | -------------------------------------------------------------------------------- /src/storage_null.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "storage.h" 33 | #include "storage_null.h" 34 | #include "config.h" 35 | 36 | static int 37 | null_initialize(struct storage_module *module) 38 | { 39 | zsys_warning("null: this output module should be used for debug only, no data will be stored"); 40 | 41 | module->is_initialized = true; 42 | return 0; 43 | } 44 | 45 | static int 46 | null_ping(struct storage_module *module __attribute__ ((unused))) 47 | { 48 | return 0; 49 | } 50 | 51 | static int 52 | null_store_report(struct storage_module *module __attribute__ ((unused)), struct payload *payload __attribute__ ((unused))) 53 | { 54 | return 0; 55 | } 56 | 57 | static int 58 | null_deinitialize(struct storage_module *module) 59 | { 60 | module->is_initialized = false; 61 | return 0; 62 | } 63 | 64 | static void 65 | null_destroy(struct storage_module *module __attribute__ ((unused))) 66 | { 67 | return; 68 | } 69 | 70 | struct storage_module * 71 | storage_null_create(struct config *config __attribute__ ((unused))) 72 | { 73 | struct storage_module *module = (struct storage_module *) malloc(sizeof(struct storage_module)); 74 | 75 | if (!module) 76 | return NULL; 77 | 78 | module->type = STORAGE_NULL; 79 | module->context = NULL; 80 | module->is_initialized = false; 81 | module->initialize = null_initialize; 82 | module->ping = null_ping; 83 | module->store_report = null_store_report; 84 | module->deinitialize = null_deinitialize; 85 | module->destroy = null_destroy; 86 | 87 | return module; 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/storage_null.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef STORAGE_NULL_H 33 | #define STORAGE_NULL_H 34 | 35 | #include "config.h" 36 | 37 | /* 38 | * storage_null_create creates and configure a null storage module. 39 | */ 40 | struct storage_module *storage_null_create(struct config *config); 41 | 42 | #endif /* STORAGE_NULL_H */ 43 | -------------------------------------------------------------------------------- /src/storage_socket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, INRIA 3 | * Copyright (c) 2021, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "perf.h" 42 | #include "report.h" 43 | #include "storage_socket.h" 44 | 45 | static struct socket_context * 46 | socket_context_create(const char *sensor_name, const char *address, const char *port) 47 | { 48 | struct socket_context *ctx = (struct socket_context *) malloc(sizeof(struct socket_context)); 49 | 50 | if (!ctx) 51 | return NULL; 52 | 53 | ctx->config.sensor_name = sensor_name; 54 | ctx->config.address = address; 55 | ctx->config.port = port; 56 | 57 | ctx->socket_fd = -1; 58 | ctx->last_retry_time = 0; 59 | ctx->retry_backoff_time = 1; 60 | 61 | return ctx; 62 | } 63 | 64 | static void 65 | socket_context_destroy(struct socket_context *ctx) 66 | { 67 | if (!ctx) 68 | return; 69 | 70 | free(ctx); 71 | } 72 | 73 | static int 74 | socket_resolve_and_connect(struct socket_context *ctx) 75 | { 76 | struct addrinfo hints = {}; 77 | struct addrinfo *result = NULL, *rp = NULL; 78 | int sfd = -1; 79 | int ret = -1; 80 | 81 | /* setup hints for address resolution */ 82 | hints.ai_family = AF_UNSPEC; 83 | hints.ai_socktype = SOCK_STREAM; 84 | 85 | if (getaddrinfo(ctx->config.address, ctx->config.port, &hints, &result)) { 86 | zsys_error("socket: Unable to resolve address: %s", ctx->config.address); 87 | goto error_no_getaddrinfo; 88 | } 89 | 90 | /* attemps to connect to any of the resolved address(es) */ 91 | for (rp = result; rp; rp = rp->ai_next) { 92 | sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); 93 | if (sfd == -1) 94 | continue; 95 | 96 | ret = connect(sfd, rp->ai_addr, rp->ai_addrlen); 97 | if (!ret) { 98 | zsys_info("socket: Successfully connected to %s:%s", ctx->config.address, ctx->config.port); 99 | break; 100 | } 101 | 102 | close(sfd); 103 | } 104 | 105 | /* no connection have been established */ 106 | if (ret == -1) { 107 | zsys_error("socket: Failed to connect to %s:%s", ctx->config.address, ctx->config.port); 108 | goto error_not_connected; 109 | } 110 | 111 | ctx->socket_fd = sfd; 112 | 113 | error_not_connected: 114 | freeaddrinfo(result); 115 | error_no_getaddrinfo: 116 | return ret; 117 | } 118 | 119 | static int 120 | socket_initialize(struct storage_module *module) 121 | { 122 | struct socket_context *ctx = (struct socket_context *) module->context; 123 | 124 | if (module->is_initialized) 125 | return -1; 126 | 127 | if (socket_resolve_and_connect(ctx)) 128 | return -1; 129 | 130 | module->is_initialized = 1; 131 | return 0; 132 | } 133 | 134 | static int 135 | socket_ping(struct storage_module *module __attribute__ ((unused))) 136 | { 137 | /* ping is not supported by this module */ 138 | return 0; 139 | } 140 | 141 | static int 142 | socket_try_reconnect(struct socket_context *ctx) 143 | { 144 | time_t current_time = time(NULL); 145 | ssize_t nbrand; 146 | uint8_t rand_jitter; 147 | 148 | /* close the current socket */ 149 | if (ctx->socket_fd != -1) { 150 | close(ctx->socket_fd); 151 | ctx->socket_fd = -1; 152 | } 153 | 154 | /* retry socket connection with an exponential backoff */ 155 | if (difftime(current_time, ctx->last_retry_time) >= (double) ctx->retry_backoff_time) { 156 | if (socket_resolve_and_connect(ctx)) { 157 | ctx->last_retry_time = current_time; 158 | if (ctx->retry_backoff_time < MAX_DURATION_CONNECTION_RETRY) { 159 | nbrand = getrandom(&rand_jitter, sizeof(uint8_t), 0); 160 | ctx->retry_backoff_time = ctx->retry_backoff_time * 2 + (nbrand != -1 ? rand_jitter % 10 : 0); 161 | } 162 | 163 | zsys_error("socket: Failed to reconnect, next try will be in %d seconds", ctx->retry_backoff_time); 164 | return -1; 165 | } else { 166 | ctx->last_retry_time = 0; 167 | ctx->retry_backoff_time = 1; 168 | 169 | zsys_info("socket: Connection recovered, resuming operation"); 170 | return 0; 171 | } 172 | } 173 | 174 | return -1; 175 | } 176 | 177 | static int 178 | socket_store_report(struct storage_module *module, struct payload *payload) 179 | { 180 | struct socket_context *ctx = (struct socket_context *) module->context; 181 | struct json_object *jobj = NULL; 182 | struct json_object *jobj_groups = NULL; 183 | struct payload_group_data *group_data = NULL; 184 | const char *group_name = NULL; 185 | struct json_object *jobj_group = NULL; 186 | struct payload_pkg_data *pkg_data = NULL; 187 | const char *pkg_id = NULL; 188 | struct json_object *jobj_pkg = NULL; 189 | struct payload_cpu_data *cpu_data = NULL; 190 | const char *cpu_id = NULL; 191 | struct json_object *jobj_cpu = NULL; 192 | const char *event_name = NULL; 193 | uint64_t *event_value = NULL; 194 | const char *json_report = NULL; 195 | size_t json_report_length = 0; 196 | struct iovec socket_iov[2] = {}; 197 | ssize_t nbsend; 198 | int retry_once = 1; 199 | int ret = -1; 200 | 201 | /* try to reconnect the socket before building the document */ 202 | if (ctx->socket_fd == -1) { 203 | if (socket_try_reconnect(ctx)) 204 | return -1; 205 | } 206 | 207 | /* 208 | * { 209 | * "timestamp": 1529868713854, 210 | * "sensor": "test.cluster.lan", 211 | * "target": "example", 212 | * "groups": { 213 | * "group_name": { 214 | * "pkg_id": { 215 | * "cpu_id": { 216 | * "time_enabled": 12345, 217 | * "time_running": 12345, 218 | * "event_name": 1234567890, 219 | * more events... 220 | * }, 221 | * more cpus... 222 | * }, 223 | * more pkgs... 224 | * }, 225 | * more groups... 226 | * } 227 | * } 228 | */ 229 | jobj = json_object_new_object(); 230 | 231 | json_object_object_add(jobj, "timestamp", json_object_new_uint64(payload->timestamp)); 232 | json_object_object_add(jobj, "sensor", json_object_new_string(ctx->config.sensor_name)); 233 | json_object_object_add(jobj, "target", json_object_new_string(payload->target_name)); 234 | 235 | jobj_groups = json_object_new_object(); 236 | json_object_object_add(jobj, "groups", jobj_groups); 237 | for (group_data = (struct payload_group_data *) zhashx_first(payload->groups); group_data; group_data = (struct payload_group_data *) zhashx_next(payload->groups)) { 238 | 239 | jobj_group = json_object_new_object(); 240 | group_name = (const char *) zhashx_cursor(payload->groups); 241 | json_object_object_add(jobj_groups, group_name, jobj_group); 242 | for (pkg_data = (struct payload_pkg_data *) zhashx_first(group_data->pkgs); pkg_data; pkg_data = (struct payload_pkg_data *) zhashx_next(group_data->pkgs)) { 243 | 244 | jobj_pkg = json_object_new_object(); 245 | pkg_id = (const char *) zhashx_cursor(group_data->pkgs); 246 | json_object_object_add(jobj_group, pkg_id, jobj_pkg); 247 | for (cpu_data = (struct payload_cpu_data *) zhashx_first(pkg_data->cpus); cpu_data; cpu_data = (struct payload_cpu_data *) zhashx_next(pkg_data->cpus)) { 248 | jobj_cpu = json_object_new_object(); 249 | cpu_id = (const char *) zhashx_cursor(pkg_data->cpus); 250 | json_object_object_add(jobj_pkg, cpu_id, jobj_cpu); 251 | 252 | for (event_value = (uint64_t *) zhashx_first(cpu_data->events); event_value; event_value = (uint64_t *) zhashx_next(cpu_data->events)) { 253 | event_name = (const char *) zhashx_cursor(cpu_data->events); 254 | json_object_object_add(jobj_cpu, event_name, json_object_new_uint64(*event_value)); 255 | } 256 | } 257 | } 258 | } 259 | 260 | json_report = json_object_to_json_string_length(jobj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE, &json_report_length); 261 | if (json_report == NULL) { 262 | zsys_error("socket: Failed to convert report to json string"); 263 | goto error_json_to_string; 264 | } 265 | 266 | /* 267 | * PowerAPI socketdb requires a newline character at the end of the json document. 268 | * Using POSIX IOV allows to efficiently append it at the end of the json string. 269 | */ 270 | socket_iov[0].iov_base = (void *) json_report; 271 | socket_iov[0].iov_len = json_report_length; 272 | socket_iov[1].iov_base = (void *) "\n"; 273 | socket_iov[1].iov_len = 1; 274 | 275 | do { 276 | /* 277 | * Try to send the serialized report to the endpoint. 278 | * If the connection have been lost, try to reconnect and send the report again. 279 | * The exponential backoff on socket reconnect prevents consecutive attempts. 280 | */ 281 | errno = 0; 282 | nbsend = writev(ctx->socket_fd, socket_iov, 2); 283 | if (nbsend == -1) { 284 | zsys_error("socket: Sending the report failed with error: %s", strerror(errno)); 285 | 286 | if (retry_once) { 287 | zsys_info("socket: Connection has been lost, attempting to reconnect..."); 288 | if (!socket_try_reconnect(ctx)) 289 | continue; 290 | } 291 | 292 | goto error_socket_disconnected; 293 | } 294 | } while (nbsend == -1 && retry_once--); 295 | 296 | ret = 0; 297 | 298 | error_json_to_string: 299 | error_socket_disconnected: 300 | json_object_put(jobj); 301 | return ret; 302 | } 303 | 304 | static int 305 | socket_deinitialize(struct storage_module *module) 306 | { 307 | struct socket_context *ctx = (struct socket_context *) module->context; 308 | 309 | if (!module->is_initialized) 310 | return -1; 311 | 312 | if (ctx->socket_fd != -1) 313 | close(ctx->socket_fd); 314 | 315 | return 0; 316 | } 317 | 318 | static void 319 | socket_destroy(struct storage_module *module) 320 | { 321 | if (!module) 322 | return; 323 | 324 | socket_context_destroy((struct socket_context *) module->context); 325 | } 326 | 327 | struct storage_module * 328 | storage_socket_create(struct config *config) 329 | { 330 | struct storage_module *module = NULL; 331 | struct socket_context *ctx = NULL; 332 | 333 | module = (struct storage_module *) malloc(sizeof(struct storage_module)); 334 | if (!module) 335 | goto error; 336 | 337 | ctx = socket_context_create(config->sensor.name, config->storage.socket.hostname, config->storage.socket.port); 338 | if (!ctx) 339 | goto error; 340 | 341 | module->type = STORAGE_SOCKET; 342 | module->context = ctx; 343 | module->is_initialized = false; 344 | module->initialize = socket_initialize; 345 | module->ping = socket_ping; 346 | module->store_report = socket_store_report; 347 | module->deinitialize = socket_deinitialize; 348 | module->destroy = socket_destroy; 349 | 350 | return module; 351 | 352 | error: 353 | socket_context_destroy(ctx); 354 | free(module); 355 | return NULL; 356 | } 357 | -------------------------------------------------------------------------------- /src/storage_socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021, INRIA 3 | * Copyright (c) 2021, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef STORAGE_SOCKET_H 33 | #define STORAGE_SOCKET_H 34 | 35 | #include "storage.h" 36 | #include "config.h" 37 | 38 | /* 39 | * MAX_DURATION_CONNECTION_RETRY stores the maximal value of a connection retry. (in seconds) 40 | */ 41 | #define MAX_DURATION_CONNECTION_RETRY 1800 42 | 43 | /* 44 | * socket_config stores the required information for the module. 45 | */ 46 | struct socket_config 47 | { 48 | const char *sensor_name; 49 | const char *address; 50 | const char *port; 51 | }; 52 | 53 | /* 54 | * socket_context stores the context of the module. 55 | */ 56 | struct socket_context 57 | { 58 | struct socket_config config; 59 | int socket_fd; 60 | time_t last_retry_time; 61 | time_t retry_backoff_time; 62 | }; 63 | 64 | /* 65 | * storage_socket_create creates and configure a socket storage module. 66 | */ 67 | struct storage_module *storage_socket_create(struct config *config); 68 | 69 | #endif /* STORAGE_SOCKET_H */ 70 | -------------------------------------------------------------------------------- /src/target.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "target.h" 41 | #include "target_docker.h" 42 | #include "target_kubernetes.h" 43 | 44 | 45 | enum target_type 46 | target_detect_type(const char *cgroup_path) 47 | { 48 | /* All running processes/threads (not a cgroup) */ 49 | if (!cgroup_path) 50 | return TARGET_TYPE_ALL; 51 | 52 | /* System (running processes/threads in system cgroup) */ 53 | if (strstr(cgroup_path, "perf_event/system")) 54 | return TARGET_TYPE_SYSTEM; 55 | 56 | /* Kernel (running processes/threads in kernel cgroup) */ 57 | if (strstr(cgroup_path, "perf_event/kernel")) 58 | return TARGET_TYPE_KERNEL; 59 | 60 | /* Docker (running containers) */ 61 | if (strstr(cgroup_path, "perf_event/docker")) 62 | return TARGET_TYPE_DOCKER; 63 | 64 | /* Kubernetes (running containers) */ 65 | if (strstr(cgroup_path, "perf_event/kubepods")) 66 | return TARGET_TYPE_KUBERNETES; 67 | 68 | /* LibVirt (running virtual machine) */ 69 | if (strstr(cgroup_path, "perf_event/machine.slice")) 70 | return TARGET_TYPE_LIBVIRT; 71 | 72 | /* LXC (running containers) */ 73 | if (strstr(cgroup_path, "perf_event/lxc")) 74 | return TARGET_TYPE_LXC; 75 | 76 | return TARGET_TYPE_UNKNOWN; 77 | } 78 | 79 | int 80 | target_validate_type(enum target_type type, const char *cgroup_path) 81 | { 82 | switch (type) { 83 | case TARGET_TYPE_DOCKER: 84 | return target_docker_validate(cgroup_path); 85 | 86 | case TARGET_TYPE_KUBERNETES: 87 | return target_kubernetes_validate(cgroup_path); 88 | 89 | default: 90 | /* other types does not need a validation */ 91 | return true; 92 | } 93 | } 94 | 95 | struct target * 96 | target_create(enum target_type type, const char *cgroup_basedir, const char *cgroup_path) 97 | { 98 | struct target *target = (struct target *) malloc(sizeof(struct target)); 99 | 100 | if (!target) 101 | return NULL; 102 | 103 | target->cgroup_basedir = cgroup_basedir; 104 | target->cgroup_path = (cgroup_path) ? strdup(cgroup_path) : NULL; 105 | target->type = type; 106 | 107 | return target; 108 | } 109 | 110 | char * 111 | target_resolve_real_name(struct target *target) 112 | { 113 | char *target_real_name = NULL; 114 | 115 | switch (target->type) { 116 | case TARGET_TYPE_DOCKER: 117 | target_real_name = target_docker_resolve_name(target); 118 | break; 119 | 120 | case TARGET_TYPE_KUBERNETES: 121 | target_real_name = target_kubernetes_resolve_name(target); 122 | break; 123 | 124 | case TARGET_TYPE_ALL: 125 | target_real_name = strdup("all"); 126 | break; 127 | 128 | case TARGET_TYPE_KERNEL: 129 | target_real_name = strdup("kernel"); 130 | break; 131 | 132 | case TARGET_TYPE_SYSTEM: 133 | target_real_name = strdup("system"); 134 | break; 135 | 136 | default: 137 | break; 138 | } 139 | 140 | /* if name cannot be resolved, use the cgroup path relative to cgroup base dir */ 141 | if (!target_real_name && target->cgroup_path && target->cgroup_basedir) { 142 | target_real_name = strdup(target->cgroup_path + strlen(target->cgroup_basedir)); 143 | } 144 | 145 | return target_real_name; 146 | } 147 | 148 | void 149 | target_destroy(struct target *target) 150 | { 151 | if (!target) 152 | return; 153 | 154 | free(target->cgroup_path); 155 | free(target); 156 | } 157 | 158 | int 159 | target_discover_running(const char *base_path, enum target_type type_mask, zhashx_t *targets) 160 | { 161 | const char *path[] = { base_path, NULL }; 162 | FTS *file_system = NULL; 163 | FTSENT *node = NULL; 164 | enum target_type type; 165 | struct target *target = NULL; 166 | 167 | file_system = fts_open((char * const *)path, FTS_LOGICAL | FTS_NOCHDIR, NULL); 168 | if (!file_system) 169 | return -1; 170 | 171 | for (node = fts_read(file_system); node; node = fts_read(file_system)) { 172 | /* 173 | * Filtering the directories having 2 hard links leading to them to only get leaves directories. 174 | * The cgroup subsystems does not support hard links, so this will always work. 175 | */ 176 | if (node->fts_info == FTS_D && node->fts_statp->st_nlink == 2) { 177 | type = target_detect_type(node->fts_path); 178 | if ((type & type_mask) && target_validate_type(type, node->fts_path)) { 179 | target = target_create(type, base_path, node->fts_path); 180 | if (target) 181 | zhashx_insert(targets, node->fts_path, target); 182 | } 183 | } 184 | } 185 | 186 | fts_close(file_system); 187 | return 0; 188 | } 189 | 190 | -------------------------------------------------------------------------------- /src/target.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef TARGET_H 33 | #define TARGET_H 34 | 35 | #include 36 | 37 | /* 38 | * target_type stores the supported target types. 39 | */ 40 | enum target_type 41 | { 42 | TARGET_TYPE_UNKNOWN = 1, 43 | TARGET_TYPE_ALL = 2, 44 | TARGET_TYPE_SYSTEM = 4, 45 | TARGET_TYPE_KERNEL = 8, 46 | TARGET_TYPE_DOCKER = 16, 47 | TARGET_TYPE_KUBERNETES = 32, 48 | TARGET_TYPE_LIBVIRT = 64, 49 | TARGET_TYPE_LXC = 128, 50 | TARGET_TYPE_EVERYTHING = 255 51 | }; 52 | 53 | /* 54 | * target stores various information about the target. 55 | */ 56 | struct target 57 | { 58 | enum target_type type; 59 | const char *cgroup_basedir; 60 | char *cgroup_path; 61 | }; 62 | 63 | /* 64 | * target_detect_type returns the target type of the given cgroup path. 65 | */ 66 | enum target_type target_detect_type(const char *cgroup_path); 67 | 68 | /* 69 | * target_validate_type validate the target type of the given cgroup path. 70 | */ 71 | int target_validate_type(enum target_type type, const char *cgroup_path); 72 | 73 | /* 74 | * target_create allocate the resources and configure the target. 75 | */ 76 | struct target *target_create(enum target_type type, const char *cgroup_basedir, const char *cgroup_path); 77 | 78 | /* 79 | * target_resolve_real_name resolve and return the real name of the given target. 80 | */ 81 | char *target_resolve_real_name(struct target *target); 82 | 83 | /* 84 | * target_destroy free the allocated resources for the target. 85 | */ 86 | void target_destroy(struct target *target); 87 | 88 | /* 89 | * target_discover_running returns a list of running targets. 90 | */ 91 | int target_discover_running(const char *base_path, enum target_type type_mask, zhashx_t *targets); 92 | 93 | #endif /* TARGET_H */ 94 | 95 | -------------------------------------------------------------------------------- /src/target_docker.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "target.h" 39 | #include "target_docker.h" 40 | 41 | /* 42 | * CONTAINER_ID_REGEX is the regex used to extract the Docker container id from the cgroup path. 43 | * CONTAINER_ID_REGEX_EXPECTED_MATCHES is the number of expected matches from the regex. (num groups + 1) 44 | */ 45 | #define CONTAINER_ID_REGEX "perf_event/docker/([a-f0-9]{64})$" 46 | #define CONTAINER_ID_REGEX_EXPECTED_MATCHES 2 47 | 48 | /* 49 | * CONTAINER_NAME_REGEX is the regex used to extract the Docker container name from its json configuration file. 50 | * CONTAINER_NAME_REGEX_EXPECTED_MATCHES is the number of expected matches from the regex. (num groups + 1) 51 | */ 52 | #define CONTAINER_NAME_REGEX "\"Name\":\"/([a-zA-Z0-9][a-zA-Z0-9_.-]+)\"" 53 | #define CONTAINER_NAME_REGEX_EXPECTED_MATCHES 2 54 | 55 | 56 | int 57 | target_docker_validate(const char *cgroup_path) 58 | { 59 | regex_t re; 60 | int is_docker_target = false; 61 | 62 | if (!cgroup_path) 63 | return false; 64 | 65 | if (!regcomp(&re, CONTAINER_ID_REGEX, REG_EXTENDED | REG_NEWLINE | REG_NOSUB)) { 66 | if (!regexec(&re, cgroup_path, 0, NULL, 0)) 67 | is_docker_target = true; 68 | 69 | regfree(&re); 70 | } 71 | 72 | return is_docker_target; 73 | } 74 | 75 | static char * 76 | build_container_config_path(const char *cgroup_path) 77 | { 78 | regex_t re; 79 | regmatch_t matches[CONTAINER_ID_REGEX_EXPECTED_MATCHES]; 80 | regoff_t length; 81 | const char *id = NULL; 82 | char buffer[PATH_MAX] = {}; 83 | char *config_path = NULL; 84 | 85 | if (!regcomp(&re, CONTAINER_ID_REGEX, REG_EXTENDED | REG_NEWLINE)) { 86 | if (!regexec(&re, cgroup_path, CONTAINER_ID_REGEX_EXPECTED_MATCHES, matches, 0)) { 87 | id = cgroup_path + matches[1].rm_so; 88 | length = matches[1].rm_eo - matches[1].rm_so; 89 | snprintf(buffer, PATH_MAX, "/var/lib/docker/containers/%.*s/config.v2.json", length, id); 90 | config_path = strdup(buffer); 91 | } 92 | regfree(&re); 93 | } 94 | 95 | return config_path; 96 | } 97 | 98 | char * 99 | target_docker_resolve_name(struct target *target) 100 | { 101 | char *config_path = NULL; 102 | FILE *json_file = NULL; 103 | char *json = NULL; 104 | size_t json_len; 105 | regex_t re; 106 | regmatch_t matches[CONTAINER_NAME_REGEX_EXPECTED_MATCHES]; 107 | char *target_name = NULL; 108 | 109 | config_path = build_container_config_path(target->cgroup_path); 110 | if (!config_path) 111 | return NULL; 112 | 113 | json_file = fopen(config_path, "r"); 114 | if (json_file) { 115 | if (getline(&json, &json_len, json_file) != -1) { 116 | if (!regcomp(&re, CONTAINER_NAME_REGEX, REG_EXTENDED | REG_NEWLINE)) { 117 | if (!regexec(&re, json, CONTAINER_NAME_REGEX_EXPECTED_MATCHES, matches, 0)) 118 | target_name = strndup(json + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); 119 | 120 | regfree(&re); 121 | } 122 | free(json); 123 | } 124 | fclose(json_file); 125 | } 126 | 127 | free(config_path); 128 | return target_name; 129 | } 130 | 131 | -------------------------------------------------------------------------------- /src/target_docker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef TARGET_DOCKER_H 33 | #define TARGET_DOCKER_H 34 | 35 | #include "target.h" 36 | 37 | /* 38 | * target_docker_validate check if the cgroup path lead to a valid Docker target. 39 | */ 40 | int target_docker_validate(const char *cgroup_path); 41 | 42 | /* 43 | * target_docker_resolve_name resolve and return the real name of the given target. 44 | */ 45 | char *target_docker_resolve_name(struct target *target); 46 | 47 | #endif /* TARGET_DOCKER_H */ 48 | 49 | -------------------------------------------------------------------------------- /src/target_kubernetes.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | 35 | #include "target.h" 36 | #include "target_kubernetes.h" 37 | 38 | /* 39 | * CONTAINER_ID_REGEX is the regex used to extract the Docker container id from a cgroup path. 40 | * CONTAINER_ID_REGEX_EXPECTED_MATCHES is the number of matches expected from the regex. (num groups + 1) 41 | */ 42 | #define CONTAINER_ID_REGEX \ 43 | "perf_event/kubepods/" \ 44 | "(besteffort/|burstable/|)" \ 45 | "(pod[a-zA-Z0-9][a-zA-Z0-9.-]+)/" /* Pod ID */ \ 46 | "([a-f0-9]{64})" /* Container ID */ \ 47 | "(/[a-zA-Z0-9][a-zA-Z0-9.-]+|)" /* Resource group */ 48 | #define CONTAINER_ID_REGEX_EXPECTED_MATCHES 5 49 | 50 | /* 51 | * CONTAINER_NAME_REGEX is the regex used to extract the name of the Docker container from its json configuration file. 52 | * CONTAINER_NAME_REGEX_EXPECTED_MATCHES is the number of matches expected from the regex. (num groups + 1) 53 | */ 54 | #define CONTAINER_NAME_REGEX "\"Name\":\"/([a-zA-Z0-9][a-zA-Z0-9_.-]+)\"" 55 | #define CONTAINER_NAME_REGEX_EXPECTED_MATCHES 2 56 | 57 | 58 | int 59 | target_kubernetes_validate(const char *cgroup_path) 60 | { 61 | regex_t re; 62 | bool is_kubernetes_target = false; 63 | 64 | if (!cgroup_path) 65 | return false; 66 | 67 | if (!regcomp(&re, CONTAINER_ID_REGEX, REG_EXTENDED | REG_NEWLINE | REG_NOSUB)) { 68 | if (!regexec(&re, cgroup_path, 0, NULL, 0)) 69 | is_kubernetes_target = true; 70 | 71 | regfree(&re); 72 | } 73 | 74 | return is_kubernetes_target; 75 | } 76 | 77 | static char * 78 | build_container_config_path(const char *cgroup_path) 79 | { 80 | regex_t re; 81 | regmatch_t matches[CONTAINER_ID_REGEX_EXPECTED_MATCHES]; 82 | regoff_t length; 83 | const char *id = NULL; 84 | char buffer[PATH_MAX] = {}; 85 | char *config_path = NULL; 86 | 87 | if (!regcomp(&re, CONTAINER_ID_REGEX, REG_EXTENDED | REG_NEWLINE)) { 88 | if (!regexec(&re, cgroup_path, CONTAINER_ID_REGEX_EXPECTED_MATCHES, matches, 0)) { 89 | id = cgroup_path + matches[3].rm_so; 90 | length = matches[3].rm_eo - matches[3].rm_so; 91 | snprintf(buffer, PATH_MAX, "/var/lib/docker/containers/%.*s/config.v2.json", length, id); 92 | config_path = strdup(buffer); 93 | } 94 | regfree(&re); 95 | } 96 | 97 | return config_path; 98 | } 99 | 100 | char * 101 | target_kubernetes_resolve_name(struct target *target) 102 | { 103 | char *config_path = NULL; 104 | FILE *json_file = NULL; 105 | char *json = NULL; 106 | size_t json_len; 107 | regex_t re; 108 | regmatch_t matches[CONTAINER_NAME_REGEX_EXPECTED_MATCHES]; 109 | char *target_name = NULL; 110 | 111 | config_path = build_container_config_path(target->cgroup_path); 112 | if (!config_path) 113 | return NULL; 114 | 115 | json_file = fopen(config_path, "r"); 116 | if (json_file) { 117 | if (getline(&json, &json_len, json_file) != -1) { 118 | if (!regcomp(&re, CONTAINER_NAME_REGEX, REG_EXTENDED | REG_NEWLINE)) { 119 | if (!regexec(&re, json, CONTAINER_NAME_REGEX_EXPECTED_MATCHES, matches, 0)) 120 | target_name = strndup(json + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); 121 | 122 | regfree(&re); 123 | } 124 | free(json); 125 | } 126 | fclose(json_file); 127 | } 128 | 129 | free(config_path); 130 | return target_name; 131 | } 132 | 133 | -------------------------------------------------------------------------------- /src/target_kubernetes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef TARGET_KUBERNETES_H 33 | #define TARGET_KUBERNETES_H 34 | 35 | #include "target.h" 36 | 37 | /* 38 | * target_kubernetes_validate check if the cgroup path lead to a valid Kubernetes target. 39 | */ 40 | int target_kubernetes_validate(const char *cgroup_path); 41 | 42 | /* 43 | * target_kubernetes_resolve_name resolve and return the real name of the given target. 44 | */ 45 | char *target_kubernetes_resolve_name(struct target *target); 46 | 47 | #endif /* TARGET_KUBERNETES_H */ 48 | 49 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "util.h" 38 | 39 | int * 40 | intdup(int val) 41 | { 42 | int *res = (int *) malloc(sizeof(int)); 43 | 44 | if (!res) 45 | return NULL; 46 | 47 | *res = val; 48 | 49 | return res; 50 | } 51 | 52 | int * 53 | intptrdup(const int *ptr) 54 | { 55 | if (!ptr) 56 | return NULL; 57 | 58 | return intdup(*ptr); 59 | } 60 | 61 | int 62 | intcmp(int a, int b) 63 | { 64 | return (a < b) ? -1 : (a > b); 65 | } 66 | 67 | int 68 | intptrcmp(const int *a, const int *b) 69 | { 70 | return intcmp((a) ? *a : 0, (b) ? *b : 0); 71 | } 72 | 73 | uint64_t * 74 | uint64dup(uint64_t val) 75 | { 76 | uint64_t *res = (uint64_t *) malloc(sizeof(uint64_t)); 77 | 78 | if (!res) 79 | return NULL; 80 | 81 | *res = val; 82 | 83 | return res; 84 | } 85 | 86 | uint64_t * 87 | uint64ptrdup(const uint64_t *ptr) 88 | { 89 | if (!ptr) 90 | return NULL; 91 | 92 | return uint64dup(*ptr); 93 | } 94 | 95 | int 96 | uint64cmp(uint64_t a, uint64_t b) 97 | { 98 | return (a < b) ? -1 : (a > b); 99 | } 100 | 101 | int 102 | uint64ptrcmp(const uint64_t *a, const uint64_t *b) 103 | { 104 | return uint64cmp((a) ? *a : 0, (b) ? *b : 0); 105 | } 106 | 107 | void 108 | ptrfree(void **ptr) 109 | { 110 | free(*ptr); 111 | *ptr = NULL; 112 | } 113 | 114 | int 115 | str_to_uint(const char *str, unsigned int *out) 116 | { 117 | unsigned long value; 118 | char *str_endp = NULL; 119 | 120 | errno = 0; 121 | value = strtoul(str, &str_endp, 0); 122 | 123 | if (errno != 0 || str == str_endp || (*str_endp != '\0' && *str_endp != '\n')) { 124 | return EINVAL; 125 | } 126 | 127 | if (value > UINT_MAX) { 128 | return ERANGE; 129 | } 130 | 131 | *out = (unsigned int) value; 132 | return 0; 133 | } 134 | 135 | int 136 | str_to_int(const char *str, int *out) 137 | { 138 | long value; 139 | char *str_endp = NULL; 140 | 141 | errno = 0; 142 | value = strtol(str, &str_endp, 0); 143 | 144 | if (errno != 0 || str == str_endp || (*str_endp != '\0' && *str_endp != '\n')) { 145 | return EINVAL; 146 | } 147 | 148 | if (value > INT_MAX) { 149 | return ERANGE; 150 | } 151 | 152 | *out = (int) value; 153 | return 0; 154 | } 155 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef UTIL_H 33 | #define UTIL_H 34 | 35 | #include 36 | 37 | /* 38 | * intdup returns a pointer to a new integer having the same value as val. 39 | */ 40 | int *intdup(int val); 41 | 42 | /* 43 | * intptrdup returns a pointer to a new integer having the same value as the one pointed by ptr. 44 | */ 45 | int *intptrdup(const int *ptr); 46 | 47 | /* 48 | * intcmp compare two integers. (same behaviour as strcmp) 49 | */ 50 | int intcmp(int a, int b); 51 | 52 | /* 53 | * intptrcmp compare the value of two pointers to integer. (same behavious as strcmp) 54 | * Value having a NULL pointer is considered as 0. 55 | */ 56 | int intptrcmp(const int *a, const int *b); 57 | 58 | /* 59 | * uint64dup returns a pointer to a new uint64_t having the same value as val. 60 | */ 61 | uint64_t *uint64dup(uint64_t val); 62 | 63 | /* 64 | * uint64ptrdup returns a pointer to a new uint64_t having the same value as the one pointed by ptr. 65 | */ 66 | uint64_t *uint64ptrdup(const uint64_t *ptr); 67 | 68 | /* 69 | * uint64cmp compare two uint64_t. (same behaviour as strcmp) 70 | */ 71 | int uint64cmp(uint64_t a, uint64_t b); 72 | 73 | /* 74 | * uint64ptrcmp compare the value of two pointers to uint64_t. (same behaviour as strcmp) 75 | * Value having a NULL pointer is considered as 0. 76 | */ 77 | int uint64ptrcmp(const uint64_t *a, const uint64_t *b); 78 | 79 | /* 80 | * ptrfree free the memory pointed by ptr and set ptr to NULL. 81 | */ 82 | void ptrfree(void **ptr); 83 | 84 | /* 85 | * str_to_uint safely converts a string to an unsigned int. 86 | */ 87 | int str_to_uint(const char *str, unsigned int *out); 88 | 89 | /* 90 | * str_to_int safely converts a string to an integer. 91 | */ 92 | int str_to_int(const char *str, int *out); 93 | 94 | #endif /* UTIL_H */ 95 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2018, INRIA 3 | * Copyright (c) 2018, University of Lille 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, this 10 | * list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright notice, 13 | * this list of conditions and the following disclaimer in the documentation 14 | * and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of the copyright holder nor the names of its 17 | * contributors may be used to endorse or promote products derived from 18 | * this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef VERSION_H 33 | #define VERSION_H 34 | 35 | /* 36 | * VERSION_GIT_TAG store the name of the current git tag. 37 | */ 38 | #ifndef VERSION_GIT_TAG 39 | #define VERSION_GIT_TAG "undefined" 40 | #endif 41 | 42 | /* 43 | * VERSION_GIT_REV store the sha1 hash of the last git commit. 44 | */ 45 | #ifndef VERSION_GIT_REV 46 | #define VERSION_GIT_REV "undefined" 47 | #endif 48 | 49 | #endif /* VERSION_H */ 50 | --------------------------------------------------------------------------------