├── .github └── workflows │ └── dynolog-ci.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── RELEASE.md ├── cli ├── CMakeLists.txt ├── Cargo.toml └── src │ ├── commands │ ├── dcgm.rs │ ├── gputrace.rs │ ├── mod.rs │ ├── status.rs │ ├── utils.rs │ └── version.rs │ └── main.rs ├── docs ├── Metrics.md ├── dyno_logo.svg ├── logging_to_ods.md ├── logging_to_scuba.md ├── profiler_flow.png └── pytorch_profiler.md ├── dynolog.dockerfile ├── dynolog ├── CMakeLists.txt ├── src │ ├── CMakeLists.txt │ ├── CPUTimeMonitor.cpp │ ├── CPUTimeMonitor.h │ ├── CompositeLogger.cpp │ ├── CompositeLogger.h │ ├── FBRelayLogger.cpp │ ├── FBRelayLogger.h │ ├── KernelCollector.cpp │ ├── KernelCollector.h │ ├── KernelCollectorBase.cpp │ ├── KernelCollectorBase.h │ ├── LibkinetoConfigManager.cpp │ ├── LibkinetoConfigManager.h │ ├── LibkinetoTypes.h │ ├── Logger.cpp │ ├── Logger.h │ ├── Main.cpp │ ├── Metrics.cpp │ ├── Metrics.h │ ├── MonitorBase.h │ ├── ODSJsonLogger.cpp │ ├── ODSJsonLogger.h │ ├── PerfMonitor.cpp │ ├── PerfMonitor.h │ ├── PrometheusLogger.cpp │ ├── PrometheusLogger.h │ ├── ScubaLogger.cpp │ ├── ScubaLogger.h │ ├── ServiceHandler.cpp │ ├── ServiceHandler.h │ ├── Ticker.h │ ├── Types.h │ ├── gpumon │ │ ├── CMakeLists.txt │ │ ├── DcgmApiStub.cpp │ │ ├── DcgmApiStub.h │ │ ├── DcgmGroupInfo.cpp │ │ ├── DcgmGroupInfo.h │ │ ├── Entity.cpp │ │ ├── Entity.h │ │ ├── Utils.cpp │ │ ├── Utils.h │ │ ├── amd │ │ │ ├── RdcWrapper.cpp │ │ │ └── RdcWrapper.h │ │ ├── dcgm_agent.h │ │ ├── dcgm_api_export.h │ │ ├── dcgm_errors.h │ │ ├── dcgm_fields.h │ │ └── dcgm_structs.h │ ├── ipcfabric │ │ ├── CMakeLists.txt │ │ ├── Endpoint.h │ │ ├── FabricManager.h │ │ └── Utils.h │ ├── metric_frame │ │ ├── CMakeLists.txt │ │ ├── ExtraTypes.cpp │ │ ├── ExtraTypes.h │ │ ├── MetricFrame.cpp │ │ ├── MetricFrame.h │ │ ├── MetricFrameBase.cpp │ │ ├── MetricFrameBase.h │ │ ├── MetricFrameTsUnit.cpp │ │ ├── MetricFrameTsUnit.h │ │ ├── MetricFrameTsUnitInterface.h │ │ ├── MetricSeries.h │ │ ├── MetricValues.h │ │ ├── TextTable.cpp │ │ └── TextTable.h │ ├── rdmamon │ │ ├── CMakeLists.txt │ │ ├── EthtoolCounters.cpp │ │ ├── EthtoolCounters.h │ │ ├── RdmaCounters.cpp │ │ ├── RdmaCounters.h │ │ ├── RdmaMonitor.cpp │ │ ├── RdmaMonitor.h │ │ ├── SysfsCounter.cpp │ │ └── SysfsCounter.h │ ├── rpc │ │ ├── CMakeLists.txt │ │ ├── SimpleJsonServer.cpp │ │ ├── SimpleJsonServer.h │ │ └── SimpleJsonServerInl.h │ └── tracing │ │ ├── CMakeLists.txt │ │ ├── IPCMonitor.cpp │ │ └── IPCMonitor.h └── tests │ ├── CMakeLists.txt │ ├── CPUTimeMonitorTest.cpp │ ├── KernelCollecterTest.cpp │ ├── MonitorTickerTest.cpp │ ├── PrometheusLoggerTest.cpp │ ├── gpumon │ └── amd │ │ └── RdcWrapperTest.cpp │ ├── ipcfabric │ ├── CMakeLists.txt │ ├── IPCFabricTest.cpp │ └── IPCSender.cpp │ ├── metric_frame │ ├── CMakeLists.txt │ ├── MetricFrameTest.cpp │ ├── MetricFrameTsUnitTest.cpp │ ├── MetricSeriesTest.cpp │ ├── MetricValuesTest.cpp │ └── TextTableTest.cpp │ ├── rdmamon │ ├── RdmaMonitorTests.cpp │ └── SysfsCountersTests.cpp │ ├── rpc │ ├── CMakeLists.txt │ ├── SimpleJsonClientTest.cpp │ ├── SimpleJsonClientTest.h │ └── SimpleJsonClientTestCLI.cpp │ └── tracing │ ├── CMakeLists.txt │ └── IPCMonitorTest.cpp ├── dynolog_hta.dockerfile ├── hbt ├── CMakeLists.txt └── src │ ├── CMakeLists.txt │ ├── common │ ├── CMakeLists.txt │ ├── Defaults.h │ ├── Defs.h │ ├── ProcFsHelpers.h │ ├── System.cpp │ ├── System.h │ └── tests │ │ ├── CMakeLists.txt │ │ ├── DefsTest.cpp │ │ ├── SystemProcFsTest.cpp │ │ └── SystemTest.cpp │ ├── intel_pt │ ├── CMakeLists.txt │ ├── IptEventBuilder.cpp │ ├── IptEventBuilder.h │ ├── examples │ │ ├── perf_script_output__cpu_wide.txt │ │ ├── perf_script_output__cpu_wide2.txt │ │ ├── perf_script_output__cpu_wide_short.txt │ │ ├── perf_script_output__cpu_wide_tiny.txt │ │ ├── perf_script_output__stress.txt │ │ └── perf_script_output__stress_ushort.txt │ ├── main.py │ ├── tests │ │ ├── CMakeLists.txt │ │ ├── IptCapChecker.cpp │ │ └── parse_lines__tests.py │ └── tracer.py │ ├── mon │ ├── CMakeLists.txt │ ├── Filter.cpp │ ├── Filter.h │ ├── IntelPTMonitor.cpp │ ├── IntelPTMonitor.h │ ├── MonData.cpp │ ├── MonData.h │ ├── Monitor.h │ ├── PerCpuSliceGenerator.h │ ├── TraceCollector.cpp │ ├── TraceCollector.h │ ├── TraceMonitor.cpp │ ├── TraceMonitor.h │ └── tests │ │ ├── CMakeLists.txt │ │ ├── MonDataTest.cpp │ │ ├── MonitorTest.cpp │ │ └── parse_procfs │ │ └── 1234 │ │ └── maps │ ├── perf_event │ ├── AmdEvents.cpp │ ├── AmdEvents.h │ ├── ArmEvents.cpp │ ├── ArmEvents.h │ ├── BPerfCountReader.cpp │ ├── BPerfCountReader.h │ ├── BPerfEventsGroup.cpp │ ├── BPerfEventsGroup.h │ ├── BPerfPerThreadReader.cpp │ ├── BPerfPerThreadReader.h │ ├── BuiltinMetrics.cpp │ ├── BuiltinMetrics.h │ ├── CMakeLists.txt │ ├── CpuArch.h │ ├── Metrics.cpp │ ├── Metrics.h │ ├── PerCpuBase.h │ ├── PerCpuCountReader.h │ ├── PerCpuCountSampleGenerator.h │ ├── PerCpuDummyGenerator.h │ ├── PerCpuSampleGeneratorBase.h │ ├── PerCpuThreadSwitchGenerator.h │ ├── PerCpuTraceAuxGenerator.h │ ├── PerPerfEventsGroupBase.h │ ├── PerUncoreCountReader.h │ ├── PerfEventsGroup.h │ ├── PmuDevices.cpp │ ├── PmuDevices.h │ ├── PmuEvent.cpp │ ├── PmuEvent.h │ ├── ThreadCountReader.h │ ├── bpf │ │ ├── bperf.h │ │ └── bperf_leader_cgroup.bpf.c │ ├── json_events │ │ ├── CMakeLists.txt │ │ └── generated │ │ │ ├── CMakeLists.txt │ │ │ ├── CpuArch.h │ │ │ └── intel │ │ │ ├── CMakeLists.txt │ │ │ ├── JsonEvents.h │ │ │ ├── broadwell_core.cpp │ │ │ ├── broadwell_uncore.cpp │ │ │ ├── broadwellde_core.cpp │ │ │ ├── broadwellde_uncore.cpp │ │ │ ├── broadwellx_core.cpp │ │ │ ├── broadwellx_uncore.cpp │ │ │ ├── cascadelakex_core.cpp │ │ │ ├── cascadelakex_uncore.cpp │ │ │ ├── cascadelakex_uncore_experimental.cpp │ │ │ ├── emeraldrapids_core.cpp │ │ │ ├── emeraldrapids_uncore.cpp │ │ │ ├── emeraldrapids_uncore_experimental.cpp │ │ │ ├── goldmont_core.cpp │ │ │ ├── haswellx_core.cpp │ │ │ ├── haswellx_uncore.cpp │ │ │ ├── icelake_core.cpp │ │ │ ├── icelake_uncore.cpp │ │ │ ├── icelake_uncore_experimental.cpp │ │ │ ├── icelakex_core.cpp │ │ │ ├── icelakex_uncore.cpp │ │ │ ├── icelakex_uncore_experimental.cpp │ │ │ ├── ivybridge_core.cpp │ │ │ ├── ivybridge_uncore.cpp │ │ │ ├── knightslanding_core.cpp │ │ │ ├── knightslanding_uncore.cpp │ │ │ ├── nehalemex_core.cpp │ │ │ ├── sandybridge_core.cpp │ │ │ ├── sandybridge_uncore.cpp │ │ │ ├── sapphirerapids_core.cpp │ │ │ ├── sapphirerapids_uncore.cpp │ │ │ ├── sapphirerapids_uncore_experimental.cpp │ │ │ ├── sierraforest_core.cpp │ │ │ ├── sierraforest_uncore.cpp │ │ │ ├── sierraforest_uncore_experimental.cpp │ │ │ ├── skylake_core.cpp │ │ │ ├── skylake_uncore.cpp │ │ │ ├── skylakex_core.cpp │ │ │ ├── skylakex_uncore.cpp │ │ │ ├── skylakex_uncore_experimental.cpp │ │ │ ├── snowridgex_core.cpp │ │ │ ├── snowridgex_uncore.cpp │ │ │ └── snowridgex_uncore_experimental.cpp │ └── tests │ │ ├── ArmEventsTest.cpp │ │ ├── BPerfEventsGroupPerThreadTest.cpp │ │ ├── BPerfEventsGroupTest.cpp │ │ ├── BuiltinMetricsTest.cpp │ │ ├── CMakeLists.txt │ │ ├── ClocksTest.cpp │ │ ├── PerCpuGeneratorsTest.cpp │ │ ├── PerfEventsGroupTest.cpp │ │ ├── PmuDevicesTest.cpp │ │ ├── ThreadCountReaderTest.cpp │ │ └── root_files │ │ └── sys │ │ └── bus │ │ └── event_source │ │ └── devices │ │ └── armv8_pmuv3_0 │ │ └── events │ │ ├── bad_event_config │ │ ├── bad_event_field │ │ └── cpu_cycles │ ├── ringbuffer │ ├── Consumer.h │ ├── PerCpuRingBuffer.h │ ├── Producer.h │ ├── README.rst │ ├── RingBuffer.h │ ├── RingBufferBlockingOps.h │ ├── Shm.h │ ├── benchmarks │ │ ├── Consumer.hpp │ │ ├── Data.hpp │ │ ├── MPMCBenchmark.hpp │ │ ├── MPMCQueueBenchmark.cpp │ │ ├── MPMCQueueConsumerWrapper.hpp │ │ ├── MPMCQueueProducerWrapper.hpp │ │ ├── MPMCRingBufferBenchmark.cpp │ │ ├── Producer.hpp │ │ ├── RingBufferConsumerWrapper.hpp │ │ ├── RingBufferProducerWrapper.hpp │ │ ├── SPSCBenchmark.hpp │ │ ├── SPSCQueueBenchmark.cpp │ │ ├── SPSCQueueConsumerWrapper.hpp │ │ ├── SPSCQueueProducerWrapper.hpp │ │ ├── SPSCRingBufferBenchmark.cpp │ │ ├── ThreadBind.hpp │ │ └── TriggerableThread.hpp │ └── tests │ │ ├── PerCpuRingBufferTest.cpp │ │ ├── RingBufferTest.cpp │ │ └── ShmPerCpuRingBufferTest.cpp │ ├── tagstack │ ├── Event.h │ ├── IntervalSlicer.cpp │ ├── IntervalSlicer.h │ ├── PerfEventStream.h │ ├── Slicer.h │ ├── Stream.h │ ├── TagStack.h │ ├── TscConverterStream.h │ └── tests │ │ ├── IntervalSlicerTest.cpp │ │ ├── SlicerTest.cpp │ │ └── StreamTest.cpp │ └── utils │ └── ValueTimeSeries.h ├── scripts ├── README.md ├── build.sh ├── debian │ ├── control │ └── make_deb.sh ├── dynolog.conf ├── dynolog.service ├── pytorch │ ├── linear_model_example.py │ ├── unitrace.py │ └── xor.py ├── rpm │ ├── dynolog.spec │ └── make_rpm.sh └── slurm │ └── run_with_dyno_wrapper.sh ├── testing ├── BuildTests.cmake └── root │ ├── proc │ ├── net │ │ └── dev │ └── stat │ └── sys │ └── class │ └── net │ ├── eth0 │ └── speed │ └── eth1 │ └── speed └── version.txt /.github/workflows/dynolog-ci.yml: -------------------------------------------------------------------------------- 1 | name: DYNOLOGCI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | build: 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest] # TODO: What other OSs should we include here? 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Checkout submodules 21 | shell: bash 22 | run: | 23 | auth_header="$(git config --local --get http.https://github.com/.extraheader)" 24 | git submodule sync --recursive 25 | git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1 26 | 27 | - name: Get env vars 28 | run: | 29 | echo GITHUB_WORKFLOW = $GITHUB_WORKFLOW 30 | echo HOME = $HOME 31 | echo GITHUB_ACTION = $GITHUB_ACTION 32 | echo GITHUB_ACTIONS = $GITHUB_ACTIONS 33 | echo GITHUB_REPOSITORY = $GITHUB_REPOSITORY 34 | echo GITHUB_EVENT_NAME = $GITHUB_EVENT_NAME 35 | echo GITHUB_EVENT_PATH = $GITHUB_EVENT_PATH 36 | echo GITHUB_WORKSPACE = $GITHUB_WORKSPACE 37 | echo GITHUB_SHA = $GITHUB_SHA 38 | echo GITHUB_REF = $GITHUB_REF 39 | c++ --verbose 40 | 41 | - name: Download and setup Ninja 42 | uses: seanmiddleditch/gha-setup-ninja@master 43 | 44 | - name: Build dynolog binary 45 | run: | 46 | set -e 47 | ./scripts/build.sh 48 | - name: Run Unit Tests 49 | run: | 50 | set -e 51 | cd $GITHUB_WORKSPACE/build 52 | ctest --output-on-failure 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | build/ 3 | *.swp 4 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/glog"] 2 | path = third_party/glog 3 | url = https://github.com/google/glog.git 4 | [submodule "third_party/gflags"] 5 | path = third_party/gflags 6 | url = https://github.com/gflags/gflags.git 7 | [submodule "third_party/fmt"] 8 | path = third_party/fmt 9 | url = https://github.com/fmtlib/fmt.git 10 | [submodule "third_party/json"] 11 | path = third_party/json 12 | url = https://github.com/nlohmann/json.git 13 | [submodule "third_party/pfs"] 14 | path = third_party/pfs 15 | url = https://github.com/dtrugman/pfs.git 16 | [submodule "third_party/googletest"] 17 | path = third_party/googletest 18 | url = https://github.com/google/googletest.git 19 | [submodule "third_party/cpr"] 20 | path = third_party/cpr 21 | url = https://github.com/libcpr/cpr.git 22 | [submodule "third_party/DCGM"] 23 | path = third_party/DCGM 24 | url = https://github.com/NVIDIA/DCGM.git 25 | [submodule "third_party/prometheus-cpp"] 26 | path = third_party/prometheus-cpp 27 | url = https://github.com/jupp0r/prometheus-cpp.git 28 | branch = v1.0.2 29 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.4.0 4 | hooks: 5 | - id: check-merge-conflict 6 | - id: check-yaml 7 | - id: end-of-file-fixer 8 | - id: trailing-whitespace 9 | - repo: https://github.com/doublify/pre-commit-rust 10 | rev: v1.0 11 | hooks: 12 | - id: fmt 13 | args: ['--manifest-path', 'cli/Cargo.toml'] 14 | pass_filenames: false 15 | - id: cargo-check 16 | args: ['--manifest-path', 'cli/Cargo.toml'] 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # July 28, 2022 2 | * Initial commit 3 | # Dec 8, 2022 (v0.1.0) 4 | - Include CPU instructions and cycles performance events 5 | - GPU trace trigger improvements: support tracing by iteration count. 6 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | cmake_minimum_required(VERSION 3.16) 3 | 4 | project(Dynolog VERSION 1.0) 5 | option(BUILD_TESTS "Build the unit tests" ON) 6 | option(USE_ODS_GRAPH_API "Enable logger to Meta ODS using public Graph API." 7 | OFF) 8 | option(USE_JSON_GENERATED_PERF_EVENTS "Add performance events generated using 9 | Intel json spec, see hbt/src/perf_event/json_events/intel" 10 | OFF) 11 | option(USE_PROMETHEUS "Enable logging to prometheus, this requires 12 | prometheus-cpp to be installed on the system" 13 | OFF) 14 | 15 | if(USE_PROMETHEUS) 16 | find_package(prometheus-cpp CONFIG REQUIRED) 17 | endif() 18 | 19 | file(READ "version.txt" DYNOLOG_VERSION) 20 | string(STRIP ${DYNOLOG_VERSION} DYNOLOG_VERSION) 21 | 22 | execute_process ( 23 | COMMAND git rev-parse --short HEAD 24 | OUTPUT_VARIABLE DYNOLOG_GIT_REV 25 | OUTPUT_STRIP_TRAILING_WHITESPACE 26 | ) 27 | 28 | set(DYNOLOG_VERSION "\"${DYNOLOG_VERSION}\"") 29 | set(DYNOLOG_GIT_REV "\"${DYNOLOG_GIT_REV}\"") 30 | message("Dynolog version = ${DYNOLOG_VERSION}") 31 | message("Dynolog git rev = ${DYNOLOG_GIT_REV}") 32 | 33 | set(CMAKE_VERBOSE_MAKEFILE ON) 34 | 35 | set(CMAKE_CXX_STANDARD 17) 36 | set(CMAKE_CXX_STANDARD_REQUIRED True) 37 | set(CMAKE_POSITION_INDEPENDENT_CODE True) 38 | 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") 40 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") 41 | 42 | if(BUILD_TESTS) 43 | enable_testing() 44 | add_subdirectory("third_party/googletest" "third_party/googletest") 45 | endif() 46 | 47 | include_directories(".") 48 | add_subdirectory(dynolog) 49 | add_subdirectory(cli) 50 | # The following dummy depdendency ensures the cli is built 51 | add_dependencies(dynolog_lib dyno) 52 | add_subdirectory(hbt) 53 | 54 | # Third party deps 55 | set(BUILD_SHARED_LIBS OFF CACHE INTERNAL "") 56 | set(BUILD_SAMPLES OFF CACHE BOOL "") 57 | set(BUILD_TEST OFF CACHE BOOL "") 58 | set(BUILD_SHARED_LIBS OFF CACHE BOOL "") 59 | 60 | set(BUILD_TESTING OFF CACHE BOOL "") 61 | set(WITH_GFLAGS OFF CACHE BOOL "") 62 | add_subdirectory(third_party/glog) 63 | target_link_libraries(dynolog_lib PUBLIC glog::glog) 64 | 65 | set(GFLAGS_BUILD_TESTING OFF CACHE BOOL "") 66 | add_subdirectory(third_party/gflags) 67 | target_link_libraries(dynolog_lib PUBLIC gflags::gflags) 68 | 69 | # https://github.com/nlohmann/json#cmake 70 | set(JSON_BuildTests OFF CACHE INTERNAL "") 71 | add_subdirectory(third_party/json) 72 | target_link_libraries(dynolog_lib PUBLIC nlohmann_json::nlohmann_json) 73 | 74 | add_subdirectory(third_party/pfs) 75 | target_include_directories(dynolog_lib PUBLIC third_party/pfs/include) 76 | target_link_libraries(dynolog_lib PUBLIC pfs) 77 | 78 | add_subdirectory(third_party/fmt) 79 | target_link_libraries(dynolog_lib PUBLIC fmt::fmt) 80 | 81 | if(USE_ODS_GRAPH_API) 82 | add_subdirectory(third_party/cpr) 83 | target_link_libraries(dynolog_lib PUBLIC cpr::cpr) 84 | endif() 85 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to dynolog 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Our Development Process 6 | Each pull request is first submitted into Meta's internal repositories by a Meta team member. Once the commit has successfully passed Meta's internal test suite, it will be exported back out from Meta's repository. We endeavour to do this as soon as possible for all commits. 7 | 8 | ## Pull Requests 9 | We actively welcome your pull requests. 10 | 11 | 1. Fork the repo and create your branch from `main`. 12 | 2. If you've added code that should be tested, add tests. 13 | 3. If you've changed APIs, update the documentation. 14 | 4. Ensure the test suite passes. 15 | 5. Make sure your code lints. 16 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 17 | 18 | ## Contributor License Agreement ("CLA") 19 | In order to accept your pull request, we need you to submit a CLA. You only need 20 | to do this once to work on any of Meta's open source projects. 21 | 22 | Complete your CLA here: 23 | 24 | ## Issues 25 | We use GitHub issues to track public bugs. Please ensure your description is 26 | clear and has sufficient instructions to be able to reproduce the issue. 27 | 28 | Meta has a [bounty program](https://www.facebook.com/whitehat/) for the safe 29 | disclosure of security bugs. In those cases, please go through the process 30 | outlined on that page and do not file a public issue. 31 | 32 | ## Coding Style 33 | * 2 spaces for indentation rather than tabs 34 | * 80 character line length 35 | * ... 36 | 37 | ## License 38 | By contributing to dynolog, you agree that your contributions will be licensed 39 | under the LICENSE file in the root directory of this source tree. 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Facebook, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | # Rust project build 4 | 5 | # Locate cargo instance 6 | find_program(CARGO cargo) 7 | 8 | set(ARGS --target-dir ${CMAKE_BINARY_DIR}) 9 | 10 | # Add release or debug args 11 | if (CMAKE_BUILD_TYPE STREQUAL "Release") 12 | set(ARGS ${ARGS} --release) 13 | endif() 14 | 15 | add_custom_target( 16 | dyno 17 | COMMAND ${CARGO} build ${ARGS} 18 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 19 | ) 20 | -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dyno" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | anyhow = "1.0.57" 8 | clap = { version = "3.1.0", features = ["derive"]} 9 | serde_json = "1.0" 10 | 11 | # Make it work with conda 12 | # See https://github.com/rust-lang/cargo/issues/6652 13 | [net] 14 | git-fetch-with-cli = true 15 | -------------------------------------------------------------------------------- /cli/src/commands/dcgm.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::net::TcpStream; 7 | 8 | use anyhow::Result; 9 | 10 | #[path = "utils.rs"] 11 | mod utils; 12 | 13 | // This module contains the handling logic for dcgm 14 | 15 | /// Pause dcgm module profiling 16 | pub fn run_dcgm_pause(client: TcpStream, duration_s: i32) -> Result<()> { 17 | let request_json = format!( 18 | r#" 19 | {{ 20 | "fn": "dcgmProfPause", 21 | "duration_s": {} 22 | }}"#, 23 | duration_s 24 | ); 25 | 26 | utils::send_msg(&client, &request_json).expect("Error sending message to service"); 27 | 28 | let resp_str = utils::get_resp(&client).expect("Unable to decode output bytes"); 29 | 30 | println!("response = {}", resp_str); 31 | 32 | Ok(()) 33 | } 34 | 35 | /// Resume dcgm module profiling 36 | pub fn run_dcgm_resume(client: TcpStream) -> Result<()> { 37 | utils::send_msg(&client, r#"{"fn":"dcgmProfResume"}"#) 38 | .expect("Error sending message to service"); 39 | 40 | let resp_str = utils::get_resp(&client).expect("Unable to decode output bytes"); 41 | 42 | println!("response = {}", resp_str); 43 | 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | // Export all command submodules to be used in main.rs 7 | // Note: This "intermediate" commands module is purely for organizational purposes. 8 | // This allows for a clear distinction between the command dispatching code and the command 9 | // handling code. Additionally, explicitly "exporting" all the command modules here allows 10 | // us to avoid having to explicitly list all the command modules in main.rs. 11 | 12 | pub mod dcgm; 13 | pub mod gputrace; 14 | pub mod status; 15 | pub mod version; 16 | // ... add new command modules here 17 | -------------------------------------------------------------------------------- /cli/src/commands/status.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::net::TcpStream; 7 | 8 | use anyhow::Result; 9 | 10 | #[path = "utils.rs"] 11 | mod utils; 12 | 13 | // This module contains the handling logic for dyno status 14 | 15 | /// Get system info 16 | pub fn run_status(client: TcpStream) -> Result<()> { 17 | utils::send_msg(&client, r#"{"fn":"getStatus"}"#).expect("Error sending message to service"); 18 | 19 | let resp_str = utils::get_resp(&client).expect("Unable to decode output bytes"); 20 | 21 | println!("response = {}", resp_str); 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /cli/src/commands/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::io::Read; 7 | use std::io::Write; 8 | use std::net::TcpStream; 9 | 10 | use anyhow::Result; 11 | 12 | pub fn send_msg(mut client: &TcpStream, msg: &str) -> Result<()> { 13 | let msg_len: [u8; 4] = i32::try_from(msg.len()).unwrap().to_ne_bytes(); 14 | 15 | client.write_all(&msg_len)?; 16 | client.write_all(msg.as_bytes()).map_err(|err| err.into()) 17 | } 18 | 19 | pub fn get_resp(mut client: &TcpStream) -> Result { 20 | // Response is prefixed with length 21 | let mut resp_len: [u8; 4] = [0; 4]; 22 | client.read_exact(&mut resp_len)?; 23 | 24 | let resp_len = i32::from_ne_bytes(resp_len); 25 | let resp_len = usize::try_from(resp_len).unwrap(); 26 | 27 | println!("response length = {}", resp_len); 28 | 29 | let mut resp_str = Vec::::new(); 30 | resp_str.resize(resp_len, 0); 31 | 32 | client.read_exact(resp_str.as_mut_slice())?; 33 | 34 | String::from_utf8(resp_str).map_err(|err| err.into()) 35 | } 36 | -------------------------------------------------------------------------------- /cli/src/commands/version.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | use std::net::TcpStream; 7 | 8 | use anyhow::Result; 9 | 10 | #[path = "utils.rs"] 11 | mod utils; 12 | 13 | // This module contains the handling logic for querying dyno version 14 | 15 | /// Get version info 16 | pub fn run_version(client: TcpStream) -> Result<()> { 17 | utils::send_msg(&client, r#"{"fn":"getVersion"}"#).expect("Error sending message to service"); 18 | 19 | let resp_str = utils::get_resp(&client).expect("Unable to decode output bytes"); 20 | 21 | println!("response = {}", resp_str); 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /docs/logging_to_ods.md: -------------------------------------------------------------------------------- 1 | # Logging to ODS 2 | 3 | Meta's Operational Data Store (ODS) system supports logging from the public internet through Graph API. The open source Dynolog supports a Graph API based ODS Logger with user specified access token and certificate. 4 | 5 | ## Entity and Keys 6 | 7 | ODS timeseries are uniquely identified by (entity, key) pair. Each entity represents a device on the host, each key represents a metric reported from the host. Every sampling interval, Dynolog will log the metrics as ((entity, keys), value) pairs and ODS will present them as timeseries. 8 | 9 | The entity name is constructed as hostname.gpu.. The keys are constructed as dynolog.. A detailed list of supported metric names can be found at docs/Metrics.md. 10 | 11 | # How to enable ODS logging 12 | 13 | To enable ODS logging, users will need to obtain an access token and category id, and passes these through the required gflags, and set the --use_ODS flag to true. 14 | 15 | ## Supported/Required Gflags 16 | 17 | * `--use_ODS` (default=false): enable ODS logging. 18 | * `--access_token` (required): the access token string to validate the Graph API access. 19 | * `--category_id` (required): the category id the ODS logger belongs to, used for admission control and identifying data owners. 20 | * `--certificate_path` (default=/etc/ssl/certs/ca-certificates.crt): path to the SSL certificate on the host that the ODS logger runs on. 21 | * `--ods_entity_prefix` (optional): the ODS entity prefix before the hostname. 22 | -------------------------------------------------------------------------------- /docs/logging_to_scuba.md: -------------------------------------------------------------------------------- 1 | # Logging to Scuba 2 | 3 | [Scuba](https://research.facebook.com/publications/scuba-diving-into-data-at-facebook/) is the data management system Meta uses for most real-time analysis. Scuba is a fast, scalable, distributed, in-memory database built at Meta, and supports logging from the public internet through Graph API. The open source Dynolog supports a Graph API based Scuba Logger with user specified access token and certificate. 4 | 5 | ## Scuba Table and Scribe Category 6 | 7 | Scuba listens to [Scribe](https://engineering.fb.com/2019/10/07/data-infrastructure/scribe/), a distributed, buffered queueing system for data ingestion. To create a Scuba table, we first need to register a Scribe category that Scuba will listen to, the scribe category name will be following perfpipe_. 8 | 9 | Scuba will listen to the scribe category and dynamically generates the Scuba table schema based on the data schema it receives. 10 | 11 | # How to enable Scuba logging 12 | 13 | To enable Scuba logging, users will need to obtain an access token and category id, create a scribe category, and passes these through the required gflags, set the --use_scuba flag to true, and pass in the scribe category name with --scribe_category flag. 14 | 15 | ## Supported/Required Gflags 16 | 17 | * `--use_scuba` (default=false): enable Scuba logging. 18 | * `--access_token` (required): the access token string to validate the Graph API access. 19 | * `--category_id` (required): the category id the Scuba logger belongs to, used for admission control and identifying data owners. 20 | * `--certificate_path` (default=/etc/ssl/certs/ca-certificates.crt): path to the SSL certificate on the host that the Scuba logger runs on. 21 | * `--scribe_category` (required): the scribe category name that sends data and generates the Scuba table. 22 | -------------------------------------------------------------------------------- /docs/profiler_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/facebookincubator/dynolog/bb3f4829bb6b594b1db3932487165c304795c85a/docs/profiler_flow.png -------------------------------------------------------------------------------- /dynolog/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | cmake_minimum_required(VERSION 3.16) 3 | 4 | add_subdirectory(src) 5 | 6 | if(BUILD_TESTS) 7 | add_subdirectory(tests) 8 | endif() 9 | -------------------------------------------------------------------------------- /dynolog/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | cmake_minimum_required(VERSION 3.16) 4 | add_definitions(-DDYNOLOG_VERSION=${DYNOLOG_VERSION} -DDYNOLOG_GIT_REV=${DYNOLOG_GIT_REV}) 5 | 6 | message("Use Prometheus = ${USE_PROMETHEUS}") 7 | message("Use ODS Graph API = ${USE_ODS_GRAPH_API}") 8 | 9 | # our build script will first create a src/ dir where all source code will exist 10 | file (GLOB dynolog_src "*.h" "*.cpp") 11 | 12 | # Remove main from library, only needed for exec. 13 | list(REMOVE_ITEM dynolog_src "${CMAKE_CURRENT_SOURCE_DIR}/Main.cpp") 14 | add_library(dynolog_lib ${dynolog_src}) 15 | 16 | if(USE_ODS_GRAPH_API) 17 | target_compile_options(dynolog_lib PUBLIC "-DUSE_GRAPH_ENDPOINT") 18 | endif() 19 | 20 | if(USE_PROMETHEUS) 21 | find_package(prometheus-cpp CONFIG REQUIRED) 22 | add_definitions(-DUSE_PROMETHEUS) 23 | target_link_libraries(dynolog_lib PRIVATE prometheus-cpp::pull) 24 | endif() 25 | 26 | target_link_libraries(dynolog_lib PUBLIC Monitor) 27 | target_link_libraries(dynolog_lib PUBLIC BuiltinMetrics) 28 | 29 | add_subdirectory(rpc) 30 | 31 | add_subdirectory(ipcfabric) 32 | target_link_libraries(dynolog_lib PUBLIC dynolog_ipcfabric_lib) 33 | 34 | # depends on ipcfabric 35 | add_subdirectory(tracing) 36 | target_link_libraries(dynolog_lib PUBLIC dynolog_ipcmonitor_lib) 37 | 38 | add_subdirectory(gpumon) 39 | target_link_libraries(dynolog_lib PUBLIC dynolog_dcgm_lib "-ldl") 40 | 41 | add_subdirectory(rdmamon) 42 | target_link_libraries(dynolog_lib PUBLIC dynolog_rdmamon_lib) 43 | 44 | add_subdirectory(metric_frame) 45 | 46 | add_executable(dynolog Main.cpp) 47 | target_link_libraries(dynolog PRIVATE dynolog_lib dynolog_rpc_lib) 48 | -------------------------------------------------------------------------------- /dynolog/src/CompositeLogger.cpp: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #include "dynolog/src/CompositeLogger.h" 4 | 5 | namespace dynolog { 6 | 7 | CompositeLogger::CompositeLogger(std::vector> loggers) { 8 | loggers_ = std::move(loggers); 9 | } 10 | 11 | void CompositeLogger::setTimestamp(Timestamp ts) { 12 | for (const auto& logger : loggers_) { 13 | logger->setTimestamp(ts); 14 | } 15 | } 16 | 17 | void CompositeLogger::logInt(const std::string& key, int64_t val) { 18 | for (const auto& logger : loggers_) { 19 | logger->logInt(key, val); 20 | } 21 | } 22 | 23 | void CompositeLogger::logUint(const std::string& key, uint64_t val) { 24 | for (const auto& logger : loggers_) { 25 | logger->logUint(key, val); 26 | } 27 | } 28 | 29 | void CompositeLogger::logFloat(const std::string& key, float val) { 30 | for (const auto& logger : loggers_) { 31 | logger->logFloat(key, val); 32 | } 33 | } 34 | 35 | void CompositeLogger::logStr(const std::string& key, const std::string& val) { 36 | for (const auto& logger : loggers_) { 37 | logger->logStr(key, val); 38 | } 39 | } 40 | 41 | void CompositeLogger::finalize() { 42 | for (const auto& logger : loggers_) { 43 | logger->finalize(); 44 | } 45 | } 46 | 47 | } // namespace dynolog 48 | -------------------------------------------------------------------------------- /dynolog/src/CompositeLogger.h: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #pragma once 4 | #include "dynolog/src/Logger.h" 5 | 6 | namespace dynolog { 7 | 8 | class CompositeLogger : public Logger { 9 | public: 10 | explicit CompositeLogger(std::vector> loggers); 11 | 12 | void setTimestamp(Timestamp ts) override; 13 | 14 | void logInt(const std::string& key, int64_t val) override; 15 | 16 | void logUint(const std::string& key, uint64_t val) override; 17 | 18 | void logFloat(const std::string& key, float val) override; 19 | 20 | void logStr(const std::string& key, const std::string& val) override; 21 | 22 | void finalize() override; 23 | 24 | private: 25 | std::vector> loggers_; 26 | }; 27 | 28 | } // namespace dynolog 29 | -------------------------------------------------------------------------------- /dynolog/src/FBRelayLogger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include "dynolog/src/Logger.h" 12 | 13 | namespace dynolog { 14 | 15 | class SocketWrapper; 16 | 17 | // An abstract class for logging metrics 18 | // 1. Create a class with this interface for each log entry 19 | // 2. Add data to it using log* methods 20 | // 3. Publish the data data with finalize() 21 | // Todo add timestamp to constructor 22 | class FBRelayLogger : public JsonLogger { 23 | public: 24 | explicit FBRelayLogger(); 25 | ~FBRelayLogger() override {} 26 | 27 | void finalize() override; 28 | 29 | private: 30 | class SocketWrapper { 31 | public: 32 | explicit SocketWrapper(const std::string& addr, int port); 33 | ~SocketWrapper(); 34 | 35 | bool success() const; 36 | bool send(const std::string& msg); 37 | 38 | private: 39 | int sock_fd_ = 0; 40 | bool success_ = false; 41 | }; 42 | 43 | void initSocket(); 44 | 45 | std::unique_ptr socket; 46 | std::string hostname_; 47 | }; 48 | 49 | } // namespace dynolog 50 | -------------------------------------------------------------------------------- /dynolog/src/KernelCollector.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/KernelCollector.h" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace dynolog { 12 | 13 | inline int64_t ticksToMs(int64_t ticks) { 14 | return ticks * 10; 15 | } 16 | KernelCollector::KernelCollector() : KernelCollectorBase() {} 17 | 18 | void KernelCollector::step() { 19 | uptime_ = readUptime(); 20 | readCpuStats(); 21 | readNetworkStats(); 22 | } 23 | 24 | void KernelCollector::log(Logger& log) { 25 | log.logInt("uptime", uptime_); 26 | 27 | // Avoid logging first sample of metrics requiring delta computation 28 | if (first_) { 29 | first_ = false; 30 | return; 31 | } 32 | 33 | float total_ticks = cpuDelta_.total(); 34 | 35 | // Relative utilization in percentage 36 | log.logFloat("cpu_u", cpuDelta_.u / total_ticks * 100.0); 37 | log.logFloat("cpu_i", cpuDelta_.i / total_ticks * 100.0); 38 | log.logFloat("cpu_s", cpuDelta_.s / total_ticks * 100.0); 39 | 40 | // CPU utilization is 1 - idle/total 41 | log.logFloat("cpu_util", 100 * (1 - cpuDelta_.i / total_ticks)); 42 | 43 | log.logInt("cpu_u_ms", ticksToMs(cpuDelta_.u)); 44 | log.logInt("cpu_s_ms", ticksToMs(cpuDelta_.s)); 45 | log.logInt("cpu_w_ms", ticksToMs(cpuDelta_.w)); 46 | log.logInt("cpu_n_ms", ticksToMs(cpuDelta_.n)); 47 | log.logInt("cpu_x_ms", ticksToMs(cpuDelta_.x)); 48 | log.logInt("cpu_y_ms", ticksToMs(cpuDelta_.y)); 49 | log.logInt("cpu_z_ms", ticksToMs(cpuDelta_.z)); 50 | 51 | if (numCpuSockets_ > 1) { 52 | for (int i = 0; i < numCpuSockets_; i++) { 53 | auto node_ticks = nodeCpuTime_[i].total(); 54 | log.logFloat( 55 | fmt::format("cpu_u_node{}", i), 56 | nodeCpuTime_[i].u / node_ticks * 100.0); 57 | log.logFloat( 58 | fmt::format("cpu_s_node{}", i), 59 | nodeCpuTime_[i].s / node_ticks * 100.0); 60 | log.logFloat( 61 | fmt::format("cpu_i_node{}", i), 62 | nodeCpuTime_[i].i / node_ticks * 100.0); 63 | } 64 | } 65 | 66 | for (const auto& [devName, devRxtx] : rxtxDelta_) { 67 | log.logUint(fmt::format("rx_bytes.{}", devName), devRxtx.rxBytes); 68 | log.logUint(fmt::format("rx_packets.{}", devName), devRxtx.rxPackets); 69 | log.logUint(fmt::format("rx_errors.{}", devName), devRxtx.rxErrors); 70 | log.logUint(fmt::format("rx_drops.{}", devName), devRxtx.rxDrops); 71 | log.logUint(fmt::format("tx_bytes.{}", devName), devRxtx.txBytes); 72 | log.logUint(fmt::format("tx_packets.{}", devName), devRxtx.txPackets); 73 | log.logUint(fmt::format("tx_errors.{}", devName), devRxtx.txErrors); 74 | log.logUint(fmt::format("tx_drops.{}", devName), devRxtx.txDrops); 75 | } 76 | 77 | log.setTimestamp(); 78 | } 79 | 80 | } // namespace dynolog 81 | -------------------------------------------------------------------------------- /dynolog/src/KernelCollector.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/KernelCollectorBase.h" 9 | #include "dynolog/src/Logger.h" 10 | 11 | namespace dynolog { 12 | 13 | /* New and improved Kernel Monitor 14 | * - no dependency on Meta infra 15 | * - support generic logging 16 | */ 17 | class KernelCollector : public KernelCollectorBase { 18 | public: 19 | explicit KernelCollector(); 20 | 21 | // perform metric lookups 22 | void step(); 23 | 24 | void log(Logger& logger); 25 | }; 26 | 27 | } // namespace dynolog 28 | -------------------------------------------------------------------------------- /dynolog/src/KernelCollectorBase.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "dynolog/src/Types.h" 13 | #include "pfs/procfs.hpp" 14 | 15 | namespace dynolog { 16 | 17 | /* KernelCollectorBase: Provides capabilities to measure system metrics using 18 | * Linux kernel provided interfaces. 19 | */ 20 | class KernelCollectorBase { 21 | public: 22 | explicit KernelCollectorBase(const std::string& rootDir = ""); 23 | 24 | time_t readUptime(); 25 | 26 | void readCpuStats(); 27 | void readNetworkStats(); 28 | 29 | protected: 30 | time_t uptime_; 31 | bool first_ = true; 32 | 33 | // Root directory containing procfs for manual parsing 34 | std::string rootDir_; 35 | // proc fs parser 36 | pfs::procfs pfs_; 37 | 38 | size_t numCpuSockets_; 39 | const size_t cpuCoresTotal_; 40 | size_t nicDevCount_; 41 | bool filterInteraces_; 42 | std::vector nicInterfacePrefixes_; 43 | 44 | // Save most recent CPU stats and delta from most recent 45 | struct CpuTime cpuTime_, cpuDelta_; 46 | std::array nodeCpuTime_; 47 | std::vector perCoreCpuTime_; 48 | 49 | // Save more recent net device stats 50 | std::map rxtx_, rxtxDelta_; 51 | // The net device limit in bps. this is read from /sys/class/net//speed 52 | std::map netLimitBps_; 53 | 54 | // network info is initialized once during construction, while network stats 55 | // is updated periodically. 56 | void readNetworkInfo(const std::string& interface); 57 | void updateNetworkStatsDelta( 58 | const std::map& rxtxNew); 59 | bool isMonitoringInterfaceActive(std::string interface); 60 | 61 | // Should match googletest/include/gtest/gtest_prod.h 62 | // friend class test_case_name##_##test_name##_Test 63 | friend class KernelCollecterTest_CpuStatsTest_Test; 64 | friend class KernelCollecterTest_NetworkStatsTest_Test; 65 | friend class KernelCollecterTest_UpdateNetworkStatsDeltaTest_Test; 66 | friend class KernelCollecterTest_MonitorInterfaceTest_Test; 67 | }; 68 | 69 | } // namespace dynolog 70 | -------------------------------------------------------------------------------- /dynolog/src/LibkinetoTypes.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace dynolog { 12 | 13 | enum class LibkinetoConfigType { 14 | NONE = 0, 15 | EVENTS = 1, 16 | ACTIVITIES = 2, 17 | }; 18 | 19 | struct GpuProfilerResult { 20 | std::vector traceIds; 21 | std::vector processesMatched; 22 | std::vector eventProfilersTriggered; 23 | std::vector activityProfilersTriggered; 24 | int32_t eventProfilersBusy; 25 | int32_t activityProfilersBusy; 26 | }; 27 | 28 | } // namespace dynolog 29 | -------------------------------------------------------------------------------- /dynolog/src/Logger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/Logger.h" 7 | #include 8 | #include 9 | #include 10 | 11 | using json = nlohmann::json; 12 | 13 | namespace dynolog { 14 | 15 | DEFINE_string( 16 | access_token, 17 | "", 18 | "The ODS access token to publish through Graph API"); 19 | DEFINE_string( 20 | certificate_path, 21 | "/etc/ssl/certs/ca-certificates.crt", 22 | "The path for SSL certificate"); 23 | 24 | std::string JsonLogger::timestampStr() const { 25 | std::time_t ts_time_t = std::chrono::system_clock::to_time_t(ts_); 26 | std::tm ts_tm = *std::localtime(&ts_time_t); 27 | char buf[512]; 28 | auto millis = std::chrono::duration_cast( 29 | ts_.time_since_epoch()); 30 | std::strftime(buf, 512, "%Y-%m-%dT%H:%M:%S", &ts_tm); 31 | return fmt::format("{}.{:03d}Z", buf, millis.count() % 1000); 32 | } 33 | 34 | json JsonLogger::sampleJson() const { 35 | return json_; 36 | } 37 | 38 | void JsonLogger::logInt(const std::string& key, int64_t val) { 39 | json_[key] = val; 40 | } 41 | 42 | void JsonLogger::logFloat(const std::string& key, float val) { 43 | json_[key] = fmt::format("{:.3f}", val); 44 | } 45 | 46 | void JsonLogger::logUint(const std::string& key, uint64_t val) { 47 | json_[key] = val; 48 | } 49 | 50 | void JsonLogger::logStr(const std::string& key, const std::string& val) { 51 | json_[key] = val; 52 | } 53 | 54 | void JsonLogger::finalize() { 55 | LOG(INFO) << "Logging : " << json_.size() << " values"; 56 | LOG(INFO) << "time = " << timestampStr() << " data = " << sampleJson().dump(); 57 | json_.clear(); 58 | } 59 | 60 | KeyParts splitKey(const std::string& full_key) { 61 | // Splits "metric.entity" 62 | KeyParts ret; 63 | size_t pos = full_key.find('.'); 64 | if (pos == std::string::npos) { 65 | ret.metric = full_key; 66 | return ret; 67 | } 68 | 69 | ret.metric = full_key.substr(0, pos); 70 | ret.entity = full_key.substr(pos + 1); 71 | return ret; 72 | } 73 | 74 | } // namespace dynolog 75 | -------------------------------------------------------------------------------- /dynolog/src/Logger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace dynolog { 16 | 17 | DECLARE_string(access_token); 18 | DECLARE_string(certificate_path); 19 | 20 | // An abstract class for logging metrics 21 | // 1. Create a class with this interface for each log entry 22 | // 2. Add data to it using log* methods 23 | // 3. Publish the data with finalize() 24 | class Logger { 25 | public: 26 | using Timestamp = std::chrono::time_point; 27 | virtual ~Logger() {} 28 | 29 | virtual void setTimestamp( 30 | Timestamp ts = std::chrono::system_clock::now()) = 0; 31 | 32 | // Logs an integer value 33 | virtual void logInt(const std::string& key, int64_t val) = 0; 34 | 35 | // Logs a floating point value 36 | virtual void logFloat(const std::string& key, float val) = 0; 37 | 38 | // Logs an unsigned integer value 39 | virtual void logUint(const std::string& key, uint64_t val) = 0; 40 | 41 | // Logs a string value 42 | virtual void logStr(const std::string& key, const std::string& val) = 0; 43 | 44 | virtual void finalize() = 0; 45 | }; 46 | 47 | struct KeyParts { 48 | std::string metric; 49 | std::string entity; 50 | }; 51 | KeyParts splitKey(const std::string& full_key); 52 | 53 | class JsonLogger : public Logger { 54 | public: 55 | void setTimestamp(Timestamp ts) override { 56 | ts_ = ts; 57 | } 58 | 59 | void logInt(const std::string& key, int64_t val) override; 60 | 61 | void logFloat(const std::string& key, float val) override; 62 | 63 | void logUint(const std::string& key, uint64_t val) override; 64 | 65 | void logStr(const std::string& key, const std::string& val) override; 66 | 67 | void finalize() override; 68 | 69 | protected: 70 | Timestamp ts_; 71 | nlohmann::json sampleJson() const; 72 | std::string timestampStr() const; 73 | 74 | private: 75 | nlohmann::json json_; 76 | }; 77 | 78 | } // namespace dynolog 79 | -------------------------------------------------------------------------------- /dynolog/src/Metrics.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace dynolog { 12 | 13 | enum class MetricType { 14 | Delta, 15 | Instant, 16 | Ratio, 17 | Rate, 18 | }; 19 | 20 | struct MetricDesc { 21 | std::string name; 22 | MetricType type; 23 | std::string desc; 24 | }; 25 | 26 | const std::vector getAllMetrics(); 27 | 28 | const std::vector getNetworkMetrics(); 29 | 30 | } // namespace dynolog 31 | -------------------------------------------------------------------------------- /dynolog/src/MonitorBase.h: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | namespace facebook { 9 | namespace dynolog { 10 | 11 | /* Skeleton Monitor class which subscribes to a Ticker for 12 | * scheduling. 13 | */ 14 | template 15 | class MonitorBase { 16 | public: 17 | using TMask = typename TTicker::TMask; 18 | MonitorBase( 19 | std::shared_ptr ticker, 20 | const std::string& name, 21 | std::vector subminor_tick_sample_rates) 22 | : _ticker(ticker), 23 | _name(name), 24 | _subminor_tick_sample_rates(subminor_tick_sample_rates) { 25 | _ticker->subscribe( 26 | name, 27 | TTicker::TSubscriberConfig::make( 28 | name, 29 | [this](TMask mask) { this->tick(mask); }, 30 | subminor_tick_sample_rates)); 31 | } 32 | 33 | bool try_change_sample_rates(std::vector subminor_tick_sample_rates) { 34 | std::shared_ptr new_config; 35 | try { 36 | new_config = std::make_shared( 37 | TTicker::TSubscriberConfig::make( 38 | _name, 39 | [this](TMask mask) { this->tick(mask); }, 40 | subminor_tick_sample_rates)); 41 | } catch (std::exception& e) { 42 | LOG(ERROR) << "Failed to create new config: " << e.what(); 43 | return false; 44 | } 45 | // At this point assume config is valid 46 | _ticker->subscribe(_name, *new_config); 47 | _subminor_tick_sample_rates = subminor_tick_sample_rates; 48 | return true; 49 | } 50 | 51 | virtual void tick(TMask mask) = 0; 52 | virtual ~MonitorBase() {} 53 | 54 | private: 55 | std::shared_ptr _ticker; 56 | std::string _name; 57 | std::vector _subminor_tick_sample_rates; 58 | 59 | protected: 60 | std::string get_name() const { 61 | return _name; 62 | } 63 | std::array get_subminor_tick_sample_rates() 64 | const { 65 | return _ticker->get_config(_name)->get_subminor_tick_sample_rates(); 66 | } 67 | }; 68 | 69 | } // namespace dynolog 70 | } // namespace facebook 71 | -------------------------------------------------------------------------------- /dynolog/src/ODSJsonLogger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/ODSJsonLogger.h" 7 | #include "hbt/src/common/System.h" 8 | 9 | #ifdef USE_GRAPH_ENDPOINT 10 | #include // @manual 11 | #include // @manual 12 | #endif 13 | #include 14 | #include 15 | #include 16 | 17 | DEFINE_string(category_id, "", "The category id of the ODS endpoint"); 18 | DEFINE_string(ods_entity_prefix, "", "The prefix for ODS entity name"); 19 | 20 | namespace dynolog { 21 | 22 | #ifdef USE_GRAPH_ENDPOINT 23 | constexpr char kODSUrl[] = "https://graph.facebook.com/v2.2/ods_metrics"; 24 | #endif 25 | 26 | ODSJsonLogger::ODSJsonLogger() : hostname_(facebook::hbt::getHostName()) {} 27 | 28 | void ODSJsonLogger::finalize() { 29 | std::string entity = FLAGS_ods_entity_prefix + hostname_; 30 | nlohmann::json metrics = sampleJson(); 31 | 32 | if (metrics.find("device") != metrics.end()) { 33 | entity += ".gpu." + std::to_string(metrics["device"].get()); 34 | } 35 | 36 | nlohmann::json out_array; 37 | for (auto it = metrics.begin(); it != metrics.end(); ++it) { 38 | if (it.key() == "device") { 39 | continue; 40 | } 41 | nlohmann::json datapoint = { 42 | {"entity", entity}, 43 | {"key", "dynolog." + it.key()}, 44 | {"value", it.value()}, 45 | }; 46 | out_array.push_back(datapoint); 47 | } 48 | 49 | #ifdef USE_GRAPH_ENDPOINT 50 | cpr::SslOptions sslOpts = 51 | cpr::Ssl(cpr::ssl::CaInfo{FLAGS_certificate_path.c_str()}); 52 | 53 | cpr::Response r = cpr::Post( 54 | cpr::Url{kODSUrl}, 55 | cpr::Payload{ 56 | {"access_token", FLAGS_access_token}, 57 | {"datapoints", out_array.dump()}, 58 | {"category_id", FLAGS_category_id}}, 59 | sslOpts); 60 | 61 | // flush the logger 62 | if (r.status_code != 200) { 63 | LOG(ERROR) << "ODS publish request failed: " << r.text; 64 | } 65 | #else 66 | LOG_FIRST_N(WARNING, 1) << "ODS logger was not included in the build"; 67 | #endif 68 | 69 | JsonLogger::finalize(); 70 | } 71 | 72 | } // namespace dynolog 73 | -------------------------------------------------------------------------------- /dynolog/src/ODSJsonLogger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/Logger.h" 9 | 10 | namespace dynolog { 11 | 12 | class ODSJsonLogger : public JsonLogger { 13 | public: 14 | ODSJsonLogger(); 15 | 16 | void logStr(const std::string& /* key */, const std::string& /* val */) 17 | override {} 18 | 19 | void finalize() override; 20 | 21 | private: 22 | std::string hostname_; 23 | }; 24 | 25 | } // namespace dynolog 26 | -------------------------------------------------------------------------------- /dynolog/src/PerfMonitor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/Logger.h" 9 | #include "hbt/src/mon/Monitor.h" 10 | #include "hbt/src/perf_event/BuiltinMetrics.h" 11 | 12 | namespace hbt = facebook::hbt; 13 | 14 | namespace dynolog { 15 | 16 | /* Open source dynolog Performance Monitor 17 | * - no dependency on Meta infra 18 | * - support generic logging 19 | */ 20 | 21 | using MuxGroupId = hbt::mon::Monitor<>::MuxGroupId; 22 | using TCpuCountReader = hbt::mon::Monitor<>::TCpuCountReader; 23 | using ElemId = hbt::mon::Monitor<>::ElemId; 24 | 25 | class PerfMonitor { 26 | public: 27 | explicit PerfMonitor( 28 | const hbt::CpuSet& monCpus, 29 | const std::vector& metricIds, 30 | std::shared_ptr pmuDeviceManager, 31 | std::shared_ptr availableMetrics); 32 | 33 | // perform metric lookups 34 | void step(); 35 | void log(Logger& logger); 36 | 37 | protected: 38 | hbt::mon::Monitor<> hbtMon_; 39 | const hbt::CpuSet& monCpus_; 40 | std::shared_ptr pmuDeviceManager_; 41 | const MuxGroupId defaultMuxGroupId_; 42 | std::map> readValues_; 43 | std::map> countReaders_; 44 | }; 45 | 46 | // singleton object for default Metrics and PmuDeviceManager 47 | std::shared_ptr getDefaultMetrics(); 48 | std::shared_ptr getDefaultPmuDeviceManager(); 49 | 50 | } // namespace dynolog 51 | -------------------------------------------------------------------------------- /dynolog/src/PrometheusLogger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/Logger.h" 9 | 10 | #ifdef USE_PROMETHEUS 11 | #include 12 | #include 13 | #include 14 | #include 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef USE_PROMETHEUS 22 | 23 | DECLARE_string(prometheus_address); 24 | DECLARE_int32(prometheus_port); 25 | 26 | namespace dynolog { 27 | 28 | class PrometheusManager { 29 | public: 30 | struct LoggingGuard { 31 | std::shared_ptr manager; 32 | std::lock_guard lock_guard; 33 | }; 34 | 35 | PrometheusManager(); 36 | 37 | // Add dynamic metrics if required 38 | // returns true if a key was added 39 | bool ensureDynamicKey(const std::string& key); 40 | 41 | // Note that this method is not thread-safe and so 42 | // should only be used with the LoggingGuard in scope 43 | void log(const std::string& key, double val); 44 | 45 | static LoggingGuard singleton(); 46 | 47 | private: 48 | using GaugeFamily = prometheus::Family; 49 | std::lock_guard lock() { 50 | return std::lock_guard{mutex_}; 51 | } 52 | 53 | std::mutex mutex_; 54 | prometheus::Exposer exposer_; 55 | std::shared_ptr registry_; 56 | 57 | // only store a reference to Gauge because copying is not allowed 58 | std::unordered_map gauges_; 59 | 60 | // metric families that need dynamic dimensions 61 | std::unordered_map dynamic_metrics_; 62 | 63 | // Should match googletest/include/gtest/gtest_prod.h 64 | // friend class test_case_name##_##test_name##_Test 65 | friend class PrometheusLoggerTest_ExporterTest_Test; 66 | }; 67 | 68 | class PrometheusLogger : public Logger { 69 | public: 70 | // Timestamp is set during logging part. 71 | void setTimestamp(Timestamp /*ts*/) override {} 72 | 73 | void logInt(const std::string& key, int64_t val) override { 74 | logImpl(key, static_cast(val)); 75 | } 76 | 77 | void logFloat(const std::string& key, float val) override { 78 | logImpl(key, static_cast(val)); 79 | } 80 | 81 | void logUint(const std::string& key, uint64_t val) override { 82 | logImpl(key, static_cast(val)); 83 | } 84 | 85 | // not supported 86 | void logStr(const std::string& /*key*/, const std::string& /*val*/) override { 87 | } 88 | 89 | void finalize() override; 90 | 91 | private: 92 | void logImpl(const std::string& key, double val); 93 | 94 | std::unordered_map kvs_; 95 | 96 | friend class PrometheusLoggerTest_BasicTest_Test; 97 | friend class PrometheusLoggerTest_ExporterTest_Test; 98 | }; 99 | 100 | } // namespace dynolog 101 | #endif // USE_PROMETHEUS 102 | -------------------------------------------------------------------------------- /dynolog/src/ScubaLogger.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/ScubaLogger.h" 7 | #include 8 | #include "hbt/src/common/System.h" 9 | #ifdef USE_GRAPH_ENDPOINT 10 | #include // @manual 11 | #include // @manual 12 | #endif 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace dynolog { 20 | #ifdef USE_GRAPH_ENDPOINT 21 | constexpr char kScubaUrl[] = "http://graph.facebook.com/v2.2/scribe_logs"; 22 | #endif 23 | 24 | DEFINE_string( 25 | scribe_category, 26 | "perfpipe_fair_cluster_gpu_stats", 27 | "The scribe category name for scuba logging"); 28 | 29 | ScubaLogger::ScubaLogger(const std::string& scribe_category) 30 | : scribe_category_(scribe_category), 31 | hostname_(facebook::hbt::getHostName()) {} 32 | 33 | void ScubaLogger::logInt(const std::string& key, int64_t val) { 34 | metrics_int_[key] = val; 35 | } 36 | 37 | void ScubaLogger::logUint(const std::string& key, uint64_t val) { 38 | metrics_int_[key] = val; 39 | } 40 | 41 | void ScubaLogger::logFloat(const std::string& key, float val) { 42 | metrics_double_[key] = val; 43 | } 44 | 45 | void ScubaLogger::logStr(const std::string& key, const std::string& val) { 46 | metrics_str_[key] = val; 47 | } 48 | 49 | void ScubaLogger::clearMetrics() { 50 | metrics_int_.clear(); 51 | metrics_str_.clear(); 52 | metrics_double_.clear(); 53 | } 54 | 55 | void ScubaLogger::finalize() { 56 | metrics_str_["host_name"] = hostname_; 57 | 58 | // user of logger need to set time 59 | metrics_int_["time"] = 60 | std::chrono::duration_cast(ts_.time_since_epoch()) 61 | .count(); 62 | 63 | nlohmann::json scuba_message = { 64 | {"int", metrics_int_}, 65 | {"normal", metrics_str_}, 66 | {"double", metrics_double_}, 67 | }; 68 | 69 | nlohmann::json log = { 70 | {"category", scribe_category_}, 71 | {"message", scuba_message.dump()}, 72 | {"line_escape", false}, 73 | }; 74 | 75 | nlohmann::json logs; 76 | logs.push_back(log); 77 | 78 | #ifdef USE_GRAPH_ENDPOINT 79 | cpr::SslOptions sslOpts = 80 | cpr::Ssl(cpr::ssl::CaInfo{FLAGS_certificate_path.c_str()}); 81 | 82 | cpr::Response r = cpr::Post( 83 | cpr::Url{kScubaUrl}, 84 | cpr::Payload{{"access_token", FLAGS_access_token}, {"logs", logs.dump()}}, 85 | sslOpts); 86 | 87 | // flush the logger 88 | if (r.status_code != 200) { 89 | LOG(ERROR) << "Scuba publish request failed: " << r.text; 90 | } 91 | #else 92 | LOG_FIRST_N(WARNING, 1) << "Scuba logger was not included in the build"; 93 | #endif 94 | 95 | LOG(INFO) << logs.dump(); 96 | clearMetrics(); 97 | } 98 | 99 | } // namespace dynolog 100 | -------------------------------------------------------------------------------- /dynolog/src/ScubaLogger.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/Logger.h" 9 | 10 | namespace dynolog { 11 | 12 | DECLARE_string(scribe_category); 13 | 14 | class ScubaLogger final : public Logger { 15 | public: 16 | explicit ScubaLogger(const std::string& scribe_category); 17 | void setTimestamp(Timestamp ts) override { 18 | ts_ = ts; 19 | } 20 | 21 | void logInt(const std::string& key, int64_t val) override; 22 | 23 | void logFloat(const std::string& key, float val) override; 24 | 25 | void logUint(const std::string& key, uint64_t val) override; 26 | 27 | void logStr(const std::string& key, const std::string& val) override; 28 | 29 | void finalize() override; 30 | 31 | private: 32 | void clearMetrics(); 33 | 34 | Timestamp ts_; 35 | std::string scribe_category_; 36 | std::string hostname_; 37 | nlohmann::json metrics_int_; 38 | nlohmann::json metrics_double_; 39 | nlohmann::json metrics_str_; 40 | }; 41 | 42 | } // namespace dynolog 43 | -------------------------------------------------------------------------------- /dynolog/src/ServiceHandler.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/ServiceHandler.h" 7 | #include "dynolog/src/LibkinetoConfigManager.h" 8 | 9 | namespace dynolog { 10 | 11 | int ServiceHandler::getStatus() { 12 | return 1; 13 | } 14 | 15 | std::string ServiceHandler::getVersion() { 16 | return DYNOLOG_VERSION; 17 | } 18 | 19 | GpuProfilerResult ServiceHandler::setKinetOnDemandRequest( 20 | int job_id, 21 | const std::set& pids, 22 | const std::string& config, 23 | int limit) { 24 | return LibkinetoConfigManager::getInstance()->setOnDemandConfig( 25 | // Temporarily cast to string while we iteratively migrate to string job 26 | // id 27 | std::to_string(job_id), 28 | pids, 29 | config, 30 | (int)LibkinetoConfigType::ACTIVITIES, 31 | limit); 32 | } 33 | 34 | bool ServiceHandler::dcgmProfPause(int duration_s) { 35 | if (dcgm_) { 36 | return dcgm_->pauseProfiling(duration_s); 37 | } 38 | return false; 39 | } 40 | 41 | bool ServiceHandler::dcgmProfResume() { 42 | if (dcgm_) { 43 | return dcgm_->resumeProfiling(); 44 | } 45 | return false; 46 | } 47 | 48 | } // namespace dynolog 49 | -------------------------------------------------------------------------------- /dynolog/src/ServiceHandler.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include "dynolog/src/LibkinetoConfigManager.h" 11 | #include "dynolog/src/gpumon/DcgmGroupInfo.h" 12 | 13 | namespace dynolog { 14 | 15 | // Service Handler : acts as a generic handler for requests made to the 16 | // daemmon. This can be wrapped inside RPC protocols to handle the 17 | // communication over the network. 18 | 19 | class ServiceHandler { 20 | public: 21 | explicit ServiceHandler(std::shared_ptr dcgm) 22 | : dcgm_(dcgm) {} 23 | // returns the state of the service 24 | int getStatus(); 25 | std::string getVersion(); 26 | 27 | GpuProfilerResult setKinetOnDemandRequest( 28 | int job_id, 29 | const std::set& pids, 30 | const std::string& config, 31 | int limit); 32 | // ... more to come 33 | bool dcgmProfPause(int duration_s); 34 | bool dcgmProfResume(); 35 | 36 | private: 37 | std::shared_ptr dcgm_; 38 | }; 39 | 40 | } // namespace dynolog 41 | -------------------------------------------------------------------------------- /dynolog/src/Types.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #define MAX_CPU_SOCKETS 8 13 | 14 | namespace dynolog { 15 | 16 | // Ensure consistent 'ticks' handling 17 | using TIC_t = unsigned long long; 18 | 19 | // This structure stores a frame's cpu tics used in history 20 | // calculations. It exists primarily for SMP support but serves 21 | // all environments. 22 | struct CpuTime { 23 | TIC_t u = 0, n = 0, s = 0, i = 0, w = 0, x = 0, y = 0, 24 | z = 0; // as represented in /proc/stat 25 | /* 26 | * u - user: normal processes executing in user mode 27 | * n - nice: niced processes executing in user mode 28 | * s - system: processes executing in kernel mode 29 | * i - idle: twiddling thumbs 30 | * w - iowait: waiting for I/O to complete 31 | * x - irq: servicing interrupts 32 | * y - softirq: servicing softirqs 33 | * z - unused? 34 | */ 35 | 36 | CpuTime operator-(const CpuTime& prev) const { 37 | return CpuTime{ 38 | .u = u - prev.u, 39 | .n = n - prev.n, 40 | .s = s - prev.s, 41 | .i = i - prev.i, 42 | .w = w - prev.w, 43 | .x = x - prev.x, 44 | .y = y - prev.y, 45 | .z = z - prev.z, 46 | }; 47 | } 48 | 49 | void operator+=(const CpuTime& other) { 50 | u += other.u; 51 | n += other.n; 52 | s += other.s; 53 | i += other.i; 54 | w += other.w; 55 | x += other.x; 56 | y += other.y; 57 | z += other.z; 58 | } 59 | 60 | TIC_t total() const { 61 | return u + n + s + i + w + x + y + z; 62 | } 63 | }; 64 | 65 | struct RxTx { 66 | uint64_t rxBytes, rxPackets; 67 | uint64_t rxErrors, rxDrops; 68 | uint64_t txBytes, txPackets; 69 | uint64_t txErrors, txDrops; 70 | 71 | RxTx operator-(const RxTx& prev) const { 72 | return RxTx{ 73 | .rxBytes = rxBytes - prev.rxBytes, 74 | .rxPackets = rxPackets - prev.rxPackets, 75 | .rxErrors = rxErrors - prev.rxErrors, 76 | .rxDrops = rxDrops - prev.rxDrops, 77 | .txBytes = txBytes - prev.txBytes, 78 | .txPackets = txPackets - prev.txPackets, 79 | .txErrors = txErrors - prev.txErrors, 80 | .txDrops = txDrops - prev.txDrops, 81 | }; 82 | } 83 | 84 | void operator+=(const RxTx& other) { 85 | rxBytes += other.rxBytes; 86 | rxPackets += other.rxPackets; 87 | rxErrors += other.rxErrors; 88 | rxDrops += other.rxDrops; 89 | txBytes += other.txBytes; 90 | txPackets += other.txPackets; 91 | txErrors += other.txErrors; 92 | txDrops += other.txDrops; 93 | } 94 | }; 95 | 96 | } // namespace dynolog 97 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_library(dynolog_dcgm_lib 4 | DcgmApiStub.cpp DcgmApiStub.h 5 | DcgmGroupInfo.cpp DcgmGroupInfo.h 6 | Utils.h Utils.cpp 7 | Entity.cpp Entity.h 8 | dcgm_fields.h 9 | dcgm_agent.h 10 | dcgm_errors.h 11 | dcgm_structs.h 12 | dcgm_api_export.h 13 | ) 14 | target_include_directories(dynolog_dcgm_lib 15 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} 16 | ) 17 | target_include_directories(dynolog_dcgm_lib PUBLIC 18 | ${PROJECT_SOURCE_DIR}/third_party/pfs/include) 19 | target_link_libraries(dynolog_dcgm_lib PUBLIC gflags::gflags) 20 | target_link_libraries(dynolog_dcgm_lib PUBLIC glog::glog) 21 | target_link_libraries(dynolog_dcgm_lib PUBLIC nlohmann_json::nlohmann_json) 22 | target_link_libraries(dynolog_dcgm_lib PUBLIC pfs) 23 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/DcgmGroupInfo.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "dynolog/src/Logger.h" 14 | #include "dynolog/src/gpumon/dcgm_structs.h" 15 | 16 | namespace dynolog { 17 | namespace gpumon { 18 | 19 | DECLARE_string(dcgm_fields); 20 | 21 | constexpr char kDcgmDefaultFieldIds[] = 22 | "100,155,204,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012"; 23 | class DcgmGroupInfo { 24 | public: 25 | ~DcgmGroupInfo(); 26 | static std::shared_ptr factory( 27 | const std::string& fields_str, 28 | int updateIntervalMs); 29 | void update(); 30 | dcgmStatus_t getDcgmStatus() const { 31 | return errorCode_; 32 | } 33 | bool isFailing() const { 34 | return errorCode_ != DCGM_ST_OK; 35 | } 36 | void log(Logger& logger); 37 | bool pauseProfiling(int duration_s); 38 | bool resumeProfiling(); 39 | 40 | private: 41 | DcgmGroupInfo( 42 | const std::vector& fields, 43 | const std::vector& prof_fields, 44 | int updateIntervalMs); 45 | void init(); 46 | void createGroups(); 47 | void createFieldGroups(const std::vector& fields); 48 | void watchFields(); 49 | void watchProfFields(const std::vector& prof_fields); 50 | 51 | std::vector gpuIdList_; 52 | int deviceCount_ = 0; 53 | bool profEnabled_ = false; 54 | int updateIntervalMs_; 55 | dcgmReturn_t errorCode_{DCGM_ST_OK}; 56 | dcgmReturn_t retCode_{DCGM_ST_OK}; 57 | dcgmHandle_t dcgmHandle_; 58 | dcgmGpuGrp_t groupId_; 59 | std::unordered_map> 60 | metricsMapDouble_; 61 | std::unordered_map> 62 | metricsMapInt_; 63 | // We currently assume at most one pid running on each GPU entity 64 | // This maps from gpu device_id to a map containing environment variables 65 | // and their values for the pid running on that gpu (if it exists). 66 | std::unordered_map> 67 | envMetadataMapString_; 68 | std::vector fieldGroupIds_; 69 | std::mutex profLock_; 70 | std::chrono::seconds profPauseTimer_; 71 | }; 72 | 73 | } // namespace gpumon 74 | } // namespace dynolog 75 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/Entity.cpp: -------------------------------------------------------------------------------- 1 | // Portions Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | 17 | #include "dynolog/src/gpumon/Entity.h" 18 | 19 | Entity::Entity(const dcgmGroupEntityPair_t& entity) 20 | : Entity{entity.entityGroupId, entity.entityId} {} 21 | 22 | Entity::Entity( 23 | dcgm_field_entity_group_t entityGroupId, 24 | dcgm_field_eid_t entityId) 25 | : m_entityGroupId{entityGroupId}, m_entityId{entityId} {} 26 | 27 | bool Entity::operator<(const Entity& other) const { 28 | if (m_entityGroupId < other.m_entityGroupId) { 29 | return true; 30 | } 31 | 32 | if (m_entityGroupId > other.m_entityGroupId) { 33 | return false; 34 | } 35 | 36 | return m_entityId < other.m_entityId; 37 | } 38 | 39 | bool Entity::operator==(const Entity& other) const { 40 | return m_entityGroupId == other.m_entityGroupId && 41 | m_entityId == other.m_entityId; 42 | } 43 | 44 | int getLatestDcgmValueCB( 45 | dcgm_field_entity_group_t entityGroupId, 46 | dcgm_field_eid_t entityId, 47 | dcgmFieldValue_v1* values, 48 | int numValues, 49 | void* userData) { 50 | if (userData == nullptr) { 51 | return DCGM_ST_BADPARAM; 52 | } 53 | 54 | cbData* data = (cbData*)userData; 55 | 56 | /** 57 | * If there's no data yet, status of the first record will be 58 | * DCGM_ST_NO_DATA 59 | */ 60 | if (values[0].status == DCGM_ST_NO_DATA) { 61 | return DCGM_ST_OK; 62 | } else if (values[0].status != DCGM_ST_OK) { 63 | return DCGM_ST_OK; 64 | } 65 | 66 | dcgmGroupEntityPair_t entity; 67 | 68 | entity.entityGroupId = entityGroupId; 69 | entity.entityId = entityId; 70 | 71 | data->m_values[entity].insert( 72 | data->m_values[entity].end(), values, values + numValues); 73 | 74 | return DCGM_ST_OK; 75 | } 76 | 77 | std::ostream& operator<<(std::ostream& os, const Entity& entity) { 78 | os << "EntityGroupId: " << entity.m_entityGroupId; 79 | os << " EntityId: " << entity.m_entityId; 80 | return os; 81 | } 82 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/Entity.h: -------------------------------------------------------------------------------- 1 | // Portions Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // 16 | // clang format off 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include "dynolog/src/gpumon/dcgm_fields.h" 24 | #include "dynolog/src/gpumon/dcgm_structs.h" 25 | 26 | // A wrapper around dcgmGroupEntityPair_t that allows comparison (for maps). 27 | struct Entity { 28 | dcgm_field_entity_group_t 29 | m_entityGroupId; //!< Entity Group ID entity belongs to 30 | dcgm_field_eid_t m_entityId; //!< Entity ID of the entity 31 | 32 | Entity(const dcgmGroupEntityPair_t&); 33 | Entity(dcgm_field_entity_group_t, dcgm_field_eid_t); 34 | 35 | bool operator<(const Entity& other) const; 36 | bool operator==(const Entity& other) const; 37 | 38 | bool isGpu() const { 39 | return m_entityGroupId == DCGM_FE_GPU; 40 | } 41 | bool isGpuInstance() const { 42 | return m_entityGroupId == DCGM_FE_GPU_I; 43 | } 44 | bool isComputeInstance() const { 45 | return m_entityGroupId == DCGM_FE_GPU_CI; 46 | } 47 | }; 48 | 49 | struct cbData { 50 | std::map>& m_values; 51 | 52 | explicit cbData(std::map>& values) 53 | : m_values(values) {} 54 | }; 55 | 56 | int getLatestDcgmValueCB( 57 | dcgm_field_entity_group_t entityGroupId, 58 | dcgm_field_eid_t entityId, 59 | dcgmFieldValue_v1* values, 60 | int numValues, 61 | void* userData); 62 | 63 | std::ostream& operator<<(std::ostream& os, const Entity& entity); 64 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/Utils.cpp: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #include "dynolog/src/gpumon/Utils.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "pfs/procfs.hpp" 10 | 11 | namespace dynolog { 12 | constexpr char kParsePidCmd[] = 13 | "nvidia-smi pmon -c 1 | awk '{print $2}' | tail -n +3"; 14 | // max length of pid 15 | constexpr int PID_SIZE_MAX = 10; 16 | 17 | bool is_number(const std::string& s) { 18 | std::string::const_iterator it = s.begin(); 19 | while (it != s.end() && std::isdigit(*it)) { 20 | ++it; 21 | } 22 | return !s.empty() && it == s.end(); 23 | } 24 | 25 | std::vector getPidsOnGpu() { 26 | FILE* fp = popen(kParsePidCmd, "r"); 27 | char pid[PID_SIZE_MAX]; 28 | std::vector res; 29 | if (fp == nullptr) { 30 | LOG(ERROR) << "Cannot run command " << kParsePidCmd; 31 | return res; 32 | } 33 | 34 | while (fgets(pid, PID_SIZE_MAX, fp) != nullptr) { 35 | std::string pid_str(pid); 36 | pid_str.erase(pid_str.find_last_not_of(" \n\r\t") + 1); 37 | 38 | if (is_number(pid_str)) { 39 | res.push_back(std::stoi(pid_str)); 40 | } else { 41 | res.push_back(-1); 42 | } 43 | } 44 | 45 | int status = pclose(fp); 46 | if (status == -1) { 47 | LOG(ERROR) << "pclose() failed"; 48 | } 49 | return res; 50 | } 51 | 52 | std::unordered_map getMetadataForPid( 53 | pid_t pid, 54 | const std::unordered_map& keysMap) { 55 | std::unordered_map varsMap; 56 | if (pid < 0) { 57 | return varsMap; 58 | } 59 | auto task = pfs::procfs().get_task(pid); 60 | auto env = task.get_environ(); 61 | for (auto key : keysMap) { 62 | if (auto val = env.find(key.first); val != env.end()) { 63 | varsMap[key.second] = val->second; 64 | } 65 | } 66 | return varsMap; 67 | } 68 | 69 | } // namespace dynolog 70 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/Utils.h: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace dynolog { 10 | 11 | // Assume at most one pid running on each gpu device_id 12 | // Result[i] is the pid running on device_id i, 13 | // or -1 if no process is running 14 | std::vector getPidsOnGpu(); 15 | // Input map is type 16 | // output map is type 17 | std::unordered_map getMetadataForPid( 18 | pid_t pid, 19 | const std::unordered_map& keysMap); 20 | 21 | } // namespace dynolog 22 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/amd/RdcWrapper.h: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "rdc/rdc.h" 12 | 13 | #pragma once 14 | 15 | namespace dynolog { 16 | namespace gpumon { 17 | 18 | using RdcMetricsValue = std::variant; 19 | using RdcMetricsMap = std::unordered_map; 20 | 21 | struct RdcRuntimeContext { 22 | std::vector enabledMetrics_; 23 | rdc_handle_t rdcHandle_; 24 | rdc_gpu_group_t gpuGroupId_; 25 | rdc_field_grp_t fieldGroupId_; 26 | }; 27 | 28 | struct RdcRuntimeContextWithWLock { 29 | RdcRuntimeContextWithWLock( 30 | RdcRuntimeContext& data, 31 | std::shared_mutex& sharedDataLock) 32 | : data{data} { 33 | lockGuard_ = std::unique_lock(sharedDataLock); 34 | } 35 | RdcRuntimeContext& data; 36 | 37 | private: 38 | std::unique_lock lockGuard_; 39 | }; 40 | 41 | struct RdcRuntimeContextWithRLock { 42 | RdcRuntimeContextWithRLock( 43 | RdcRuntimeContext& data, 44 | std::shared_mutex& sharedDataLock) 45 | : data{data} { 46 | lockGuard_ = std::shared_lock(sharedDataLock); 47 | } 48 | RdcRuntimeContext& data; 49 | 50 | private: 51 | std::shared_lock lockGuard_; 52 | }; 53 | 54 | class RdcRuntimeContextSynchronized { 55 | public: 56 | RdcRuntimeContextWithRLock rlock() { 57 | return RdcRuntimeContextWithRLock(data_, sharedDataLock_); 58 | } 59 | RdcRuntimeContextWithWLock wlock() { 60 | return RdcRuntimeContextWithWLock(data_, sharedDataLock_); 61 | } 62 | 63 | protected: 64 | RdcRuntimeContext data_; 65 | std::shared_mutex sharedDataLock_; 66 | }; 67 | 68 | class RdcWrapper { 69 | public: 70 | RdcWrapper(std::vector enabledMetrics); 71 | ~RdcWrapper(); 72 | void init(std::vector enabledMetrics); 73 | void clean(); 74 | RdcMetricsMap getRdcMetricsForDevice(size_t device); 75 | 76 | protected: 77 | void init_( 78 | std::vector enabledMetrics, 79 | RdcRuntimeContextWithWLock& context); 80 | void clean_(RdcRuntimeContextWithWLock& context); 81 | 82 | RdcRuntimeContextSynchronized context_; 83 | }; 84 | 85 | } // namespace gpumon 86 | } // namespace dynolog 87 | -------------------------------------------------------------------------------- /dynolog/src/gpumon/dcgm_api_export.h: -------------------------------------------------------------------------------- 1 | // clang-format off 2 | 3 | /* 4 | * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | #ifndef DCGM_DCGM_API_EXPORT_H 19 | #define DCGM_DCGM_API_EXPORT_H 20 | 21 | #undef DCGM_PUBLIC_API 22 | #undef DCGM_PRIVATE_API 23 | 24 | #if defined(DCGM_API_EXPORT) 25 | #define DCGM_PUBLIC_API __attribute((visibility("default"))) 26 | #else 27 | #define DCGM_PUBLIC_API 28 | #if defined(ERROR_IF_NOT_PUBLIC) 29 | #error(Should be public) 30 | #endif 31 | #endif 32 | 33 | #define DCGM_PRIVATE_API __attribute((visibility("hidden"))) 34 | 35 | 36 | #endif // DCGM_DCGM_API_EXPORT_H 37 | -------------------------------------------------------------------------------- /dynolog/src/ipcfabric/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_library (dynolog_ipcfabric_lib INTERFACE) 4 | -------------------------------------------------------------------------------- /dynolog/src/ipcfabric/Utils.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace dynolog { 12 | namespace ipcfabric { 13 | 14 | // struct to register libkineto process 15 | struct LibkinetoContext { 16 | // gpu id of the process running on 17 | int32_t gpu; 18 | // pid to register to dynolog 19 | pid_t pid; 20 | // job id of the process 21 | int64_t jobid; 22 | }; 23 | 24 | // struct to request libkineto ondemand config 25 | struct LibkinetoRequest { 26 | // type of libkineto config 27 | int type; 28 | // size of pids 29 | int n; 30 | // job id of the libkineto process 31 | int64_t jobid; 32 | // pids of the process and its ancestors 33 | int32_t pids[]; 34 | }; 35 | 36 | constexpr char dynolog_endpoint[] = "dynolog"; 37 | constexpr char kLibkinetoRequest[] = "req"; 38 | constexpr char kLibkinetoContext[] = "ctxt"; 39 | } // namespace ipcfabric 40 | } // namespace dynolog 41 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(extra_types STATIC ExtraTypes.h ExtraTypes.cpp) 2 | add_library(text_table STATIC TextTable.h TextTable.cpp) 3 | add_library(metric_series STATIC MetricSeries.h) 4 | set_target_properties(metric_series PROPERTIES LINKER_LANGUAGE CXX) 5 | add_library(metric_values STATIC MetricValues.h) 6 | set_target_properties(metric_values PROPERTIES LINKER_LANGUAGE CXX) 7 | add_library(metric_frame_ts_unit STATIC 8 | MetricFrameTsUnit.h MetricFrameTsUnit.cpp MetricFrameTsUnitInterface.h) 9 | add_library(metric_frame STATIC 10 | MetricFrame.h MetricFrame.cpp MetricFrameBase.h MetricFrameBase.cpp) 11 | target_link_libraries(metric_frame 12 | metric_series metric_frame_ts_unit extra_types text_table) 13 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/ExtraTypes.cpp: -------------------------------------------------------------------------------- 1 | #include "dynolog/src/metric_frame/ExtraTypes.h" 2 | 3 | namespace facebook::dynolog { 4 | 5 | std::ostream& operator<<( 6 | std::ostream& os, 7 | const PerfReadValues& perfReadValues) { 8 | os << perfReadValues.toString(); 9 | return os; 10 | } 11 | 12 | } // namespace facebook::dynolog 13 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/ExtraTypes.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace facebook::dynolog { 12 | 13 | struct PerfReadValues { 14 | uint64_t timeEnabled = 0; 15 | uint64_t timeRunning = 0; 16 | uint64_t count = 0; 17 | 18 | std::string toString() const { 19 | return std::string(""; 23 | } 24 | 25 | PerfReadValues operator-(const PerfReadValues& other) const { 26 | if (other.timeRunning > timeRunning || other.timeEnabled > timeEnabled || 27 | other.count > count) { 28 | throw std::underflow_error( 29 | std::string("lhs value ") + toString() + 30 | " is smaller than rhs value " + other.toString()); 31 | } 32 | PerfReadValues res; 33 | res.timeEnabled = timeEnabled - other.timeEnabled; 34 | res.timeRunning = timeRunning - other.timeRunning; 35 | res.count = count - other.count; 36 | return res; 37 | } 38 | 39 | PerfReadValues operator+(const PerfReadValues& other) const { 40 | PerfReadValues res; 41 | res.timeEnabled = timeEnabled + other.timeEnabled; 42 | res.timeRunning = timeRunning + other.timeRunning; 43 | res.count = count + other.count; 44 | return res; 45 | } 46 | 47 | template 48 | R getCount() const { 49 | if (timeRunning == 0) { 50 | return static_cast(0); 51 | } 52 | return static_cast( 53 | static_cast(count) * static_cast(timeEnabled) / 54 | static_cast(timeRunning)); 55 | } 56 | 57 | // override type cast to double operator here. 58 | // when cast PerfReadValues to double, it will calculate a single double value 59 | // based on timeEnabled and timeRunning. 60 | operator double() const { 61 | return getCount(); 62 | } 63 | }; 64 | 65 | std::ostream& operator<<( 66 | std::ostream& os, 67 | const PerfReadValues& perfReadValues); 68 | 69 | } // namespace facebook::dynolog 70 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/MetricFrame.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/metric_frame/MetricFrameBase.h" 9 | #include "dynolog/src/metric_frame/MetricFrameTsUnitInterface.h" 10 | #include "dynolog/src/metric_frame/MetricSeries.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace facebook::dynolog { 19 | 20 | using MapSamplesT = std::vector>; 21 | using VectorSamplesT = std::vector; 22 | 23 | class MetricFrameMap : public MetricFrameBase { 24 | public: 25 | MetricFrameMap( 26 | size_t capacity, 27 | std::string name, 28 | std::string description, 29 | std::shared_ptr ts); 30 | bool addSeries(const std::string& key, MetricSeriesVar seriesVar); 31 | bool eraseSeries(const std::string& name); 32 | bool addSamples(const MapSamplesT& samples, TimePoint time); 33 | // add a new sample with value = valueOfLastSample + delta 34 | bool incFromLastSample(const MapSamplesT& samples, TimePoint time); 35 | size_t width() const override; 36 | std::optional series(const std::string& name) const override; 37 | std::optional series(int name) const override; 38 | std::vector allSeriesVec() const override; 39 | void show(std::ostream& s) const override; 40 | 41 | protected: 42 | std::map series_; 43 | }; 44 | 45 | using VectorSeriesDefList = std::vector; 46 | 47 | class MetricFrameVector : public MetricFrameBase { 48 | public: 49 | MetricFrameVector( 50 | VectorSeriesDefList defs, 51 | std::string name, 52 | std::string description, 53 | std::shared_ptr ts); 54 | bool addSamples(const VectorSamplesT& samples, TimePoint time); 55 | // add a new sample with value = valueOfLastSample + delta 56 | bool incFromLastSample(const VectorSamplesT& delta, TimePoint time); 57 | size_t width() const override; 58 | std::optional series(const std::string& name) const override; 59 | std::optional series(int name) const override; 60 | std::vector allSeriesVec() const override; 61 | void show(std::ostream& s) const override; 62 | 63 | protected: 64 | VectorSeriesDefList series_; 65 | }; 66 | 67 | } // namespace facebook::dynolog 68 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/MetricFrameTsUnit.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "dynolog/src/metric_frame/MetricFrameTsUnitInterface.h" 9 | #include "dynolog/src/metric_frame/MetricSeries.h" 10 | 11 | #include 12 | 13 | namespace facebook::dynolog { 14 | 15 | class MetricFrameTsUnit : public MetricFrameTsUnitInterface { 16 | public: 17 | explicit MetricFrameTsUnit(size_t frameLength); 18 | virtual void addSample(TimePoint time) override; 19 | virtual std::optional firstSampleTime() const override; 20 | virtual std::optional lastSampleTime() const override; 21 | virtual std::vector getTimeVector() const override; 22 | virtual size_t length() const override; 23 | virtual size_t maxLength() const override; 24 | virtual std::optional getRange( 25 | TimePoint startTime, 26 | TimePoint endTime, 27 | MATCH_POLICY startTimePolicy, 28 | MATCH_POLICY endTimePolicy) const override; 29 | 30 | protected: 31 | MetricSeries timeSeries_; 32 | 33 | std::optional findMatchingOffset( 34 | TimePoint time, 35 | MATCH_POLICY policy) const; 36 | std::optional closestPolicy(TimePoint time) const; 37 | std::optional prevClosestPolicy(TimePoint time) const; 38 | std::optional nextClosestPolicy(TimePoint time) const; 39 | }; 40 | 41 | class MetricFrameTsUnitFixInterval : public MetricFrameTsUnitInterface { 42 | public: 43 | MetricFrameTsUnitFixInterval( 44 | std::chrono::microseconds interval, 45 | size_t frameLength); 46 | virtual void addSample(TimePoint time) override; 47 | virtual std::optional firstSampleTime() const override; 48 | virtual std::optional lastSampleTime() const override; 49 | virtual std::vector getTimeVector() const override; 50 | virtual size_t length() const override; 51 | virtual size_t maxLength() const override; 52 | virtual std::optional getRange( 53 | TimePoint startTime, 54 | TimePoint endTime, 55 | MATCH_POLICY startTimePolicy, 56 | MATCH_POLICY endTimePolicy) const override; 57 | 58 | protected: 59 | const std::chrono::microseconds interval_; 60 | const size_t frameLength_; 61 | size_t sampleCount_ = 0; 62 | TimePoint lastSampleTime_; 63 | 64 | TimePoint offsetToTime(size_t offset) const; 65 | std::optional findMatchingOffset( 66 | TimePoint requestedTime, 67 | MATCH_POLICY policy) const; 68 | 69 | std::optional closestPolicy(double offsetFloat) const; 70 | std::optional prevClosestPolicy(double offsetFloat) const; 71 | std::optional nextClosestPolicy(double offsetFloat) const; 72 | }; 73 | 74 | } // namespace facebook::dynolog 75 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/MetricFrameTsUnitInterface.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace facebook::dynolog { 13 | 14 | using TimePoint = std::chrono::time_point; 15 | using TimeDuration = decltype(TimePoint() - TimePoint()); 16 | 17 | enum class MATCH_POLICY { 18 | CLOSEST, // use the closest sample 19 | PREV_CLOSEST, // use the closest sample previous to provided timepoint 20 | NEXT_CLOSEST, // use the closest sample next to provided timepoint 21 | }; 22 | 23 | struct MetricFrameOffset { 24 | size_t offset; 25 | TimePoint time; 26 | }; 27 | 28 | struct MetricFrameRange { 29 | MetricFrameOffset start; 30 | MetricFrameOffset end; 31 | }; 32 | 33 | class MetricFrameTsUnitInterface { 34 | public: 35 | virtual ~MetricFrameTsUnitInterface() = default; 36 | virtual void addSample(TimePoint time) = 0; 37 | virtual std::optional firstSampleTime() const = 0; 38 | virtual std::optional lastSampleTime() const = 0; 39 | virtual std::vector getTimeVector() const = 0; 40 | virtual size_t length() const = 0; 41 | virtual size_t maxLength() const = 0; 42 | virtual std::optional getRange( 43 | TimePoint startTime, 44 | TimePoint endTime, 45 | MATCH_POLICY startTimePolicy = MATCH_POLICY::CLOSEST, 46 | MATCH_POLICY endTimePolicy = MATCH_POLICY::CLOSEST) const = 0; 47 | }; 48 | 49 | } // namespace facebook::dynolog 50 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/MetricValues.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace facebook::dynolog { 16 | 17 | /* MetricValues: 18 | * An list of metric values that can be aggregated in various ways. 19 | * MetricValues is similar to MetricSeries except it helps to track 20 | * and aggregate values across space rather than time. 21 | * For example, aggregate cpu time across cpu cores. 22 | */ 23 | template 24 | class MetricValues final { 25 | public: 26 | explicit MetricValues(size_t size = 0) : data_{std::vector(size)} {} 27 | 28 | explicit MetricValues(std::vector&& values) : data_{std::move(values)} {} 29 | 30 | size_t size() const { 31 | return data_.size(); 32 | } 33 | 34 | void push_back(T val) { 35 | data_.push_back(val); 36 | } 37 | 38 | const T& at(size_t idx) { 39 | return data_.at(idx); 40 | } 41 | 42 | const T& operator[](const size_t idx) const { 43 | return *(begin() + idx); 44 | } 45 | 46 | void reserve(const size_t size) { 47 | data_.reserve(size); 48 | } 49 | 50 | void resize(const size_t size) { 51 | data_.resize(size); 52 | } 53 | 54 | T sum() const { 55 | return std::accumulate(begin(), end(), T{0}); 56 | } 57 | 58 | std::optional avg() const { 59 | if (size() == 0) { 60 | return std::nullopt; 61 | } 62 | return static_cast(sum()) / size(); 63 | } 64 | 65 | std::optional percentile(float percentage) const { 66 | if (size() == 0) { 67 | return std::nullopt; 68 | } 69 | std::vector dataCopy(begin(), end()); 70 | auto nthIdx = lround(percentage * (dataCopy.size() - 1)); 71 | std::nth_element( 72 | dataCopy.begin(), dataCopy.begin() + nthIdx, dataCopy.end()); 73 | return dataCopy[nthIdx]; 74 | } 75 | 76 | std::optional> minmax() const { 77 | if (size() == 0) { 78 | return std::nullopt; 79 | } 80 | const auto [min_it, max_it] = std::minmax_element(begin(), end()); 81 | return std::make_pair(*min_it, *max_it); 82 | } 83 | 84 | void merge(const MetricValues& other) { 85 | data_.reserve(data_.size() + other.data_.size()); 86 | data_.insert(data_.end(), other.data_.begin(), other.data_.end()); 87 | } 88 | 89 | private: 90 | auto begin() const { 91 | return data_.begin(); 92 | } 93 | 94 | auto end() const { 95 | return data_.end(); 96 | } 97 | 98 | std::vector data_; 99 | }; 100 | 101 | } // namespace facebook::dynolog 102 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/TextTable.cpp: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #include "dynolog/src/metric_frame/TextTable.h" 4 | 5 | namespace facebook::dynolog { 6 | 7 | TextTable::TextTable(std::vector>&& data) 8 | : data_{std::move(data)} {} 9 | 10 | std::ostream& TextTable::show(std::ostream& s) { 11 | updateCachedWidth(); 12 | for (size_t row = 0; row < height(); row++) { 13 | writeLine(s); 14 | writeRow(row, s); 15 | } 16 | writeLine(s); 17 | return s; 18 | } 19 | 20 | size_t TextTable::width() const { 21 | return data_.empty() ? 0 : data_[0].size(); 22 | } 23 | 24 | size_t TextTable::height() const { 25 | return data_.size(); 26 | } 27 | 28 | void TextTable::updateCachedWidth() { 29 | colWidthCache_.resize(width()); 30 | for (size_t col = 0; col < width(); col++) { 31 | colWidthCache_[col] = getColWidth(col); 32 | } 33 | } 34 | 35 | [[nodiscard]] size_t TextTable::getColWidth(size_t col) const { 36 | size_t width = 0; 37 | for (const auto& row : data_) { 38 | width = std::max(width, row[col].size()); 39 | } 40 | return width; 41 | } 42 | 43 | void TextTable::writeRow(size_t row, std::ostream& s) const { 44 | for (size_t col = 0; col < width(); col++) { 45 | size_t cellWidth = colWidthCache_[col] + extraPad_; 46 | size_t textWidth = data_[row][col].size(); 47 | size_t lPad = (cellWidth - textWidth) / 2; 48 | size_t rPad = cellWidth - textWidth - lPad; 49 | s << '|'; 50 | for (int i = 0; i < lPad; i++) { 51 | s << ' '; 52 | } 53 | s << data_[row][col]; 54 | for (int i = 0; i < rPad; i++) { 55 | s << ' '; 56 | } 57 | } 58 | s << '|' << std::endl; 59 | } 60 | 61 | void TextTable::writeLine(std::ostream& s) const { 62 | for (auto cellWidth : colWidthCache_) { 63 | for (int i = 0; i < cellWidth + 1 + extraPad_; i++) { 64 | s << '-'; 65 | } 66 | } 67 | s << '-' << std::endl; 68 | } 69 | 70 | } // namespace facebook::dynolog 71 | -------------------------------------------------------------------------------- /dynolog/src/metric_frame/TextTable.h: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace facebook::dynolog { 10 | 11 | // for basic visualizaion of a MetricFrame 12 | // print a text table like below 13 | /* 14 | ------------------------------- 15 | | | t1 | t2 | t3 | 16 | ------------------------------- 17 | | metric1 | 12 | 21 | 38 | 18 | ------------------------------- 19 | | metric2 | 17423 | 992 | 157 | 20 | ------------------------------- 21 | */ 22 | 23 | class TextTable { 24 | public: 25 | explicit TextTable(std::vector>&& data); 26 | std::ostream& show(std::ostream& s); 27 | 28 | private: 29 | size_t width() const; 30 | size_t height() const; 31 | void updateCachedWidth(); 32 | [[nodiscard]] size_t getColWidth(size_t col) const; 33 | void writeRow(size_t row, std::ostream& s) const; 34 | void writeLine(std::ostream& s) const; 35 | 36 | std::vector> data_; 37 | std::vector colWidthCache_; 38 | const size_t extraPad_ = 2; 39 | }; 40 | 41 | } // namespace facebook::dynolog 42 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | file (GLOB dynolog_rdmamon_files "*.h" "*.cpp") 4 | add_library(dynolog_rdmamon_lib ${dynolog_rdmamon_files}) 5 | target_link_libraries(dynolog_rdmamon_lib PUBLIC gflags::gflags) 6 | target_link_libraries(dynolog_rdmamon_lib PUBLIC glog::glog) 7 | target_link_libraries(dynolog_rdmamon_lib PUBLIC fmt::fmt) 8 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/EthtoolCounters.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | 9 | #include "dynolog/src/rdmamon/EthtoolCounters.h" 10 | 11 | #ifdef FBCODE 12 | #include "secure_lib/secure_string.h" 13 | #endif // FBCODE 14 | 15 | namespace dynolog { 16 | namespace rdmamon { 17 | 18 | bool EthtoolCounters::setupEthtoolCounters() { 19 | const std::vector eth_counter_names_ = { 20 | "tx_pause_ctrl_phy", 21 | "tx_prio0_pause", 22 | "tx_prio1_pause", 23 | "tx_prio2_pause", 24 | "tx_prio3_pause", 25 | "tx_prio4_pause", 26 | "tx_prio5_pause", 27 | "tx_prio6_pause", 28 | "tx_prio7_pause", 29 | "tx_pause_storm_warning_events", 30 | "tx_pause_storm_error_events", 31 | }; 32 | return setup_ethtool_counters(eth_counter_names_); 33 | } 34 | 35 | bool EthtoolCounters::sampleEthtoolCounters( 36 | std::map& countersMap) { 37 | if (!get_current_ethtool_counters()) { 38 | return false; 39 | } 40 | 41 | if (!first_sample_) { 42 | for (auto it = eth_counters_.begin(); it != eth_counters_.end(); it++) { 43 | int64_t diff = 44 | cur_eth_stats_->data[it->second] - prev_eth_stats_->data[it->second]; 45 | const auto key = fmt::format("{}.{}", ifname_, it->first); 46 | countersMap[key] = diff; 47 | } 48 | } 49 | first_sample_ = false; 50 | size_t copy_sz = 51 | (gstrings_->len * sizeof(uint64_t)) + sizeof(struct ethtool_stats); 52 | #ifdef FBCODE 53 | if (try_checked_memcpy(prev_eth_stats_, stats_sz_, cur_eth_stats_, copy_sz) != 54 | 0) { 55 | LOG_EVERY_N(WARNING, 100) 56 | << "Uanble to copy current stats due to insufficient space"; 57 | return false; 58 | } 59 | #else 60 | memcpy(prev_eth_stats_, cur_eth_stats_, copy_sz); 61 | #endif // FBCODE 62 | return true; 63 | } 64 | 65 | } // namespace rdmamon 66 | } // namespace dynolog 67 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/RdmaCounters.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/rdmamon/RdmaCounters.h" 7 | #include 8 | #include 9 | #include 10 | 11 | namespace dynolog { 12 | namespace rdmamon { 13 | 14 | bool RdmaCounters::setupRdmaCounters() { 15 | const std::vector rdma_port_counters_ = { 16 | "port_xmit_data", 17 | "port_xmit_packets", 18 | "port_xmit_discards", 19 | "port_rcv_data", 20 | "port_rcv_packets", 21 | "port_rcv_errors", 22 | }; 23 | 24 | const std::vector rdma_hw_counters_ = { 25 | "np_cnp_sent", 26 | "rp_cnp_handled", 27 | "np_ecn_marked_roce_packets", 28 | "rx_atomic_requests", 29 | "rx_read_requests", 30 | "rx_write_requests", 31 | }; 32 | 33 | return ( 34 | init_rdma_counters_(rdma_port_counter_path_, rdma_port_counters_) && 35 | init_rdma_counters_(rdma_hw_counter_path_, rdma_hw_counters_)); 36 | } 37 | 38 | bool RdmaCounters::sampleRdmaCounters( 39 | std::map& rdmaCountersMap) { 40 | for (auto& rdma_counter : rdma_counters_) { 41 | auto sysfs_counter = std::move(rdma_counter->sysfs_counter); 42 | uint64_t prev_val = rdma_counter->prev; 43 | auto val = sysfs_counter->getSysfsCounter(); 44 | uint64_t cur_val = (val) ? *val : prev_val; 45 | if (!first_sample_) { 46 | uint64_t diff = cur_val - prev_val; 47 | DLOG(INFO) << sysfs_counter->getSysfsCounterName() 48 | << ": will return report value " << diff; 49 | if (diff < 0) { 50 | LOG(ERROR) << sysfs_counter->getSysfsCounterName() 51 | << ": current counter value " << cur_val 52 | << " is lower than previous counter value " << prev_val 53 | << " thus giving negative delta " << diff; 54 | diff = 0; 55 | } else { 56 | const auto key = fmt::format( 57 | "{}.{}", 58 | std::string(ifname_), 59 | sysfs_counter->getSysfsCounterName()); 60 | rdmaCountersMap[key] = diff; 61 | DLOG(INFO) << "Value stored in map: " << rdmaCountersMap[key]; 62 | } 63 | } 64 | prev_val = cur_val; 65 | rdma_counter->sysfs_counter = std::move(sysfs_counter); 66 | rdma_counter->prev = prev_val; 67 | rdma_counter->cur = cur_val; 68 | } 69 | first_sample_ = false; 70 | return true; 71 | } 72 | 73 | } // namespace rdmamon 74 | } // namespace dynolog 75 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/RdmaCounters.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "dynolog/src/rdmamon/SysfsCounter.h" 13 | 14 | namespace dynolog { 15 | namespace rdmamon { 16 | 17 | class RdmaCounters { 18 | public: 19 | explicit RdmaCounters(const std::string& if_name) : ifname_(if_name) { 20 | std::string path = "/sys/class/net/"; 21 | std::string netdev_path = path + if_name; 22 | for (const auto& entry : std::filesystem::directory_iterator(path)) { 23 | if (0 == netdev_path.compare(entry.path())) { 24 | std::string ibdev_path = netdev_path + "/device/infiniband"; 25 | for (const auto& deventry : 26 | std::filesystem::directory_iterator(ibdev_path)) { 27 | std::string ib_devname = deventry.path().stem(); 28 | std::string ib_dev_path = "/sys/class/infiniband/"; 29 | ib_dev_path += ib_devname; 30 | if (rdma_port_counter_path_.empty()) { 31 | rdma_port_counter_path_ = ib_dev_path + "/ports/1/counters/"; 32 | } 33 | if (rdma_hw_counter_path_.empty()) { 34 | rdma_hw_counter_path_ = ib_dev_path + "/ports/1/hw_counters/"; 35 | } 36 | } 37 | } 38 | } 39 | } 40 | 41 | virtual ~RdmaCounters() { 42 | rdma_counters_.clear(); 43 | } 44 | 45 | bool setupRdmaCounters(); 46 | bool sampleRdmaCounters(std::map& rdmaCountersMap); 47 | 48 | private: 49 | struct rdma_counter { 50 | std::unique_ptr sysfs_counter; 51 | uint64_t prev; 52 | uint64_t cur; 53 | 54 | explicit rdma_counter(std::unique_ptr sysfs_counter) { 55 | this->sysfs_counter = std::move(sysfs_counter); 56 | this->prev = 0; 57 | this->cur = 0; 58 | } 59 | }; 60 | std::vector> rdma_counters_; 61 | std::string ifname_; 62 | std::string rdma_port_counter_path_; 63 | std::string rdma_hw_counter_path_; 64 | bool first_sample_ = true; 65 | 66 | bool init_rdma_counters_( 67 | const std::string& path, 68 | const std::vector& counters) { 69 | for (auto counter : counters) { 70 | std::string path_to_name = path + counter; 71 | auto sysfs_counter = 72 | std::make_unique(counter, path_to_name); 73 | if (!sysfs_counter->openSysfsCounter()) { 74 | LOG(WARNING) << "Unable to initialize rdma counter " << path_to_name; 75 | return false; 76 | } 77 | auto rdma_counter_obj = 78 | std::make_unique(std::move(sysfs_counter)); 79 | rdma_counters_.push_back(std::move(rdma_counter_obj)); 80 | } 81 | return true; 82 | } 83 | }; 84 | 85 | } // namespace rdmamon 86 | } // namespace dynolog 87 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/RdmaMonitor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/rdmamon/RdmaMonitor.h" 7 | 8 | namespace dynolog { 9 | namespace rdmamon { 10 | 11 | bool RdmaPortMonitor::setupRdmaPortMonitor() { 12 | bool r1 = eth_counters_->setupEthtoolCounters(); 13 | bool r2 = rdma_counters_->setupRdmaCounters(); 14 | return (r1 && r2); 15 | } 16 | 17 | bool RdmaPortMonitor::sampleRdmaPortMonitor( 18 | std::map& rdmaPortCounterMap) { 19 | bool r1 = eth_counters_->sampleEthtoolCounters(rdmaPortCounterMap); 20 | bool r2 = rdma_counters_->sampleRdmaCounters(rdmaPortCounterMap); 21 | return (r1 && r2); 22 | } 23 | 24 | bool RdmaMonitor::setupRdmaMonitor() { 25 | bool ret = true; 26 | for (auto const& monitor : monitors_) { 27 | if (!monitor->setupRdmaPortMonitor()) { 28 | LOG(ERROR) << "Unable to setup RDMA monitor for " 29 | << monitor->getRdmaIfname(); 30 | ret = false; 31 | } 32 | } 33 | return ret; 34 | } 35 | 36 | bool RdmaMonitor::sampleRdmaMonitor( 37 | std::map& rdmaCounterMap) { 38 | bool ret = true; 39 | for (auto const& monitor : monitors_) { 40 | if (!monitor->sampleRdmaPortMonitor(rdmaCounterMap)) { 41 | LOG(ERROR) << "Unable to sample RDMA monitor for " 42 | << monitor->getRdmaIfname(); 43 | ret = false; 44 | } 45 | } 46 | return ret; 47 | } 48 | 49 | } // namespace rdmamon 50 | } // namespace dynolog 51 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/RdmaMonitor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include "dynolog/src/rdmamon/EthtoolCounters.h" 11 | #include "dynolog/src/rdmamon/RdmaCounters.h" 12 | 13 | namespace dynolog { 14 | namespace rdmamon { 15 | 16 | class RdmaPortMonitor { 17 | public: 18 | explicit RdmaPortMonitor(const std::string& ifname) : ifname_(ifname) { 19 | eth_counters_ = std::make_unique(ifname); 20 | rdma_counters_ = std::make_unique(ifname); 21 | } 22 | 23 | [[nodiscard]] bool setupRdmaPortMonitor(); 24 | [[nodiscard]] bool sampleRdmaPortMonitor( 25 | std::map& rdmaPortCounterMap); 26 | 27 | std::string getRdmaIfname() const { 28 | return ifname_; 29 | } 30 | 31 | private: 32 | std::string ifname_; 33 | std::unique_ptr eth_counters_; 34 | std::unique_ptr rdma_counters_; 35 | }; 36 | 37 | class RdmaMonitor { 38 | public: 39 | explicit RdmaMonitor() { 40 | std::string path = "/sys/class/net/"; 41 | for (const auto& entry : std::filesystem::directory_iterator(path)) { 42 | std::string ifname = entry.path().stem(); 43 | if (0 == ifname.compare("lo")) { 44 | continue; 45 | } 46 | auto monitor = std::make_unique(ifname); 47 | monitors_.push_back(std::move(monitor)); 48 | } 49 | } 50 | 51 | virtual ~RdmaMonitor() { 52 | monitors_.clear(); 53 | } 54 | 55 | [[nodiscard]] bool setupRdmaMonitor(); 56 | [[nodiscard]] bool sampleRdmaMonitor( 57 | std::map& rdmaCounterMap); 58 | 59 | private: 60 | std::vector> monitors_; 61 | }; 62 | 63 | } // namespace rdmamon 64 | } // namespace dynolog 65 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/SysfsCounter.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/rdmamon/SysfsCounter.h" 7 | 8 | // @lint-ignore-every CLANGTIDY facebook-hte-BadCall-strerror 9 | 10 | namespace dynolog { 11 | namespace rdmamon { 12 | 13 | bool SysfsCounter::openSysfsCounter() { 14 | fd_ = open(path_.c_str(), O_RDONLY); 15 | if (fd_ < 0) { 16 | LOG(WARNING) << "Unable to open " << path_ << "(" << std::strerror(errno) 17 | << ")"; 18 | return false; 19 | } 20 | return true; 21 | } 22 | 23 | std::optional SysfsCounter::getSysfsCounter() { 24 | return get_current_sysfs_counters_(); 25 | } 26 | 27 | } // namespace rdmamon 28 | } // namespace dynolog 29 | -------------------------------------------------------------------------------- /dynolog/src/rdmamon/SysfsCounter.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // @lint-ignore-every CLANGTIDY facebook-hte-BadCall-strerror 18 | 19 | namespace dynolog { 20 | namespace rdmamon { 21 | 22 | class SysfsCounter { 23 | public: 24 | explicit SysfsCounter(const std::string& name, const std::string& path) 25 | : name_(name), path_(path) {} 26 | virtual ~SysfsCounter() { 27 | if (fd_ >= 0) { 28 | close(fd_); 29 | } 30 | } 31 | 32 | bool openSysfsCounter(); 33 | std::optional getSysfsCounter(); 34 | std::string getSysfsCounterName() const { 35 | return name_; 36 | } 37 | 38 | private: 39 | int fd_ = -1; 40 | std::string name_; 41 | std::string path_; 42 | 43 | // Sysfs couunters are 64-bit integers. 44 | // Their string representation should not exceed 128 bytes. 45 | #define BUFSZ (128) 46 | bool get_line_(char* line) { 47 | if (lseek(fd_, 0, SEEK_SET) < 0) { 48 | LOG(ERROR) << "Unable to lseek for sysfs counter (" 49 | << std::strerror(errno) << ")"; 50 | return false; 51 | } 52 | if (ssize_t sz = read(fd_, line, BUFSZ); sz <= 0) { 53 | LOG(ERROR) << "Unable to read sysfs counter (" << std::strerror(errno) 54 | << ")"; 55 | return false; 56 | } 57 | return true; 58 | } 59 | 60 | std::optional get_current_sysfs_counters_() { 61 | char line[BUFSZ] = {'\0'}; 62 | if (!get_line_(line)) { 63 | return std::nullopt; 64 | } 65 | 66 | uint64_t cntr = 0; 67 | const auto res = std::from_chars(line, line + strlen(line), cntr, 10); 68 | if (res.ec != std::errc()) { 69 | LOG(ERROR) << "Unable to convert " << line << " to integer"; 70 | return std::nullopt; 71 | } 72 | 73 | return cntr; 74 | } 75 | }; 76 | 77 | } // namespace rdmamon 78 | } // namespace dynolog 79 | -------------------------------------------------------------------------------- /dynolog/src/rpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_library(dynolog_rpc_lib STATIC 4 | SimpleJsonServer.cpp SimpleJsonServer.h 5 | ${CMAKE_CURRENT_SOURCE_DIR}/../ServiceHandler.h 6 | ) 7 | target_include_directories(dynolog_rpc_lib 8 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} 9 | ) 10 | 11 | target_include_directories(dynolog_rpc_lib 12 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/.. 13 | ) 14 | target_link_libraries(dynolog_rpc_lib PRIVATE dynolog_lib) 15 | target_link_libraries(dynolog_rpc_lib PUBLIC gflags::gflags) 16 | target_link_libraries(dynolog_rpc_lib PUBLIC glog::glog) 17 | target_link_libraries(dynolog_rpc_lib PUBLIC nlohmann_json::nlohmann_json) 18 | target_link_libraries(dynolog_rpc_lib PUBLIC fmt::fmt) 19 | -------------------------------------------------------------------------------- /dynolog/src/rpc/SimpleJsonServer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "dynolog/src/ServiceHandler.h" 14 | 15 | namespace dynolog { 16 | 17 | // This is a simple service built using UNIX Sockets 18 | // with remote procedure calls implemented via JSON string. 19 | 20 | class SimpleJsonServerBase { 21 | public: 22 | explicit SimpleJsonServerBase(int port); 23 | 24 | virtual ~SimpleJsonServerBase(); 25 | 26 | int getPort() const { 27 | return port_; 28 | } 29 | 30 | bool initSuccessful() const { 31 | return initSuccess_; 32 | } 33 | // spin up a new thread to process requets 34 | void run(); 35 | 36 | void stop() { 37 | run_ = 0; 38 | thread_->join(); 39 | } 40 | 41 | // synchronously processes a request 42 | void processOne() noexcept; 43 | 44 | protected: 45 | void initSocket(); 46 | 47 | // process requests in a loop 48 | void loop() noexcept; 49 | 50 | // implement processing of request using the handler 51 | virtual std::string processOneImpl(const std::string& request_str) { 52 | return ""; 53 | } 54 | 55 | int port_; 56 | int sock_fd_{-1}; 57 | bool initSuccess_{false}; 58 | 59 | std::atomic run_{true}; 60 | std::unique_ptr thread_; 61 | }; 62 | 63 | } // namespace dynolog 64 | -------------------------------------------------------------------------------- /dynolog/src/tracing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_library (dynolog_ipcmonitor_lib IPCMonitor.cpp IPCMonitor.h 4 | ${CMAKE_CURRENT_SOURCE_DIR}/../LibkinetoConfigManager.h 5 | ) 6 | 7 | target_include_directories(dynolog_ipcmonitor_lib 8 | INTERFACE ${CMAKE_CURRENT_SOURCE_DIR} 9 | ) 10 | target_include_directories(dynolog_ipcmonitor_lib 11 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/.. 12 | ) 13 | 14 | target_link_libraries(dynolog_ipcmonitor_lib PUBLIC glog::glog) 15 | target_link_libraries(dynolog_ipcmonitor_lib PUBLIC dynolog_ipcfabric_lib) 16 | -------------------------------------------------------------------------------- /dynolog/src/tracing/IPCMonitor.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | // Use glog for FabricManager.h 11 | #define USE_GOOGLE_LOG 12 | 13 | #include "dynolog/src/ipcfabric/FabricManager.h" 14 | 15 | namespace dynolog { 16 | namespace tracing { 17 | 18 | class IPCMonitor { 19 | public: 20 | using FabricManager = dynolog::ipcfabric::FabricManager; 21 | IPCMonitor(const std::string& ipc_fabric_name = "dynolog"); 22 | virtual ~IPCMonitor() {} 23 | 24 | void loop(); 25 | 26 | public: 27 | virtual void processMsg(std::unique_ptr msg); 28 | void getLibkinetoOnDemandRequest(std::unique_ptr msg); 29 | void registerLibkinetoContext(std::unique_ptr msg); 30 | 31 | std::unique_ptr ipc_manager_; 32 | 33 | // friend class test_case_name##_##test_name##_Test 34 | friend class IPCMonitorTest_LibkinetoRegisterAndOndemandTest_Test; 35 | }; 36 | 37 | } // namespace tracing 38 | } // namespace dynolog 39 | -------------------------------------------------------------------------------- /dynolog/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | include(${PROJECT_SOURCE_DIR}/testing/BuildTests.cmake) 4 | 5 | dynolog_add_test(KernelCollecterTest KernelCollecterTest.cpp) 6 | if(USE_PROMETHEUS) 7 | add_definitions(-DUSE_PROMETHEUS) 8 | dynolog_add_test(PrometheusLoggerTest PrometheusLoggerTest.cpp) 9 | endif() 10 | 11 | add_subdirectory(rpc) 12 | add_subdirectory(tracing) 13 | add_subdirectory(metric_frame) 14 | -------------------------------------------------------------------------------- /dynolog/tests/PrometheusLoggerTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "dynolog/src/PrometheusLogger.h" 7 | #include 8 | #include 9 | #include 10 | #include "dynolog/src/Metrics.h" 11 | 12 | namespace dynolog { 13 | 14 | TEST(PrometheusLoggerTest, BasicTest) { 15 | // check that basic logger class is collecting values 16 | // from various log functions. 17 | 18 | PrometheusLogger logger; 19 | 20 | logger.logInt("uptime", 10000); 21 | logger.logFloat("pi", 3.1457); 22 | logger.logUint("cpu_util", 25); 23 | logger.logUint("rx_bytes.eth0", 42); 24 | 25 | auto& kvs = logger.kvs_; 26 | EXPECT_FLOAT_EQ(kvs["uptime"], 10000); 27 | EXPECT_FLOAT_EQ(kvs["pi"], 3.1457); 28 | EXPECT_FLOAT_EQ(kvs["cpu_util"], 25); 29 | EXPECT_FLOAT_EQ(kvs["rx_bytes.eth0"], 42); 30 | 31 | // DO NOT RUN finalize() on logger to avoid sending data to prometheus 32 | // exporter. 33 | } 34 | 35 | TEST(PrometheusLoggerTest, ExporterTest) { 36 | /* Allow Prometheus exporter to use any available port*/ 37 | FLAGS_prometheus_port = 0; 38 | auto ethm = [](const std::string& name) -> std::string { 39 | return fmt::format("{}.eth0", name); 40 | }; 41 | 42 | float i = 0, j = 0; 43 | { 44 | PrometheusLogger logger; 45 | for (const auto& m : getAllMetrics()) { 46 | logger.logFloat(m.name, i++); 47 | } 48 | for (const auto& m : getNetworkMetrics()) { 49 | logger.logFloat(ethm(m.name), i++); 50 | } 51 | logger.finalize(); 52 | 53 | auto logging_guard = PrometheusManager::singleton(); 54 | auto prom = logging_guard.manager; 55 | for (const auto& m : getAllMetrics()) { 56 | EXPECT_FLOAT_EQ(prom->gauges_[m.name]->Value(), j) 57 | << "Metric " << m.name << " did not match expected value"; 58 | j++; 59 | } 60 | // match network metrics that are generated dynamically 61 | for (const auto& m : getNetworkMetrics()) { 62 | EXPECT_FLOAT_EQ(prom->gauges_[ethm(m.name)]->Value(), j) 63 | << "Metric " << ethm(m.name) << " did not match expected value"; 64 | j++; 65 | } 66 | } 67 | ASSERT_GT(i, 0) << "No metrics were logged!"; 68 | ASSERT_EQ(i, j); 69 | } 70 | 71 | } // namespace dynolog 72 | -------------------------------------------------------------------------------- /dynolog/tests/gpumon/amd/RdcWrapperTest.cpp: -------------------------------------------------------------------------------- 1 | // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | 3 | #include "dynolog/src/gpumon/amd/RdcWrapper.h" 4 | #include 5 | 6 | using namespace ::testing; 7 | using namespace ::dynolog::gpumon; 8 | 9 | class RdcRuntimeContextSynchronizedTest : public RdcRuntimeContextSynchronized { 10 | public: 11 | std::shared_mutex& getMutex() { 12 | return sharedDataLock_; 13 | } 14 | }; 15 | 16 | TEST(RdcWrapperTest, testRdcRuntimeContextSynchronized) { 17 | RdcRuntimeContextSynchronizedTest t; 18 | { 19 | // two readers 20 | auto a = t.rlock(); 21 | std::shared_lock b(t.getMutex(), std::defer_lock); 22 | EXPECT_TRUE(b.try_lock()); 23 | } 24 | 25 | { 26 | // two writers 27 | auto a = t.wlock(); 28 | std::unique_lock b(t.getMutex(), std::defer_lock); 29 | EXPECT_FALSE(b.try_lock()); 30 | } 31 | 32 | { 33 | // reader then writer 34 | auto a = t.rlock(); 35 | std::unique_lock b(t.getMutex(), std::defer_lock); 36 | EXPECT_FALSE(b.try_lock()); 37 | } 38 | 39 | { auto a = t.rlock(); } 40 | // lock released outside of scope 41 | std::unique_lock b(t.getMutex(), std::defer_lock); 42 | EXPECT_TRUE(b.try_lock()); 43 | } 44 | -------------------------------------------------------------------------------- /dynolog/tests/ipcfabric/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | include(${PROJECT_SOURCE_DIR}/tests/BuildTests.cmake) 4 | 5 | macro(ipcfabric_add_test TESTNAME) 6 | dynolog_add_test_base(${TESTNAME} ${ARGN}) 7 | target_link_libraries(${TESTNAME} PRIVATE dynolog_ipcfabric_lib) 8 | target_link_libraries(${TESTNAME} PRIVATE glog::glog) 9 | target_include_directories(${TESTNAME} PRIVATE 10 | "${PROJECT_SOURCE_DIR}/src/ipcfabric") 11 | endmacro() 12 | 13 | ipcfabric_add_test(IPCFabricTest IPCFabricTest.cpp) 14 | 15 | add_executable(ipcsender IPCSender.cpp) 16 | target_link_libraries(ipcsender PRIVATE dynolog_ipcfabric_lib glog) 17 | -------------------------------------------------------------------------------- /dynolog/tests/ipcfabric/IPCSender.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #define USE_GOOGLE_LOG 7 | #include "dynolog/src/ipcfabric/FabricManager.h" 8 | 9 | int main() { 10 | int data = 1000; 11 | std::string datastring = "this"; 12 | auto sender = dynolog::ipcfabric::FabricManager::factory("sender"); 13 | std::string type_int = "int"; 14 | std::unique_ptr msgint = 15 | dynolog::ipcfabric::Message::constructMessage( 16 | data, type_int); 17 | std::string type_string = "string"; 18 | std::unique_ptr msgstring = 19 | dynolog::ipcfabric::Message::constructMessage( 20 | datastring, type_string); 21 | 22 | sender->sync_send(*msgint, "dynolog"); 23 | sender->sync_send(*msgstring, "dynolog"); 24 | sender->sync_send(*msgint, "dynolog"); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /dynolog/tests/metric_frame/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. 2 | dynolog_add_test(TextTableTest TextTableTest.cpp) 3 | target_link_libraries(TextTableTest PRIVATE text_table) 4 | 5 | dynolog_add_test(MetricSeriesTest MetricSeriesTest.cpp) 6 | target_link_libraries(MetricSeriesTest PRIVATE metric_series) 7 | 8 | dynolog_add_test(MetricValuesTest MetricValuesTest.cpp) 9 | target_link_libraries(MetricValuesTest PRIVATE metric_values) 10 | 11 | dynolog_add_test(MetricFrameTsUnitTest MetricFrameTsUnitTest.cpp) 12 | target_link_libraries(MetricFrameTsUnitTest PRIVATE metric_frame_ts_unit) 13 | 14 | dynolog_add_test(MetricFrameTest MetricFrameTest.cpp) 15 | target_link_libraries(MetricFrameTest PRIVATE metric_frame) 16 | -------------------------------------------------------------------------------- /dynolog/tests/metric_frame/MetricValuesTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | 8 | #include "dynolog/src/metric_frame/MetricValues.h" 9 | 10 | #include 11 | 12 | using namespace ::testing; 13 | using namespace ::facebook::dynolog; 14 | 15 | TEST(MetricValuesTest, basicTest) { 16 | MetricValues v(5); 17 | EXPECT_EQ(v.size(), 5); 18 | EXPECT_EQ(v[3], 0); 19 | 20 | MetricValues v1({0.1, 0.2, 0.3}); 21 | EXPECT_EQ(v1.size(), 3); 22 | 23 | EXPECT_FLOAT_EQ(v1[0], 0.1); 24 | EXPECT_FLOAT_EQ(v1[1], 0.2); 25 | 26 | v1.resize(5); 27 | EXPECT_EQ(v1.size(), 5); 28 | 29 | v1.push_back(42); 30 | EXPECT_EQ(v1.size(), 6); 31 | } 32 | 33 | TEST(MetricValuesTest, aggTest) { 34 | MetricValues v; 35 | 36 | // empty lists will fail in some aggregations 37 | EXPECT_FALSE(v.avg()); 38 | EXPECT_FALSE(v.minmax()); 39 | EXPECT_FALSE(v.percentile(0.5)); 40 | EXPECT_EQ(v.sum(), 0); 41 | 42 | for (int i = 0; i < 5; i++) { 43 | v.push_back(i); 44 | } 45 | 46 | EXPECT_EQ(v.sum(), 10); 47 | 48 | auto minmaxMaybe = v.minmax(); 49 | EXPECT_TRUE(minmaxMaybe); 50 | EXPECT_EQ(minmaxMaybe.value().first, 0); 51 | EXPECT_EQ(minmaxMaybe.value().second, 4); 52 | 53 | auto avgMaybe = v.avg(); 54 | EXPECT_TRUE(avgMaybe); 55 | EXPECT_EQ(avgMaybe.value(), 2.0); 56 | 57 | auto p50Maybe = v.percentile(0.5); 58 | EXPECT_TRUE(p50Maybe); 59 | EXPECT_EQ(p50Maybe.value(), 2.0); 60 | } 61 | 62 | TEST(MetricValuesTest, mergeTest) { 63 | MetricValues u, v; 64 | for (int i = 0; i < 5; i++) { 65 | u.push_back(i); 66 | } 67 | for (int i = 0; i < 5; i++) { 68 | v.push_back(i); 69 | } 70 | EXPECT_EQ(u.size(), 5); 71 | 72 | u.merge(v); 73 | EXPECT_EQ(u.size(), 10); 74 | EXPECT_EQ(u.sum(), 20); 75 | 76 | auto avgMaybe = u.avg(); 77 | EXPECT_TRUE(avgMaybe); 78 | EXPECT_EQ(avgMaybe.value(), 2.0); 79 | } 80 | -------------------------------------------------------------------------------- /dynolog/tests/metric_frame/TextTableTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | 8 | #include "dynolog/src/metric_frame/TextTable.h" 9 | 10 | using namespace ::testing; 11 | using namespace ::facebook::dynolog; 12 | 13 | /* 14 | ------------------------------- 15 | | | t1 | t2 | t3 | 16 | -------------------------- 17 | | metric1 | 12 | 21 | 38 | 18 | -------------------------- 19 | | metric2 | 17423 | 992 | 157 | 20 | ------------------------------- 21 | */ 22 | 23 | TEST(TextTableTest, checkOutputHasRightShape) { 24 | std::vector> data = { 25 | {"", "t1", "t2", "t3"}, 26 | {"metric1", "12", "21", "38"}, 27 | {"metric2", "17423", "992", "157"}}; 28 | 29 | TextTable t(std::move(data)); 30 | std::stringstream s; 31 | 32 | t.show(s); 33 | size_t nLine = 0; 34 | auto output = s.str(); 35 | int lastLineWidth = -1; 36 | while (true) { 37 | auto nl = output.find('\n'); 38 | if (nl == std::string::npos) { 39 | break; 40 | } 41 | nLine++; 42 | EXPECT_TRUE(lastLineWidth == -1 || lastLineWidth == nl); 43 | lastLineWidth = nl; 44 | output = output.substr(nl + 1, output.size() - nl - 1); 45 | } 46 | EXPECT_EQ(nLine, 7); 47 | } 48 | -------------------------------------------------------------------------------- /dynolog/tests/rdmamon/RdmaMonitorTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "dynolog/src/rdmamon/RdmaMonitor.h" 11 | 12 | using namespace dynolog::rdmamon; 13 | 14 | int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) { 15 | std::map counters; 16 | std::unique_ptr monitor; 17 | try { 18 | monitor = std::make_unique(); 19 | } catch (std::exception& ex) { 20 | std::cerr << " Failed to create RDMA monitor" << ex.what() << std::endl; 21 | monitor = nullptr; 22 | } 23 | if (monitor && !monitor->setupRdmaMonitor()) { 24 | std::cerr << "Unable to setup the RDMA monitor" << std::endl; 25 | } 26 | while (true) { 27 | usleep(1000000); // NOLINT(facebook-hte-BadCall-sleep) 28 | if (monitor && !monitor->sampleRdmaMonitor(counters)) { 29 | std::cerr << "Unable to sample the RDMA monitor" << std::endl; 30 | } 31 | std::cout << "+++++++++++++++++++++++++" << std::endl; 32 | for (auto it = counters.begin(); it != counters.end(); it++) { 33 | std::cout << it->first << "-->" << it->second << std::endl; 34 | } 35 | std::cout << "-------------------------" << std::endl; 36 | } 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /dynolog/tests/rdmamon/SysfsCountersTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | 8 | #include 9 | #include "dynolog/src/rdmamon/SysfsCounter.h" 10 | 11 | using namespace dynolog::rdmamon; 12 | 13 | TEST(SysfsCounters, getCounterTest) { 14 | char buffer[256]; 15 | std::string tmpfile_path = std::tmpnam(nullptr); 16 | std::string test_counter_name = "foobar"; 17 | uint64_t expected_val1 = 101; 18 | 19 | // Create a temprary file and write a counter to that file. 20 | std::sprintf(buffer, "%lu", expected_val1); 21 | std::FILE* tmpf = fopen(tmpfile_path.c_str(), "w+"); 22 | std::fputs(buffer, tmpf); 23 | fclose(tmpf); 24 | 25 | auto sysfs_counters = 26 | std::make_shared(test_counter_name, tmpfile_path); 27 | EXPECT_TRUE(sysfs_counters->openSysfsCounter()); 28 | 29 | auto actual_val1_ref = sysfs_counters->getSysfsCounter(); 30 | EXPECT_TRUE(actual_val1_ref); 31 | EXPECT_EQ(expected_val1, *actual_val1_ref); 32 | 33 | // Update the content of the file 34 | uint64_t expected_val2 = 202; 35 | std::sprintf(buffer, "%lu", expected_val2); 36 | tmpf = fopen(tmpfile_path.c_str(), "w+"); 37 | std::fputs(buffer, tmpf); 38 | fclose(tmpf); 39 | auto actual_val2_ref = sysfs_counters->getSysfsCounter(); 40 | EXPECT_TRUE(actual_val2_ref); 41 | EXPECT_EQ(expected_val2, *actual_val2_ref); 42 | 43 | // Check the name of the counter 44 | EXPECT_EQ(test_counter_name, sysfs_counters->getSysfsCounterName()); 45 | 46 | remove(tmpfile_path.c_str()); 47 | } 48 | -------------------------------------------------------------------------------- /dynolog/tests/rpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_executable(json_client SimpleJsonClientTestCLI.cpp SimpleJsonClientTest.h) 4 | 5 | target_link_libraries(json_client PUBLIC gflags::gflags) 6 | target_link_libraries(json_client PUBLIC glog::glog) 7 | 8 | dynolog_add_test(SimpleJsonClientTest 9 | SimpleJsonClientTest.cpp SimpleJsonClientTest.h) 10 | 11 | target_link_libraries(SimpleJsonClientTest PRIVATE dynolog_rpc_lib) 12 | -------------------------------------------------------------------------------- /dynolog/tests/rpc/SimpleJsonClientTestCLI.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | #include "dynolog/tests/rpc/SimpleJsonClientTest.h" 9 | 10 | DEFINE_int32(port, 1778, "Port for sending RPC requests"); 11 | DEFINE_string( 12 | message, 13 | "{\"fn\":\"getStatus\"", 14 | "A Json formatted message to send to server."); 15 | DEFINE_string(host_ip, "::1", "Host ipv6 address."); 16 | 17 | int main(int argc, char** argv) { 18 | google::InitGoogleLogging(argv[0]); 19 | gflags::ParseCommandLineFlags(&argc, &argv, true); 20 | FLAGS_logtostderr = 1; 21 | 22 | auto client = dynolog::SimpleJsonServerClient(FLAGS_host_ip, FLAGS_port); 23 | if (!client.initSuccessful()) { 24 | LOG(ERROR) << "Failed to initialize RPC client, check if server is running"; 25 | return -1; 26 | } 27 | 28 | auto response = client.invoke_rpc(FLAGS_message); 29 | if (!response) { 30 | LOG(ERROR) << "Failed to call RPC on :" << response.value(); 31 | return -1; 32 | } 33 | 34 | return 0; 35 | } 36 | -------------------------------------------------------------------------------- /dynolog/tests/tracing/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | dynolog_add_test(IPCMonitorTest IPCMonitorTest.cpp) 4 | 5 | target_link_libraries(IPCMonitorTest PRIVATE glog::glog) 6 | target_link_libraries(IPCMonitorTest PRIVATE dynolog_ipcmonitor_lib) 7 | target_link_libraries(IPCMonitorTest PRIVATE dynolog_lib) 8 | -------------------------------------------------------------------------------- /dynolog_hta.dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | FROM pytorch/pytorch:2.0.1-cuda11.7-cudnn8-devel 3 | RUN apt update 4 | 5 | ENV DEBIAN_FRONTEND=noninteractive 6 | RUN apt install -y curl tmux wget 7 | 8 | # install rust 9 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 10 | ENV PATH="$HOME/.cargo/env:${PATH}" 11 | 12 | # install dynolog 13 | RUN wget https://github.com/facebookincubator/dynolog/releases/download/v0.2.2/dynolog_0.2.2-0-amd64.deb 14 | RUN dpkg -i dynolog_0.2.2-0-amd64.deb 15 | RUN wget https://raw.githubusercontent.com/facebookincubator/dynolog/main/scripts/pytorch/xor.py 16 | 17 | # install HTA 18 | RUN pip install HolisticTraceAnalysis 19 | WORKDIR /workspace 20 | ENV KINETO_USE_DAEMON=1 21 | -------------------------------------------------------------------------------- /hbt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | cmake_minimum_required(VERSION 3.16) 3 | 4 | add_subdirectory(src) 5 | -------------------------------------------------------------------------------- /hbt/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | cmake_minimum_required(VERSION 3.16) 3 | 4 | include_directories(${PROJECT_SOURCE_DIR}/third_party/pfs/include) 5 | 6 | add_subdirectory(common) 7 | add_subdirectory(intel_pt) 8 | add_subdirectory(perf_event) 9 | add_subdirectory(mon) 10 | -------------------------------------------------------------------------------- /hbt/src/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_compile_options("-Wconversion") 4 | 5 | add_library(Defs Defs.h) 6 | set_target_properties(Defs PROPERTIES LINKER_LANGUAGE CXX) 7 | 8 | add_library(System System.h System.cpp) 9 | target_include_directories(System PUBLIC ${PROJECT_SOURCE_DIR}) 10 | target_link_libraries(System PUBLIC fmt::fmt) 11 | target_link_libraries(System PUBLIC pfs) 12 | target_link_libraries(System PUBLIC Defs) 13 | target_link_libraries(System PUBLIC CpuArch) 14 | 15 | add_library(ProcFsHelpers ProcFsHelpers.h) 16 | set_target_properties(ProcFsHelpers PROPERTIES LINKER_LANGUAGE CXX) 17 | target_link_libraries(ProcFsHelpers PUBLIC pfs) 18 | 19 | add_library(Defaults Defaults.h) 20 | set_target_properties(Defaults PROPERTIES LINKER_LANGUAGE CXX) 21 | 22 | if(BUILD_TESTS) 23 | enable_testing() 24 | add_subdirectory(tests) 25 | endif() 26 | -------------------------------------------------------------------------------- /hbt/src/common/Defaults.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace facebook::hbt::defaults { 12 | 13 | inline auto shmRbNames(const std::string& shm_base_path) { 14 | return std::make_pair(shm_base_path + "/events", shm_base_path + "/metadata"); 15 | } 16 | 17 | inline auto shmRbNames(std::optional shm_base_path_opt) { 18 | // XXX: Currently depends on hard-coded paths in hbt/src/shm and in CLI. 19 | return shmRbNames(shm_base_path_opt.value_or("hbt")); 20 | } 21 | 22 | inline auto shmRbNames(const char* shm_base_path) { 23 | return shmRbNames(std::string{shm_base_path}); 24 | } 25 | 26 | } // namespace facebook::hbt::defaults 27 | -------------------------------------------------------------------------------- /hbt/src/common/ProcFsHelpers.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "pfs/procfs.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | namespace facebook::hbt { 15 | 16 | inline std::optional readProcFsCmdLine(pid_t tid) noexcept { 17 | pfs::procfs pfs; 18 | try { 19 | auto vec = pfs.get_task(tid).get_cmdline(); 20 | return fmt::format("{}", fmt::join(vec, " ")); 21 | } catch (const std::system_error&) { 22 | return std::nullopt; 23 | } 24 | } 25 | 26 | } // namespace facebook::hbt 27 | -------------------------------------------------------------------------------- /hbt/src/common/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | include(${PROJECT_SOURCE_DIR}/testing/BuildTests.cmake) 4 | macro(hbt_common_add_test TESTNAME) 5 | dynolog_add_test_base(${TESTNAME} ${ARGN}) 6 | target_compile_options(${TESTNAME} PRIVATE ${HBT_COMMON_COMPILE_OPTIONS}) 7 | target_include_directories(${TESTNAME} PRIVATE ${PROJECT_SOURCE_DIR}) 8 | endmacro() 9 | 10 | hbt_common_add_test(DefsTest DefsTest.cpp) 11 | target_link_libraries(DefsTest PRIVATE Defs) 12 | 13 | hbt_common_add_test(SystemTest SystemTest.cpp) 14 | target_link_libraries(SystemTest PRIVATE System) 15 | target_link_libraries(SystemTest PRIVATE ProcFsHelpers) 16 | 17 | # todo: include folly:subprocess library (or alternative) for SystemProcFsTest.cpp 18 | -------------------------------------------------------------------------------- /hbt/src/common/tests/DefsTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/common/Defs.h" 7 | 8 | #include 9 | #include 10 | 11 | TEST(Defs, Exception) { 12 | EXPECT_THROW(HBT_THROW_EINVAL(), std::invalid_argument); 13 | EXPECT_THROW(HBT_THROW_EINVAL() << "hola", std::invalid_argument); 14 | EXPECT_THROW(HBT_THROW_EINVAL() << "adioshola", std::invalid_argument); 15 | EXPECT_THROW(HBT_THROW_SYSTEM(ENODATA) << "adioshola", std::system_error); 16 | EXPECT_THROW(HBT_THROW_SYSTEM(EBUSY), std::system_error); 17 | EXPECT_THROW(HBT_THROW_SYSTEM(EBUSY) << "my message", std::system_error); 18 | EXPECT_THROW(HBT_DCHECK_NOT_NULLPTR(nullptr), std::runtime_error); 19 | EXPECT_NO_THROW(HBT_DCHECK_NOT_NULLPTR((void*)1)); 20 | } 21 | -------------------------------------------------------------------------------- /hbt/src/common/tests/SystemProcFsTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | #include 8 | 9 | #include "hbt/src/common/ProcFsHelpers.h" 10 | #include "hbt/src/common/System.h" 11 | 12 | using namespace facebook::hbt; 13 | 14 | TEST(ParseCmdLine, readProcFsCmdLine) { 15 | pid_t mypid = getpid(); 16 | folly::Subprocess proc( 17 | {"/usr/bin/ps", "-p", std::to_string(mypid), "-o", "cmd="}, 18 | folly::Subprocess::Options().pipeStdout()); 19 | std::pair output = proc.communicate(); 20 | proc.wait(); 21 | 22 | auto cmdline = output.first; 23 | if (cmdline.back() == '\n') { 24 | cmdline.pop_back(); 25 | } 26 | 27 | auto pfsCmdline = readProcFsCmdLine(mypid); 28 | EXPECT_TRUE(pfsCmdline != std::nullopt); 29 | EXPECT_TRUE(pfsCmdline.value().compare(cmdline) == 0); 30 | 31 | EXPECT_TRUE(readProcFsCmdLine(0) == std::nullopt); 32 | } 33 | -------------------------------------------------------------------------------- /hbt/src/intel_pt/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | include_directories(${PROJECT_SOURCE_DIR}) 4 | 5 | add_library(IptEventBuilder IptEventBuilder.h IptEventBuilder.cpp) 6 | target_link_libraries(IptEventBuilder PUBLIC PmuDevices) 7 | target_link_libraries(IptEventBuilder PUBLIC PmuEvent) 8 | 9 | add_subdirectory(tests) 10 | -------------------------------------------------------------------------------- /hbt/src/intel_pt/examples/perf_script_output__cpu_wide_tiny.txt: -------------------------------------------------------------------------------- 1 | :1686527 1686527/1686527 [010] 25738.668928: call do_syscall_64 ffffffff81a0006c entry_SYSCALL_64+0x5c ([kernel.kallsyms]) 2 | :1686527 1686527/1686527 [010] 25738.668928: call __fentry__ ffffffff81001bf0 do_syscall_64+0x0 ([kernel.kallsyms]) 3 | instruction trace error type 1 time 25738.668928432 cpu 10 pid 1686527 tid 1686527 ip 0xffffffff81a01610 code 6: Trace doesn't match instruction 4 | :1686527 1686527/1686527 [010] 25738.668928: return __audit_syscall_entry ffffffff81121d7e __audit_syscall_entry+0xce ([kernel.kallsyms]) 5 | :1686527 1686527/1686527 [010] 25738.668932: return syscall_trace_enter ffffffff8100151f syscall_trace_enter+0x10f ([kernel.kallsyms]) 6 | -------------------------------------------------------------------------------- /hbt/src/intel_pt/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | import argparse 8 | import time 9 | 10 | from hbt.intel_pt.tracer import Tracer 11 | 12 | 13 | def main() -> None: 14 | parser = argparse.ArgumentParser(description="Intel PT tracer.") 15 | parser.add_argument("pid", type=int, default=None, help="PIDs to filter on.") 16 | parser.add_argument( 17 | "-c", "--cpus", type=str, default=None, help="CPUs to filter on. E.g. 1-3,5" 18 | ) 19 | parser.add_argument( 20 | "-t", "--timeout", type=int, default=5, help="Number of seconds to run for." 21 | ) 22 | 23 | args = parser.parse_args() 24 | 25 | t = Tracer(cpus=args.cpus, pid=args.pid) 26 | t.start() 27 | 28 | print(f"Running for {args.timeout} seconds") 29 | for i in range(args.timeout): 30 | time.sleep(1) 31 | 32 | for p in t.read(): 33 | print(f"Event at iteration {i}: {p}") 34 | t.stop() 35 | 36 | 37 | if __name__ == "__main__": 38 | main() # pragma: no cover 39 | -------------------------------------------------------------------------------- /hbt/src/intel_pt/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_library(IptCapChecker IptCapChecker.cpp) 4 | target_link_libraries(IptCapChecker PUBLIC IptEventBuilder) 5 | target_link_libraries(IptCapChecker PUBLIC BuiltinMetrics) 6 | -------------------------------------------------------------------------------- /hbt/src/intel_pt/tests/IptCapChecker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include 7 | 8 | #include "hbt/src/intel_pt/IptEventBuilder.h" 9 | #include "hbt/src/perf_event/BuiltinMetrics.h" 10 | 11 | using namespace facebook::hbt::intel_pt; 12 | using namespace facebook::hbt::perf_event; 13 | 14 | int main() { 15 | auto pmuManager = makePmuDeviceManager(); 16 | if (!pmuManager) { 17 | std::cout << "Failed to make pmu manager" << std::endl; 18 | return -1; 19 | } 20 | auto iptEventBuilder = IptEventBuilder::createIptEventBuilder(*pmuManager); 21 | for (int i = 0; i <= 15; i++) { 22 | std::cout << "Support CYC(" << i 23 | << "): " << iptEventBuilder->hasCycSupport(i) << std::endl; 24 | } 25 | for (int i = 0; i <= 15; i++) { 26 | std::cout << "Support MTC(" << i 27 | << "): " << iptEventBuilder->hasMtcSupport(i) << std::endl; 28 | } 29 | std::cout << "Support PTW: " << iptEventBuilder->hasPtwSupport() << std::endl; 30 | for (int i = 0; i <= 15; i++) { 31 | std::cout << "Support PSB(" << i 32 | << "): " << iptEventBuilder->hasPsbPeriodSupport(i) << std::endl; 33 | } 34 | std::cout << "SupportPwrEvt: " << iptEventBuilder->hasPwrEvtSupport() 35 | << std::endl; 36 | 37 | std::cout << "Set pt to true: "; 38 | iptEventBuilder->setPt(true); 39 | std::cout << "0x" << std::hex << iptEventBuilder->getPerfConfig() 40 | << std::endl; 41 | 42 | std::cout << "Set branch to true: "; 43 | iptEventBuilder->setBranch(true); 44 | std::cout << "0x" << std::hex << iptEventBuilder->getPerfConfig() 45 | << std::endl; 46 | 47 | std::cout << "Set tsc to true: "; 48 | iptEventBuilder->setTsc(true); 49 | std::cout << "0x" << std::hex << iptEventBuilder->getPerfConfig() 50 | << std::endl; 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /hbt/src/mon/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_compile_options("-Wconversion") 4 | 5 | add_library(Monitor Monitor.h) 6 | set_target_properties(Monitor PROPERTIES LINKER_LANGUAGE CXX) 7 | target_link_libraries(Monitor PUBLIC System) 8 | target_link_libraries(Monitor PUBLIC IntelPTMonitor) 9 | target_link_libraries(Monitor PUBLIC MonData) 10 | 11 | add_library(IntelPTMonitor IntelPTMonitor.h IntelPTMonitor.cpp) 12 | target_link_libraries(IntelPTMonitor PUBLIC System) 13 | target_link_libraries(IntelPTMonitor PUBLIC Defaults) 14 | target_link_libraries(IntelPTMonitor PUBLIC IptEventBuilder) 15 | target_link_libraries(IntelPTMonitor PUBLIC PerCpuTraceAuxGenerator) 16 | 17 | add_library(MonData MonData.h MonData.cpp) 18 | target_link_libraries(MonData PUBLIC pfs) 19 | target_link_libraries(MonData PUBLIC System) 20 | 21 | add_subdirectory(tests) 22 | 23 | add_library(PerCpuSliceGenerator PerCpuSliceGenerator.h) 24 | set_target_properties(PerCpuSliceGenerator PROPERTIES LINKER_LANGUAGE CXX) 25 | -------------------------------------------------------------------------------- /hbt/src/mon/MonData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/mon/MonData.h" 7 | #include 8 | #include 9 | 10 | using namespace facebook::hbt::mon; 11 | 12 | std::vector detail::getFileBackedExecutableModules( 13 | const std::filesystem::path& rootPath, 14 | std::vector& mem_regions) { 15 | // Sort mem_regions by start_address (ascending) so we can easily set each 16 | // module's load address to the lowest start address of its sections. 17 | std::sort(mem_regions.begin(), mem_regions.end()); 18 | 19 | // A module is an executable file mapped into the process' address space. 20 | // Since there can be multiple mem_regions (sections) per file we set the 21 | // module's load address to the lowest start address of its sections. 22 | // The sections are sorted by start_address, so for each module we simply set 23 | // its load address to the start address of its first section. 24 | // 25 | // In the future this will need to be updated to account for "special" modules 26 | // that aren't backed by a file (vdso). 27 | 28 | // The inodes that are backed by a file and have an executable section. 29 | std::unordered_set executable_inodes; 30 | 31 | // rootPath is used here to handle chroot. 32 | // Path in the mem_region is relative to the root directory seen by the 33 | // process. 34 | // /proc//root will always point to the real root directory. 35 | 36 | // First pass to populate exectuable_inodes 37 | for (const auto& region : mem_regions) { 38 | ino_t inode = region.inode; 39 | if (inode > 0 && region.perm.can_execute && 40 | std::filesystem::exists( 41 | rootPath / 42 | std::filesystem::path(region.pathname).relative_path())) { 43 | executable_inodes.insert(inode); 44 | } 45 | } 46 | 47 | std::vector modules; 48 | // Second pass to only populate the modules based on the set of executable 49 | // inodes. 50 | for (const auto& region : mem_regions) { 51 | ino_t inode = region.inode; 52 | if (executable_inodes.find(inode) != executable_inodes.end()) { 53 | modules.emplace_back( 54 | rootPath / std::filesystem::path(region.pathname).relative_path(), 55 | region.start_address); 56 | // remove from set so we don't store duplicate modules. 57 | executable_inodes.erase(inode); 58 | } 59 | } 60 | return modules; 61 | } 62 | -------------------------------------------------------------------------------- /hbt/src/mon/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_compile_options("-Wconversion") 4 | add_compile_options("-Wconversion-null") 5 | 6 | add_executable(MonitorTest MonitorTest.cpp) 7 | target_link_libraries(MonitorTest PUBLIC Monitor) 8 | target_link_libraries(MonitorTest PUBLIC BuiltinMetrics) 9 | target_link_libraries(MonitorTest PRIVATE gtest gmock gtest_main) 10 | -------------------------------------------------------------------------------- /hbt/src/mon/tests/MonDataTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/mon/MonData.h" 7 | 8 | #include "tools/cxx/Resources.h" 9 | 10 | #include 11 | 12 | using namespace facebook::hbt::mon; 13 | 14 | TEST(MonData, ContDataCreation) { 15 | CountData cd{{"event1", "event2"}}; 16 | } 17 | 18 | class ModuleCollectionTest : public ::testing::Test { 19 | protected: 20 | void SetUp() override { 21 | // Pass _ec to suppress exceptions since we don't care if this fails. 22 | std::error_code _ec; 23 | std::filesystem::remove_all(TEST_MODULES_DIR, _ec); 24 | 25 | try { 26 | std::filesystem::create_directory(TEST_MODULES_DIR); 27 | } catch (const std::filesystem::filesystem_error&) { 28 | FAIL() << "Unable to create test file directory: " << TEST_MODULES_DIR; 29 | } 30 | 31 | for (const auto& [module_path, _] : EXPECTED_MODULES) { 32 | std::ofstream ofs(module_path); 33 | ofs.close(); 34 | if (!ofs) { 35 | FAIL() << "Unable to create test file: " << module_path; 36 | } 37 | } 38 | } 39 | 40 | void TearDown() override { 41 | // Pass _ec to suppress exceptions since we don't care if this fails. 42 | std::error_code _ec; 43 | std::filesystem::remove_all(TEST_MODULES_DIR, _ec); 44 | } 45 | 46 | static constexpr auto TEST_PROCFS_ROOT_DIR = "hbt/src/mon/tests/parse_procfs"; 47 | static constexpr auto TEST_MODULES_DIR = "/tmp/module_collection_test"; 48 | static constexpr size_t EXPECTED_NUM_MODULES = 3; 49 | static inline const ModuleInfo EXPECTED_MODULES[EXPECTED_NUM_MODULES] = { 50 | {fmt::format("{}/lib0", TEST_MODULES_DIR), 0x200000}, 51 | {fmt::format("{}/lib1", TEST_MODULES_DIR), 0x7fe828f6c000}, 52 | {fmt::format("{}/lib2", TEST_MODULES_DIR), 0x7fe829bfd000}, 53 | }; 54 | }; 55 | 56 | TEST_F(ModuleCollectionTest, Collect) { 57 | auto path = build::getResourcePath(TEST_PROCFS_ROOT_DIR); 58 | pfs::procfs pfs(path.string()); 59 | 60 | auto maps = pfs.get_task(1234).get_maps(); 61 | auto modules = detail::getFileBackedExecutableModules("/", maps); 62 | ASSERT_EQ(modules.size(), EXPECTED_NUM_MODULES); 63 | 64 | for (size_t i = 0; i < EXPECTED_NUM_MODULES; i++) { 65 | EXPECT_EQ(modules[i], EXPECTED_MODULES[i]); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /hbt/src/mon/tests/parse_procfs/1234/maps: -------------------------------------------------------------------------------- 1 | 00200000-01eaf000 r--p 00000000 00:1c 268353812 /tmp/module_collection_test/lib0 2 | 01eaf000-09e97000 r-xp 01caf000 00:1c 268353812 /tmp/module_collection_test/lib0 3 | 09e97000-09ea0000 r--p 09c97000 00:1c 268353812 /tmp/module_collection_test/lib0 4 | 09ea0000-09ea1000 r--p 00000000 00:00 0 5 | 09ea1000-09ef7000 rw-p 09ca0000 00:1c 268353812 /tmp/module_collection_test/lib0 6 | 09ef7000-0a368000 rw-p 00000000 00:00 0 [heap] 7 | 7fe7d48c8000-7fe7d48c9000 ---p 00000000 00:00 0 8 | 7fe7d48c9000-7fe7d50c9000 rwxp 00000000 00:00 0 9 | 7fe7ee291000-7fe7ee2b2000 rw-s 00000000 00:0c 14368 anon_inode:[perf_event] 10 | 7fe828c1a000-7fe828c1b000 r--s 00000000 00:0c 14368 anon_inode:bpf-map 11 | 7fe828f6a000-7fe828f6c000 rw-p 00000000 00:00 0 12 | 7fe828f6c000-7fe829002000 r--p 00000000 00:1c 240656293 /tmp/module_collection_test/lib1 13 | 7fe829002000-7fe829110000 r-xp 00096000 00:1c 240656293 /tmp/module_collection_test/lib1 14 | 7fe829110000-7fe829150000 r--p 001a4000 00:1c 240656293 /tmp/module_collection_test/lib1 15 | 7fe829150000-7fe82915b000 r--p 001e3000 00:1c 240656293 /tmp/module_collection_test/lib1 16 | 7fe82915b000-7fe82915e000 rw-p 001ee000 00:1c 240656293 /tmp/module_collection_test/lib1 17 | 7fe829bfb000-7fe829bfd000 rw-p 00000000 00:00 0 18 | 7fe829bfd000-7fe829bfe000 r--p 00000000 00:1c 240656152 /tmp/module_collection_test/lib2 19 | 7fe829bfe000-7fe829c1f000 r-xp 00001000 00:1c 240656152 /tmp/module_collection_test/lib2 20 | 7fe829c1f000-7fe829c26000 r--p 00022000 00:1c 240656152 /tmp/module_collection_test/lib2 21 | 7fe829c26000-7fe829c27000 r--p 00000000 00:00 0 22 | 7fe829c27000-7fe829c28000 r--p 00029000 00:1c 240656152 /tmp/module_collection_test/lib2 23 | 7fe829c28000-7fe829c29000 rw-p 0002a000 00:1c 240656152 /tmp/module_collection_test/lib2 24 | 7fe829c29000-7fe829c2a000 rw-p 00000000 00:00 0 25 | 7ffccb80f000-7ffccb872000 rwxp 00000000 00:00 0 [stack] 26 | 7ffccb872000-7ffccb873000 rw-p 00000000 00:00 0 27 | 7ffccb8ae000-7ffccb8b2000 r--p 00000000 00:00 0 [vvar] 28 | 7ffccb8b2000-7ffccb8b4000 r-xp 00000000 00:00 0 [vdso] 29 | ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 30 | -------------------------------------------------------------------------------- /hbt/src/perf_event/ArmEvents.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | #include "hbt/src/common/System.h" 8 | #include "hbt/src/perf_event/PmuDevices.h" 9 | 10 | namespace facebook::hbt::perf_event { 11 | namespace neoverse_v2 { 12 | void setTestRootDir(const std::string& rootDir); 13 | void addEvents(PmuDeviceManager& pmu_manager); 14 | } // namespace neoverse_v2 15 | 16 | void addArmEvents(const CpuInfo& cpu_info, PmuDeviceManager& pmu_manager); 17 | 18 | } // namespace facebook::hbt::perf_event 19 | -------------------------------------------------------------------------------- /hbt/src/perf_event/BPerfCountReader.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/common/System.h" 9 | #include "hbt/src/perf_event/Metrics.h" 10 | #include "hbt/src/perf_event/PerfEventsGroup.h" 11 | 12 | namespace facebook::hbt::perf_event { 13 | 14 | class BPerfEventsGroup; 15 | 16 | // Wrap BPerfEventsGroup. Do not inherit because 17 | // BPerfEventsGroup expands eBPF macros that very lenient on type conversions 18 | // and other compilation warnings. This gets in the way of HBT's usage of strict 19 | // compilation mode. To avoid conflict, we keep BPerf compilation unit 20 | // separated. 21 | class BPerfCountReader { 22 | public: 23 | using ReadValues = GroupReadValues; 24 | 25 | BPerfCountReader( 26 | std::shared_ptr bperf_eg_in, 27 | std::shared_ptr cgroup_fd_wrapper, 28 | int cgroup_update_level); 29 | 30 | size_t getNumEvents() const; 31 | 32 | ReadValues makeReadValues() const; 33 | 34 | bool enable(); 35 | 36 | bool disable(); 37 | 38 | bool read(ReadValues& rv, bool skip_offset = false); 39 | 40 | std::optional read(bool skip_offset = false); 41 | 42 | bool isEnabled() const; 43 | 44 | std::ostream& printStatus(std::ostream& os); 45 | 46 | BPerfEventsGroup* getBPerfEventsGroup() const; 47 | 48 | ~BPerfCountReader(); 49 | 50 | protected: 51 | // Use raw pointer because BPerfEventsGroup is only a forward declaration 52 | // here, as we don't want to include BPerfEventsGroup due to different 53 | // compilation flags. 54 | std::shared_ptr bperf_eg_; 55 | 56 | std::shared_ptr cgroup_fd_wrapper_; 57 | int cgroup_update_level_; 58 | 59 | bool cgroup_tracking_ = false; 60 | }; 61 | 62 | } // namespace facebook::hbt::perf_event 63 | -------------------------------------------------------------------------------- /hbt/src/perf_event/BPerfPerThreadReader.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "hbt/src/perf_event/Metrics.h" 11 | #include "hbt/src/perf_event/PerfEventsGroup.h" 12 | #include "hbt/src/perf_event/PmuDevices.h" 13 | #include "hbt/src/perf_event/PmuEvent.h" 14 | #include "hbt/src/perf_event/bpf/bperf.h" 15 | 16 | namespace facebook::hbt::perf_event { 17 | 18 | struct BPerfThreadData { 19 | __u64 cpuTime; 20 | __u64 monoTime; 21 | struct bpf_perf_event_value values[BPERF_MAX_GROUP_SIZE]; 22 | }; 23 | 24 | class BPerfPerThreadReader { 25 | public: 26 | BPerfPerThreadReader(std::string pin_name, int event_cnt); 27 | ~BPerfPerThreadReader(); 28 | int read(struct BPerfThreadData* data); 29 | int enable(); 30 | void disable(); 31 | bool isEnabled() { 32 | return enabled_; 33 | } 34 | 35 | protected: 36 | void* mmap_ptr_ = nullptr; 37 | struct bperf_thread_data* data_ = nullptr; 38 | // For compatibility (newer leader with older reader), we cannot use 39 | // data_->events directly. Instead use event_data_ which is adjusted 40 | // based on leader data structure. 41 | struct bperf_perf_event_data* event_data_[BPERF_MAX_GROUP_SIZE] = {nullptr}; 42 | int data_fd_ = -1; 43 | const std::string pin_name_; 44 | __s64 initial_clock_drift_ = 0; 45 | int event_cnt_ = -1; 46 | int data_size_ = 0; 47 | int mmap_size_ = 0; 48 | int getDataSize_(); 49 | int dummy_pe_fd_ = -1; 50 | void* dummy_pe_mmap_ = nullptr; 51 | bool enabled_ = false; 52 | // Previous reading of event 0, used to detect when the lead exits 53 | __u64 prev_counter_zero_; 54 | bool leadExited(__u64 counter_zero); 55 | }; 56 | 57 | } // namespace facebook::hbt::perf_event 58 | -------------------------------------------------------------------------------- /hbt/src/perf_event/BuiltinMetrics.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/perf_event/CpuArch.h" 9 | #include "hbt/src/perf_event/Metrics.h" 10 | 11 | namespace facebook::hbt::perf_event { 12 | 13 | std::shared_ptr makePmuDeviceManager(); 14 | std::shared_ptr makeAvailableMetrics(); 15 | void addArmCoreMetrics(std::shared_ptr& metrics); 16 | void addIntelCoreMetrics(std::shared_ptr& metrics); 17 | void addUncoreMetrics(std::shared_ptr& metrics); 18 | void addArmUncoreMetrics(std::shared_ptr& metrics); 19 | void addIntelUncoreMetrics(std::shared_ptr& metrics); 20 | 21 | struct HwCacheEventInfo { 22 | // this is not unique, but enum of different cache event 23 | uint32_t id; 24 | std::string description; 25 | HwCacheEventInfo(uint32_t id, std::string descrption) 26 | : id(id), description(descrption) {} 27 | }; 28 | 29 | const ReducerFunc& getAddReducer(); 30 | const ReducerFunc& getRateReducer(); 31 | } // namespace facebook::hbt::perf_event 32 | -------------------------------------------------------------------------------- /hbt/src/perf_event/Metrics.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/perf_event/Metrics.h" 7 | 8 | namespace facebook::hbt::perf_event { 9 | 10 | std::ostream& operator<<(std::ostream& os, const EventRef& ev_ref) { 11 | auto pmu_type = ev_ref.pmu_type; 12 | os << "pmu: " << PmuTypeToStr(pmu_type); 13 | os << " \"" << ev_ref.nickname << "\" id: \"" << ev_ref.event_id << "\" "; 14 | os << ev_ref.extra_attr; 15 | os << ev_ref.transforms; 16 | 17 | return os; 18 | } 19 | 20 | std::ostream& operator<<(std::ostream& os, const TOptCpuArch& cpu_arch) { 21 | if (cpu_arch.has_value()) { 22 | return os << *cpu_arch; 23 | } else { 24 | return os << ""; 25 | } 26 | } 27 | 28 | std::ostream& operator<<(std::ostream& os, const MetricDesc& desc) { 29 | os << "\nMetricDesc id:\"" << desc.id << "\"\n brief: \"" << desc.brief_desc 30 | << "\"\n Default sampling period: " << desc.default_sampling_period 31 | << "\n" 32 | << "\n Events:\n"; 33 | for (const auto& [arch_info, event_refs] : desc.event_refs_by_arch) { 34 | if (arch_info.has_value()) { 35 | os << *arch_info << "\n"; 36 | } else { 37 | os << "Generic\n"; 38 | } 39 | for (const auto& r : event_refs) { 40 | os << " " << r << "\n"; 41 | } 42 | } 43 | 44 | if (desc.dives.size()) { 45 | os << "\n dives=[ "; 46 | for (const auto& d : desc.dives) { 47 | os << d << " "; 48 | } 49 | os << "]"; 50 | } 51 | if (desc.reducer) { 52 | os << "\n has reducer"; 53 | } else { 54 | os << "\n does not have reducer"; 55 | } 56 | return os << "\n"; 57 | } 58 | 59 | } // namespace facebook::hbt::perf_event 60 | -------------------------------------------------------------------------------- /hbt/src/perf_event/PerCpuBase.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/perf_event/PerPerfEventsGroupBase.h" 9 | 10 | namespace facebook::hbt::perf_event { 11 | 12 | template 13 | class PerCpuBase : public PerPerfEventsGroupBase { 14 | public: 15 | using TBase = PerPerfEventsGroupBase; 16 | PerCpuBase( 17 | const CpuSet& mon_cpus, 18 | std::shared_ptr cgroup_fd_wrapper) 19 | : TBase(cgroup_fd_wrapper), mon_cpus_{mon_cpus} { 20 | for_each_cpu(cpu, mon_cpus) { 21 | TBase::generators_.insert(std::make_pair(static_cast(cpu), nullptr)); 22 | } 23 | } 24 | auto getMonCpus() const { 25 | return mon_cpus_; 26 | } 27 | const TPerfEventGroupGenBase& getCpuGenerator(CpuId cpu) const { 28 | auto key = static_cast(cpu); 29 | HBT_ARG_CHECK(TBase::generators_.count(key)) 30 | << "CPU " << std::to_string(key) + " not monitored"; 31 | HBT_ARG_CHECK(TBase::generators_.at(key) != nullptr) 32 | << "No generator in CPU " << key; 33 | return *TBase::generators_.at(key); 34 | } 35 | 36 | TPerfEventGroupGenBase& getCpuGenerator(CpuId cpu) { 37 | auto key = static_cast(cpu); 38 | HBT_ARG_CHECK(TBase::generators_.count(key)) 39 | << "CPU " << std::to_string(key) + " not monitored"; 40 | HBT_ARG_CHECK(TBase::generators_.at(key) != nullptr) 41 | << "No generator in CPU " << key; 42 | return *TBase::generators_.at(key); 43 | } 44 | 45 | auto getCpuGeneratorPtr(CpuId cpu) { 46 | auto key = static_cast(cpu); 47 | HBT_ARG_CHECK(TBase::generators_.count(key)) 48 | << "CPU " << std::to_string(key) + " not monitored"; 49 | HBT_ARG_CHECK(TBase::generators_.at(key) != nullptr) 50 | << "No generator in CPU " << key; 51 | return TBase::generators_.at(key); 52 | } 53 | 54 | protected: 55 | CpuSet mon_cpus_; 56 | }; 57 | 58 | } // namespace facebook::hbt::perf_event 59 | -------------------------------------------------------------------------------- /hbt/src/perf_event/PerCpuDummyGenerator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/common/System.h" 9 | #include "hbt/src/perf_event/Metrics.h" 10 | #include "hbt/src/perf_event/PerCpuSampleGeneratorBase.h" 11 | #include "hbt/src/perf_event/PerfEventsGroup.h" 12 | 13 | #include 14 | #include 15 | 16 | namespace facebook::hbt::perf_event { 17 | 18 | // 19 | // CRTP extension of CpuEventsGroup<>. 20 | // 21 | // Uses a dummy event that does trigger samples but still 22 | // builds perf_event mmap pages. 23 | // Useful to access to TSC and other information available 24 | // in perf_event mmap page. 25 | // 26 | class CpuDummyGenerator final 27 | : public PerfEventsGroup { 28 | public: 29 | using TBase = PerfEventsGroup; 30 | 31 | inline static const EventConfs event_confs = {EventConf{ 32 | .id = "sw_dummy", 33 | .configs = EventConfigs{ 34 | .type = PERF_TYPE_SOFTWARE, 35 | .config = PERF_COUNT_SW_DUMMY}}}; 36 | 37 | CpuDummyGenerator(CpuId cpu) : TBase{cpu, -1, -1, event_confs} {} 38 | 39 | void enableImpl() noexcept {} 40 | 41 | void disableImpl() noexcept {} 42 | 43 | void open() { 44 | TBase::open_(0, 0, true, 0); 45 | } 46 | 47 | TimeStamp getLastTimeStamp() const noexcept { 48 | return kInvalidTimeStamp; 49 | } 50 | }; 51 | 52 | using TBasePerCpuDummyGenerator = PerCpuSampleGeneratorBase; 53 | 54 | // Wrap many instances of CpuDummyGenerator, one per CPU. 55 | class PerCpuDummyGenerator : public TBasePerCpuDummyGenerator { 56 | public: 57 | explicit PerCpuDummyGenerator(const CpuSet& mon_cpus) 58 | : TBasePerCpuDummyGenerator{mon_cpus, nullptr} { 59 | for_each_cpu(cpu, mon_cpus) { 60 | this->generators_[static_cast(cpu)] = 61 | std::make_shared(cpu); 62 | } 63 | } 64 | 65 | void open() { 66 | for (const auto& [_, gen] : this->generators_) { 67 | gen->open(); 68 | } 69 | } 70 | }; 71 | 72 | } // namespace facebook::hbt::perf_event 73 | -------------------------------------------------------------------------------- /hbt/src/perf_event/bpf/bperf.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #ifndef BPERF_H 7 | #define BPERF_H 8 | 9 | #define BPERF_MAX_GROUP_SIZE 8 10 | 11 | #define BPERF_MAX_THREAD_READER 65535 12 | // index 0 is reserved for metadata, and will never be used for a thread 13 | #define BPERF_INVALID_THREAD_IDX 0 14 | typedef __u16 idx_t; 15 | 16 | _Static_assert( 17 | (1 << (sizeof(idx_t) * 8)) > BPERF_MAX_THREAD_READER, 18 | "idx_t need to be an unsigned type with the max value >= BPERF_MAX_THREAD_READER"); 19 | 20 | /* x86 21 | * struct cyc2ns_data { 22 | * u32 cyc2ns_mul; 23 | * u32 cyc2ns_shift; 24 | * u64 cyc2ns_offset; 25 | * }; 26 | * 27 | * time_ns = (tsc * multi) >> shift - offset 28 | * 29 | * On arm64 30 | * time_ns = TODO 31 | */ 32 | struct bperf_clock_param { 33 | __u32 multi; 34 | __u32 shift; 35 | __u64 offset; 36 | __u64 reserved; 37 | }; 38 | 39 | /* data of a single perf_event */ 40 | struct bperf_perf_event_data { 41 | struct bpf_perf_event_value output_value; 42 | __u64 offset; 43 | __u32 idx; 44 | }; 45 | 46 | struct bperf_thread_metadata { 47 | __u32 metadata_size; /* sizeof(bperf_thread_metadata) */ 48 | __u32 thread_data_size; /* sizeof(bperf_thread_data) */ 49 | __u32 event_data_size; /* sizeof(bperf_perf_event_data) */ 50 | __u32 event_cnt; 51 | __u32 flags; 52 | }; 53 | 54 | /* BPerfEventsGroup may have variable number of perf events. 55 | * Therefore, the bperf_thread_data used for each thread may 56 | * change. To show the data, we use fixed size header for per 57 | * group data (time, etc.), and a variable sized array for 58 | * per event data (bperf_perf_event_data). 59 | */ 60 | struct bperf_thread_data { 61 | __u32 lock; 62 | __u32 __reserved; 63 | struct bperf_clock_param tsc_param; 64 | 65 | /* all the times are in nano seconds */ 66 | /* when the perf value offset is updated */ 67 | __u64 offset_update_time; 68 | 69 | /* runtime = runtime_until_offset_update + tsc_time - offset_time; */ 70 | __u64 runtime_until_offset_update; 71 | 72 | struct bperf_perf_event_data events[]; 73 | }; 74 | 75 | inline int bperf_roundup(int size, int align) { 76 | return (size + align - 1) / align * align; 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /hbt/src/perf_event/json_events/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | add_subdirectory(generated) 3 | -------------------------------------------------------------------------------- /hbt/src/perf_event/json_events/generated/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_library(CpuArchGen CpuArch.h) 4 | set_target_properties(CpuArchGen PROPERTIES LINKER_LANGUAGE CXX) 5 | 6 | if(USE_JSON_GENERATED_PERF_EVENTS) 7 | add_subdirectory(intel) 8 | endif() 9 | -------------------------------------------------------------------------------- /hbt/src/perf_event/json_events/generated/intel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | file (GLOB json_events_src "*.h" "*.cpp") 4 | add_library(JsonEvents ${json_events_src}) 5 | target_link_libraries(JsonEvents PUBLIC PmuDevices) 6 | target_link_libraries(JsonEvents PUBLIC PmuEvent) 7 | -------------------------------------------------------------------------------- /hbt/src/perf_event/json_events/generated/intel/icelake_uncore.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | // 6 | // Auto generated for hbt. Do not manually edit. 7 | // @generated 8 | 9 | #include 10 | #include "hbt/src/perf_event/json_events/generated/intel/JsonEvents.h" 11 | 12 | namespace facebook::hbt::perf_event::generated { 13 | namespace icelake_uncore { 14 | 15 | void addEvents(PmuDeviceManager& pmu_manager) { 16 | /* 17 | Events from icelake_uncore.json (3 events). 18 | 19 | Supported SKUs: 20 | - Arch: x86, Model: ICL id: 125 21 | - Arch: x86, Model: ICL id: 126 22 | */ 23 | #ifdef HBT_ADD_ALL_GENERATED_EVENTS 24 | pmu_manager.addEvent(std::make_shared( 25 | PmuType::uncore_arb, 26 | "UNC_ARB_TRK_REQUESTS.ALL", 27 | EventDef::Encoding{.code = 0x81, .umask = 0x01, .cmask = 0}, 28 | R"(Total number of all outgoing entries allocated. Accounts for Coherent and non-coherent traffic.)", 29 | R"(Total number of all outgoing entries allocated. Accounts for Coherent and non-coherent traffic.)", 30 | std::nullopt, 31 | std::nullopt, // ScaleUnit 32 | EventDef::IntelFeatures{}, 33 | std::nullopt // Errata 34 | )); 35 | #endif // HBT_ADD_ALL_GENERATED_EVENTS 36 | 37 | #ifdef HBT_ADD_ALL_GENERATED_EVENTS 38 | pmu_manager.addEvent(std::make_shared( 39 | PmuType::uncore_arb, 40 | "UNC_ARB_COH_TRK_REQUESTS.ALL", 41 | EventDef::Encoding{.code = 0x84, .umask = 0x01, .cmask = 0}, 42 | R"(Number of entries allocated. Account for Any type: e.g. Snoop, etc.)", 43 | R"(Number of entries allocated. Account for Any type: e.g. Snoop, etc.)", 44 | std::nullopt, 45 | std::nullopt, // ScaleUnit 46 | EventDef::IntelFeatures{}, 47 | std::nullopt // Errata 48 | )); 49 | #endif // HBT_ADD_ALL_GENERATED_EVENTS 50 | 51 | #ifdef HBT_ADD_ALL_GENERATED_EVENTS 52 | pmu_manager.addEvent(std::make_shared( 53 | PmuType::uncore_ncu, 54 | "UNC_CLOCK.SOCKET", 55 | EventDef::Encoding{.code = 0x00, .umask = 0x01, .cmask = 0}, 56 | R"(This 48-bit fixed counter counts the UCLK cycles.)", 57 | R"(This 48-bit fixed counter counts the UCLK cycles.)", 58 | std::nullopt, 59 | std::nullopt, // ScaleUnit 60 | EventDef::IntelFeatures{}, 61 | std::nullopt // Errata 62 | )); 63 | #endif // HBT_ADD_ALL_GENERATED_EVENTS 64 | } 65 | 66 | } // namespace icelake_uncore 67 | } // namespace facebook::hbt::perf_event::generated 68 | -------------------------------------------------------------------------------- /hbt/src/perf_event/tests/ArmEventsTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/perf_event/ArmEvents.h" 7 | #include 8 | #include "hbt/src/perf_event/PmuDevices.h" 9 | 10 | #include 11 | 12 | using namespace facebook::hbt; 13 | using namespace facebook::hbt::perf_event; 14 | 15 | std::string kTestRootEnvKey = "testroot"; 16 | 17 | unsigned int kDeviceEnum = 0; 18 | 19 | auto default_pmu_manager(facebook::hbt::CpuInfo cpu_info) { 20 | // Load CPU Info 21 | auto pmu_manager = PmuDeviceManager(cpu_info); 22 | EXPECT_EQ(pmu_manager.getNumPmus(), 0); 23 | 24 | // Create a PMU. 25 | auto pmu = std::make_shared( 26 | "armv8_pmuv3", 27 | PmuType::armv8_pmuv3, 28 | kDeviceEnum, 29 | PERF_TYPE_RAW, 30 | "A Dummy armv8_pmuv3 device", 31 | true); 32 | 33 | pmu_manager.addPmu(pmu); 34 | EXPECT_EQ(pmu_manager.getNumPmus(), 1); 35 | 36 | return pmu_manager; 37 | } 38 | 39 | TEST(ArmEventsTest, ScanPmu) { 40 | neoverse_v2::setTestRootDir(getenv(kTestRootEnvKey.c_str())); 41 | auto cpu_info = CpuInfo::load(); 42 | cpu_info.cpu_arch = CpuArch::NEOVERSE_V2; 43 | auto pmu_manager = default_pmu_manager(cpu_info); 44 | 45 | addArmEvents(cpu_info, pmu_manager); 46 | 47 | auto pmu = pmu_manager.getPmuGroups().at(PmuType::armv8_pmuv3); 48 | EXPECT_EQ(pmu.at(kDeviceEnum)->getEventDefs().size(), 4); 49 | 50 | auto event1 = pmu_manager.findEventDef("cpu_cycles"); 51 | EXPECT_TRUE(event1 != nullptr); 52 | EXPECT_EQ(event1->id, "cpu_cycles"); 53 | EXPECT_EQ(event1->pmu_type, PmuType::armv8_pmuv3); 54 | EXPECT_EQ(event1->encoding.code, 0x11); 55 | 56 | auto event2 = pmu_manager.findEventDef("bad_event_config"); 57 | EXPECT_TRUE(event2 == nullptr); 58 | 59 | auto event3 = pmu_manager.findEventDef("bad_event_field"); 60 | EXPECT_TRUE(event3 == nullptr); 61 | } 62 | 63 | TEST(ArmEventsTest, AddArmEvents) { 64 | neoverse_v2::setTestRootDir(getenv(kTestRootEnvKey.c_str())); 65 | 66 | auto cpu_info = CpuInfo::load(); 67 | cpu_info.cpu_arch = CpuArch::UNKNOWN; 68 | 69 | auto pmu_manager = default_pmu_manager(cpu_info); 70 | 71 | addArmEvents(cpu_info, pmu_manager); 72 | 73 | auto pmu = pmu_manager.getPmuGroups().at(PmuType::armv8_pmuv3); 74 | EXPECT_EQ(pmu.at(kDeviceEnum)->getEventDefs().size(), 0); 75 | } 76 | -------------------------------------------------------------------------------- /hbt/src/perf_event/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | 3 | add_compile_options("-Wconversion") 4 | 5 | include_directories(${PROJECT_SOURCE_DIR}/third_party/googletest/googletest/include) 6 | 7 | add_executable(PerfEventsGroupTest PerfEventsGroupTest.cpp) 8 | target_link_libraries(PerfEventsGroupTest PRIVATE gtest gmock gtest_main) 9 | target_link_libraries(PerfEventsGroupTest PUBLIC stdc++fs) 10 | target_link_libraries(PerfEventsGroupTest PUBLIC PerfEventsGroup) 11 | 12 | # add_executable(PmuDevicesTest PmuDevicesTest.cpp) 13 | # target_link_libraries(PmuDevicesTest PRIVATE gtest gmock gtest_main) 14 | # target_link_libraries(PmuDevicesTest PUBLIC PerCpuCountSampleGenerator) 15 | # target_link_libraries(PmuDevicesTest PUBLIC PmuDevices) 16 | 17 | # add_executable(MetricsTest BuiltinMetricsTest.cpp) 18 | # target_link_libraries(MetricsTest PRIVATE gtest gmock gtest_main) 19 | # target_link_libraries(MetricsTest BuiltinMetrics) 20 | # target_link_libraries(MetricsTest PerCpuCountSampleGenerator) 21 | 22 | # add_executable(ClocksTest ClocksTest.cpp) 23 | # target_link_libraries(ClocksTest PRIVATE gtest gmock gtest_main) 24 | # target_link_libraries(ClocksTest PUBLIC BuiltinMetrics) 25 | # target_link_libraries(ClocksTest PUBLIC PerCpuCountSampleGenerator) 26 | # target_link_libraries(ClocksTest PUBLIC PerCpuThreadSwitchGenerator) 27 | # target_link_libraries(ClocksTest PUBLIC RingBuffer) 28 | -------------------------------------------------------------------------------- /hbt/src/perf_event/tests/root_files/sys/bus/event_source/devices/armv8_pmuv3_0/events/bad_event_config: -------------------------------------------------------------------------------- 1 | fail 2 | -------------------------------------------------------------------------------- /hbt/src/perf_event/tests/root_files/sys/bus/event_source/devices/armv8_pmuv3_0/events/bad_event_field: -------------------------------------------------------------------------------- 1 | event=0x0,bad_field=0x0 2 | -------------------------------------------------------------------------------- /hbt/src/perf_event/tests/root_files/sys/bus/event_source/devices/armv8_pmuv3_0/events/cpu_cycles: -------------------------------------------------------------------------------- 1 | event=0x0011 2 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/Consumer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/ringbuffer/benchmarks/Data.hpp" 9 | 10 | #include 11 | #include 12 | 13 | // Consumer class is a functor which reads from readable endpoint represented 14 | // by TReadableEntPoint type. The endpoint is constructable form TQueuePtr 15 | // which supposed to be C++ shared pointer pointing to a queue serving 16 | // as a media for data transfer between endpoints. 17 | // 18 | // Consumer reads and count data tokens until 19 | // certain amount of termination tokens were met. 20 | // The amount of termination tokens required to be met before functor exit is 21 | // passed to ctor as initialTermCnt parameter. 22 | // 23 | // The amount of data tokens reads bu consumer is available 24 | // through getReadCount() function. 25 | 26 | namespace { 27 | 28 | template 29 | class Consumer { 30 | private: 31 | using TQueuePtr = typename TReadableEndPoint::TQueuePtr; 32 | TReadableEndPoint endPoint; 33 | size_t initialTermCnt; 34 | size_t readCount = 0; 35 | 36 | protected: 37 | void read(T& value) { 38 | while (!endPoint.tryRead(value)) 39 | ; 40 | } 41 | 42 | public: 43 | Consumer(TQueuePtr& queuePtr, size_t initialTermCnt = 1) 44 | : endPoint(queuePtr), initialTermCnt(initialTermCnt) {} 45 | Consumer(const Consumer&) = delete; 46 | Consumer(Consumer&&) = default; 47 | Consumer& operator=(Consumer&&) = default; 48 | Consumer& operator=(const Consumer&) = delete; 49 | 50 | void operator()() { 51 | size_t termCnt = initialTermCnt; 52 | readCount = 0; 53 | while (true) { 54 | T value; 55 | read(value); 56 | if (!DataTraits::isTerminationToken(value)) { 57 | ++readCount; 58 | continue; 59 | } 60 | if (--termCnt == 0) { 61 | break; 62 | } 63 | } 64 | } 65 | 66 | size_t getReadCount() const { 67 | return readCount; 68 | } 69 | }; 70 | 71 | } // end of namespace 72 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/Data.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | 11 | namespace { 12 | 13 | struct POD16 { 14 | int32_t a; 15 | int32_t b; 16 | int32_t c; 17 | int32_t d; 18 | }; 19 | 20 | static_assert(std::is_pod::value, "POD16 is not a POD."); 21 | static_assert(sizeof(POD16) == 16, "Size of POD16 is not 16 bytes"); 22 | 23 | template 24 | struct DataTraits; 25 | 26 | template <> 27 | struct DataTraits { 28 | static constexpr int data = 13; 29 | static constexpr int terminationToken = 0xFFFFFFFF; 30 | static bool isTerminationToken(int tok) { 31 | return tok == terminationToken; 32 | } 33 | }; 34 | 35 | template <> 36 | struct DataTraits { 37 | static constexpr POD16 data = {1, 2, 3, 4}; 38 | static constexpr POD16 terminationToken = {-1, -1, -1, -1}; 39 | static bool isTerminationToken(const POD16& tok) { 40 | return tok.a == terminationToken.a; 41 | } 42 | }; 43 | 44 | } // end of namespace 45 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/MPMCQueueBenchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/ringbuffer/benchmarks/Consumer.hpp" 7 | #include "hbt/src/ringbuffer/benchmarks/Data.hpp" 8 | #include "hbt/src/ringbuffer/benchmarks/MPMCBenchmark.hpp" 9 | #include "hbt/src/ringbuffer/benchmarks/MPMCQueueConsumerWrapper.hpp" 10 | #include "hbt/src/ringbuffer/benchmarks/MPMCQueueProducerWrapper.hpp" 11 | #include "hbt/src/ringbuffer/benchmarks/Producer.hpp" 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // Multi-producer Multi-consumer benchmark transferring data through 18 | // folly::MPMCQueue 19 | 20 | template 21 | using MPMCQueueBenchmark = MPMCBenchmark< 22 | T, 23 | MPMCQueueProducerWrapper, 24 | MPMCQueueConsumerWrapper, 25 | folly::MPMCQueue>; 26 | 27 | BENCHMARK(MPMCQueue_1P1C_int_1K_1M) { 28 | MPMCQueueBenchmark(1024).run(1024 * 1024, 1, 1); 29 | } 30 | 31 | BENCHMARK(MPMCQueue_2P13C_int_1K_1M) { 32 | MPMCQueueBenchmark(1024).run(1024 * 1024, 2, 13); 33 | } 34 | 35 | BENCHMARK(MPMCQueue_17P3C_POD16_1K_1M) { 36 | MPMCQueueBenchmark(1024).run(1024 * 1024, 17, 3); 37 | } 38 | 39 | BENCHMARK(MPMCQueue_14P14C_POD16_1K_1M) { 40 | MPMCQueueBenchmark(1024).run(1024 * 1024, 14, 14); 41 | } 42 | 43 | BENCHMARK(MPMCQueue_14P14C_POD16_1K_1M_CPU_13_13) { 44 | MPMCQueueBenchmark(1024) 45 | .setProducerCpuSet({13}) 46 | .setConsumerCpuSet({13}) 47 | .run(1024 * 1024, 14, 14); 48 | } 49 | 50 | BENCHMARK(MPMCQueue_14P14C_POD16_1K_1M_CPU_0_7) { 51 | MPMCQueueBenchmark(1024) 52 | .setProducerCpuSet({0}) 53 | .setConsumerCpuSet({7}) 54 | .run(1024 * 1024, 14, 14); 55 | } 56 | 57 | // Boilerplate 58 | int main(int argc, char** argv) { 59 | facebook::initFacebook(&argc, &argv); 60 | folly::runBenchmarks(); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/MPMCQueueConsumerWrapper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | // MPMCQueueConsumerWrapper is a readable end point reading data from 11 | // folly::MPMCQueue. 12 | 13 | namespace { 14 | 15 | template 16 | class MPMCQueueConsumerWrapper { 17 | public: 18 | using TQueue = folly::MPMCQueue; 19 | using TQueuePtr = std::shared_ptr; 20 | 21 | private: 22 | TQueuePtr queuePtr; 23 | 24 | public: 25 | MPMCQueueConsumerWrapper(TQueuePtr qPtr) : queuePtr(qPtr) {} 26 | MPMCQueueConsumerWrapper(const MPMCQueueConsumerWrapper&) = delete; 27 | MPMCQueueConsumerWrapper(MPMCQueueConsumerWrapper&&) = default; 28 | MPMCQueueConsumerWrapper& operator=(MPMCQueueConsumerWrapper&&) = default; 29 | MPMCQueueConsumerWrapper& operator=(const MPMCQueueConsumerWrapper&) = 30 | default; 31 | 32 | size_t tryRead(T& data) { 33 | return queuePtr->read(data); 34 | } 35 | }; 36 | 37 | } // end of namespace 38 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/MPMCQueueProducerWrapper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | // MPMCQueueProducerWrapper is writable end point writing data 11 | // to folly::MPMCQueue. 12 | 13 | namespace { 14 | 15 | template 16 | class MPMCQueueProducerWrapper { 17 | public: 18 | using TQueue = folly::MPMCQueue; 19 | using TQueuePtr = std::shared_ptr; 20 | 21 | private: 22 | TQueuePtr queuePtr; 23 | 24 | public: 25 | MPMCQueueProducerWrapper(TQueuePtr qPtr) : queuePtr(qPtr) {} 26 | MPMCQueueProducerWrapper(const MPMCQueueProducerWrapper&) = delete; 27 | MPMCQueueProducerWrapper(MPMCQueueProducerWrapper&&) = default; 28 | MPMCQueueProducerWrapper& operator=(MPMCQueueProducerWrapper&&) = default; 29 | MPMCQueueProducerWrapper& operator=(const MPMCQueueProducerWrapper&) = 30 | default; 31 | 32 | size_t tryWrite(const T& data) { 33 | return queuePtr->write(data); 34 | } 35 | }; 36 | 37 | } // end of namespace 38 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/MPMCRingBufferBenchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/ringbuffer/RingBuffer.h" 7 | #include "hbt/src/ringbuffer/benchmarks/Data.hpp" 8 | #include "hbt/src/ringbuffer/benchmarks/MPMCBenchmark.hpp" 9 | #include "hbt/src/ringbuffer/benchmarks/RingBufferConsumerWrapper.hpp" 10 | #include "hbt/src/ringbuffer/benchmarks/RingBufferProducerWrapper.hpp" 11 | 12 | #include 13 | #include 14 | 15 | template 16 | using MPMCRingBufferBenchmark = MPMCBenchmark< 17 | T, 18 | RingBufferProducerWrapper, 19 | RingBufferConsumerWrapper, 20 | facebook::hbt::ringbuffer::RingBuffer>; 21 | 22 | BENCHMARK(MPMCRingBuffer_1P1C_int_1K_1M) { 23 | MPMCRingBufferBenchmark(1024).run(1024 * 1024, 1, 1); 24 | } 25 | 26 | BENCHMARK(MPMCRingBuffer_2P13C_int_1K_1M) { 27 | MPMCRingBufferBenchmark(1024).run(1024 * 1024, 2, 13); 28 | } 29 | 30 | BENCHMARK(MPMCRingBuffer_17P3C_POD16_1K_1M) { 31 | MPMCRingBufferBenchmark(1024).run(1024 * 1024, 17, 3); 32 | } 33 | 34 | BENCHMARK(MPMCRingBuffer_14P14C_POD16_1K_1M) { 35 | MPMCRingBufferBenchmark(1024).run(1024 * 1024, 14, 14); 36 | } 37 | 38 | // Boilerplate 39 | int main(int argc, char** argv) { 40 | facebook::initFacebook(&argc, &argv); 41 | folly::runBenchmarks(); 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/Producer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/ringbuffer/benchmarks/Data.hpp" 9 | 10 | // The Producer sends to a writable end point initialDataCnt elements 11 | // representing data tokens and initialTermCnt elements representing 12 | // termination tokens. 13 | // Data termination token signals a Consumer that data transfer has finished. 14 | // Multiple termination tokens are needed for the case imbalanced amount 15 | // of producers vs. consumers. 16 | 17 | namespace { 18 | 19 | template 20 | class Producer { 21 | private: 22 | // using TWritableEndPointTraits = WritableEndPointTraits>; 24 | // using TWritableEndPointPtr = typename std::shared_ptr; 25 | using TQueuePtr = typename TWritableEndPoint::TQueuePtr; 26 | TWritableEndPoint endPoint; 27 | size_t initialDataCnt; 28 | size_t initialTermCnt; 29 | 30 | protected: 31 | void write(const T& data) { 32 | while (!endPoint.tryWrite(data)) 33 | ; 34 | } 35 | 36 | public: 37 | Producer( 38 | TQueuePtr& queuePtr, 39 | size_t initialDataCnt, 40 | size_t initialTermCnt = 1) 41 | : endPoint(queuePtr), 42 | initialDataCnt(initialDataCnt), 43 | initialTermCnt(initialTermCnt) {} 44 | 45 | Producer(const Producer&) = delete; 46 | Producer(Producer&&) = default; 47 | Producer& operator=(Producer&&) = default; 48 | Producer& operator=(const Producer&) = delete; 49 | 50 | void operator()() { 51 | size_t dataCnt = initialDataCnt; 52 | size_t termCnt = initialTermCnt; 53 | 54 | while (dataCnt) { 55 | write(DataTraits::data); 56 | --dataCnt; 57 | } 58 | while (termCnt) { 59 | write(DataTraits::terminationToken); 60 | --termCnt; 61 | } 62 | } 63 | }; 64 | 65 | } // end of namespace 66 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/RingBufferConsumerWrapper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/ringbuffer/Consumer.h" 9 | #include "hbt/src/ringbuffer/RingBuffer.h" 10 | 11 | #include 12 | 13 | // RingBufferConsumerWrapper is a readable endpoint reading from 14 | // facebook::hbt::ringbuffer::RingBuffer 15 | 16 | namespace { 17 | 18 | template 19 | class RingBufferConsumerWrapper 20 | : public facebook::hbt::ringbuffer::Consumer { 21 | using TParent = facebook::hbt::ringbuffer::Consumer; 22 | 23 | public: 24 | using TQueue = facebook::hbt::ringbuffer::RingBuffer; 25 | using TQueuePtr = std::shared_ptr; 26 | 27 | RingBufferConsumerWrapper(TQueuePtr qPtr) : TParent(qPtr) {} 28 | RingBufferConsumerWrapper(const RingBufferConsumerWrapper&) = delete; 29 | RingBufferConsumerWrapper(RingBufferConsumerWrapper&&) = default; 30 | RingBufferConsumerWrapper& operator=(RingBufferConsumerWrapper&&) = default; 31 | RingBufferConsumerWrapper& operator=(const RingBufferConsumerWrapper&) = 32 | delete; 33 | 34 | size_t tryRead(T& data) { 35 | // Ring buffer encodes errors in the upper part of numeric range of size_t. 36 | // In a case of an error zero is returned since none of data were red. 37 | // This lets Consumer to reiterate read cycle. 38 | size_t rc = this->template copy(data); 39 | if (rc >= std::numeric_limits::max() - 1024) 40 | return 0; 41 | return rc; 42 | } 43 | }; 44 | 45 | } // end of namespace 46 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/RingBufferProducerWrapper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/ringbuffer/Producer.h" 9 | #include "hbt/src/ringbuffer/RingBuffer.h" 10 | 11 | #include 12 | 13 | // RingBufferProducerWrapper is writable end point writing to 14 | // facebook::hbt::ringbuffer::RingBuffer 15 | 16 | namespace { 17 | 18 | template 19 | class RingBufferProducerWrapper 20 | : public facebook::hbt::ringbuffer::Producer { 21 | using TParent = facebook::hbt::ringbuffer::Producer; 22 | 23 | public: 24 | using TQueue = facebook::hbt::ringbuffer::RingBuffer; 25 | using TQueuePtr = std::shared_ptr; 26 | 27 | RingBufferProducerWrapper(TQueuePtr qPtr) : TParent(qPtr) {} 28 | RingBufferProducerWrapper(const RingBufferProducerWrapper&) = delete; 29 | RingBufferProducerWrapper(RingBufferProducerWrapper&&) = default; 30 | RingBufferProducerWrapper& operator=(RingBufferProducerWrapper&&) = default; 31 | RingBufferProducerWrapper& operator=(const RingBufferProducerWrapper&) = 32 | default; 33 | 34 | size_t tryWrite(const T& data) { 35 | // Ring buffer encodes errors in the upper part of numeric range of size_t. 36 | // In a case of an error zero is returned since none of data were written. 37 | // This let Producer to reiterate write cycle. 38 | size_t rc = this->template write(data); 39 | if (rc >= std::numeric_limits::max() - 1024) 40 | return 0; 41 | return rc; 42 | } 43 | }; 44 | 45 | } // end of namespace 46 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/SPSCQueueBenchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/ringbuffer/benchmarks/SPSCBenchmark.hpp" 7 | #include "hbt/src/ringbuffer/benchmarks/SPSCQueueConsumerWrapper.hpp" 8 | #include "hbt/src/ringbuffer/benchmarks/SPSCQueueProducerWrapper.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // Single producer single consumer benchmark transferring data through 15 | // folly::ProducerConsumerQueue 16 | 17 | template 18 | using SPSCQueueBenchmark = SPSCBenchmark< 19 | T, 20 | SPSCQueueProducerWrapper, 21 | SPSCQueueConsumerWrapper, 22 | folly::ProducerConsumerQueue>; 23 | 24 | BENCHMARK(SPSCQueue_int_1K_1M) { 25 | SPSCQueueBenchmark(1024).run(1024 * 1024); 26 | } 27 | 28 | BENCHMARK(SPSCQueue_POD16_1K_1M) { 29 | SPSCQueueBenchmark(1024).run(1024 * 1024); 30 | } 31 | 32 | BENCHMARK(SPSCQueue_int_64K_32M) { 33 | SPSCQueueBenchmark(64 * 1024).run(64 * 1024 * 1024); 34 | } 35 | 36 | BENCHMARK(SPSCQueue_POD16_64K_32M) { 37 | SPSCQueueBenchmark(64 * 1024).run(64 * 1024 * 1024); 38 | } 39 | 40 | // Boilerplate.... 41 | int main(int argc, char** argv) { 42 | facebook::initFacebook(&argc, &argv); 43 | folly::runBenchmarks(); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/SPSCQueueConsumerWrapper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | // SPSCQueueConsumerWrapper is a readable end point reading data from 11 | // folly::ProducerConsumerQueue. 12 | 13 | namespace { 14 | 15 | template 16 | class SPSCQueueConsumerWrapper { 17 | public: 18 | using TQueue = folly::ProducerConsumerQueue; 19 | using TQueuePtr = std::shared_ptr; 20 | 21 | private: 22 | TQueuePtr queuePtr; 23 | 24 | public: 25 | SPSCQueueConsumerWrapper(TQueuePtr qPtr) : queuePtr(qPtr) {} 26 | SPSCQueueConsumerWrapper(const SPSCQueueConsumerWrapper&) = delete; 27 | SPSCQueueConsumerWrapper(SPSCQueueConsumerWrapper&&) = default; 28 | SPSCQueueConsumerWrapper& operator=(SPSCQueueConsumerWrapper&&) = default; 29 | SPSCQueueConsumerWrapper& operator=(const SPSCQueueConsumerWrapper&) = 30 | default; 31 | 32 | size_t tryRead(T& data) { 33 | return queuePtr->read(data); 34 | } 35 | }; 36 | 37 | } // end of namespace 38 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/SPSCQueueProducerWrapper.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | // SPSCQueueProducerWrapper is writable end point writing data 11 | // to folly::ProducerConsumerQueue. 12 | 13 | namespace { 14 | 15 | template 16 | class SPSCQueueProducerWrapper { 17 | public: 18 | using TQueue = folly::ProducerConsumerQueue; 19 | using TQueuePtr = std::shared_ptr; 20 | 21 | private: 22 | TQueuePtr queuePtr; 23 | 24 | public: 25 | SPSCQueueProducerWrapper(TQueuePtr qPtr) : queuePtr(qPtr) {} 26 | SPSCQueueProducerWrapper(const SPSCQueueProducerWrapper&) = delete; 27 | SPSCQueueProducerWrapper(SPSCQueueProducerWrapper&&) = default; 28 | SPSCQueueProducerWrapper& operator=(SPSCQueueProducerWrapper&&) = default; 29 | SPSCQueueProducerWrapper& operator=(const SPSCQueueProducerWrapper&) = 30 | default; 31 | 32 | size_t tryWrite(const T& data) { 33 | return queuePtr->write(data); 34 | } 35 | }; 36 | 37 | } // end of namespace 38 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/SPSCRingBufferBenchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/ringbuffer/benchmarks/RingBufferConsumerWrapper.hpp" 7 | #include "hbt/src/ringbuffer/benchmarks/RingBufferProducerWrapper.hpp" 8 | #include "hbt/src/ringbuffer/benchmarks/SPSCBenchmark.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // Single producer single consumer benchmark transferring data through 15 | // HBT/RingBuffer 16 | 17 | template 18 | using SPSCRingBufferBenchmark = SPSCBenchmark< 19 | T, 20 | RingBufferProducerWrapper, 21 | RingBufferConsumerWrapper, 22 | facebook::hbt::ringbuffer::RingBuffer>; 23 | 24 | BENCHMARK(SPSCRingBuffer_int_1K_1M) { 25 | SPSCRingBufferBenchmark(1024).run(1024 * 1024); 26 | } 27 | 28 | BENCHMARK(SPSCRingBuffer_POD16_1K_1M) { 29 | SPSCRingBufferBenchmark(1024).run(1024 * 1024); 30 | } 31 | 32 | BENCHMARK(SPSCRingBuffer_int_64K_32M) { 33 | SPSCRingBufferBenchmark(64 * 1024).run(64 * 1024 * 1024); 34 | } 35 | 36 | BENCHMARK(SPSCRingBuffer_POD16_64K_32M) { 37 | SPSCRingBufferBenchmark(64 * 1024).run(64 * 1024 * 1024); 38 | } 39 | 40 | // Boilerplate.... 41 | int main(int argc, char** argv) { 42 | facebook::initFacebook(&argc, &argv); 43 | folly::runBenchmarks(); 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/ThreadBind.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/common/System.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace { 14 | 15 | inline void threadBind( 16 | std::thread& thread, 17 | const facebook::hbt::CpuSet& cpuSet) { 18 | pthread_setaffinity_np( 19 | thread.native_handle(), sizeof(cpu_set_t), &cpuSet.cpu_set); 20 | } 21 | 22 | } // end of namespace 23 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/benchmarks/TriggerableThread.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // TriggerableTread is a wrapper around thread class and its main idea is to 13 | // make constructed threads to wait for a certain event. In the beginning a 14 | // bunch of TriggerableThread objects are constructed referencing to the 15 | // same std::atomic& mentioned as the latch and pre-initialized to zero. 16 | // All the threads are waiting when latch value changed to non-zero and start 17 | // execution of worker function. 18 | 19 | namespace { 20 | 21 | class TriggerableThread : public std::thread { 22 | public: 23 | using TLatchPtr = std::shared_ptr>; 24 | 25 | public: 26 | template 27 | TriggerableThread(TLatchPtr latch, Fn&& fn, Args&&... args) 28 | : std::thread([=]() { 29 | while (!latch->load()) { 30 | } 31 | fn(&args...); 32 | }) {} 33 | 34 | TriggerableThread(const TriggerableThread&) = delete; 35 | TriggerableThread(TriggerableThread&&) = default; 36 | TriggerableThread& operator=(TriggerableThread&&) = default; 37 | TriggerableThread& operator=(const TriggerableThread&) = delete; 38 | }; 39 | 40 | } // end of namespace 41 | -------------------------------------------------------------------------------- /hbt/src/ringbuffer/tests/PerCpuRingBufferTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #include "hbt/src/ringbuffer/PerCpuRingBuffer.h" 7 | 8 | #include 9 | 10 | using namespace facebook::hbt::ringbuffer; 11 | 12 | struct CustomExtraData { 13 | int my_int = 0; 14 | }; 15 | 16 | TEST(PerCpuRingBuffer, ReadWriteTest) { 17 | using TProducer = Producer; 18 | using TConsumer = Consumer; 19 | 20 | // Do not explicitly pass CPUs to get all online. 0, and 1 must be available. 21 | auto rb = 22 | std::make_shared>(1024, std::nullopt); 23 | EXPECT_TRUE(rb->getCpuSet().hasCpu(0)); 24 | EXPECT_TRUE(rb->getCpuSet().hasCpu(1)); 25 | 26 | // Create producer and consumer. 27 | PerCpuRingBufferHelper p{rb}; 28 | PerCpuRingBufferHelper c{rb}; 29 | 30 | // Check extra data is accessible. 31 | EXPECT_EQ(rb->getCpuRingBuffer(0)->getExtraData().my_int, 0); 32 | rb->getCpuRingBuffer(0)->getExtraData().my_int = 0xFFFF; 33 | EXPECT_EQ(rb->getCpuRingBuffer(0)->getExtraData().my_int, 0xFFFF); 34 | 35 | EXPECT_EQ(p.atCpu(0).getRingBuffer()->getExtraData().my_int, 0xFFFF); 36 | 37 | // Write at CPU 0. 38 | { 39 | auto ret = p.atCpu(0).template writeSizedChunk("hola"); 40 | EXPECT_EQ(ret, sizeof(uint16_t) + 4); 41 | } 42 | 43 | // Read in different CPU than the one written on. 44 | { 45 | auto [ret, opt_str] = c.atCpu(1).template readSizedChunk(); 46 | EXPECT_EQ(ret, -ENODATA); 47 | EXPECT_FALSE(opt_str.has_value()); 48 | } 49 | 50 | // Read in same CPU as was written. 51 | { 52 | auto [ret, opt_str] = c.atCpu(0).template readSizedChunk(); 53 | EXPECT_EQ(ret, sizeof(uint16_t) + 4); 54 | EXPECT_TRUE(opt_str.has_value()); 55 | EXPECT_EQ(*opt_str, "hola"); 56 | } 57 | 58 | // Read in same CPU as was written but find no data. 59 | { 60 | auto [ret, opt_str] = c.atCpu(0).template readSizedChunk(); 61 | EXPECT_EQ(ret, -ENODATA); 62 | EXPECT_FALSE(opt_str.has_value()); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /hbt/src/tagstack/PerfEventStream.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/perf_event/PerfEventsGroup.h" 9 | #include "hbt/src/ringbuffer/Consumer.h" 10 | #include "hbt/src/tagstack/Event.h" 11 | 12 | namespace facebook::hbt::tagstack::stream { 13 | 14 | template 15 | class PerfEventStream { 16 | using TRingBuffer = ringbuffer::RingBuffer; 17 | using TRingBufferStream = RingBufferStream; 18 | 19 | public: 20 | PerfEventStream(const PerfEventStream&) = delete; 21 | PerfEventStream(PerfEventStream&&) = delete; 22 | 23 | PerfEventStream( 24 | std::shared_ptr rb, 25 | std::shared_ptr events_gen) 26 | : rb_{rb}, 27 | rb_stream_{rb_}, 28 | kBatchSize_{static_cast( 29 | rb->getHeader().kDataPoolByteSize / sizeof(Event))}, 30 | events_gen_{events_gen} {} 31 | 32 | // XXX: Articulate the stream API. 33 | inline const Event* prepareNext(TimeStamp stop_ts) noexcept { 34 | if (__hbt_unlikely(rb_->getHeader().usedSizeWeak() == 0)) { 35 | ssize_t ret = events_gen_->consume(kBatchSize_); 36 | HBT_LOG_ERROR_IF(0 > ret && ret != -ENODATA && ret != -ENOSPC) 37 | << "Unexpected error consuming perf_event ringbuffer data. " 38 | << "Error: " << toErrorCode(-ret); 39 | } 40 | 41 | return rb_stream_.prepareNext(stop_ts); 42 | } 43 | 44 | void complete() noexcept { 45 | rb_stream_.complete(); 46 | } 47 | 48 | protected: 49 | std::shared_ptr rb_; 50 | TRingBufferStream rb_stream_; 51 | const unsigned kBatchSize_; 52 | std::shared_ptr events_gen_; 53 | }; 54 | 55 | } // namespace facebook::hbt::tagstack::stream 56 | -------------------------------------------------------------------------------- /hbt/src/tagstack/TscConverterStream.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/common/System.h" 9 | #include "hbt/src/tagstack/Event.h" 10 | 11 | namespace facebook::hbt::tagstack::stream { 12 | 13 | /// Wraps a ringbuffer Consumer whose RingBuffer's ExtraData 14 | /// has an ErrorCtxt. The Consumer behaves like a stream that 15 | /// generates WriteError Events when ringbuffer is in error. 16 | // 17 | /// If is true, it also converts timestamps from TSC to 18 | /// perf_event kernel clock (nanoseconds from boot) by using the sched_clock 19 | /// conversion variables exposed in perf_event mmap's header page. 20 | /// 21 | /// If the RingBuffer is in error, use the number of errors to 22 | /// produce WriteError events. 23 | /// 24 | /// Note: This class uses rdtscp to obtain times. The template 25 | /// parameter kTscConversion indicates if it would be converted 26 | /// to system time to be used together with perf_event 27 | /// events or another data with different time source. 28 | template 29 | class TscConverterStream { 30 | public: 31 | using TRingBufferStream = RingBufferStream; 32 | using TRingBuffer = typename TRingBufferStream::TRingBuffer; 33 | 34 | TscConverterStream(const TscConverterStream&) = delete; 35 | TscConverterStream(TscConverterStream&&) = delete; 36 | 37 | TscConverterStream( 38 | std::shared_ptr rb, 39 | std::shared_ptr tsc_converter) 40 | : rb_stream_{rb}, tsc_converter_{tsc_converter} { 41 | HBT_DCHECK(rb != nullptr); 42 | HBT_DCHECK(tsc_converter != nullptr); 43 | } 44 | 45 | inline const Event* prepareNext(TimeStamp stop_ts) { 46 | if (aux_.tstamp != kInvalidTimeStamp && aux_.tstamp <= stop_ts) { 47 | return &aux_; 48 | } 49 | // The ringbuffer's timestamp is in TSC time, so always 50 | // get the event, convert timestamp and then enforce stop_ts. 51 | const auto* ev_ptr = rb_stream_.prepareNext(kMaxTimeStamp); 52 | if (ev_ptr == nullptr) { 53 | return nullptr; 54 | } 55 | aux_ = *ev_ptr; 56 | rb_stream_.complete(); 57 | aux_.tstamp = tsc_converter_->kernelTimeFromTsc(aux_.tstamp); 58 | 59 | return ((aux_.tstamp <= stop_ts) ? &aux_ : nullptr); 60 | } 61 | 62 | inline void complete() DEXCEPT { 63 | HBT_DCHECK_NE(aux_.tstamp, kInvalidTimeStamp); 64 | aux_.tstamp = kInvalidTimeStamp; 65 | } 66 | 67 | protected: 68 | TRingBufferStream rb_stream_; 69 | std::shared_ptr tsc_converter_; 70 | tagstack::Event aux_ = {.tstamp = kInvalidTimeStamp}; 71 | }; 72 | 73 | } // namespace facebook::hbt::tagstack::stream 74 | -------------------------------------------------------------------------------- /hbt/src/utils/ValueTimeSeries.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Meta Platforms, Inc. and affiliates. 2 | // 3 | // This source code is licensed under the MIT license found in the 4 | // LICENSE file in the root directory of this source tree. 5 | 6 | #pragma once 7 | 8 | #include "hbt/src/common/Defs.h" 9 | 10 | #include 11 | 12 | namespace facebook::hbt::utils::value_time_series { 13 | 14 | template 15 | struct Point { 16 | TimeStamp tstamp; 17 | T value; 18 | 19 | Point(TimeStamp tstamp, const T& value) : tstamp{tstamp}, value{value} {} 20 | 21 | Point(TimeStamp tstamp, T&& value) : tstamp{tstamp}, value{value} {} 22 | }; 23 | 24 | template 25 | bool operator<(const Point& a, const Point& b) { 26 | return a.tstamp < b.tstamp; 27 | } 28 | 29 | // XXX: Underlying vector is good for small series. Do we need a map instead? 30 | template 31 | class Series { 32 | public: 33 | using TPoint = Point; 34 | using TPoints = std::vector; 35 | 36 | Series() {} 37 | 38 | // Constructor for serialized data. 39 | Series(TPoints&& ps) : points_(std::move(ps)) {} 40 | 41 | Series(TPoint&& p) : points_{std::move(p)} {} 42 | 43 | /// Insert value and sorts the series by timestamp, if needed. 44 | // XXX: This is hacky and simple. Optimized for small number of elements. 45 | // Check if something more complex is needing. 46 | void emplace(TimeStamp tstamp, T&& value) { 47 | bool need_sort = points_.size() > 0 && points_.back().tstamp > tstamp; 48 | points_.emplace_back(tstamp, value); 49 | if (need_sort) { 50 | std::sort(points_.begin(), points_.end()); 51 | } 52 | } 53 | 54 | /// Last value at or before . 55 | const T* valueAt(TimeStamp tstamp) const { 56 | const T* p = nullptr; 57 | TimeStamp last_tstamp = std::numeric_limits::min(); 58 | 59 | for (const auto& next_p : points_) { 60 | if (next_p.tstamp > tstamp) 61 | break; 62 | HBT_DCHECK_LE(last_tstamp, next_p.tstamp); 63 | p = &next_p.value; 64 | last_tstamp = next_p.tstamp; 65 | } 66 | return p; 67 | } 68 | 69 | const T* last() const { 70 | return valueAt(kMaxTimeStamp); 71 | } 72 | 73 | size_t size() const { 74 | return points_.size(); 75 | } 76 | 77 | const auto& getPoints() const { 78 | return points_; 79 | } 80 | 81 | protected: 82 | TPoints points_; 83 | }; 84 | 85 | template 86 | inline std::ostream& operator<<(std::ostream& os, const Series& s) { 87 | for (const auto& p : s.getPoints()) { 88 | os << " 5 | # Scripts 6 | 7 | This directory contains the following script utilities - 8 | 1. Basic build script. 9 | 2. Debian and Red Hat Package (RPM) build scripts. 10 | 11 | Please run all scripts from the root of the project as they 12 | may refer to other scripts using a relative path. 13 | 14 | # Basic build script 15 | 16 | Please refer to the top level README file for requirements for building from source. 17 | To run the build use - 18 | ``` 19 | ./scripts/build.sh 20 | ``` 21 | This should setup a build/ directory, run cmake and build steps. The last line 22 | of the output will point to generated binaries for the server (dynolog) and the 23 | command line tool (dyno). 24 | 25 | # Packaging and deployment 26 | 27 | In order to deploy dynolog we support two popular package formats: RPM and Debian. 28 | Note: these currently do require installation using root due the use of systemd but we plan 29 | to enable a user mode package in the future. 30 | 31 | ## Packaging using Debian 32 | 33 | The Debian or dpkg format is supported by Debian based distributions of Linux 34 | such as Ubuntu. 35 | 36 | Before beginning please ensure you have [dpkg-deb](https://manpages.ubuntu.com/manpages/trusty/man1/dpkg-deb.1.html) on your system. This install only works on Debian based Linux distros. 37 | You will also need all prequisites for building dynolog mentioned in top-level README file. 38 | 39 | Run the script to build dynolog/cli and package them. 40 | ```bash 41 | ./scripts/debian/make_deb.sh 42 | ``` 43 | Your package will be generated in the dynolog root directory at 44 | `dynolog_-.deb` such as `dynolog_0.0.1-0-amd64.deb`. 45 | 46 | To install the package use the dpkg command 47 | ```bash 48 | sudo dpkg -i 49 | ``` 50 | 51 | ## Packaging using RPM 52 | 53 | The Red Hat Package Manager (RPM) format can be used on CentoOS based linux distros. 54 | These steps will need a CentOS based system. Also, the script only generates x86 55 | packages at the moment. 56 | 57 | Before beginning please ensure you have `rpmbuild` and `rpmlint` tools intalled. 58 | You will also need all prequisites for building dynolog mentioned in top-level README file. 59 | ```bash 60 | sudo dnf install -y rpmdevtools rpmlint 61 | ``` 62 | 63 | Run the script to build dynolog/cli and package them. 64 | ```bash 65 | ./scripts/rpm/make_rpm.sh 66 | ``` 67 | Your package will be generated in your home directory at `~/rpmbuild/`, The script generates 68 | the output path for the package. 69 | 70 | To install the RPM use the following command 71 | ```bash 72 | sudo rpm -i 73 | ``` 74 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | # Please run this script from the root level of the project. 8 | # ./scripts/build.sh 9 | 10 | set -eux -o pipefail 11 | BUILD_PROMETHEUS="${BUILD_PROMETHEUS:-0}" 12 | USE_PROMETHEUS="OFF" 13 | 14 | # Check dependencies 15 | cmake --version || echo "Please install cmake for your platform using dnf/apt-get etc." 16 | ninja --version || echo "Please install ninja for your platform using dnf/apt-get etc." 17 | rustc --version || echo "Please install Rust and Cargo - see https://www.rust-lang.org/tools/install" 18 | cargo --version || echo "Please install Rust and Cargo - see https://www.rust-lang.org/tools/install" 19 | 20 | ## Build Prometheus if enabled 21 | if [ "${BUILD_PROMETHEUS}" -eq 1 ] 22 | then 23 | mkdir -p ./third_party/prometheus-cpp/_build 24 | pushd ./third_party/prometheus-cpp/ 25 | 26 | git submodule init 27 | git submodule update 28 | 29 | cd ./_build 30 | cmake .. -DBUILD_SHARED_LIBS=ON -DENABLE_PUSH=OFF -DENABLE_COMPRESSION=OFF \ 31 | -DENABLE_TESTING=OFF 32 | cmake --build . --parallel 4 33 | cmake --install . 34 | 35 | USE_PROMETHEUS="ON" 36 | popd 37 | fi 38 | 39 | ## Build dynolog 40 | echo "Running cmake" 41 | mkdir -p build; cd build; 42 | 43 | # note we can build without ninja if not available on this system 44 | cmake "-DUSE_PROMETHEUS=${USE_PROMETHEUS}" \ 45 | -DCMAKE_BUILD_TYPE=Release -G Ninja "$@" .. 46 | cmake --build . 47 | 48 | mkdir -p bin 49 | ln -sf "$PWD/dynolog/src/dynolog" bin/dynolog 50 | ln -sf "$PWD/release/dyno" bin/dyno 51 | 52 | echo "Binary files =" 53 | echo "$PWD/dynolog/src/dynolog" "$PWD/release/dyno" 54 | -------------------------------------------------------------------------------- /scripts/debian/control: -------------------------------------------------------------------------------- 1 | Package: dynolog 2 | Version: __VERSION__ 3 | Architecture: amd64 4 | Homepage: https://github.com/facebookincubator/dynolog 5 | Maintainer: bcoutinho@meta.com 6 | Description: Dynolog is an observability daemon for system monitoring and profiling. 7 | * Dynolog support monitoring for heterogenous CPU/GPU platforms. 8 | * Integrates with pytorch and can trigger traces for distributed training applications. 9 | -------------------------------------------------------------------------------- /scripts/debian/make_deb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | # Please run this script from the root level of the project. 8 | # ./scripts/debian/make_deb.sh 9 | # The script build x86 deb packages by default, use 10 | # ARCH= to tweak this to build for other archs. 11 | 12 | set -eux -o pipefail 13 | 14 | VERSION=${VERSION:-"$(tr -d '\n' < version.txt)"} 15 | ARCH=${ARCH:-"amd64"} 16 | DEBDIR="dynolog_${VERSION}-0-${ARCH}" 17 | 18 | mkdir -p "$DEBDIR/usr/local/bin/" 19 | mkdir -p "$DEBDIR/usr/lib/systemd/system/" 20 | # control file goes here 21 | mkdir -p "$DEBDIR/DEBIAN" 22 | # logrotate config file goes here 23 | mkdir -p "$DEBDIR/etc/logrotate.d/" 24 | 25 | # Build the binaries 26 | IFS=" " read -r -a generated_bins <<< "$(./scripts/build.sh | tail -n 1)" 27 | 28 | cp -p "${generated_bins[@]}" "$DEBDIR/usr/local/bin" 29 | cp -p scripts/dynolog.service "$DEBDIR/usr/lib/systemd/system" 30 | cp -p scripts/pytorch/unitrace.py "$DEBDIR/usr/local/bin" 31 | cp -p scripts/dynolog.conf "$DEBDIR/etc/logrotate.d" 32 | perl -p -e "s/__VERSION__/$VERSION/" scripts/debian/control > "$DEBDIR/DEBIAN/control" 33 | 34 | tree "$DEBDIR" 35 | dpkg-deb --build --root-owner-group "$DEBDIR" 36 | -------------------------------------------------------------------------------- /scripts/dynolog.conf: -------------------------------------------------------------------------------- 1 | /var/log/dynolog.log { 2 | daily 3 | rotate 7 4 | size 10M 5 | dateext 6 | compress 7 | delaycompress 8 | } 9 | -------------------------------------------------------------------------------- /scripts/dynolog.service: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | [Unit] 7 | Description=Dynolog performance monitoring daemon 8 | Documentation=https://github.com/facebookincubator/dynolog 9 | 10 | [Service] 11 | ExecStartPre=/usr/bin/touch /etc/dynolog.gflags 12 | ExecStartPre=/usr/bin/touch /var/log/dynolog.log 13 | ExecStart=/usr/local/bin/dynolog --flagfile=/etc/dynolog.gflags 14 | 15 | StandardOutput=file:/var/log/dynolog.log 16 | StandardError=file:/var/log/dynolog.log 17 | 18 | # Syslog for versions of systemd pre 236 19 | # SyslogIdentifier=dynolog 20 | 21 | # Limit CPU usage to 1 CPU core 22 | CPUQuota=100% 23 | 24 | # Limit Memory usage to 1 GB, beyond that the process will be OOM killed 25 | MemoryMax=1G 26 | 27 | # Limit CPU usage to 1 CPU core 28 | CPUQuota=100% 29 | 30 | # Limit Memory usage to 1 GB, beyond that the process will be OOM killed 31 | MemoryMax=1G 32 | 33 | Restart=always 34 | 35 | [Install] 36 | WantedBy=multi-user.target 37 | -------------------------------------------------------------------------------- /scripts/pytorch/xor.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import torch.optim as optim 4 | 5 | 6 | class Xor(nn.Module): 7 | def __init__(self): 8 | super(Xor, self).__init__() 9 | self.fc1 = nn.Linear(2, 3, True) 10 | self.fc2 = nn.Linear(3, 1, True) 11 | 12 | def forward(self, x): 13 | x = self.fc1(x) 14 | x = torch.sigmoid(x) 15 | x = self.fc2(x) 16 | return x 17 | 18 | 19 | def train_xor(): 20 | xor = Xor() 21 | xor.to("cuda:0") 22 | inputs = torch.Tensor([[0, 0], [0, 1], [1, 0], [1, 1]]) 23 | targets = torch.Tensor([0, 1, 1, 0]).view(-1, 1) 24 | 25 | criterion = nn.MSELoss() 26 | optimizer = optim.SGD(xor.parameters(), lr=0.01) 27 | xor.train() 28 | 29 | for idx in range(0, 15001): 30 | 31 | for input, target in zip(inputs, targets): 32 | input, target = input.to(device="cuda:0"), target.to(device="cuda:0") 33 | optimizer.zero_grad() 34 | output = xor(input) 35 | 36 | loss = criterion(output, target) 37 | loss.backward() 38 | optimizer.step() 39 | 40 | if idx % 5000 == 0: 41 | print(f"Epoch {idx} Loss: {loss.data.cpu().numpy()}") 42 | 43 | 44 | train_xor() 45 | -------------------------------------------------------------------------------- /scripts/rpm/dynolog.spec: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | Name: dynolog 6 | Version: __VERSION__ 7 | Release: 1%{?dist} 8 | Summary: A system monitoring and profiling daemon. 9 | 10 | License: MIT 11 | URL: https://github.com/facebookincubator/dynolog 12 | Source0: %{name}-%{version}.tar.gz 13 | 14 | BuildRequires: systemd-rpm-macros 15 | 16 | %global debug_package %{nil} 17 | %define _mybindir /usr/local/bin 18 | 19 | %description 20 | Dynolog is an observability daemon for system monitoring and profiling. 21 | * It support telemetry on heterogenous CPU/GPU platforms. 22 | * Integrates with pytorch and can trigger traces for distributed training applications. 23 | 24 | %prep 25 | %autosetup 26 | 27 | %post 28 | %systemd_post dynolog.service 29 | 30 | %preun 31 | %systemd_preun dynolog.service 32 | 33 | %postun 34 | %systemd_postun_with_restart dynolog.service 35 | 36 | # perc build 37 | # perc configure 38 | # perc make_build 39 | 40 | %install 41 | rm -rf $RPM_BUILD_ROOT 42 | mkdir -p $RPM_BUILD_ROOT/%{_mybindir} 43 | mkdir -p $RPM_BUILD_ROOT/%{_unitdir} 44 | cp dynolog $RPM_BUILD_ROOT/%{_mybindir} 45 | cp dyno $RPM_BUILD_ROOT/%{_mybindir} 46 | cp unitrace.py $RPM_BUILD_ROOT/%{_mybindir} 47 | cp dynolog.service $RPM_BUILD_ROOT/%{_unitdir} 48 | 49 | %files 50 | # perc license add-license-file-here 51 | # perc doc add-docs-here 52 | %{_mybindir}/dynolog 53 | %{_mybindir}/dyno 54 | %{_mybindir}/unitrace.py 55 | %{_unitdir}/dynolog.service 56 | 57 | %changelog 58 | * Wed Jun 15 2022 briancoutinho added v0.0.1 with system perf monitoring capabilities. 59 | - 60 | -------------------------------------------------------------------------------- /scripts/rpm/make_rpm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | # Please run this script from the root level of the project. 8 | # ./scripts/rpm/make_rpm.sh 9 | # Currently the script only build x86 rpms. It should be easy 10 | # to tweak this to build for ARM etc. 11 | 12 | set -eux -o pipefail 13 | 14 | VERSION=${VERSION:-"$(tr -d '\n' < version.txt)"} 15 | DYNOROOT="$PWD" 16 | SPECBASE="$DYNOROOT/scripts/rpm/" 17 | SCRIPTS="$DYNOROOT/scripts/" 18 | 19 | # For some reason the RPM build tools only work with the directory 20 | # path at ~/rpmbuild. Just using this path for now. 21 | RPMDIR="$HOME/rpmbuild" 22 | 23 | # Check dependecies 24 | if ! command -v rpmbuild; 25 | then 26 | echo "Please install : sudo dnf install -y rpmdevtools rpmlint" 27 | exit 1 28 | fi 29 | if ! command -v rpmlint; 30 | then 31 | echo "Please install : sudo dnf install -y rpmdevtools rpmlint" 32 | exit 1 33 | fi 34 | 35 | rm -rf "$RPMDIR" 36 | mkdir "$RPMDIR" 37 | cd "$RPMDIR" 38 | 39 | mkdir BUILD RPMS SOURCES SPECS SRPMS 40 | 41 | # Setup RPM spec file with specified version 42 | perl -p -e "s/__VERSION__/$VERSION/" "$SPECBASE/dynolog.spec" > SPECS/dynolog.spec 43 | 44 | # Setup sources 45 | mkdir "dynolog-${VERSION}" 46 | 47 | # Build the binary 48 | cd - 49 | IFS=" " read -r -a generated_bins <<< "$(./scripts/build.sh | tail -n 1)" 50 | 51 | cd "$RPMDIR" 52 | 53 | cp "${generated_bins[@]}" "dynolog-${VERSION}/" 54 | 55 | # Add static files 56 | cp "$SCRIPTS/dynolog.service" "dynolog-${VERSION}/" 57 | cp "$SCRIPTS/pytorch/unitrace.py" "dynolog-${VERSION}/" 58 | cp "$SCRIPTS/dynolog.conf" "dynolog-${VERSION}/" 59 | 60 | # Compress sources 61 | tar --create --file "dynolog-${VERSION}.tar.gz" "dynolog-${VERSION}" 62 | 63 | mv "dynolog-${VERSION}.tar.gz" SOURCES 64 | 65 | # Sanity checks 66 | rpmlint SPECS/dynolog.spec 67 | tree . 68 | 69 | # Build src rpm 70 | rpmbuild -bs SPECS/dynolog.spec 71 | 72 | # Build binary rpm 73 | rpmbuild -bb SPECS/dynolog.spec 74 | 75 | # Test results 76 | tree . 77 | rpmfile=$(ls "$RPMDIR/RPMS/x86_64/dynolog"-*rpm) 78 | rpm -qi "$rpmfile" 79 | 80 | { 81 | echo 82 | echo "---------------------------------------------------------------" 83 | echo "RPM generated at $rpmfile" 84 | echo "To install you can use --nodeps flag " 85 | echo "rpm -i $rpmfile" 86 | echo "---------------------------------------------------------------" 87 | } 2> /dev/null 88 | -------------------------------------------------------------------------------- /scripts/slurm/run_with_dyno_wrapper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Meta Platforms, Inc. and affiliates. 3 | # 4 | # This source code is licensed under the MIT license found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | # Please run this script from the root level of the project. 8 | # ./scripts/slurm/run_with_dyno_wrapper.sh 9 | # 10 | # This script can be used to wrap a command launched on slurm. 11 | # It will spin up dynolog daemon in parallel before launching the full cmd. 12 | 13 | set -eux -o pipefail 14 | 15 | BASEPATH=/private/home/${USER}/ 16 | PROJECTROOT=${PROJECTROOT:-"."} 17 | DYNOBINPATH=${DYNOBINPATH:-"${PROJECTROOT}/build/dynolog/src/"} 18 | 19 | echo "Starting dynolog" 20 | "${DYNOBINPATH}/dynolog" --enable_ipc_monitor & 21 | 22 | dyno_pid=$! 23 | echo " dyno pid = ${dyno_pid}" 24 | sleep 2s; 25 | 26 | echo "Running command" 27 | export KINETO_CONFIG=${BASEPATH}/libkineto.conf 28 | export KINETO_USE_DAEMON=1 29 | "$@" 30 | 31 | echo "Cleaning up dynolog" 32 | kill -9 $dyno_pid 33 | -------------------------------------------------------------------------------- /testing/BuildTests.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # This source code is licensed under the MIT license found in the 4 | # LICENSE file in the root directory of this source tree. 5 | 6 | # Follow recipe here: https://cliutils.gitlab.io/modern-cmake/chapters/testing/googletest.html 7 | 8 | # Add this to top level CMake 9 | # add_subdirectory("${PROJECT_SOURCE_DIR}/third_party/googletest" "third_party/googletest") 10 | 11 | include(GoogleTest) 12 | macro(dynolog_add_test_base TESTNAME) 13 | # create an executable in which the tests will be stored 14 | add_executable(${TESTNAME} ${ARGN}) 15 | 16 | # link the Google test infrastructure, mocking library, and a default main function. 17 | target_link_libraries(${TESTNAME} PRIVATE gtest gmock gtest_main) 18 | 19 | # gtest_discover_tests replaces gtest_add_tests, 20 | # see https://cmake.org/cmake/help/v3.10/module/GoogleTest.html for more options to pass to it 21 | gtest_discover_tests(${TESTNAME} 22 | # set a working directory so your project root so that you can find test data via paths relative to the project root 23 | WORKING_DIRECTORY ${PROJECT_DIR} 24 | PROPERTIES ENVIRONMENT "TESTROOT=${PROJECT_SOURCE_DIR}/testing/root/" 25 | ) 26 | set_target_properties(${TESTNAME} PROPERTIES FOLDER tests) 27 | endmacro() 28 | 29 | macro(dynolog_add_test TESTNAME) 30 | dynolog_add_test_base(${TESTNAME} ${ARGN}) 31 | target_link_libraries(${TESTNAME} PRIVATE dynolog_lib) 32 | endmacro() 33 | -------------------------------------------------------------------------------- /testing/root/proc/net/dev: -------------------------------------------------------------------------------- 1 | Inter-| Receive | Transmit 2 | face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed 3 | eth0: 17566262828804 18353492234 0 6734908 1316 1316 0 30710543 4485442728479 11129776879 0 0 0 0 0 0 4 | eth1: 5651376349 3826963 0 0 0 0 0 452906 8626730 100254 0 0 0 0 0 0 5 | lo: 15218789826969 2393400479 0 0 0 0 0 0 15218789826969 2393400479 0 0 0 0 0 0 6 | -------------------------------------------------------------------------------- /testing/root/sys/class/net/eth0/speed: -------------------------------------------------------------------------------- 1 | 25000 2 | -------------------------------------------------------------------------------- /testing/root/sys/class/net/eth1/speed: -------------------------------------------------------------------------------- 1 | 40000 2 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 0.5.0 2 | --------------------------------------------------------------------------------