diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index cdb4512b9f..2e8c20bf95 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -56,11 +56,11 @@ jobs: strategy: # fail-fast: false matrix: - runner: ['rocprof-azure'] + runner: ['mi300x'] os: ['ubuntu-22.04'] build-type: ['Release'] - runs-on: ${{ matrix.runner }}-emu-runner-set + runs-on: rocprof-ubuntu-22 # define this for containers env: diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 7591d73d07..90e058d236 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -52,12 +52,13 @@ jobs: strategy: fail-fast: false matrix: - runner: ['navi3', 'navi4', 'mi3xx'] - os: ['ubuntu-22.04'] - build-type: ['RelWithDebInfo'] - ci-flags: ['--linter clang-tidy'] + system: [ + {gpu: 'navi3', runner: 'navi3-emu-runner-set', os: 'ubuntu-22.04', build-type: 'RelWithDebInfo', ci-flags: '--linter clang-tidy'}, + {gpu: 'navi4', runner: 'navi4-emu-runner-set', os: 'ubuntu-22.04', build-type: 'RelWithDebInfo', ci-flags: '--linter clang-tidy'}, + {gpu: 'mi3xx', runner: 'rocprof-ubuntu-22', os: 'ubuntu-22.04', build-type: 'RelWithDebInfo', ci-flags: '--linter clang-tidy'} + ] - runs-on: ${{ matrix.runner == 'mi3xx' && 'rocprof-azure' || matrix.runner }}-emu-runner-set + runs-on: ${{ matrix.system.runner }} # define this for containers env: @@ -90,7 +91,7 @@ jobs: ls -la - name: Enable PC Sampling - if: ${{ contains(matrix.runner, 'mi200') || contains(matrix.runner, 'mi300a') }} + if: ${{ contains(matrix.system.gpu, 'mi200') || contains(matrix.system.gpu, 'mi300a') }} shell: bash run: | echo 'ROCPROFILER_PC_SAMPLING_BETA_ENABLED=1' >> $GITHUB_ENV @@ -100,39 +101,39 @@ jobs: shell: bash run: python3 ./source/scripts/run-ci.py -B build - --name ${{ github.repository }}-${{ github.ref_name }}-${{ matrix.os }}-${{ matrix.runner }}-core + --name ${{ github.repository }}-${{ github.ref_name }}-${{ matrix.system.os }}-${{ matrix.system.gpu }}-core --build-jobs 16 --site ${RUNNER_HOSTNAME} --gpu-targets ${{ env.GPU_TARGETS }} --run-attempt ${{ github.run_attempt }} - ${{ matrix.ci-flags }} + ${{ matrix.system.ci-flags }} -- -DROCPROFILER_DEP_ROCMCORE=ON -DROCPROFILER_BUILD_DOCS=OFF - -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} + -DCMAKE_BUILD_TYPE=${{ matrix.system.build-type }} -DCMAKE_INSTALL_PREFIX=/opt/rocprofiler-sdk -DCPACK_GENERATOR='DEB;RPM;TGZ' -DCPACK_PACKAGING_INSTALL_PREFIX="$(realpath /opt/rocm)" -DPython3_EXECUTABLE=$(which python3) ${{ env.GLOBAL_CMAKE_OPTIONS }} -- - -LE "${${{ matrix.runner }}_EXCLUDE_LABEL_REGEX}" - -E "${${{ matrix.runner }}_EXCLUDE_TESTS_REGEX}" + -LE "${${{ matrix.system.gpu }}_EXCLUDE_LABEL_REGEX}" + -E "${${{ matrix.system.gpu }}_EXCLUDE_TESTS_REGEX}" - name: Install - if: ${{ contains(matrix.runner, env.CORE_EXT_RUNNER) }} + if: ${{ contains(matrix.system.gpu, env.CORE_EXT_RUNNER) }} timeout-minutes: 10 run: cmake --build build --target install --parallel 16 - name: Build Packaging - if: ${{ contains(matrix.runner, env.CORE_EXT_RUNNER) }} + if: ${{ contains(matrix.system.gpu, env.CORE_EXT_RUNNER) }} timeout-minutes: 10 run: cmake --build build --target package --parallel 16 - name: Test Install Build - if: ${{ contains(matrix.runner, env.CORE_EXT_RUNNER) }} + if: ${{ contains(matrix.system.gpu, env.CORE_EXT_RUNNER) }} timeout-minutes: 20 shell: bash run: | @@ -141,11 +142,11 @@ jobs: export LD_LIBRARY_PATH=/opt/rocprofiler-sdk/lib:${LD_LIBRARY_PATH} cmake --build build-samples --target all --parallel 16 cmake --build build-tests --target all --parallel 16 - ctest --test-dir build-samples -LE "${${{ matrix.runner }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.runner }}_EXCLUDE_TESTS_REGEX}" --output-on-failure - ctest --test-dir build-tests -LE "${${{ matrix.runner }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.runner }}_EXCLUDE_TESTS_REGEX}" --output-on-failure + ctest --test-dir build-samples -LE "${${{ matrix.system.gpu }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.system.gpu }}_EXCLUDE_TESTS_REGEX}" --output-on-failure + ctest --test-dir build-tests -LE "${${{ matrix.system.gpu }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.system.gpu }}_EXCLUDE_TESTS_REGEX}" --output-on-failure - name: Install Packages - if: ${{ contains(matrix.runner, env.CORE_EXT_RUNNER) }} + if: ${{ contains(matrix.system.gpu, env.CORE_EXT_RUNNER) }} timeout-minutes: 5 shell: bash run: | @@ -157,7 +158,7 @@ jobs: for i in $(ls -S ./build/rocprofiler-sdk*.deb | egrep -v 'roctx|rocpd'); do dpkg --force-all -i ${i}; done; - name: Test Installed Packages - if: ${{ contains(matrix.runner, env.CORE_EXT_RUNNER) }} + if: ${{ contains(matrix.system.gpu, env.CORE_EXT_RUNNER) }} timeout-minutes: 20 shell: bash run: | @@ -165,11 +166,11 @@ jobs: CMAKE_PREFIX_PATH=/opt/rocm cmake -B build-tests-deb -DGPU_TARGETS="gfx942" /opt/rocm/share/rocprofiler-sdk/tests cmake --build build-samples-deb --target all --parallel 16 cmake --build build-tests-deb --target all --parallel 16 - ctest --test-dir build-samples-deb -LE "${${{ matrix.runner }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.runner }}_EXCLUDE_TESTS_REGEX}" --output-on-failure - ctest --test-dir build-tests-deb -LE "${${{ matrix.runner }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.runner }}_EXCLUDE_TESTS_REGEX}" --output-on-failure + ctest --test-dir build-samples-deb -LE "${${{ matrix.system.gpu }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.system.gpu }}_EXCLUDE_TESTS_REGEX}" --output-on-failure + ctest --test-dir build-tests-deb -LE "${${{ matrix.system.gpu }}_EXCLUDE_LABEL_REGEX}" -E "${${{ matrix.system.gpu }}_EXCLUDE_TESTS_REGEX}" --output-on-failure - name: Archive production artifacts - if: ${{ github.event_name == 'workflow_dispatch' && contains(matrix.runner, env.CORE_EXT_RUNNER) }} + if: ${{ github.event_name == 'workflow_dispatch' && contains(matrix.system.gpu, env.CORE_EXT_RUNNER) }} uses: actions/upload-artifact@v4 with: name: installers-deb @@ -184,11 +185,11 @@ jobs: fail-fast: false matrix: runner: ['mi300'] - os: ['rhel', 'sles'] + os: ['rhel-8', 'sles-15'] build-type: ['RelWithDebInfo'] ci-flags: [''] - runs-on: rocprof-azure-${{ matrix.os }}-emu-runner-set + runs-on: rocprof-${{ matrix.os }} # define this for containers env: @@ -203,7 +204,7 @@ jobs: shell: bash run: | git config --global --add safe.directory '*' - if [ "${OS_TYPE}" == "rhel" ]; then + if [ "${OS_TYPE}" == "rhel-8" ]; then dnf makecache dnf groupinstall -y "Development Tools" dnf remove -y gcc-c++ @@ -261,7 +262,7 @@ jobs: build-type: ['RelWithDebInfo'] if: ${{ contains(github.event_name, 'pull_request') }} - runs-on: ${{ matrix.runner == 'mi3xx' && 'rocprof-azure' || matrix.runner }}-emu-runner-set + runs-on: ${{ matrix.runner == 'mi3xx' && 'rocprof-ubuntu-22' || matrix.runner }} # define this for containers env: diff --git a/.github/workflows/generate_afar.yml b/.github/workflows/generate_afar.yml deleted file mode 100644 index 8c3a49bb7e..0000000000 --- a/.github/workflows/generate_afar.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Generate AFAR - -on: - workflow_dispatch: - inputs: - rocm-release: - description: 'ROCm Release Version' - required: true - type: string - build-number: - description: 'ROCm CI Build Number' - required: true - type: string - build-job-name: - description: 'ROCm CI Build Job Name' - required: true - type: string - rocprofiler-sdk-revision: - description: 'ROCProfiler-SDK Commit Hash' - default: 'amd-staging' - required: true - type: string - extra-cmake-args: - description: 'Extra Arguments to give to cmake configuration' - required: false - type: string - package-types: - description: 'Package types provided to `CPACK_GENERATOR`' - required: true - default: 'STGZ' - type: string - -jobs: - generate-packages-ubuntu-22: - runs-on: rocprof-azure-emu-runner-set - - steps: - - - uses: actions/checkout@v4 - with: - ref: '${{ inputs.rocprofiler-sdk-revision }}' - path: 'rocprofiler-sdk' - - - name: Install Requirements - timeout-minutes: 10 - shell: bash - run: | - git config --global --add safe.directory '${{github.workspace}}/*' - apt-get update - amdgpu-repo --rocm-build=${{ inputs.build-job-name }}/${{ inputs.build-number }} - apt-get upgrade -y - apt-get install -y build-essential cmake rocm-llvm-dev python3-pip libdw-dev libsqlite3-dev - python3 -m pip install -r ${{github.workspace}}/rocprofiler-sdk/requirements.txt - - - name: Building ROCProfiler-SDK Packages - shell: bash - run: | - cmake -B build-rocprofiler-sdk -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX=/opt/rocm -DCPACK_GENERATOR="${{ inputs.package-types }}" -DROCPROFILER_BUILD_{TESTS,SAMPLES}=ON ${{ inputs.extra-cmake-args }} ${{github.workspace}}/rocprofiler-sdk - cmake --build build-rocprofiler-sdk --parallel 16 - cmake --build build-rocprofiler-sdk --target package --parallel 16 - - - name: Archive Packages - uses: actions/upload-artifact@v4 - with: - name: Packages-${{ inputs.rocm-release }} - path: | - ${{github.workspace}}/build-*/*.sh - ${{github.workspace}}/build-*/*.deb - ${{github.workspace}}/build-*/*.rpm - ${{github.workspace}}/build-*/*.tar.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index aba0370ffd..8bda48ab9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -174,7 +174,7 @@ Full documentation for ROCprofiler-SDK is available at [rocm.docs.amd.com/projec - absolute == node_id - relative == logical_node_id - type-relative == logical_node_type_id -- Added MI300 stochastic (hardware-based) PC sampling support in ROCProfiler-SDK and ROCProfV3 +- Added MI300/MI350 stochastic (hardware-based) PC sampling support in ROCProfiler-SDK and ROCProfV3 - Python bindings for rocprofiler-sdk-roctx - SQLite3 output support for rocprofv3 (`--output-format rocpd`) - Added `rocprofiler-sdk-rocpd` package @@ -193,16 +193,20 @@ Full documentation for ROCprofiler-SDK is available at [rocm.docs.amd.com/projec - Added `rocpd` output format documentation - Requires the ROCprof Trace Decoder plugin installed (see above) - Added perfetto support for scratch memory. +- Added documentation for rocprofv3 advanced options ### Changed - SDK no longer creates a background thread when every tool returns a nullptr from `rocprofiler_configure`. - Updated disassembly.hpp's vaddr-to-file-offset mapping to use the dedicated comgr API. +- rocprofiler_uuid_t ABI is changed to hold 128 bit value. - rocprofv3 shorthand argument for `--collection-period` is now `-P` (upper-case) as `-p` (lower-case) is reserved for later use - default output format for rocprofv3 is now `rocpd` (SQLite3 database) - rocprofv3 avail tool renamed from rocprofv3_avail to rocprofv3-avail tool - rocprofv3 avail tool has support for command line arguments. - rocprofv3 tool now allows for Thread Trace + PC Sampling on the same agent +- fixed inconsistency for what is a "null" handle in `rocprofiler_*_id_t` structs. + - correct answer is `.handle = 0` but some definitions used `UINT64_MAX` ### Resolved issues @@ -210,8 +214,10 @@ Full documentation for ROCprofiler-SDK is available at [rocm.docs.amd.com/projec - Fixed potential data race in rocprofiler-sdk double buffering scheme - Usage of std::regex in core rocprofiler-sdk library which causes segfaults/exceptions when used under dual ABI - Fixed perfetto counter collection by introducing per dispatch accumulation. +- Code object disassembly was missing function inlining information - Fixed queue preemption error and HSA_STATUS_ERROR_INVALID_PACKET_FORMAT error for stochastic PC-sampling for MI300X, leading to more stable runs. - Fixed the system hang issue for host-trap PC-sampling on MI300X. +- Fixed rocpd counter collection issue when counter collection alone is enabled, rocpd_kernel_dispatch table gets populated by counters data instead of kernel_dispatch data. ### Removed diff --git a/README.md b/README.md index 921d54e961..5e60fe46aa 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ cmake \ -DCMAKE_PREFIX_PATH=/opt/rocm \ rocprofiler-sdk-source - cmake --build rocprofiler-sdk-build --target all --parallel $(nproc) +cmake --build rocprofiler-sdk-build --target all --parallel $(nproc) ``` To install ROCprofiler, run: diff --git a/requirements.txt b/requirements.txt index 178bc1fa4b..6eb9da496e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ cmake>=3.21.0 cmake-format dataclasses flake8 +jinja2 numpy otf2 pandas @@ -12,3 +13,4 @@ perfetto pycobertura pytest pyyaml +reportlab diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index b2f2e9633a..1790a85cde 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -47,3 +47,4 @@ add_subdirectory(code_object_isa_decode) add_subdirectory(external_correlation_id_request) add_subdirectory(pc_sampling) add_subdirectory(openmp_target) +add_subdirectory(thread_trace) diff --git a/samples/counter_collection/buffered_client.cpp b/samples/counter_collection/buffered_client.cpp index 058eb13a91..560aff5990 100644 --- a/samples/counter_collection/buffered_client.cpp +++ b/samples/counter_collection/buffered_client.cpp @@ -112,7 +112,7 @@ fill_dimension_cache(rocprofiler_counter_id_t counter) (*dimension_cache()) ->emplace(counter.handle, std::vector{ - info.dimensions, info.dimensions + info.dimensions_count}); + *info.dimensions, *info.dimensions + info.dimensions_count}); } /** diff --git a/samples/counter_collection/device_counting_sync_client.cpp b/samples/counter_collection/device_counting_sync_client.cpp index 1a9ecfdaf6..cc2a0a0156 100644 --- a/samples/counter_collection/device_counting_sync_client.cpp +++ b/samples/counter_collection/device_counting_sync_client.cpp @@ -294,7 +294,7 @@ counter_sampler::get_counter_size(rocprofiler_counter_id_t counter) ROCPROFILER_CALL(rocprofiler_query_counter_info( counter, ROCPROFILER_COUNTER_INFO_VERSION_1, static_cast(&info)), "Could not query info for counter"); - return info.instance_ids_count; + return info.dimensions_instances_count; } std::unordered_map @@ -339,7 +339,7 @@ counter_sampler::get_counter_dimensions(rocprofiler_counter_id_t counter) counter, ROCPROFILER_COUNTER_INFO_VERSION_1, static_cast(&info)), "Could not query info for counter"); return std::vector{ - info.dimensions, info.dimensions + info.dimensions_count}; + *info.dimensions, *info.dimensions + info.dimensions_count}; } std::atomic& diff --git a/samples/counter_collection/print_functional_counters_client.cpp b/samples/counter_collection/print_functional_counters_client.cpp index 82a3dbfb3d..a242b502f2 100644 --- a/samples/counter_collection/print_functional_counters_client.cpp +++ b/samples/counter_collection/print_functional_counters_client.cpp @@ -292,7 +292,7 @@ dispatch_callback(rocprofiler_dispatch_counting_service_data_t dispatch_data, "Could not query counter_id"); cap.expected_counter_names.emplace(found_counter.handle, std::string(info.name)); cap.remaining.push_back(found_counter); - cap.expected.emplace(found_counter.handle, info.instance_ids_count); + cap.expected.emplace(found_counter.handle, info.dimensions_instances_count); auto& info_vector = cap.expected_data_dims.emplace(found_counter.handle, validate_dim_presence{}) @@ -300,7 +300,7 @@ dispatch_callback(rocprofiler_dispatch_counting_service_data_t dispatch_data, for(uint64_t i = 0; i < info.dimensions_count; i++) { - info_vector.maybe_forward(info.dimensions[i]); + info_vector.maybe_forward(*info.dimensions[i]); } } if(cap.expected.empty()) diff --git a/samples/thread_trace/CMakeLists.txt b/samples/thread_trace/CMakeLists.txt new file mode 100644 index 0000000000..b9996dbfda --- /dev/null +++ b/samples/thread_trace/CMakeLists.txt @@ -0,0 +1,86 @@ +# +# +# +cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR) + +if(NOT CMAKE_HIP_COMPILER) + find_program( + amdclangpp_EXECUTABLE + NAMES amdclang++ + HINTS ${ROCM_PATH} ENV ROCM_PATH /opt/rocm + PATHS ${ROCM_PATH} ENV ROCM_PATH /opt/rocm + PATH_SUFFIXES bin llvm/bin NO_CACHE) + mark_as_advanced(amdclangpp_EXECUTABLE) + + if(amdclangpp_EXECUTABLE) + set(CMAKE_HIP_COMPILER "${amdclangpp_EXECUTABLE}") + endif() +endif() + +project(rocprofiler-sdk-samples-thread-trace LANGUAGES CXX HIP) + +find_package(rocprofiler-sdk REQUIRED) +find_package(rocprofiler-sdk-roctx REQUIRED) +find_package( + amd_comgr + REQUIRED + CONFIG + HINTS + ${rocm_version_DIR} + ${ROCM_PATH} + PATHS + ${rocm_version_DIR} + ${ROCM_PATH}) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_HIP_STANDARD 17) +set(CMAKE_HIP_EXTENSIONS OFF) +set(CMAKE_HIP_STANDARD_REQUIRED ON) + +foreach(_TYPE DEBUG MINSIZEREL RELEASE RELWITHDEBINFO) + if("${CMAKE_HIP_FLAGS_${_TYPE}}" STREQUAL "") + set(CMAKE_HIP_FLAGS_${_TYPE} "${CMAKE_CXX_FLAGS_${_TYPE}}") + endif() +endforeach() + +set_source_files_properties( + main.cpp PROPERTIES LANGUAGE HIP COMPILE_FLAGS "${CMAKE_HIP_FLAGS_RELWITHDEBINFO}") + +add_executable(thread-trace-sample) +target_sources(thread-trace-sample PRIVATE main.cpp agent.cpp) + +target_link_libraries( + thread-trace-sample + PUBLIC rocprofiler-sdk::samples-build-flags + PRIVATE rocprofiler-sdk::rocprofiler-sdk rocprofiler-sdk::samples-common-library + rocprofiler-sdk-roctx::rocprofiler-sdk-roctx amd_comgr) + +add_test(NAME thread-trace-sample COMMAND $) + +find_library( + attdecoder_LIBRARY + NAMES rocprof-trace-decoder + HINTS ${ROCM_PATH} + PATHS ${ROCM_PATH} + PATH_SUFFIXES lib) + +if(attdecoder_LIBRARY) + cmake_path(GET attdecoder_LIBRARY PARENT_PATH attdecoder_LIB_DIR) +endif() + +find_package_handle_standard_args(attdecoder REQUIRED_VARS attdecoder_LIB_DIR + attdecoder_LIBRARY) + +set(IS_DISABLED ON) +if(attdecoder_FOUND) + set(IS_DISABLED OFF) +endif() + +rocprofiler_samples_get_preload_env(PRELOAD_ENV) +list(APPEND PRELOAD_ENV "ROCPROFILER_TRACE_DECODER_LIB_PATH=${attdecoder_LIB_DIR}") + +set_tests_properties( + thread-trace-sample PROPERTIES TIMEOUT 60 ENVIRONMENT "${PRELOAD_ENV}" LABELS + "samples;thread-trace" DISABLED ${IS_DISABLED}) diff --git a/samples/thread_trace/README.md b/samples/thread_trace/README.md new file mode 100644 index 0000000000..48528a75a0 --- /dev/null +++ b/samples/thread_trace/README.md @@ -0,0 +1,26 @@ +# Thread Trace and ROCprof Trace Decoder + +## Services + +- Thread trace in device profiling mode +- ROCprof Trace Decoder decodes the received thread trace data +- Thread trace start/stop using roctx + +## Properties + +### [agent.cpp](agent.cpp): + +- Configures thread trace in all GPU agents found with `rocprofiler_configure_device_thread_trace_service` +- Waits until `roctxProfilerResume` is called to start thread trace +- Stops tracing at `roctxProfilerPause` +- Receives the trace data in `shader_data_callback` and calls `rocprofiler_trace_decode` to decode the data +- `rocprofiler_trace_decode` calls `parse` (a lambda) +- `parse` receives the dedecoded data and increments hitcount/latencies by pc address +- At application end, `tool_fini` calls `gen_output_stream` to write the top hotspots into `thread_trace.log` + +### [main.cpp](main.cpp): + +- Defines a few different kernels and runs them +- The first loop iteration warms up the kernels +- The second iteration calls `roctxProfilerResume` to start thread trace +- After the loop ends, `roctxProfilerPause` is called to stop tracing diff --git a/samples/thread_trace/agent.cpp b/samples/thread_trace/agent.cpp new file mode 100644 index 0000000000..e8d9240e29 --- /dev/null +++ b/samples/thread_trace/agent.cpp @@ -0,0 +1,391 @@ +// MIT License +// +// Copyright (c) 2024-2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// undefine NDEBUG so asserts are implemented +#ifdef NDEBUG +# undef NDEBUG +#endif + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ROCPROFILER_CALL(result, msg) \ + if(auto ec = (result); ec != ROCPROFILER_STATUS_SUCCESS) \ + { \ + std::cerr << "rocprofiler-sdk error at " << __FILE__ << ":" << __LINE__ \ + << " :: " << #result << std::endl; \ + std::cerr << "rocprofiler-sdk error code " << ec << ": " \ + << rocprofiler_get_status_string(ec) << " :: " << msg << std::endl; \ + abort(); \ + } + +#define DECODER_CALL(result) \ + if(auto ec = (result); ec != ROCPROFILER_STATUS_SUCCESS) \ + { \ + std::cerr << "Decoder error at " << __FILE__ << ":" << __LINE__ << std::endl; \ + std::cerr << "rocprofiler-sdk error code " << ec << ": " \ + << rocprofiler_get_status_string(ec) << std::endl; \ + } + +#define CHECK_NOTNULL(x) \ + if(!(x)) \ + { \ + abort(); \ + }; + +namespace +{ +constexpr uint64_t TARGET_CU = 1; // CU (gfx9) or WGP (gfx10+) +constexpr uint64_t SHADER_MASK = 0x1; // Only enable SE=0 +constexpr uint64_t BUFFER_SIZE = 0x10000000; // 256MB +}; // namespace + +namespace Results +{ +using pcinfo_t = rocprofiler_thread_trace_decoder_pc_t; + +struct address_sort_t +{ + bool operator()(const pcinfo_t& a, const pcinfo_t& b) const + { + if(a.marker_id == b.marker_id) return a.addr < b.addr; + return a.marker_id < b.marker_id; + } +}; + +struct Latency +{ + uint64_t latency{0}; + uint64_t hitcount{0}; +}; + +// Maps address to latency +using LatencyTable = std::map; +// Used to disassemble instructions at (id, vaddr) pair +using AddressTable = rocprofiler::sdk::codeobj::disassembly::CodeobjAddressTranslate; + +AddressTable* table{nullptr}; +LatencyTable* latencies{nullptr}; + +// used to calculate mean wave lifetime +int64_t wave_lifetime = 0; +int64_t waves_started = 0; +int64_t waves_ended = 0; + +void +gen_output_stream() +{ + CHECK_NOTNULL(Results::latencies); + CHECK_NOTNULL(Results::table); + + const char* OUTPUT_OFSTREAM = "thread_trace.log"; + std::ofstream file(OUTPUT_OFSTREAM); + + if(!file.is_open()) + std::cout << "Could not open log file: " << OUTPUT_OFSTREAM << ", writing to stdout\n"; + else + std::cout << "Writing log to: " << OUTPUT_OFSTREAM << std::endl; + + std::ostream& output = file.is_open() ? file : std::cout; + + // Sort map by instruction cost + using Element = std::pair; + + std::vector sorted(latencies->begin(), latencies->end()); + std::stable_sort(sorted.begin(), sorted.end(), [](const Element& a, const Element& b) { + return a.second.latency > b.second.latency; + }); + + output << "Top 50 hotspots for trace (cycles):\n"; + for(size_t i = 0; i < sorted.size() && i < 50; i++) + { + auto& addr = sorted.at(i).first; + auto& latency = sorted.at(i).second; + auto inst = table->get(addr.marker_id, addr.addr); + + auto comment = inst->comment; + size_t pos = comment.rfind('/'); + if(pos != std::string::npos && pos + 1 < comment.size()) comment = comment.substr(pos + 1); + + output << "Latency:" << latency.latency << "\tHit:" << latency.hitcount << " \t" + << inst->inst << " [" << comment << "]\n"; + } + + if(waves_started != waves_ended) + std::cerr << "Error: Some waves have not ended!" << std::endl; + else if(waves_started == 0) + std::cerr << "Error: No waves started!" << std::endl; + else + output << "\nMean wave lifetime: " << wave_lifetime / waves_started << " cycles"; + + output << "\nWaves started: " << waves_started << "\nWaves ended: " << waves_ended << "\n"; +}; +} // namespace Results + +namespace Decoder +{ +rocprofiler_thread_trace_decoder_id_t decoder{}; + +void +tool_codeobj_tracing_callback(rocprofiler_callback_tracing_record_t record, + rocprofiler_user_data_t* /* user_data */, + void* /* userdata */) +{ + if(record.kind != ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT) return; + if(record.operation != ROCPROFILER_CODE_OBJECT_LOAD) return; + + CHECK_NOTNULL(Results::table); + auto* data = static_cast(record.payload); + + if(data->storage_type == ROCPROFILER_CODE_OBJECT_STORAGE_TYPE_FILE) + { + Results::table->addDecoder( + data->uri, data->code_object_id, data->load_delta, data->load_size); + return; + } + + auto* memorybase = reinterpret_cast(data->memory_base); + CHECK_NOTNULL(memorybase); + + DECODER_CALL(rocprofiler_thread_trace_decoder_codeobj_load(decoder, + data->code_object_id, + data->load_delta, + data->load_size, + memorybase, + data->memory_size)); + + Results::table->addDecoder( + memorybase, data->memory_size, data->code_object_id, data->load_delta, data->load_size); +} + +void +shader_data_callback(rocprofiler_agent_id_t /* agent */, + int64_t /* se_id */, + void* se_data, + size_t data_size, + rocprofiler_user_data_t /* userdata */) +{ + CHECK_NOTNULL(Results::latencies); + + auto parse = [](rocprofiler_thread_trace_decoder_record_type_t record_type_id, + void* events, + uint64_t num_events, + void* /* userdata */) { + if(record_type_id == ROCPROFILER_THREAD_TRACE_DECODER_RECORD_OCCUPANCY) + { + for(size_t i = 0; i < num_events; i++) + { + auto& event = static_cast(events)[i]; + + if(event.start) + { + Results::wave_lifetime -= static_cast(event.time); + Results::waves_started++; + } + else + { + Results::wave_lifetime += static_cast(event.time); + Results::waves_ended++; + } + } + } + + if(record_type_id != ROCPROFILER_THREAD_TRACE_DECODER_RECORD_WAVE) return; + + for(size_t w = 0; w < num_events; w++) + { + auto* wave = static_cast(events); + for(size_t i = 0; i < wave->instructions_size; i++) + { + auto& inst = wave->instructions_array[i]; + auto& latency = (*Results::latencies)[inst.pc]; + latency.latency += inst.duration; + latency.hitcount += 1; + } + } + }; + + DECODER_CALL(rocprofiler_trace_decode(decoder, parse, se_data, data_size, nullptr)); +} + +} // namespace Decoder + +namespace ThreadTracer +{ +rocprofiler_client_id_t* client_id = nullptr; +rocprofiler_context_id_t agent_ctx = {}; +rocprofiler_context_id_t tracing_ctx = {}; + +rocprofiler_status_t +query_available_agents(rocprofiler_agent_version_t /* version */, + const void** agents, + size_t num_agents, + void* user_data) +{ + rocprofiler_user_data_t user{}; + user.ptr = user_data; + + for(size_t idx = 0; idx < num_agents; idx++) + { + const auto* agent = static_cast(agents[idx]); + if(agent->type != ROCPROFILER_AGENT_TYPE_GPU) continue; + + auto parameters = std::vector{}; + parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_TARGET_CU, TARGET_CU}); + parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_BUFFER_SIZE, BUFFER_SIZE}); + parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SHADER_ENGINE_MASK, SHADER_MASK}); + + ROCPROFILER_CALL( + rocprofiler_configure_device_thread_trace_service(agent_ctx, + agent->id, + parameters.data(), + parameters.size(), + Decoder::shader_data_callback, + user), + "thread trace service configure"); + } + return ROCPROFILER_STATUS_SUCCESS; +} + +void +cntrl_tracing_callback(rocprofiler_callback_tracing_record_t record, + rocprofiler_user_data_t* /* user_data */, + void* /* cb_data */) +{ + if(record.kind != ROCPROFILER_CALLBACK_TRACING_MARKER_CONTROL_API) return; + + if(record.phase == ROCPROFILER_CALLBACK_PHASE_ENTER && + record.operation == ROCPROFILER_MARKER_CONTROL_API_ID_roctxProfilerPause) + { + ROCPROFILER_CALL(rocprofiler_stop_context(agent_ctx), "stopping context"); + } + else if(record.phase == ROCPROFILER_CALLBACK_PHASE_EXIT && + record.operation == ROCPROFILER_MARKER_CONTROL_API_ID_roctxProfilerResume) + { + ROCPROFILER_CALL(rocprofiler_start_context(agent_ctx), "starting context"); + } +} + +int +tool_init(rocprofiler_client_finalize_t /* fini_func */, void* /* tool_data */) +{ + Results::latencies = new Results::LatencyTable{}; + Results::table = new Results::AddressTable{}; + + // This is set by ctests: TODO: move to client.cpp + // If nullptr, searches rocprofiler-sdk install location + const char* lib_path = std::getenv("ROCPROFILER_TRACE_DECODER_LIB_PATH"); + + DECODER_CALL(rocprofiler_thread_trace_decoder_create(&Decoder::decoder, lib_path)); + + ROCPROFILER_CALL(rocprofiler_create_context(&tracing_ctx), "context creation"); + ROCPROFILER_CALL(rocprofiler_create_context(&agent_ctx), "context creation"); + + ROCPROFILER_CALL( + rocprofiler_configure_callback_tracing_service(tracing_ctx, + ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT, + nullptr, + 0, + Decoder::tool_codeobj_tracing_callback, + nullptr), + "code object tracing service configure"); + + ROCPROFILER_CALL(rocprofiler_configure_callback_tracing_service( + tracing_ctx, + ROCPROFILER_CALLBACK_TRACING_MARKER_CONTROL_API, + nullptr, + 0, + cntrl_tracing_callback, + nullptr), + "marker tracing callback service configure"); + + ROCPROFILER_CALL(rocprofiler_query_available_agents(ROCPROFILER_AGENT_INFO_VERSION_0, + &query_available_agents, + sizeof(rocprofiler_agent_t), + nullptr), + "Failed to find GPU agents"); + + int valid_ctx = 0; + ROCPROFILER_CALL(rocprofiler_context_is_valid(agent_ctx, &valid_ctx), "validity check"); + assert(valid_ctx != 0); + ROCPROFILER_CALL(rocprofiler_context_is_valid(tracing_ctx, &valid_ctx), "validity check"); + assert(valid_ctx != 0); + + ROCPROFILER_CALL(rocprofiler_start_context(tracing_ctx), "context start"); + + // no errors + return 0; +} + +void +tool_fini(void* /* tool_data */) +{ + rocprofiler_thread_trace_decoder_destroy(Decoder::decoder); + + Results::gen_output_stream(); + + delete Results::latencies; + delete Results::table; +} + +} // namespace ThreadTracer + +extern "C" rocprofiler_tool_configure_result_t* +rocprofiler_configure(uint32_t /* version */, + const char* /* runtime_version */, + uint32_t priority, + rocprofiler_client_id_t* id) +{ + // only activate if main tool + if(priority > 0) return nullptr; + + // set the client name + id->name = "Thread Trace Sample"; + + // store client info + ThreadTracer::client_id = id; + + // create configure data + static auto cfg = + rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t), + &ThreadTracer::tool_init, + &ThreadTracer::tool_fini, + nullptr}; + + // return pointer to configure data + return &cfg; +} diff --git a/samples/thread_trace/main.cpp b/samples/thread_trace/main.cpp new file mode 100644 index 0000000000..e189b41472 --- /dev/null +++ b/samples/thread_trace/main.cpp @@ -0,0 +1,178 @@ +// MIT License +// +// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// undefine NDEBUG so asserts are implemented +#ifdef NDEBUG +# undef NDEBUG +#endif + +#include +#include +#include +#include + +#include + +#include "hip/hip_runtime.h" + +// Two waves per SIMD on MI300 +#define DATA_SIZE (304 * 64 * 4 * 2) +#define HIP_API_CALL(CALL) \ + if((CALL) != hipSuccess) \ + { \ + abort(); \ + } + +#define LDS_SIZE 1024 + +__global__ void +divide_kernel(float* a, const float* b, const float* c, int /* unused */) +{ + int index = blockDim.x * blockIdx.x + threadIdx.x; + + if(index >= DATA_SIZE) return; + + a[index] = (b[index] - c[index]) / abs(c[index] + b[index]) + 1; +} + +__global__ void +looping_lds_kernel(float* a, const float* b, const float* c, int loopcount) +{ + __shared__ float interm[LDS_SIZE]; + + size_t index = blockDim.x * blockIdx.x + threadIdx.x; + + for(size_t i = index; i < DATA_SIZE; i += blockDim.x * gridDim.x) + interm[threadIdx.x % LDS_SIZE] = b[index] + threadIdx.x; + + for(int it = 0; it < loopcount; it++) + { + __syncthreads(); + float value = interm[(it + threadIdx.x + LDS_SIZE / 2) % LDS_SIZE]; + __syncthreads(); + interm[threadIdx.x % LDS_SIZE] += value; + } + + a[index] = interm[threadIdx.x % LDS_SIZE] + c[index]; +} + +__global__ void +fifo_kernel(float* /* a */, const float* /* b */, const float* /* c */, int loops) +{ + using _float4 = __attribute__((__vector_size__(4 * sizeof(float)))) float; + + __shared__ _float4 lds[LDS_SIZE]; + lds[threadIdx.x] = _float4{float(threadIdx.x)}; + lds[threadIdx.x + 512] = _float4{float(threadIdx.x)}; + + __syncthreads(); + + _float4 dst[16]; + + float res1 = 0, res2 = 0; + + for(int l = 0; l < loops; l++) + { +#pragma unroll 16 + for(int i = 0; i < 16; i++) + dst[i] = lds[threadIdx.x + i * 8]; + + __syncthreads(); + +#pragma unroll 16 + for(int i = 0; i < 16; i++) + { + res1 += dst[i][0] + dst[i][1]; + res2 += dst[i][2] + dst[i][3]; + } + asm volatile("v_add_f32 %0, %1, %2" : "=v"(res1) : "v"(res1), "v"(res2)); + } +}; + +class hipMemory +{ +public: + hipMemory(size_t size = DATA_SIZE) + { + HIP_API_CALL(hipMalloc(&ptr, size * sizeof(float))); + HIP_API_CALL(hipMemset(ptr, 0, size * sizeof(float))); + } + ~hipMemory() + { + if(ptr) HIP_API_CALL(hipFree(ptr)); + } + hipMemory(hipMemory&& other) + { + ptr = other.ptr; + other.ptr = nullptr; + } + float* ptr = nullptr; +}; + +class HipStream +{ +public: + HipStream() { HIP_API_CALL(hipStreamCreateWithFlags(&stream, hipStreamNonBlocking)); } + ~HipStream() { HIP_API_CALL(hipStreamDestroy(stream)); } + + hipStream_t stream; + + hipMemory src1{}; + hipMemory src2{}; + hipMemory dst{}; +}; + +#define Launch(kernel, stream, arglast) \ + hipLaunchKernelGGL( \ + kernel, DATA_SIZE / 512, 512, 0, 0, stream.dst.ptr, stream.src1.ptr, stream.src2.ptr, 6); + +int +main(int /*argc*/, char** /*argv*/) +{ + std::array streams{}; + std::vector kernels{}; + + kernels.push_back(divide_kernel); + kernels.push_back(looping_lds_kernel); + kernels.push_back(fifo_kernel); + + for(size_t i = 0; i < streams.size() * kernels.size(); i++) + { + // Warmup then start + if(i == streams.size()) + { + HIP_API_CALL(hipDeviceSynchronize()); + roctxProfilerResume(0); + } + + auto& stream = streams.at(i % streams.size()); + auto& kernel = kernels.at(i % kernels.size()); + + Launch(kernel, stream, 3); + HIP_API_CALL(hipGetLastError()); + } + + HIP_API_CALL(hipDeviceSynchronize()); + roctxProfilerPause(0); + + return 0; +} diff --git a/source/bin/CMakeLists.txt b/source/bin/CMakeLists.txt index cba31cf1cc..7f0bb05908 100644 --- a/source/bin/CMakeLists.txt +++ b/source/bin/CMakeLists.txt @@ -25,3 +25,39 @@ install( PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE COMPONENT tools) + +# for each entry here there must be a ROCPD_BIN_ list +set(ROCPD_EXECUTABLES "all" "csv" "otf2" "pftrace" "summary") + +# format is list: [, , ] +set(ROCPD_BIN_all "rocpd" "rocpd" "") +set(ROCPD_BIN_csv "rocpd2csv" "rocpd.csv" "") +set(ROCPD_BIN_otf2 "rocpd2otf2" "rocpd.otf2" "") +set(ROCPD_BIN_pftrace "rocpd2pftrace" "rocpd.pftrace" "") +set(ROCPD_BIN_summary "rocpd2summary" "rocpd.summary" "") + +foreach(_EXE IN LISTS ROCPD_EXECUTABLES) + list(GET ROCPD_BIN_${_EXE} 0 ROCPD_EXE_NAME) + list(GET ROCPD_BIN_${_EXE} 1 ROCPD_EXE_MODULE) + list(GET ROCPD_BIN_${_EXE} 2 ROCPD_EXE_MODULE_ARGS) + + if(NOT ROCPD_EXE_NAME + OR NOT ROCPD_EXE_MODULE + OR (NOT ROCPD_EXE_MODULE_ARGS AND NOT ROCPD_EXE_MODULE_ARGS STREQUAL "")) + message( + FATAL_ERROR "ROCPD_BIN_${_EXE} not properly defined: ${ROCPD_BIN_${_EXE}}") + endif() + + set(ROCPD_EXE_OUTPUT_FILE + ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/${ROCPD_EXE_NAME}) + + # Adding main rocpd + configure_file(rocpd.py ${ROCPD_EXE_OUTPUT_FILE} @ONLY) + + install( + FILES ${ROCPD_EXE_OUTPUT_FILE} + DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + COMPONENT rocpd) +endforeach() diff --git a/source/bin/rocpd.py b/source/bin/rocpd.py new file mode 100755 index 0000000000..ffe8ce4f9e --- /dev/null +++ b/source/bin/rocpd.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2024-2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import os +import sys + +""" +Simple Python executable script for invoking `python3 -m @ROCPD_EXE_MODULE@` +""" + + +def main(argv=sys.argv[1:], environ=dict(os.environ)): + """ + Executes {sys.executable} -m @ROCPD_EXE_MODULE@ @ROCPD_EXE_MODULE_ARGS@ + """ + + ROCPD_SUPPORTED_PYTHON_VERSIONS = [ + ".".join(itr.split(".")[:2]) for itr in "@ROCPROFILER_PYTHON_VERSIONS@".split(";") + ] + ROCPD_MODULE_ARGS = [f"{itr}" for itr in "@ROCPD_EXE_MODULE_ARGS@".split(" ") if itr] + + this_dir = os.path.dirname(os.path.realpath(__file__)) + this_python_ver = f"{sys.version_info.major}.{sys.version_info.minor}" + if this_python_ver not in ROCPD_SUPPORTED_PYTHON_VERSIONS: + raise ImportError( + "@ROCPD_EXE_NAME@ not supported for Python version {} (sys.executable='{}').\n@ROCPD_EXE_NAME@ supported python versions: {}".format( + this_python_ver, + sys.executable, + ", ".join(ROCPD_SUPPORTED_PYTHON_VERSIONS), + ) + ) + + module_path = os.path.join( + this_dir, + "..", + "@CMAKE_INSTALL_LIBDIR@", + f"python{this_python_ver}", + "site-packages", + ) + + python_path = [module_path] + os.environ.get("PYTHONPATH", "").split(":") + + # update PYTHONPATH environment variable + environ["PYTHONPATH"] = ":".join(python_path) + + args = [f"{sys.executable}", "-m", "@ROCPD_EXE_MODULE@"] + ROCPD_MODULE_ARGS + argv + + # does not return + os.execvpe(args[0], args, env=environ) + + +if __name__ == "__main__": + main() diff --git a/source/bin/rocprofv3-avail.py b/source/bin/rocprofv3-avail.py index 66d6ddb98e..f0932a9fa7 100755 --- a/source/bin/rocprofv3-avail.py +++ b/source/bin/rocprofv3-avail.py @@ -217,7 +217,7 @@ def print_basic_info(info): agent_info_map = avail.get_agent_info_map() agent_counters = avail.get_counters() - for agent, info in agent_info_map.items(): + for agent, info in dict(sorted(agent_info_map.items())).items(): if ( info["type"] == 2 and args.device is not None @@ -238,7 +238,7 @@ def list_pc_sampling(args): sampling_agents = avail.get_pc_sample_configs() agent_info_map = avail.get_agent_info_map() print("Agents supporting PC Sampling\n") - for agent in sampling_agents.keys(): + for agent in dict(sorted(sampling_agents.items())).keys(): info = agent_info_map[agent] print( "{:8}:\t{}\n{:8}:\t{}".format( @@ -251,8 +251,9 @@ def list_pc_sampling(args): def info_pc_sampling(args): sampling_agents = avail.get_pc_sample_configs() agent_info_map = avail.get_agent_info_map() - for agent, configs in sampling_agents.items(): + for agent, configs in dict(sorted(sampling_agents.items())).items(): info = agent_info_map[agent] + print( "{:8}:\t{}\n{:8}:\t{}".format( "GPU", info["logical_node_type_id"], "Name", info["name"] @@ -280,7 +281,7 @@ def print_agent_counter(counters): agent_counters = avail.get_counters() agent_info_map = avail.get_agent_info_map() - for agent, info in agent_info_map.items(): + for agent, info in dict(sorted(agent_info_map.items())).items(): if ( info["type"] == 2 and args.device is not None @@ -321,7 +322,7 @@ def print_pmc_info(args, pmc_counters): print(counter) print("\n") - for agent, info in agent_info_map.items(): + for agent, info in dict(sorted(agent_info_map.items())).items(): if ( info["type"] == 2 and args.device is not None diff --git a/source/bin/rocprofv3.py b/source/bin/rocprofv3.py index 6cc43190db..1b2acd5dbc 100755 --- a/source/bin/rocprofv3.py +++ b/source/bin/rocprofv3.py @@ -769,7 +769,7 @@ def add_parser_bool_argument(gparser, *args, **kwargs): att_options.add_argument( "--att-buffer-size", - help="Thread trace buffer size. Default 96MB", + help="Thread trace buffer size. Default 256MB", default=None, type=str, ) @@ -1198,7 +1198,7 @@ def _write_env_value(): if args.marker_trace and not args.suppress_marker_preload: update_env("LD_PRELOAD", ROCPROF_ROCTX_LIBRARY, append=True) - if trace_count == 0: + if trace_count == 0 and len(app_args) != 0: warning("No tracing options were enabled.") # if no tracing was enabled but the options below were enabled, raise an error @@ -1403,6 +1403,11 @@ def log_config(_env): ) else: app_args = [sys.executable, path, "info", "--pmc"] + for itr in ("ROCPROF", "ROCPROFILER", "ROCTX"): + update_env( + f"{itr}_LOG_LEVEL", + "error", + ) exit_code = subprocess.check_call( [sys.executable, path, "info", "--pc-sampling"], env=app_env, diff --git a/source/docs/api-reference/thread_trace.rst b/source/docs/api-reference/thread_trace.rst index 78e1343076..6531fb9fdd 100644 --- a/source/docs/api-reference/thread_trace.rst +++ b/source/docs/api-reference/thread_trace.rst @@ -188,7 +188,7 @@ To decode the raw thread trace data, create and initialize a Trace Decoder: .. code-block:: cpp - rocprofiler_thread_trace_decoder_handle_t decoder{}; + rocprofiler_thread_trace_decoder_id_t decoder{}; // Create the Trace Decoder with the path to the decoder library ROCPROFILER_CALL( diff --git a/source/docs/data/list-avail.txt b/source/docs/data/list-avail.txt new file mode 100644 index 0000000000..9fe41deac5 --- /dev/null +++ b/source/docs/data/list-avail.txt @@ -0,0 +1,26 @@ +GPU : 0 +Name : gfx90a +configs : + Method : host_trap + Unit : time + Min_Interval : 1 + Max_Interval : 18446744073709551615 + Flags : none + +GPU:0 +Name:gfx90a +Counter_Name : processor_id_low +Description : Constant value processor_id_low from agent properties + + +Counter_Name : ALUStalledByLDS +Description : The percentage of GPUTime ALU units are stalled by the LDS input queue being full or the output queue being not ready. If there are LDS bank conflicts, reduce them. Otherwise, try reducing the number of LDS accesses if possible. Value range: 0% (optimal) to 100% (bad). +Expression : 400*reduce(SQ_WAIT_INST_LDS,sum)/reduce(SQ_WAVES,sum)/reduce(GRBM_GUI_ACTIVE,max) +Dimensions : DIMENSION_INSTANCE[0:0] + +Counter_Name : SQ_WAVES +Description : Count number of waves sent to distributed sequencers (SQs). This value represents the number of waves that are sent to each SQ. This only counts new waves sent since the start of collection (for dispatch profiling this is the timeframe of kernel execution, for agent profiling it is the timeframe between start_context and read counter data). A sum of all SQ_WAVES values will give the total number of waves started by the application during the collection timeframe. Returns one value per-SE (aggregates of SIMD values). +Block : SQ +Dimensions : DIMENSION_INSTANCE[0:0] DIMENSION_SHADER_ENGINE[0:7] + +... diff --git a/source/docs/how-to/using-rocpd-output-format.rst b/source/docs/how-to/using-rocpd-output-format.rst index db0a0b2749..b0c98e153b 100644 --- a/source/docs/how-to/using-rocpd-output-format.rst +++ b/source/docs/how-to/using-rocpd-output-format.rst @@ -1,6 +1,6 @@ .. meta:: - :description: "ROCprofiler-SDK is a tooling infrastructure for profiling general-purpose GPU compute applications running on the ROCm software." - :keywords: "ROCprofiler-SDK, ROCProfiler-SDK output formats, rocpd, SQLite3, CSV, JSON, PFTrace, OTF2" + :description: "ROCprofiler-SDK rocpd output format documentation - comprehensive guide for SQLite3 database storage, format conversion utilities, and multi-format export capabilities for GPU profiling data analysis." + :keywords: "ROCprofiler-SDK, rocpd, SQLite3, profiling database, format conversion, CSV export, JSON export, PFTrace, OTF2, GPU profiling, trace analysis" .. _using-rocpd-output-format: @@ -8,73 +8,76 @@ Using rocpd Output Format ========================= -``rocprofv3`` supports the following output formats: +``rocprofv3`` provides comprehensive support for multiple output formats to accommodate diverse analysis workflows: -- **rocpd** (SQLite3 Database, Default) -- **CSV** -- **JSON** (Custom format for programmatic analysis only) -- **PFTrace** (Perfetto trace for visualization with Perfetto) -- **OTF2** (Open Trace Format for visualization with compatible third-party tools) +- **rocpd** (SQLite3 Database) - Default format providing structured data storage +- **CSV** (Comma-Separated Values) - Tabular format for spreadsheet applications and data analysis tools +- **JSON** (JavaScript Object Notation) - Structured format optimized for programmatic analysis and integration +- **PFTrace** (Perfetto Protocol Buffers) - Binary trace format for high-performance visualization using Perfetto +- **OTF2** (Open Trace Format 2) - Standardized trace format for interoperability with third-party analysis tools -The ``rocpd`` output format is the default for ``rocprofv3``. It stores profiling results in a SQLite3 database, providing a structured and efficient way to analyze and post-process profiling data. This format allows users to query and manipulate profiling data using SQL, making it easy to extract specific information or perform complex analyses. +The ``rocpd`` output format serves as the primary data repository for ``rocprofv3`` profiling sessions. This format leverages SQLite3's ACID-compliant database engine to provide robust, structured storage of comprehensive profiling datasets. The relational schema enables efficient querying and manipulation of profiling data through standard SQL interfaces, facilitating complex analytical operations and custom reporting workflows. Features ++++++++ -- **Rich Data Model**: Stores all collected profiling data, including traces, counters, and metadata, in a single `.db` (SQLite3) file. -- **Programmatic Access**: Can be queried using standard SQL tools or libraries (e.g., `sqlite3` CLI, Python's `sqlite3` module). -- **Post-Processing**: Enables advanced analysis and visualization using custom scripts or third-party tools that support SQLite3. +- **Comprehensive Data Model**: Consolidates all profiling artifacts including execution traces, performance counters, hardware metrics, and contextual metadata within a single SQLite3 database file (`.db` extension). +- **Standards-Compliant Access**: Supports querying through industry-standard SQL interfaces including command-line tools (``sqlite3`` CLI), programming language bindings (Python ``sqlite3`` module, C/C++ SQLite API), and database management applications. +- **Advanced Analytics Integration**: Facilitates sophisticated post-processing workflows through custom analytical scripts, automated reporting systems, and integration with third-party visualization and analysis frameworks that provide SQLite3 connectivity. Generating rocpd Output +++++++++++++++++++++++ -To generate output in rocpd format, simply use: +To generate profiling data in the default rocpd format: .. code-block:: bash rocprofv3 --hip-trace -- -Or use the ``--output-format`` option with ``rocpd``: +Alternatively, explicitly specify the rocpd output format using the ``--output-format`` parameter: .. code-block:: bash rocprofv3 --hip-trace --output-format rocpd -- -The output will be saved as ``%hostname%/%pid%_results.db``, where ``%hostname%`` is the name of the host machine and ``%pid%`` is the process ID of the application being profiled. +The profiling session generates output files following the naming convention ``%hostname%/%pid%_results.db``, where ``%hostname%`` represents the system hostname and ``%pid%`` corresponds to the process identifier of the profiled application. -Converting rocpd to Other Formats -+++++++++++++++++++++++++++++++++ +Converting rocpd to Alternative Formats ++++++++++++++++++++++++++++++++++++++ -The ``rocpd`` output format can be converted to other formats for further analysis or visualization. -First, ensure the ``rocpd`` Python module is available in your environment: +The ``rocpd`` database format supports conversion to alternative output formats for specialized analysis and visualization workflows. -.. code-block:: bash +The ``rocpd`` conversion utility is distributed as part of the ROCm installation package, located in ``/opt/rocm-/bin``, and provides both executable and Python module interfaces for programmatic integration. + +Invoke the ``rocpd convert`` command with appropriate parameters to transform database files into target formats. - export PYTHONPATH=/lib/pythonX.Y/site-packages:$PYTHONPATH +**CSV Format Conversion:** + +.. code-block:: bash -where ```` is the ROCm installation path (usually ``/opt/rocm-``), and ``X.Y`` is your Python version. + /opt/rocm/bin/rocpd convert -i .db --output-format csv -Once the ``rocpd`` module is available, use the ``rocpd convert`` command to convert the output to other formats. +**Python Interpreter Compatibility:** -Convert to CSV format: +When encountering Python interpreter version conflicts, specify the appropriate Python executable explicitly: .. code-block:: bash - python3 -m rocpd convert -i .db --output-format csv + python3.10 $(which rocpd) convert -f csv -i .db -The converted CSV will be saved as ``rocpd-output-data/out_hip_api_trace.csv`` in the current working directory. +The CSV conversion process generates output files in the ``rocpd-output-data/out_hip_api_trace.csv`` path relative to the current working directory. -Convert to OTF2 format: +**OTF2 Format Conversion:** .. code-block:: bash - python3 -m rocpd convert -i .db --output-format otf2 + /opt/rocm/bin/rocpd convert -i .db --output-format otf2 -Convert to PFTrace format: +**Perfetto Trace Format Conversion:** .. code-block:: bash - python3 -m rocpd convert -i .db --output-format pftrace + /opt/rocm/bin/rocpd convert -i .db --output-format pftrace rocpd convert Command-Line Options ++++++++++++++++++++++++++++++++++ @@ -98,96 +101,102 @@ Options **Required Arguments:** - ``-i INPUT [INPUT ...]``, ``--input INPUT [INPUT ...]`` - Input path and filename to one or more database(s), separated by spaces. + Specifies input database file paths. Accepts multiple SQLite3 database files separated by whitespace for batch processing operations. - ``-f {csv,pftrace,otf2} [{csv,pftrace,otf2} ...]``, ``--output-format {csv,pftrace,otf2} [{csv,pftrace,otf2} ...]`` - Specify one or more output formats. Supported: ``csv``, ``pftrace``, ``otf2``. + Defines target output format(s). Supports concurrent conversion to multiple formats: ``csv`` (Comma-Separated Values), ``pftrace`` (Perfetto Protocol Buffers), ``otf2`` (Open Trace Format 2). -**I/O Options:** +**I/O Configuration:** - ``-o OUTPUT_FILE``, ``--output-file OUTPUT_FILE`` - Sets the base output file name (default: ``out``). + Configures the base filename for generated output files (default: ``out``). - ``-d OUTPUT_PATH``, ``--output-path OUTPUT_PATH`` - Sets the output directory (default: ``./rocpd-output-data``). + Specifies the target directory for output file generation (default: ``./rocpd-output-data``). -**Kernel Naming Options:** +**Kernel Identification Options:** - ``--kernel-rename`` - Use ROCTx marker names instead of kernel names. + Substitutes kernel function names with corresponding ROCTx marker annotations for enhanced semantic context. -**Generic Options:** +**Device Identification Configuration:** - ``--agent-index-value {absolute,relative,type-relative}`` - Device identification format in output: + Controls device identification methodology in converted output: - - ``absolute``: Uses node_id (e.g., Agent-0, Agent-2, Agent-4), ignoring cgroups. - - ``relative``: Uses logical_node_id (e.g., Agent-0, Agent-1, Agent-2), considering cgroups. *(Default)* - - ``type-relative``: Uses logical_node_type_id (e.g., CPU-0, GPU-0, GPU-1), numbering resets for each device type. + - ``absolute``: Utilizes hardware node identifiers (e.g., Agent-0, Agent-2, Agent-4), bypassing container group abstractions. + - ``relative``: Employs logical node identifiers (e.g., Agent-0, Agent-1, Agent-2), incorporating container group context. *(Default)* + - ``type-relative``: Applies device-type-specific logical identifiers (e.g., CPU-0, GPU-0, GPU-1), with independent numbering sequences per device class. -**Perfetto Trace (pftrace) Options:** +**Perfetto Trace Configuration:** - ``--perfetto-backend {inprocess,system}`` - Perfetto data collection backend. ``system`` mode requires running ``traced`` and ``perfetto`` daemons (default: ``inprocess``). + Configures Perfetto data collection architecture. The ``system`` backend requires active ``traced`` and ``perfetto`` daemon processes, while ``inprocess`` operates autonomously (default: ``inprocess``). - ``--perfetto-buffer-fill-policy {discard,ring_buffer}`` - Policy for handling new records when buffer is full (default: ``discard``). + Defines buffer overflow handling strategy: ``discard`` drops new records when capacity is exceeded, ``ring_buffer`` overwrites oldest records (default: ``discard``). - ``--perfetto-buffer-size KB`` - Buffer size for perfetto output in KB (default: 1 GB). + Sets the trace buffer capacity in kilobytes for Perfetto output generation (default: 1,048,576 KB / 1 GB). - ``--perfetto-shmem-size-hint KB`` - Perfetto shared memory size hint in KB (default: 64 KB). + Specifies shared memory allocation hint for Perfetto inter-process communication in kilobytes (default: 64 KB). - ``--group-by-queue`` - Display HIP streams that kernels and memory copy operations are submitted to, rather than HSA queues. + Organizes trace data by HIP stream abstractions rather than low-level HSA queue identifiers, providing higher-level application context for kernel and memory transfer operations. -**Time Window Options:** +**Temporal Filtering Configuration:** - ``--start START`` - Start time as percentage or nanoseconds from trace file (e.g., ``50%`` or ``781470909013049``). + Defines trace window start boundary using percentage notation (e.g., ``50%``) or absolute nanosecond timestamps (e.g., ``781470909013049``). - ``--start-marker START_MARKER`` - Named marker event to use as window start point. + Specifies named marker event identifier to establish trace window start boundary. - ``--end END`` - End time as percentage or nanoseconds from trace file (e.g., ``75%`` or ``3543724246381057``). + Defines trace window end boundary using percentage notation (e.g., ``75%``) or absolute nanosecond timestamps (e.g., ``3543724246381057``). - ``--end-marker END_MARKER`` - Named marker event to use as window end point. + Specifies named marker event identifier to establish trace window end boundary. - ``--inclusive INCLUSIVE`` - ``True``: include events if START or END in window; ``False``: only if BOTH in window (default: ``True``). + Controls event inclusion criteria: ``True`` includes events with either start or end timestamps within the specified window; ``False`` requires both timestamps within the window (default: ``True``). -**Help:** +**Command-Line Help:** - ``-h``, ``--help`` - Show help message and exit. + Displays comprehensive command syntax, parameter descriptions, and usage examples. Examples ++++++++ -Convert one database to Perfetto trace: +**Single Database Conversion to Perfetto Format:** .. code-block:: bash - python3 -m rocpd convert -i db1.db --output-format pftrace + /opt/rocm/bin/rocpd convert -i db1.db --output-format pftrace -Convert two databases to Perfetto trace, set output path and filename, and limit to last 70% of trace: +**Multi-Database Conversion with Temporal Filtering:** + +Convert multiple databases to Perfetto format, specifying custom output directory and filename, with temporal window constraint to the final 70% of the trace duration: .. code-block:: bash - python3 -m rocpd convert -i db1.db db2.db --output-format pftrace -d "./output/" -o "twoFileTraces" --start 30% --end 100% + /opt/rocm/bin/rocpd convert -i db1.db db2.db --output-format pftrace -d "./output/" -o "twoFileTraces" --start 30% --end 100% + +**Batch Conversion to Multiple Formats:** -Convert six databases to CSV and Perfetto trace formats: +Process six database files simultaneously, generating both CSV and Perfetto trace outputs with custom output configuration: .. code-block:: bash - python3 -m rocpd convert -i db{0..5}.db --output-format csv pftrace -d "~/output_folder/" -o "sixFileTraces" + /opt/rocm/bin/rocpd convert -i db{0..5}.db --output-format csv pftrace -d "~/output_folder/" -o "sixFileTraces" + +**Comprehensive Format Conversion:** -Convert two databases to CSV, OTF2, and Perfetto trace formats: +Convert multiple databases to all supported formats (CSV, OTF2, and Perfetto trace) in a single operation: .. code-block:: bash - python3 -m rocpd convert -i db{3,4}.db --output-format csv otf2 pftrace + /opt/rocm/bin/rocpd convert -i db{3,4}.db --output-format csv otf2 pftrace diff --git a/source/docs/how-to/using-rocprofv3.rst b/source/docs/how-to/using-rocprofv3.rst index 938efac996..4f0e82699a 100644 --- a/source/docs/how-to/using-rocprofv3.rst +++ b/source/docs/how-to/using-rocprofv3.rst @@ -169,11 +169,17 @@ The following table lists the commonly used ``rocprofv3`` command-line options c * - Other - | ``--preload`` PRELOAD |br| |br| - | ``--minimum-output-data`` |br| |br| - | ``--disable-signal-handlers`` - - | Specifies libraries to prepend to ``LD_PRELOAD``. It is useful for sanitizer libraries. |br| |br| - | Output files are generated only if output data size is greater than minimum output data size. It can be used for controlling the generation of output files so that user don't recieve empty files. The input is in KB units. |br| |br| - | Disables the signal handlers in the rocprofv3 tool. It disables the prioritizing of rocprofv3 signal handler over application installed signal handler. When --disable-signal-handlers is set to true, and application has its signal handler on SIGSEGV or similar installed, then its signal handler will be used not the rocprofv3 signal handler. Note: glog still installs signal handlers which provide backtraces. + | ``--minimum-output-data`` KB |br| |br| + | ``--disable-signal-handlers`` [BOOL] |br| |br| + | ``--rocm-root`` PATH |br| |br| + | ``--sdk-soversion`` SDK_SOVERSION |br| |br| + | ``--sdk-version`` SDK_VERSION + - | Specifies libraries to prepend to ``LD_PRELOAD``. Useful for sanitizer libraries and custom instrumentation tools. Multiple libraries can be specified. |br| |br| + | Specifies the minimum output data size threshold in KB. Output files are generated only if the collected profiling data exceeds this threshold. This prevents creation of empty or very small output files. Default is 0 (no threshold). |br| |br| + | Controls signal handler prioritization. When set to true, disables rocprofv3 signal handler prioritization, allowing application signal handlers to take precedence. Useful for applications with custom crash handling or when integrating with testing frameworks. Default is false (rocprofv3 handlers have priority). |br| |br| + | Specifies custom ROCm installation directory instead of automatic detection. Useful for multiple ROCm installations, custom builds, or non-standard locations. |br| |br| + | Specifies the shared object version number for ROCProfiler SDK library resolution. Controls which major version of librocprofiler-sdk.so.X to use. |br| |br| + | Specifies the exact version number for ROCProfiler SDK library resolution. Controls library selection with full semantic versioning (X.Y.Z format). To see exhaustive list of ``rocprofv3`` options: @@ -780,6 +786,12 @@ To see the counters available on the GPU, use: rocprofv3 --list-avail +Sample output for the list-avail command: + +.. file:: /data/list-avail.txt + :width: 100% + :align: center + You can also customize the counters according to the requirement. Such counters are named :ref:`extra-counters`. For a comprehensive list of counters available on MI200, see `MI200 performance counters and metrics `_. @@ -1118,6 +1130,565 @@ Advanced options ``rocprofv3`` provides the following miscellaneous functionalities for improved control and flexibility. +Minimum output data threshold ++++++++++++++++++++++++++++++ + +The ``--minimum-output-data`` option allows you to control the generation of output files by setting a minimum data size threshold. This prevents the creation of empty or very small output files that contain no meaningful profiling data. + +When this option is specified, ``rocprofv3`` only generates output files if the collected data size exceeds the specified threshold. This is particularly useful in scenarios where: + +- You're profiling applications that may have sporadic GPU activity +- You want to avoid processing empty trace files in automated workflows +- You're running batch jobs and only want meaningful results + +To specify the minimum output data threshold, use the ``--minimum-output-data`` option followed by the size in KB: + +.. code-block:: bash + + rocprofv3 --minimum-output-data 100 --hip-trace --output-format csv -- + +The preceding command only generates output files if the HIP trace data is larger than 100 KB. + +**Example scenarios:** + +**Scenario 1: Filtering out applications with minimal GPU activity** + +.. code-block:: bash + + # Only generate output if kernel trace data > 50 KB + rocprofv3 --minimum-output-data 50 --kernel-trace --output-format csv -- + +**Scenario 2: Batch profiling with meaningful data collection** + +.. code-block:: bash + + # For system tracing, only output files if data > 1 MB + rocprofv3 --minimum-output-data 1024 --sys-trace --output-format pftrace -- + +**Using with input files:** + +You can also specify this option in YAML or JSON input files: + +.. code-block:: yaml + + jobs: + - hip_trace: true + kernel_trace: true + minimum_output_data: 100 + output_format: ["csv", "json"] + output_directory: "filtered_results" + +.. code-block:: json + + { + "jobs": [ + { + "hip_trace": true, + "kernel_trace": true, + "minimum_output_data": 100, + "output_format": ["csv", "json"], + "output_directory": "filtered_results" + } + ] + } + +**Important notes:** + +- The threshold applies to the raw profiling data size, not the final output file size +- If multiple output formats are specified, the threshold check applies to each format independently +- A value of 0 (default) means all output files are generated regardless of size +- This option works with all tracing and counter collection modes + +This feature is especially valuable in automated testing environments where you want to ensure that only applications with meaningful GPU activity generate profiling outputs, reducing storage overhead and simplifying result analysis. + +Signal handler control +++++++++++++++++++++++ + +The ``--disable-signal-handlers`` option provides control over signal handling behavior in ``rocprofv3``, allowing you to manage how the profiler responds to system signals like SIGSEGV, SIGTERM, and others. + +By default, ``rocprofv3`` installs its own signal handlers to ensure proper cleanup and data collection when the application encounters errors or is terminated. However, in some scenarios, you may want the application's own signal handlers to take precedence. + +When ``--disable-signal-handlers`` is set to ``true``, ``rocprofv3`` disables the prioritization of its signal handlers over application-installed signal handlers. This means: + +- If your application has custom signal handlers for SIGSEGV, SIGTERM, or similar signals, those handlers will be executed instead of ``rocprofv3``'s handlers +- The application maintains full control over signal handling behavior +- ``rocprofv3`` will still attempt to collect and save profiling data when possible + +**Important note**: Even with this option enabled, the underlying ``glog`` library may still install signal handlers that provide stack backtraces for debugging purposes. + +**Basic usage:** + +.. code-block:: bash + + rocprofv3 --disable-signal-handlers --hip-trace --output-format csv -- + +The preceding command disables ``rocprofv3`` signal handler prioritization, allowing the application's signal handlers to take precedence. + +**Example scenarios:** + +**Scenario 1: Application with custom crash handling** + +.. code-block:: bash + + # For applications that implement custom crash reporting or recovery + rocprofv3 --disable-signal-handlers --sys-trace --output-format pftrace -- ./my_app_with_custom_handlers + +**Scenario 2: Debugging applications with existing signal handlers** + +.. code-block:: bash + + # When debugging applications that rely on specific signal handling behavior + rocprofv3 --disable-signal-handlers --kernel-trace --pmc SQ_WAVES -- ./debug_application + +**Scenario 3: Integration with testing frameworks** + +.. code-block:: bash + + # For test frameworks that need to handle signals for test orchestration + rocprofv3 --disable-signal-handlers --runtime-trace --output-directory test_results -- ./test_suite + +**Using with input files:** + +You can also specify this option in YAML or JSON input files: + +.. code-block:: yaml + + jobs: + - hip_trace: true + kernel_trace: true + disable_signal_handlers: true + output_format: ["csv", "json"] + output_directory: "custom_signal_handling" + +.. code-block:: json + + { + "jobs": [ + { + "hip_trace": true, + "kernel_trace": true, + "disable_signal_handlers": true, + "output_format": ["csv", "json"], + "output_directory": "custom_signal_handling" + } + ] + } + +**When to use this option:** + + **Use when:** +- Your application has custom signal handlers that must execute +- You're integrating with testing frameworks that manage signals +- Debugging applications where signal handling behavior is critical +- Working with applications that implement custom crash reporting + + **Avoid when:** +- You want ``rocprofv3`` to provide maximum protection against data loss +- Your application doesn't have custom signal handlers +- You're doing standard profiling where signal handling isn't a concern + +**Example: Application with custom SIGSEGV handler** + +If your application has a custom segmentation fault handler: + +.. code-block:: cpp + + #include + #include + + void custom_sigsegv_handler(int sig) { + printf("Custom SIGSEGV handler called\n"); + // Custom crash reporting logic + exit(1); + } + + int main() { + signal(SIGSEGV, custom_sigsegv_handler); + + // Application code that might trigger SIGSEGV + return 0; + } + +Use ``--disable-signal-handlers`` to ensure your custom handler executes: + +.. code-block:: bash + + rocprofv3 --disable-signal-handlers --hip-trace -- ./app_with_custom_handler + +**Troubleshooting:** + +- If profiling data appears incomplete with this option enabled, check if your application's signal handlers are properly saving or flushing data +- Consider implementing explicit ``rocprofv3`` cleanup calls in your application's signal handlers if data integrity is important +- Monitor application behavior to ensure custom signal handling doesn't interfere with profiling data collection + +This option provides the flexibility needed for complex applications and testing environments while maintaining ``rocprofv3``'s core profiling functionality. + +Library preloading ++++++++++++++++++++ + +The ``--preload`` option allows you to specify additional libraries to prepend to the ``LD_PRELOAD`` environment variable. This is particularly useful when working with sanitizer libraries, debugging tools, or other instrumentation libraries that need to be loaded before the application starts. + +``LD_PRELOAD`` is a powerful mechanism in Linux that allows you to load shared libraries before any other libraries, effectively intercepting and overriding function calls. The ``--preload`` option in ``rocprofv3`` provides a convenient way to manage this without manually setting environment variables. + +**Basic usage:** + +.. code-block:: bash + + rocprofv3 --preload /path/to/library.so --hip-trace --output-format csv -- + +The preceding command preloads the specified library and enables HIP tracing. + +**Example scenarios:** + +**Scenario 1: Using AddressSanitizer (ASan)** + +.. code-block:: bash + + # Preload AddressSanitizer for memory error detection + rocprofv3 --preload /usr/lib/x86_64-linux-gnu/libasan.so.5 --sys-trace -- ./my_application + +**Scenario 2: Using ThreadSanitizer (TSan)** + +.. code-block:: bash + + # Preload ThreadSanitizer for race condition detection + rocprofv3 --preload /usr/lib/x86_64-linux-gnu/libtsan.so.0 --kernel-trace --pmc SQ_WAVES -- ./threaded_app + +**Scenario 3: Multiple preloaded libraries** + +.. code-block:: bash + + # Preload multiple libraries (custom profiler and sanitizer) + rocprofv3 --preload /opt/custom/libprofiler.so /usr/lib/libasan.so --runtime-trace -- ./complex_app + +**Scenario 4: Using MemorySanitizer (MSan)** + +.. code-block:: bash + + # Preload MemorySanitizer for uninitialized memory detection + rocprofv3 --preload /usr/lib/x86_64-linux-gnu/libmsan.so.0 --hip-trace -- ./memory_intensive_app + +**Using with input files:** + +You can also specify this option in YAML or JSON input files: + +.. code-block:: yaml + + jobs: + - hip_trace: true + kernel_trace: true + preload: + - "/usr/lib/x86_64-linux-gnu/libasan.so.5" + - "/opt/custom/libprofiler.so" + output_format: ["csv"] + +.. code-block:: json + + { + "jobs": [ + { + "hip_trace": true, + "kernel_trace": true, + "preload": [ + "/usr/lib/x86_64-linux-gnu/libasan.so.5", + "/opt/custom/libprofiler.so" + ], + "output_format": ["csv"] + } + ] + } + +**Common use cases:** + +**Sanitizer libraries:** +- AddressSanitizer (``libasan.so``) for memory error detection +- ThreadSanitizer (``libtsan.so``) for race condition detection +- MemorySanitizer (``libmsan.so``) for uninitialized memory detection +- UndefinedBehaviorSanitizer (``libubsan.so``) for undefined behavior detection + + **Debugging and profiling tools:** +- Custom memory allocators (``jemalloc``, ``tcmalloc``) +- Performance profiling libraries +- Custom instrumentation libraries +- Mock libraries for testing + + **Third-party analysis tools:** +- Valgrind replacement libraries +- Custom logging frameworks +- Security analysis tools + +**Library order considerations:** + +The order of libraries in ``--preload`` matters as they are processed in the order specified: + +.. code-block:: bash + + # Library1 will be loaded before Library2 + rocprofv3 --preload /path/to/library1.so /path/to/library2.so --hip-trace -- ./app + +**Environment variable interaction:** + +The ``--preload`` option works alongside existing ``LD_PRELOAD`` settings: + +.. code-block:: bash + + # If LD_PRELOAD is already set, --preload libraries are prepended + export LD_PRELOAD="/existing/library.so" + rocprofv3 --preload /new/library.so --hip-trace -- ./app + # Effective LD_PRELOAD: "/new/library.so:/existing/library.so" + +**Troubleshooting:** + +- **Library not found**: Ensure the library path is correct and the library exists +- **Symbol conflicts**: Check for conflicting symbols between preloaded libraries +- **Performance impact**: Sanitizers can significantly slow down execution +- **Memory usage**: Some tools like AddressSanitizer increase memory consumption substantially + +ROCm root path configuration +++++++++++++++++++++++++++++ + +The ``--rocm-root`` option allows you to specify a custom ROCm installation directory instead of using the default relative path detection. This is useful when working with multiple ROCm installations, custom builds, or non-standard installation locations. + +By default, ``rocprofv3`` automatically detects the ROCm installation path relative to its own location. However, in some environments, you may need to explicitly specify which ROCm installation to use. + +**Basic usage:** + +.. code-block:: bash + + rocprofv3 --rocm-root /opt/custom-rocm --hip-trace --output-format csv -- + +The preceding command uses the ROCm installation located at ``/opt/custom-rocm``. + +**Example scenarios:** + +**Scenario 1: Multiple ROCm versions** + +.. code-block:: bash + + # Use ROCm 5.7.0 specifically + rocprofv3 --rocm-root /opt/rocm-5.7.0 --sys-trace -- ./app_for_rocm_5_7 + + # Use ROCm 6.0.0 for comparison + rocprofv3 --rocm-root /opt/rocm-6.0.0 --sys-trace -- ./app_for_rocm_6_0 + +**Scenario 2: Custom ROCm build** + +.. code-block:: bash + + # Use custom ROCm build with debugging symbols + rocprofv3 --rocm-root /home/developer/rocm-debug-build --kernel-trace --pmc SQ_WAVES -- ./debug_app + +**Scenario 3: Development environment** + +.. code-block:: bash + + # Use locally built ROCm for development + rocprofv3 --rocm-root /workspace/rocm-dev --runtime-trace -- ./test_application + +**Scenario 4: Container environments** + +.. code-block:: bash + + # Use ROCm mounted at custom location in container + rocprofv3 --rocm-root /usr/local/rocm --hip-trace -- ./containerized_app + +**Directory structure requirements:** + +The specified ROCm root path should contain the standard ROCm directory structure: + +.. code-block:: shell + + /opt/custom-rocm/ + ├── bin/ # ROCm executables + ├── lib/ # ROCm libraries + ├── include/ # ROCm headers + ├── share/ # Shared resources + └── ... + +**Using with input files:** + +This option is typically used from the command line, but can be specified in wrapper scripts: + +.. code-block:: bash + + #!/bin/bash + # profile_with_custom_rocm.sh + ROCM_PATH="/opt/rocm-custom" + rocprofv3 --rocm-root "$ROCM_PATH" -i input.yaml -- "$@" + +**Environment variable interaction:** + +The ``--rocm-root`` option overrides automatic path detection and environment variables like ``ROCM_PATH``: + +.. code-block:: bash + + # --rocm-root takes precedence over environment variables + export ROCM_PATH="/opt/rocm-default" + rocprofv3 --rocm-root /opt/rocm-override --hip-trace -- ./app + # Uses /opt/rocm-override, not /opt/rocm-default + +**Validation and troubleshooting:** + +- **Path validation**: Ensure the specified path contains a valid ROCm installation +- **Library compatibility**: Verify that the ROCm version is compatible with your application +- **Permission issues**: Check read permissions for the ROCm directory +- **Path format**: Use absolute paths to avoid ambiguity + +SDK shared object version control +++++++++++++++++++++++++++++++++++ + +The ``--sdk-soversion`` option allows you to specify the shared object version number for the ROCProfiler SDK library. This provides precise control over which version of the library is loaded, useful for testing, compatibility verification, or working with specific library versions. + +Shared object versioning follows the Linux convention where libraries have version suffixes like ``.so.X`` where X is the major version number. This option helps resolve library paths when multiple versions are installed. + +**Basic usage:** + +.. code-block:: bash + + rocprofv3 --sdk-soversion 2 --hip-trace --output-format csv -- + +The preceding command uses ``librocprofiler-sdk.so.2`` instead of the default version. + +**Example scenarios:** + +**Scenario 1: Testing with specific library version** + +.. code-block:: bash + + # Test application with SDK version 1 + rocprofv3 --sdk-soversion 1 --kernel-trace --pmc SQ_WAVES -- ./app_v1_test + + # Test same application with SDK version 2 + rocprofv3 --sdk-soversion 2 --kernel-trace --pmc SQ_WAVES -- ./app_v2_test + +**Scenario 2: Compatibility verification** + +.. code-block:: bash + + # Verify backward compatibility with older SDK + rocprofv3 --sdk-soversion 0 --sys-trace -- ./legacy_application + +**Scenario 3: Development and testing** + +.. code-block:: bash + + # Use specific version for regression testing + rocprofv3 --sdk-soversion 3 --runtime-trace --output-directory regression_test -- ./test_suite + +**Scenario 4: Production environment pinning** + +.. code-block:: bash + + # Pin to specific version for production consistency + rocprofv3 --sdk-soversion 1 --hip-trace --minimum-output-data 100 -- ./production_app + +**Library resolution behavior:** + +The option affects library loading in the following order: + +1. ``librocprofiler-sdk.so.X`` (where X is the specified soversion) +2. Fallback to default library if specific version not found + +**Using with scripts:** + +.. code-block:: bash + + #!/bin/bash + # test_matrix.sh - Test with multiple SDK versions + for version in 0 1 2; do + echo "Testing with SDK SO version $version" + rocprofv3 --sdk-soversion $version --hip-trace -- ./test_app + done + +**Troubleshooting:** + +- **Library not found**: Verify the specified soversion exists in the library path +- **ABI compatibility**: Ensure the SDK version is compatible with your ROCm installation +- **Symbol mismatches**: Check for symbol compatibility between versions +- **Performance differences**: Different versions may have performance characteristics + +SDK version specification ++++++++++++++++++++++++++ + +The ``--sdk-version`` option allows you to specify the exact version number for the ROCProfiler SDK library resolution. This provides the finest level of control over library selection, useful for testing specific versions, development workflows, or ensuring reproducible profiling environments. + +This option helps resolve library paths for version-specific libraries like ``librocprofiler-sdk.so.X.Y.Z`` where X.Y.Z represents the full semantic version. + +**Basic usage:** + +.. code-block:: bash + + rocprofv3 --sdk-version 1.2.3 --hip-trace --output-format csv -- + +The preceding command uses ``librocprofiler-sdk.so.1.2.3`` if available. + +**Example scenarios:** + +**Scenario 1: Exact version testing** + +.. code-block:: bash + + # Test with specific patch version for bug verification + rocprofv3 --sdk-version 2.1.5 --kernel-trace -- ./bug_reproduction_case + + # Test with fixed version + rocprofv3 --sdk-version 2.1.6 --kernel-trace -- ./bug_verification_case + +**Scenario 2: Reproducible profiling** + +.. code-block:: bash + + # Ensure exact same SDK version for reproducible results + rocprofv3 --sdk-version 2.2.1 --pmc SQ_WAVES GRBM_COUNT --output-format pftrace -- ./benchmark_app + +**Version format support:** + +The option supports various version formats: + +- **Semantic versioning**: ``1.2.3``, ``2.0.0``, ``1.5.10`` + +**Library resolution priority:** + +When ``--sdk-version`` is specified, the library resolution follows this order: + +1. ``librocprofiler-sdk.so.X.Y.Z`` (exact version match) +2. ``librocprofiler-sdk.so.X.Y`` (major.minor match) +3. ``librocprofiler-sdk.so.X`` (major version match) +4. Default library (``librocprofiler-sdk.so``) + +**Using with input files:** + +While typically used from command line, it can be scripted: + +.. code-block:: bash + + #!/bin/bash + # version_matrix_test.sh + VERSIONS=("2.1.0" "2.1.1" "2.1.2" "2.2.0") + + for version in "${VERSIONS[@]}"; do + echo "Testing SDK version $version" + rocprofv3 --sdk-version "$version" --hip-trace --output-directory "results_$version" -- ./test_app + done + +**Combined with other version options:** + +.. code-block:: bash + + # Combine with soversion for maximum control + rocprofv3 --sdk-version 2.1.5 --sdk-soversion 2 --hip-trace -- ./app + + # Combine with custom ROCm root + rocprofv3 --rocm-root /opt/rocm-6.0 --sdk-version 2.2.0 --sys-trace -- ./app + +**Environment integration:** + +.. code-block:: bash + + # Use environment variable for version + export ROCPROF_SDK_VERSION="2.1.3" + rocprofv3 --sdk-version "$ROCPROF_SDK_VERSION" --kernel-trace -- ./app + Agent index ++++++++++++++ @@ -1522,16 +2093,16 @@ The following table lists the various fields or the columns in the output CSV fi - The group segment memory required by a workgroup in bytes. This does not include any dynamically allocated group segment memory that may be added when the kernel is dispatched. * - Workgroup_Size - - Size of the workgroup as declared by the compute shader. + - The total number of work-items (or, threads) in each workgroup (or, block) launched as part of the kernel dispatch. In HIP, this is equivalent to the total block size. * - Workgroup_Size_n - Size of the workgroup in the nth dimension as declared by the compute shader, where n = X, Y, or Z. * - Grid_Size - - Number of thread blocks required to launch the kernel. + - The total number of work-items (or, threads) launched as a part of the kernel dispatch. In HIP, this is equivalent to the total grid size multiplied by the total workgroup (or, block) size. * - Grid_Size_n - - Number of thread blocks in the nth dimension required to launch the kernel, where n = X, Y, or Z. + - Number of work-items (or, threads) in the nth dimension required to launch the kernel, where n = X, Y, or Z. * - LDS_Block_Size - Thread block size for the kernel's Local Data Share (LDS) memory. diff --git a/source/include/rocprofiler-sdk/context.h b/source/include/rocprofiler-sdk/context.h index af3a22d2b3..4d4a321ae6 100644 --- a/source/include/rocprofiler-sdk/context.h +++ b/source/include/rocprofiler-sdk/context.h @@ -38,7 +38,7 @@ ROCPROFILER_EXTERN_C_INIT /** * The NULL Context handle. */ -#define ROCPROFILER_CONTEXT_NONE ROCPROFILER_HANDLE_LITERAL(rocprofiler_context_id_t, UINT64_MAX) +#define ROCPROFILER_CONTEXT_NONE ROCPROFILER_HANDLE_LITERAL(rocprofiler_context_id_t, 0UL) /** * @brief Create context. diff --git a/source/include/rocprofiler-sdk/counters.h b/source/include/rocprofiler-sdk/counters.h index a63544a8b2..9562ed8e07 100644 --- a/source/include/rocprofiler-sdk/counters.h +++ b/source/include/rocprofiler-sdk/counters.h @@ -50,24 +50,83 @@ typedef struct ROCPROFILER_SDK_EXPERIMENTAL rocprofiler_counter_info_v0_t uint8_t is_derived : 1; ///< If this counter is a derived counter } rocprofiler_counter_info_v0_t; +/** + * @brief (experimental) Represents metadata about a single dimension of a counter instance. + * + * This structure provides the name of the dimension (e.g., "XCC", "SE", etc.) + * and the index indicating the position of a specific counter instance within that dimension. + */ +typedef struct ROCPROFILER_SDK_EXPERIMENTAL rocprofiler_counter_dimension_info_t +{ + uint64_t size; ///< Size of this structure. Used for versioning and validation. + const char* dimension_name; + size_t index; + + /** + * @var dimension_name + * @brief Name of the dimension this instance belongs to. + + * @var index + * @brief Position (zero-based) of the instance within the specified dimension. + */ + +} rocprofiler_counter_dimension_info_t; + +/** + * @brief (experimental) Describes a specific counter instance and its position across multiple + * dimensions. + * + * This structure provides the unique instance ID, associated counter ID, number of dimensions for + * the instance, and a pointer to an array of metadata describing each dimension's name and index. + */ +typedef struct ROCPROFILER_SDK_EXPERIMENTAL rocprofiler_counter_record_dimension_instance_info_t +{ + uint64_t size; ///< Size of this structure. Used for versioning and validation. + rocprofiler_counter_instance_id_t instance_id; + uint64_t counter_id; + uint64_t dimensions_count; + const rocprofiler_counter_dimension_info_t** dimensions; + + /** + * @var instance_id + * @brief Encoded identifier for the instance, which includes the counter ID and all dimension + positions. + + * @var counter_id + * @brief Identifier of the counter associated with this instance. + + * @var dimensions_count + * @brief Number of dimensions associated with this instance. + + * @var dimensions + * @brief Array of pointers to dimension info structures, each representing one dimension + * and the position of this instance within that dimension. + * + * The array has `dimensions_count` elements, and each element is a pointer to a + * `rocprofiler_counter_dimension_info_t` structure. + */ + +} rocprofiler_counter_record_dimension_instance_info_t; + /** * @brief (experimental) Counter info struct version 1. Combines information from * ::rocprofiler_counter_info_v0_t with the dimension information. */ typedef struct ROCPROFILER_SDK_EXPERIMENTAL rocprofiler_counter_info_v1_t { - rocprofiler_counter_id_t id; ///< Id of this counter - const char* name; ///< Name of the counter + uint64_t size; ///< Size of this structure. Used for versioning and validation. + rocprofiler_counter_id_t id; ///< Id of this counter + const char* name; ///< Name of the counter const char* description; ///< Description of the counter const char* block; ///< Block of the counter (non-derived only) const char* expression; ///< Counter expression (derived counters only) uint8_t is_constant : 1; ///< If this counter is HW constant uint8_t is_derived : 1; ///< If this counter is a derived counter - uint64_t dimensions_count; - const rocprofiler_counter_record_dimension_info_t* dimensions; - uint64_t instance_ids_count; - const rocprofiler_counter_instance_id_t* instance_ids; + uint64_t dimensions_count; + const rocprofiler_counter_record_dimension_info_t** dimensions; + uint64_t dimensions_instances_count; + const rocprofiler_counter_record_dimension_instance_info_t** dimensions_instances; /// @var dimensions_count /// @brief Number of dimensions for the counter @@ -75,11 +134,12 @@ typedef struct ROCPROFILER_SDK_EXPERIMENTAL rocprofiler_counter_info_v1_t /// @var dimensions /// @brief Dimension information of the counter /// - /// @var instance_ids_count - /// @brief Number of instance ids for the counter + /// @var dimensions_instances_count + /// @brief Number of unique instances for this counter, across all dimension combinations. /// - /// @var instance_ids - /// @brief Instance ids that can be generated by the counter + /// @var dimensions_instances + /// @brief Array of pointers to instance info structs, each describing a unique instance + /// and its specific dimension mapping. } rocprofiler_counter_info_v1_t; /** diff --git a/source/include/rocprofiler-sdk/cxx/CMakeLists.txt b/source/include/rocprofiler-sdk/cxx/CMakeLists.txt index f4d1a93c1a..ddd0844172 100644 --- a/source/include/rocprofiler-sdk/cxx/CMakeLists.txt +++ b/source/include/rocprofiler-sdk/cxx/CMakeLists.txt @@ -14,3 +14,4 @@ install( add_subdirectory(codeobj) add_subdirectory(details) add_subdirectory(serialization) +add_subdirectory(container) diff --git a/source/include/rocprofiler-sdk/cxx/codeobj/code_printing.hpp b/source/include/rocprofiler-sdk/cxx/codeobj/code_printing.hpp index 0ff1a6ada0..515c0c1907 100644 --- a/source/include/rocprofiler-sdk/cxx/codeobj/code_printing.hpp +++ b/source/include/rocprofiler-sdk/cxx/codeobj/code_printing.hpp @@ -25,11 +25,13 @@ #include "disassembly.hpp" #include "segment.hpp" +#include #include #include #include #include +#include #include #include #include @@ -62,6 +64,8 @@ struct Instruction size_t size{0}; uint64_t ld_addr{0}; // Instruction load address, if from loaded codeobj marker_id_t codeobj_id{0}; // Instruction code object load id, if from loaded codeobj + + static constexpr std::string_view separator = " -> "; }; class CodeobjDecoderComponent @@ -111,11 +115,19 @@ class CodeobjDecoderComponent 0) { Dwarf_Die die; - if(!dwarf_offdie(dbg.get(), cu_offset + header_size, &die)) continue; + if(!dwarf_offdie(dbg.get(), cu_offset + header_size, &die)) + { + cu_offset = next_offset; + continue; + } Dwarf_Lines* lines; size_t line_count; - if(dwarf_getsrclines(&die, &lines, &line_count) != 0) continue; + if(dwarf_getsrclines(&die, &lines, &line_count) != 0) + { + cu_offset = next_offset; + continue; + } for(size_t i = 0; i < line_count; ++i) { @@ -126,16 +138,22 @@ class CodeobjDecoderComponent if(line && dwarf_lineaddr(line, &addr) == 0 && dwarf_lineno(line, &line_number) == 0 && line_number != 0) { - std::string src = dwarf_linesrc(line, nullptr, nullptr); - auto dwarf_line = src + ':' + std::to_string(line_number); + std::string src = dwarf_linesrc(line, nullptr, nullptr); + auto dwarf_line = src + ':' + std::to_string(line_number); + auto call_stack_info = extractInlinedCallStackInfo(dbg.get(), addr); + + size_t capacity = dwarf_line.size() + + Instruction::separator.size() * call_stack_info.size(); + for(const auto& call : call_stack_info) + capacity += call.size(); - if(line_addrs.find(addr) != line_addrs.end()) + dwarf_line.reserve(capacity); + for(const auto& call : call_stack_info) { - line_addrs.at(addr) += ' ' + dwarf_line; - continue; + dwarf_line += Instruction::separator; + dwarf_line += call; } - - line_addrs.emplace(addr, std::move(dwarf_line)); + line_addrs[addr] = std::move(dwarf_line); } } cu_offset = next_offset; @@ -192,6 +210,79 @@ class CodeobjDecoderComponent std::unique_ptr disassembly{}; std::map m_line_number_map{}; + +private: + /** + * @brief Extracts inlined function call stack information for a given address + * + * This function searches through DWARF debug information to find all inlined functions + * that contain the specified address, building a complete call stack from the outermost + * function down to the innermost inlined function. + * + * @param dbg DWARF debug information handle + * @param addr The address to analyze for inlined function information + * @return Vector of strings representing the call stack, formatted as "filename:line" + * The stack is ordered from caller to callee (outermost to innermost) + */ + static std::vector extractInlinedCallStackInfo(Dwarf* dbg, Dwarf_Addr addr); + + /** + * @brief Checks if a DWARF Debug Information Entry (DIE) contains a specific address + * + * This function recursively searches through a DIE and its children to determine + * if any of them contain the specified address within their address ranges. + * Used as an optimization to quickly determine if a compilation unit or function + * contains the target address before doing expensive traversal. + * + * @param die Pointer to the DWARF DIE to check + * @param addr The address to search for + * @return true if the DIE or any of its children contain the address, false otherwise + */ + static bool checkDIEContainsAddress(Dwarf_Die* die, Dwarf_Addr addr); + + /** + * @brief Recursively traverses all DWARF DIEs to find inlined functions at a specific address + * + * This function performs a depth-first traversal of the DWARF debug information tree, + * checking each DIE for inlined function information that covers the specified address. + * It processes both the current DIE and all its children (including siblings at each level) + * to ensure comprehensive coverage of all possible inlined function contexts. + * + * The traversal is necessary because inlined functions can be nested (function A inlines + * function B which inlines function C) and multiple inlined functions can exist at the + * same scope level as siblings in the DWARF tree. + * + * @param die Pointer to the current DWARF DIE to examine + * @param addr The address to search for inlined function information + * @param call_stack Reference to vector that accumulates the call stack information + */ + static void traverseAllDIEs(Dwarf_Die* die, + Dwarf_Addr addr, + std::vector& call_stack); + + /** + * @brief Examines a specific DWARF DIE for inlined function information at an address + * + * This function checks if a given DIE represents an inlined subroutine that contains + * the specified address. If it does, it extracts the call site information (filename + * and line number where the function was inlined) and adds it to the call stack. + * + * The function specifically looks for DW_TAG_inlined_subroutine DIEs and validates + * that the address falls within the DIE's address range (either contiguous via + * low_pc/high_pc or non-contiguous via ranges attribute). It then extracts the + * call site information using DW_AT_call_file and DW_AT_call_line attributes. + * + * @param die Pointer to the DWARF DIE to examine + * @param addr The address to check against the DIE's address ranges + * @param call_stack Reference to vector where call site info will be added + * + * @note Only processes DW_TAG_inlined_subroutine DIEs. Regular functions + * (DW_TAG_subprogram) are ignored since this function specifically + * extracts inlined function call information. + */ + static void checkDIEForInlinedFunction(Dwarf_Die* die, + Dwarf_Addr addr, + std::vector& call_stack); }; class LoadedCodeobjDecoder @@ -230,8 +321,7 @@ class LoadedCodeobjDecoder : load_addr(_load_addr) , load_end(load_addr + _memsize) { - decoder = - std::make_unique(reinterpret_cast(data), size); + decoder = std::make_unique(static_cast(data), size); } std::unique_ptr get(uint64_t ld_addr) { @@ -433,6 +523,170 @@ class CodeobjAddressTranslate : public CodeobjMap segment::CodeobjTableTranslator table{}; }; +inline std::vector +CodeobjDecoderComponent::extractInlinedCallStackInfo(Dwarf* dbg, Dwarf_Addr addr) +{ + std::vector call_stack{}; + + // Iterate through all compilation units to find the one containing our address + Dwarf_Off cu_offset{}; + Dwarf_Off next_offset{}; + size_t header_size{}; + + while(dwarf_nextcu(dbg, cu_offset, &next_offset, &header_size, nullptr, nullptr, nullptr) == 0) + { + Dwarf_Die cu_die{}; + if(!dwarf_offdie(dbg, cu_offset + header_size, &cu_die)) + { + cu_offset = next_offset; + continue; + } + + bool cu_contains_addr = false; + + // Try to get low_pc and high_pc from CU + // If no simple range, check if any child DIE contains this address + Dwarf_Addr low_pc{}; + Dwarf_Addr high_pc{}; + if(dwarf_lowpc(&cu_die, &low_pc) == 0 && dwarf_highpc(&cu_die, &high_pc) == 0) + cu_contains_addr = (addr >= low_pc && addr < high_pc); + else + cu_contains_addr = checkDIEContainsAddress(&cu_die, addr); + + if(cu_contains_addr) + { + traverseAllDIEs(&cu_die, addr, call_stack); + break; + } + + cu_offset = next_offset; + } + + // Reverse the call stack to show from caller to callee + std::reverse(call_stack.begin(), call_stack.end()); + + return call_stack; +} + +inline bool +CodeobjDecoderComponent::checkDIEContainsAddress(Dwarf_Die* die, Dwarf_Addr addr) +{ + if(die == nullptr) return false; + // Check current DIE's address range + Dwarf_Addr low_pc{}; + Dwarf_Addr high_pc{}; + if(dwarf_lowpc(die, &low_pc) == 0 && dwarf_highpc(die, &high_pc) == 0) + { + if(addr >= low_pc && addr < high_pc) return true; + } + else + { + // Check ranges attribute for non-contiguous ranges + Dwarf_Addr base{}; + ptrdiff_t offset = 0; + while((offset = dwarf_ranges(die, offset, &base, &low_pc, &high_pc)) > 0) + if(addr >= low_pc && addr < high_pc) return true; + } + + // Check children recursively + Dwarf_Die child{}; + if(dwarf_child(die, &child) == 0) + if(checkDIEContainsAddress(&child, addr)) return true; + + return false; +} + +inline void +CodeobjDecoderComponent::traverseAllDIEs(Dwarf_Die* die, + Dwarf_Addr addr, + std::vector& call_stack) +{ + if(die == nullptr) return; + // Check current DIE for inlined function information + checkDIEForInlinedFunction(die, addr, call_stack); + + // Traverse children recursively (depth-first) + Dwarf_Die child{}; + if(dwarf_child(die, &child) == 0) + { + // Check all children AND their siblings at this level + // This is crucial because inlined functions can appear as siblings + // when multiple functions are inlined at the same scope level + do + { + traverseAllDIEs(&child, addr, call_stack); + } while(dwarf_siblingof(&child, &child) == 0); + } +} + +inline void +CodeobjDecoderComponent::checkDIEForInlinedFunction(Dwarf_Die* die, + Dwarf_Addr addr, + std::vector& call_stack) +{ + // Only process inlined subroutines - these are functions that were + // expanded inline at compile time and have call site information + if(die == nullptr || dwarf_tag(die) != DW_TAG_inlined_subroutine) return; + + Dwarf_Addr low_pc{}; + Dwarf_Addr high_pc{}; + bool has_range{false}; + + // Check if this inlined subroutine covers the target address + // First try simple contiguous range (low_pc to high_pc) + + if(dwarf_lowpc(die, &low_pc) == 0 && dwarf_highpc(die, &high_pc) == 0) + { + // Simple contiguous range - check if address falls within + has_range = (addr >= low_pc && addr < high_pc); + } + else + { + // Function may have non-contiguous ranges (optimized code) + // Check all address ranges associated with this DIE + Dwarf_Addr base{}; + ptrdiff_t offset{}; + + while((offset = dwarf_ranges(die, offset, &base, &low_pc, &high_pc)) > 0) + { + if(addr >= low_pc && addr < high_pc) + { + has_range = true; + break; + } + } + } + + // If address doesn't fall within this inlined function, skip it + if(!has_range) return; + + // Extract call site information - where this function was inlined + Dwarf_Attribute call_file_attr{}; + Dwarf_Attribute call_line_attr{}; + Dwarf_Word call_file{}; + Dwarf_Word call_line{}; + + // Get the file and line number where this function was called/inlined + + if(!dwarf_attr(die, DW_AT_call_file, &call_file_attr) || + !dwarf_attr(die, DW_AT_call_line, &call_line_attr) || + dwarf_formudata(&call_file_attr, &call_file) != 0 || + dwarf_formudata(&call_line_attr, &call_line) != 0) + return; // No call site information available + + // Get the compilation unit to resolve file names + Dwarf_Die cu_die{}; + if(!dwarf_diecu(die, &cu_die, nullptr, nullptr)) return; + + // Get the source files table for this compilation unit + Dwarf_Files* files{}; + size_t nfiles{}; + if(dwarf_getsrcfiles(&cu_die, &files, &nfiles) == 0 && call_file < nfiles) + if(const char* filename = dwarf_filesrc(files, call_file, nullptr, nullptr)) + // Add "filename:line" to call stack showing where this function was inlined + call_stack.push_back(std::string(filename) + ":" + std::to_string(call_line)); +} + } // namespace disassembly } // namespace codeobj } // namespace sdk diff --git a/source/include/rocprofiler-sdk/cxx/container/CMakeLists.txt b/source/include/rocprofiler-sdk/cxx/container/CMakeLists.txt new file mode 100644 index 0000000000..f100a22cf4 --- /dev/null +++ b/source/include/rocprofiler-sdk/cxx/container/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# +# Installation of public C++ headers (implementations) +# +# +set(ROCPROFILER_CXX_CONTAINERS_HEADER_FILES c_array.hpp) + +install( + FILES ${ROCPROFILER_CXX_CONTAINERS_HEADER_FILES} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/rocprofiler-sdk/cxx/container + COMPONENT development) diff --git a/source/include/rocprofiler-sdk/cxx/container/c_array.hpp b/source/include/rocprofiler-sdk/cxx/container/c_array.hpp new file mode 100644 index 0000000000..2c120b2bf1 --- /dev/null +++ b/source/include/rocprofiler-sdk/cxx/container/c_array.hpp @@ -0,0 +1,136 @@ +// MIT License +// +// Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#pragma once + +#include +#include +#include +#include + +namespace rocprofiler +{ +namespace sdk +{ +namespace container +{ +template +struct c_array +{ + // Construct an array wrapper from a base pointer and array size + c_array(Tp* _base, size_t _size) + : m_base{_base} + , m_size{_size} + {} + + ~c_array() = default; + c_array(const c_array&) = default; + c_array(c_array&&) noexcept = default; + c_array& operator=(const c_array&) = default; + c_array& operator=(c_array&&) noexcept = default; + + // Get the size of the wrapped array + size_t size() const { return m_size; } + + // Access an element by index + Tp& operator[](size_t i) { return m_base[i]; } + + // Access an element by index + const Tp& operator[](size_t i) const { return m_base[i]; } + + // Access an element by index with bounds check + Tp& at(size_t i) + { + if(i < m_size) return m_base[i]; + throw std::out_of_range(std::string{typeid(*this).name()} + std::to_string(i) + + " exceeds size " + std::to_string(m_size)); + } + + // Access an element by index with bounds check + const Tp& at(size_t i) const + { + if(i < m_size) return m_base[i]; + throw std::out_of_range(std::string{typeid(*this).name()} + std::to_string(i) + + " exceeds size " + std::to_string(m_size)); + } + + // Get a slice of this array, from a start index (inclusive) to end index (exclusive) + c_array slice(size_t start, size_t end) { return c_array(&m_base[start], end - start); } + + void pop_front() + { + ++m_base; + --m_size; + } + + void pop_back() { --m_size; } + + operator Tp*() const { return m_base; } + + // Iterator class for convenient range-based for loop support + template + struct iterator + { + // Start the iterator at a given pointer + iterator(Tp* p) + : m_ptr{p} + {} + + // Advance to the next element + void operator++() { ++m_ptr; } + void operator++(int) { m_ptr++; } + + // Get the current element + Up& operator*() const { return *m_ptr; } + + // Compare iterators + bool operator==(const iterator& rhs) const { return m_ptr == rhs.m_ptr; } + bool operator!=(const iterator& rhs) const { return m_ptr != rhs.m_ptr; } + + private: + Tp* m_ptr = nullptr; + }; + + // Get an iterator positioned at the beginning of the wrapped array + iterator begin() { return iterator{m_base}; } + iterator begin() const { return iterator{m_base}; } + + // Get an iterator positioned at the end of the wrapped array + iterator end() { return iterator{&m_base[m_size]}; } + iterator end() const { return iterator{&m_base[m_size]}; } + +private: + Tp* m_base = nullptr; + size_t m_size = 0; +}; + +// Function for automatic template argument deduction +template +c_array +make_c_array(Tp* base, size_t size) +{ + return c_array(base, size); +} +} // namespace container +} // namespace sdk +} // namespace rocprofiler diff --git a/source/include/rocprofiler-sdk/cxx/hash.hpp b/source/include/rocprofiler-sdk/cxx/hash.hpp index 6ba6eade8f..200a48f26f 100644 --- a/source/include/rocprofiler-sdk/cxx/hash.hpp +++ b/source/include/rocprofiler-sdk/cxx/hash.hpp @@ -23,6 +23,7 @@ #pragma once +#include #include #include #include @@ -70,6 +71,7 @@ ROCPROFILER_CXX_SPECIALIZE_HANDLE_HASHER(hsa_signal_t) ROCPROFILER_CXX_SPECIALIZE_HANDLE_HASHER(hsa_executable_t) ROCPROFILER_CXX_SPECIALIZE_HANDLE_HASHER(hsa_region_t) ROCPROFILER_CXX_SPECIALIZE_HANDLE_HASHER(hsa_amd_memory_pool_t) +ROCPROFILER_CXX_SPECIALIZE_HANDLE_HASHER(rocprofiler_thread_trace_decoder_id_t) #undef ROCPROFILER_CXX_SPECIALIZE_HANDLE_HASHER } // namespace std diff --git a/source/include/rocprofiler-sdk/cxx/operators.hpp b/source/include/rocprofiler-sdk/cxx/operators.hpp index 9580cfa5c1..d09956e124 100644 --- a/source/include/rocprofiler-sdk/cxx/operators.hpp +++ b/source/include/rocprofiler-sdk/cxx/operators.hpp @@ -24,11 +24,16 @@ #pragma once #include +#include #include +#include #include #include #include +#include "rocprofiler-sdk/cxx/details/mpl.hpp" + +#include #include #define ROCPROFILER_CXX_DECLARE_OPERATORS(TYPE) \ @@ -88,6 +93,37 @@ less(Tp lhs, Tp rhs) static_assert(sizeof(Tp) == sizeof(uint64_t), "error! only for opaque handle types"); return lhs.handle < rhs.handle; } + +namespace detail +{ +template +struct sv_trait +{ + static constexpr auto is_string_type() noexcept + { + return mpl::is_string_type>::value; + } + + using type = std::conditional_t; + + constexpr type operator()(Tp& v) noexcept + { + if constexpr(is_string_type()) + return std::string_view{v}; + else + return v; + } +}; +} // namespace detail + +// tie_sv(): deduce each Tp from your lvalues, then build tuple<...> out of +// either Tp& or std::string_view, calling operator() on each. +template +constexpr auto +tie_sv(Tp&... vs) noexcept +{ + return std::make_tuple(detail::sv_trait{}(vs)...); +} } // namespace operators } // namespace sdk } // namespace rocprofiler @@ -110,7 +146,10 @@ ROCPROFILER_CXX_DECLARE_OPERATORS(rocprofiler_dim3_t) ROCPROFILER_CXX_DECLARE_OPERATORS(hsa_region_t) ROCPROFILER_CXX_DECLARE_OPERATORS(hsa_amd_memory_pool_t) ROCPROFILER_CXX_DECLARE_OPERATORS(const rocprofiler_counter_record_dimension_info_t&) +ROCPROFILER_CXX_DECLARE_OPERATORS(const rocprofiler_counter_record_dimension_instance_info_t&) +ROCPROFILER_CXX_DECLARE_OPERATORS(const rocprofiler_counter_dimension_info_t&) ROCPROFILER_CXX_DECLARE_OPERATORS(rocprofiler_version_triplet_t) +ROCPROFILER_CXX_DECLARE_OPERATORS(rocprofiler_thread_trace_decoder_id_t) // definitions of operator== ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR(rocprofiler_context_id_t) @@ -127,6 +166,7 @@ ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR(hsa_signal_t) ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR(hsa_executable_t) ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR(hsa_region_t) ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR(hsa_amd_memory_pool_t) +ROCPROFILER_CXX_DEFINE_EQ_HANDLE_OPERATOR(rocprofiler_thread_trace_decoder_id_t) inline bool operator==(const rocprofiler_agent_v0_t& lhs, const rocprofiler_agent_v0_t& rhs) @@ -141,11 +181,43 @@ operator==(rocprofiler_dim3_t lhs, rocprofiler_dim3_t rhs) } inline bool -operator==(rocprofiler_counter_record_dimension_info_t lhs, - rocprofiler_counter_record_dimension_info_t rhs) +operator==(const rocprofiler_counter_record_dimension_info_t& lhs, + const rocprofiler_counter_record_dimension_info_t& rhs) { - return std::tie(lhs.id, lhs.instance_size, lhs.name) == - std::tie(rhs.id, rhs.instance_size, rhs.name); + namespace op = ::rocprofiler::sdk::operators; + return op::tie_sv(lhs.id, lhs.instance_size, lhs.name) == + op::tie_sv(rhs.id, rhs.instance_size, rhs.name); +} + +inline bool +operator==(const rocprofiler_counter_dimension_info_t& lhs, + const rocprofiler_counter_dimension_info_t& rhs) +{ + namespace op = ::rocprofiler::sdk::operators; + return op::tie_sv(lhs.dimension_name, lhs.index, lhs.size) == + op::tie_sv(rhs.dimension_name, rhs.index, rhs.size); +} + +inline bool +operator==(const rocprofiler_counter_record_dimension_instance_info_t& lhs, + const rocprofiler_counter_record_dimension_instance_info_t& rhs) +{ + if(std::tie(lhs.instance_id, lhs.counter_id, lhs.dimensions_count) != + std::tie(rhs.instance_id, rhs.counter_id, rhs.dimensions_count)) + { + return false; + } + + for(uint64_t i = 0; i < lhs.dimensions_count; ++i) + { + if(lhs.dimensions[i] == nullptr && rhs.dimensions[i] == nullptr) continue; + if(!lhs.dimensions[i] || !rhs.dimensions[i] || *lhs.dimensions[i] != *rhs.dimensions[i]) + { + return false; + } + } + + return true; } inline bool @@ -172,6 +244,7 @@ ROCPROFILER_CXX_DEFINE_NE_OPERATOR(rocprofiler_dim3_t) ROCPROFILER_CXX_DEFINE_NE_OPERATOR(hsa_region_t) ROCPROFILER_CXX_DEFINE_NE_OPERATOR(hsa_amd_memory_pool_t) ROCPROFILER_CXX_DEFINE_NE_OPERATOR(rocprofiler_version_triplet_t) +ROCPROFILER_CXX_DEFINE_NE_OPERATOR(rocprofiler_thread_trace_decoder_id_t) // definitions of operator< ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_context_id_t) @@ -188,13 +261,41 @@ ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_signal_t) ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_executable_t) ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_region_t) ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(hsa_amd_memory_pool_t) +ROCPROFILER_CXX_DEFINE_LT_HANDLE_OPERATOR(rocprofiler_thread_trace_decoder_id_t) inline bool operator<(const rocprofiler_counter_record_dimension_info_t& lhs, const rocprofiler_counter_record_dimension_info_t& rhs) { - return std::tie(lhs.id, lhs.instance_size, lhs.name) < - std::tie(rhs.id, rhs.instance_size, rhs.name); + namespace op = ::rocprofiler::sdk::operators; + return op::tie_sv(lhs.id, lhs.instance_size, lhs.name) < + op::tie_sv(rhs.id, rhs.instance_size, rhs.name); +} + +inline bool +operator<(const rocprofiler_counter_dimension_info_t& lhs, + const rocprofiler_counter_dimension_info_t& rhs) +{ + namespace op = ::rocprofiler::sdk::operators; + return op::tie_sv(lhs.dimension_name, lhs.index, lhs.size) < + op::tie_sv(rhs.dimension_name, rhs.index, rhs.size); +} + +inline bool +operator<(const rocprofiler_counter_record_dimension_instance_info_t& lhs, + const rocprofiler_counter_record_dimension_instance_info_t& rhs) +{ + if(lhs.instance_id != rhs.instance_id) return lhs.instance_id < rhs.instance_id; + if(lhs.counter_id != rhs.counter_id) return lhs.counter_id < rhs.counter_id; + if(lhs.dimensions_count != rhs.dimensions_count) + return lhs.dimensions_count < rhs.dimensions_count; + + for(uint64_t i = 0; i < lhs.dimensions_count; ++i) + { + if(!lhs.dimensions[i] || !rhs.dimensions[i]) return *lhs.dimensions[i] < *rhs.dimensions[i]; + } + + return false; } inline bool @@ -238,6 +339,7 @@ ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_dim3_t) ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(hsa_region_t) ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(hsa_amd_memory_pool_t) ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_version_triplet_t) +ROCPROFILER_CXX_DEFINE_COMPARE_OPERATORS(rocprofiler_thread_trace_decoder_id_t) // cleanup defines #undef ROCPROFILER_CXX_DECLARE_OPERATORS diff --git a/source/include/rocprofiler-sdk/cxx/serialization/save.hpp b/source/include/rocprofiler-sdk/cxx/serialization/save.hpp index d2edc5c274..8af9a76031 100644 --- a/source/include/rocprofiler-sdk/cxx/serialization/save.hpp +++ b/source/include/rocprofiler-sdk/cxx/serialization/save.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -1132,7 +1133,7 @@ template void save(ArchiveT& ar, rocprofiler_uuid_t data) { - ROCP_SDK_SAVE_DATA_FIELD(value); + ROCP_SDK_SAVE_DATA_FIELD(bytes); } template @@ -1236,16 +1237,33 @@ save(ArchiveT& ar, rocprofiler_counter_info_v1_t data) ROCP_SDK_SAVE_DATA_CSTR(block); ROCP_SDK_SAVE_DATA_CSTR(expression); - auto convert = [](const auto* val, uint64_t sz) { - using data_type = std::remove_cv_t>; - auto retval = std::vector{}; - for(uint64_t i = 0; i < sz; ++i) - retval.emplace_back(val[i]); - return retval; - }; + ROCP_SDK_SAVE_VALUE( + "dimensions", + rocprofiler::sdk::container::make_c_array(data.dimensions, data.dimensions_count)); - ROCP_SDK_SAVE_VALUE("dims", convert(data.dimensions, data.dimensions_count)); - ROCP_SDK_SAVE_VALUE("instances", convert(data.instance_ids, data.instance_ids_count)); + ROCP_SDK_SAVE_VALUE("instances", + rocprofiler::sdk::container::make_c_array(data.dimensions_instances, + data.dimensions_instances_count)); +} + +template +void +save(ArchiveT& ar, rocprofiler_counter_dimension_info_t data) +{ + ROCP_SDK_SAVE_DATA_CSTR(dimension_name); + ROCP_SDK_SAVE_DATA_FIELD(index); +} + +template +void +save(ArchiveT& ar, rocprofiler_counter_record_dimension_instance_info_t data) +{ + ROCP_SDK_SAVE_DATA_FIELD(instance_id); + ROCP_SDK_SAVE_DATA_FIELD(counter_id); + + ROCP_SDK_SAVE_VALUE( + "dimensions", + rocprofiler::sdk::container::make_c_array(data.dimensions, data.dimensions_count)); } template @@ -1303,6 +1321,28 @@ save(ArchiveT& ar, const rocprofiler::sdk::utility::name_info_impl +void +save(ArchiveT& ar, const rocprofiler::sdk::container::c_array& data) +{ + ar(make_size_tag(data.size())); + for(auto itr : data) + ar(itr); +} + +template +void +save(ArchiveT& ar, const rocprofiler::sdk::container::c_array& data) +{ + size_type _sz = 0; + for(auto* itr : data) + if(itr) ++_sz; + + ar(make_size_tag(_sz)); + for(auto* itr : data) + ar(*itr); +} + ROCPROFILER_SDK_CEREAL_NAMESPACE_END #undef ROCP_SDK_SAVE_DATA_FIELD diff --git a/source/include/rocprofiler-sdk/experimental/thread-trace/trace_decoder.h b/source/include/rocprofiler-sdk/experimental/thread-trace/trace_decoder.h index 746e21278b..b2e34f2c1e 100644 --- a/source/include/rocprofiler-sdk/experimental/thread-trace/trace_decoder.h +++ b/source/include/rocprofiler-sdk/experimental/thread-trace/trace_decoder.h @@ -36,10 +36,10 @@ ROCPROFILER_EXTERN_C_INIT /** * @brief Handle containing a loaded rocprof-trace-decoder and a decoder state. */ -typedef struct rocprofiler_thread_trace_decoder_handle_t +typedef struct rocprofiler_thread_trace_decoder_id_t { uint64_t handle; -} rocprofiler_thread_trace_decoder_handle_t; +} rocprofiler_thread_trace_decoder_id_t; /** * @brief Initializes Trace Decoder library with a library search path @@ -51,7 +51,7 @@ typedef struct rocprofiler_thread_trace_decoder_handle_t * @retval ::ROCPROFILER_STATUS_SUCCESS Handle created */ rocprofiler_status_t -rocprofiler_thread_trace_decoder_create(rocprofiler_thread_trace_decoder_handle_t* handle, +rocprofiler_thread_trace_decoder_create(rocprofiler_thread_trace_decoder_id_t* handle, const char* path) ROCPROFILER_API ROCPROFILER_NONNULL(1, 2); /** @@ -59,7 +59,7 @@ rocprofiler_thread_trace_decoder_create(rocprofiler_thread_trace_decoder_handle_ * @param[in] handle Handle to destroy */ void -rocprofiler_thread_trace_decoder_destroy(rocprofiler_thread_trace_decoder_handle_t handle) +rocprofiler_thread_trace_decoder_destroy(rocprofiler_thread_trace_decoder_id_t handle) ROCPROFILER_API; /** @@ -83,11 +83,11 @@ rocprofiler_thread_trace_decoder_destroy(rocprofiler_thread_trace_decoder_handle * @retval ::ROCPROFILER_STATUS_SUCCESS Code object loaded */ rocprofiler_status_t -rocprofiler_thread_trace_decoder_codeobj_load(rocprofiler_thread_trace_decoder_handle_t handle, - uint64_t load_id, - uint64_t load_addr, - uint64_t load_size, - const void* data, +rocprofiler_thread_trace_decoder_codeobj_load(rocprofiler_thread_trace_decoder_id_t handle, + uint64_t load_id, + uint64_t load_addr, + uint64_t load_size, + const void* data, uint64_t size) ROCPROFILER_API ROCPROFILER_NONNULL(5); /** @@ -100,7 +100,7 @@ rocprofiler_thread_trace_decoder_codeobj_load(rocprofiler_thread_trace_decoder_h * @retval ::ROCPROFILER_STATUS_SUCCESS Code object unloaded */ rocprofiler_status_t -rocprofiler_thread_trace_decoder_codeobj_unload(rocprofiler_thread_trace_decoder_handle_t handle, +rocprofiler_thread_trace_decoder_codeobj_unload(rocprofiler_thread_trace_decoder_id_t handle, uint64_t load_id) ROCPROFILER_API; /** @@ -135,7 +135,7 @@ typedef void (*rocprofiler_thread_trace_decoder_callback_t)( * @retval ::ROCPROFILER_STATUS_SUCCESS on success */ rocprofiler_status_t -rocprofiler_trace_decode(rocprofiler_thread_trace_decoder_handle_t handle, +rocprofiler_trace_decode(rocprofiler_thread_trace_decoder_id_t handle, rocprofiler_thread_trace_decoder_callback_t callback, void* data, uint64_t size, @@ -148,8 +148,8 @@ rocprofiler_trace_decode(rocprofiler_thread_trace_decoder_handle_t handle, * @retval null terminated string as description of "info". */ const char* -rocprofiler_thread_trace_decoder_info_string(rocprofiler_thread_trace_decoder_handle_t handle, - rocprofiler_thread_trace_decoder_info_t info) +rocprofiler_thread_trace_decoder_info_string(rocprofiler_thread_trace_decoder_id_t handle, + rocprofiler_thread_trace_decoder_info_t info) ROCPROFILER_API; /** @} */ diff --git a/source/include/rocprofiler-sdk/fwd.h b/source/include/rocprofiler-sdk/fwd.h index 12d1813214..d8e3705746 100644 --- a/source/include/rocprofiler-sdk/fwd.h +++ b/source/include/rocprofiler-sdk/fwd.h @@ -572,10 +572,9 @@ typedef union rocprofiler_address_t * @brief Stores UUID for devices. * */ -typedef union rocprofiler_uuid_t +typedef struct rocprofiler_uuid_t { - uint64_t value; ///< numerical value - void* bytes; ///< uuid in hexadecimal + uint8_t bytes[16]; // numerical value } rocprofiler_uuid_t; //--------------------------------------------------------------------------------------// diff --git a/source/lib/att-tool/att_lib_wrapper.cpp b/source/lib/att-tool/att_lib_wrapper.cpp index e2bf7842c0..d07800b36f 100644 --- a/source/lib/att-tool/att_lib_wrapper.cpp +++ b/source/lib/att-tool/att_lib_wrapper.cpp @@ -41,9 +41,9 @@ namespace rocprofiler { namespace att_wrapper { -ATTFileMgr::ATTFileMgr(Fspath _dir, - std::vector _counters, - rocprofiler_thread_trace_decoder_handle_t _decoder) +ATTFileMgr::ATTFileMgr(Fspath _dir, + std::vector _counters, + rocprofiler_thread_trace_decoder_id_t _decoder) : dir(std::move(_dir)) , decoder(_decoder) { diff --git a/source/lib/att-tool/att_lib_wrapper.hpp b/source/lib/att-tool/att_lib_wrapper.hpp index 58e3f6deb6..b5a8dcb493 100644 --- a/source/lib/att-tool/att_lib_wrapper.hpp +++ b/source/lib/att-tool/att_lib_wrapper.hpp @@ -71,7 +71,7 @@ class ATTDecoder bool valid() const; protected: - rocprofiler_thread_trace_decoder_handle_t decoder{}; + rocprofiler_thread_trace_decoder_id_t decoder{}; }; class ATTFileMgr @@ -79,9 +79,9 @@ class ATTFileMgr using AddressTable = rocprofiler::sdk::codeobj::disassembly::CodeobjAddressTranslate; public: - ATTFileMgr(Fspath _dir, - std::vector _counters, - rocprofiler_thread_trace_decoder_handle_t _decoder); + ATTFileMgr(Fspath _dir, + std::vector _counters, + rocprofiler_thread_trace_decoder_id_t _decoder); ~ATTFileMgr(); void addDecoder(const char* filepath, uint64_t id, uint64_t load_addr, uint64_t memsize); @@ -95,7 +95,7 @@ class ATTFileMgr std::shared_ptr table{nullptr}; std::map> occupancy{}; std::vector codeobjs_to_delete{}; - rocprofiler_thread_trace_decoder_handle_t decoder{}; + rocprofiler_thread_trace_decoder_id_t decoder{}; std::array, ROCPROFILER_THREAD_TRACE_DECODER_WSTATE_LAST> wstates; diff --git a/source/lib/att-tool/code.cpp b/source/lib/att-tool/code.cpp index 7d48306e70..b1cb6ed77f 100644 --- a/source/lib/att-tool/code.cpp +++ b/source/lib/att-tool/code.cpp @@ -151,13 +151,24 @@ CodeFile::~CodeFile() jcode.push_back(nlohmann::json::parse(code.str())); - size_t lineref = isa.code_line->comment.rfind(':'); - if(lineref == 0 || lineref == std::string::npos) continue; + auto& comment = isa.code_line->comment; + size_t lineref = comment.find(':'); + size_t previous = 0; - auto source_ref = isa.code_line->comment.substr(0, lineref); + // size() + 2 because we need at least ':' and one number after + while(lineref != std::string::npos && lineref < comment.size() + 2) + { + auto source_ref = comment.substr(previous, lineref - previous); + + if(!source_ref.empty() && snapshots.find(source_ref) == snapshots.end()) + snapshots.insert(std::move(source_ref)); - if(!source_ref.empty() && snapshots.find(source_ref) == snapshots.end()) - snapshots.insert(std::move(source_ref)); + previous = comment.find(CodeLine::Instruction::separator, lineref); + if(previous == std::string::npos) break; + + previous += CodeLine::Instruction::separator.size(); + lineref = comment.find(':', previous); + } } nlohmann::json json; diff --git a/source/lib/att-tool/profile_interface.cpp b/source/lib/att-tool/profile_interface.cpp index f20348617d..1d50c422df 100644 --- a/source/lib/att-tool/profile_interface.cpp +++ b/source/lib/att-tool/profile_interface.cpp @@ -103,9 +103,9 @@ get_trace_data(rocprofiler_thread_trace_decoder_record_type_t trace_id, C_API_END } -ToolData::ToolData(std::vector& _data, - WaveConfig& _config, - rocprofiler_thread_trace_decoder_handle_t _decoder) +ToolData::ToolData(std::vector& _data, + WaveConfig& _config, + rocprofiler_thread_trace_decoder_id_t _decoder) : cfile(_config.code) , config(_config) , decoder(_decoder) diff --git a/source/lib/att-tool/profile_interface.hpp b/source/lib/att-tool/profile_interface.hpp index dc08f1c28d..54d38835b1 100644 --- a/source/lib/att-tool/profile_interface.hpp +++ b/source/lib/att-tool/profile_interface.hpp @@ -60,9 +60,9 @@ using SymbolInfo = rocprofiler::sdk::codeobj::disassembly::SymbolInfo; struct ToolData { - ToolData(std::vector& data, - WaveConfig& config, - rocprofiler_thread_trace_decoder_handle_t decoder); + ToolData(std::vector& data, + WaveConfig& config, + rocprofiler_thread_trace_decoder_id_t decoder); ~ToolData(); CodeLine& get(pcinfo_t pc); @@ -73,7 +73,7 @@ struct ToolData std::vector shader_data{}; size_t num_waves = 0; - rocprofiler_thread_trace_decoder_handle_t decoder{}; + rocprofiler_thread_trace_decoder_id_t decoder{}; }; } // namespace att_wrapper diff --git a/source/lib/common/hasher.hpp b/source/lib/common/hasher.hpp index 22b0b2326d..00da3c496f 100644 --- a/source/lib/common/hasher.hpp +++ b/source/lib/common/hasher.hpp @@ -28,7 +28,9 @@ #include #include +#include #include +#include #include namespace rocprofiler @@ -53,17 +55,20 @@ struct fnv1a_hasher // Hashes a numeric value. template ::value, bool> = true> - void update(Tp data); + fnv1a_hasher& update(Tp data); + + template + fnv1a_hasher& update(const std::optional& data); // Using the loop instead of "update(str, strlen(str))" to avoid looping twice - void update(const char* str); + fnv1a_hasher& update(const char* str); // Hashes a byte array. - void update(const char* data, size_t size); + fnv1a_hasher& update(const char* data, size_t size); - void update(std::string_view s) { update(s.data(), s.size()); } + fnv1a_hasher& update(std::string_view s) { return update(s.data(), s.size()); } - void update(const std::string& s) { update(s.data(), s.size()); } + fnv1a_hasher& update(const std::string& s) { return update(s.data(), s.size()); } // Usage: uint64_t hashed_value = Hash::combine(33, false, "ABC", 458L, 3u, 'x'); template @@ -72,7 +77,7 @@ struct fnv1a_hasher // fnv1a_hasher.update_all(33, false, "ABC")` is shorthand for calling fnv1a_hasher.update(...) // for each value in the same order template - void update_all(Tp&& arg, TailT&&... args); + fnv1a_hasher& update_all(Tp&& arg, TailT&&... args); uint64_t digest() const { return m_result; } @@ -84,13 +89,20 @@ struct fnv1a_hasher }; template ::value, bool>> -void +fnv1a_hasher& fnv1a_hasher::update(Tp data) { - update(reinterpret_cast(&data), sizeof(data)); + return update(reinterpret_cast(&data), sizeof(data)); +} + +template +fnv1a_hasher& +fnv1a_hasher::update(const std::optional& data) +{ + return (data) ? update(*data) : *this; } -inline void +inline fnv1a_hasher& fnv1a_hasher::update(const char* str) { constexpr auto max_n = 4096; @@ -101,9 +113,10 @@ fnv1a_hasher::update(const char* str) update(*p); if(++n >= max_n) break; // prevent infinite loop } + return *this; } -inline void +inline fnv1a_hasher& fnv1a_hasher::update(const char* data, size_t size) { for(size_t i = 0; i < size; ++i) @@ -113,23 +126,23 @@ fnv1a_hasher::update(const char* data, size_t size) // signed integers. m_result *= kFnv1a64Prime; } + return *this; } template uint64_t fnv1a_hasher::combine(Tp&& arg, TailT&&... args) { - auto _hasher = fnv1a_hasher{}; - _hasher.update_all(std::forward(arg), std::forward(args)...); - return _hasher.digest(); + return fnv1a_hasher{}.update_all(std::forward(arg), std::forward(args)...).digest(); } template -void +fnv1a_hasher& fnv1a_hasher::update_all(Tp&& arg, TailT&&... args) { update(arg); if constexpr(sizeof...(TailT) > 0) update_all(std::forward(args)...); + return *this; } } // namespace common } // namespace rocprofiler diff --git a/source/lib/output/csv.hpp b/source/lib/output/csv.hpp index 90a7c98f05..0bb0c9e7fb 100644 --- a/source/lib/output/csv.hpp +++ b/source/lib/output/csv.hpp @@ -112,6 +112,21 @@ using pc_sampling_host_trap_csv_encoder = csv_encoder<6>; using kernel_trace_with_stream_csv_encoder = csv_encoder<22>; using memory_copy_with_stream_csv_encoder = csv_encoder<8>; using pc_sampling_stochastic_csv_encoder = csv_encoder<10>; + +using api_csv_encoder_guid = csv_encoder<8>; +using agent_info_csv_encoder_guid = csv_encoder<54>; +using counter_collection_csv_encoder_guid = csv_encoder<20>; +using memory_allocation_csv_encoder_guid = csv_encoder<9>; +using marker_csv_encoder_guid = csv_encoder<8>; +using list_basic_metrics_csv_encoder_guid = csv_encoder<6>; +using list_derived_metrics_csv_encoder_guid = csv_encoder<6>; +using scratch_memory_encoder_guid = csv_encoder<10>; +using stats_csv_encoder_guid = csv_encoder<9>; +using pc_sampling_host_trap_csv_encoder_guid = csv_encoder<7>; +using kernel_trace_with_stream_csv_encoder_guid = csv_encoder<23>; +using memory_copy_with_stream_csv_encoder_guid = csv_encoder<9>; +using pc_sampling_stochastic_csv_encoder_guid = csv_encoder<11>; + } // namespace csv } // namespace tool } // namespace rocprofiler diff --git a/source/lib/output/generateOTF2.cpp b/source/lib/output/generateOTF2.cpp index 7c31f8b187..d95894fecb 100644 --- a/source/lib/output/generateOTF2.cpp +++ b/source/lib/output/generateOTF2.cpp @@ -491,9 +491,8 @@ write_otf2(const output_config& cfg, { // Free functions do not track agent information. Below handles case where // null rocprof agent id is passed to generate OTF2 - constexpr auto null_rocp_agent_id = - rocprofiler_agent_id_t{.handle = std::numeric_limits::max()}; - const rocprofiler_agent_t* _agent = nullptr; + constexpr auto null_rocp_agent_id = rocprofiler_agent_id_t{.handle = 0}; + const rocprofiler_agent_t* _agent = nullptr; if(agent != null_rocp_agent_id) { _agent = _get_agent(agent); @@ -896,9 +895,8 @@ write_otf2(const output_config& cfg, auto _hash = get_hash_id(evt.name); // Using max numeric limits results in an out-of-bound runtime error for OTF2 // and perfetto for agent ids. Setting handle to 0 for free functions. - constexpr auto null_rocp_agent_id = - rocprofiler_agent_id_t{.handle = std::numeric_limits::max()}; - auto handle = agent.handle; + constexpr auto null_rocp_agent_id = rocprofiler_agent_id_t{.handle = 0}; + auto handle = agent.handle; if(agent == null_rocp_agent_id) handle = 0; add_write_string(_hash, evt.name); diff --git a/source/lib/output/generatePerfetto.cpp b/source/lib/output/generatePerfetto.cpp index 56e365819c..315fa7a642 100644 --- a/source/lib/output/generatePerfetto.cpp +++ b/source/lib/output/generatePerfetto.cpp @@ -837,8 +837,7 @@ write_perfetto( } // memory allocation counter track - constexpr auto null_rocp_agent_id = - rocprofiler_agent_id_t{.handle = std::numeric_limits::max()}; + constexpr auto null_rocp_agent_id = rocprofiler_agent_id_t{.handle = 0}; struct free_memory_information { rocprofiler_timestamp_t start_timestamp = 0; @@ -855,9 +854,8 @@ write_perfetto( struct agent_and_size { - rocprofiler_agent_id_t agent_id = - rocprofiler_agent_id_t{.handle = std::numeric_limits::max()}; - uint64_t size = {0}; + rocprofiler_agent_id_t agent_id = rocprofiler_agent_id_t{.handle = 0}; + uint64_t size = {0}; }; auto mem_alloc_endpoints = diff --git a/source/lib/output/generateRocpd.cpp b/source/lib/output/generateRocpd.cpp index 6899919ade..b1cf68c993 100644 --- a/source/lib/output/generateRocpd.cpp +++ b/source/lib/output/generateRocpd.cpp @@ -970,46 +970,45 @@ write_rocpd( } }; - auto insert_kernel_dispatch_data = [&conn, &tool_metadata, &string_entries, node_id, this_pid]( - const auto& _gen, auto& dispatch_evt_ids) { + auto insert_kernel_dispatch_data = [&, node_id, this_pid](auto& dispatch_evt_ids) { auto _sqlgenperf_rocpd = get_simple_timer("rocpd_kernel_dispatch"); - for(auto pitr : _gen) - { - auto _deferred = sql::deferred_transaction{conn}; - for(auto itr : _gen.get(pitr)) + auto process_dispatch = [&](uint64_t dispatch_id, + uint64_t kernel_id, + const auto& corr_id, + const auto& info, + const auto& kind, + uint32_t thread_id, + uint64_t queue_id, + uint64_t stream_id, + uint64_t start_timestamp, + uint64_t end_timestamp, + const auto& grid, + const auto& workgroup, + bool enable_duplicate_check) { + // Skip if we've already processed this dispatch_id + if(dispatch_evt_ids.size() > dispatch_id && dispatch_evt_ids[dispatch_id] != 0) return; + + auto kern_name = (kernel_id > 0) + ? tool_metadata.get_kernel_symbol(kernel_id)->formatted_kernel_name + : "unknown_kernel"; + + auto evt_id = create_event(conn, + { + insert_value("category_id", string_entries.at(kind)), + insert_value("stack_id", corr_id.internal), + insert_value("parent_stack_id", corr_id.internal), + insert_value("correlation_id", corr_id.external.value), + }); + + // Ensure dispatch_evt_ids is large enough + if(dispatch_evt_ids.size() < dispatch_id + 1) + common::container::resize(dispatch_evt_ids, dispatch_id + 1, 0UL); + + // Check for duplicates if requested + if(enable_duplicate_check && dispatch_evt_ids.at(dispatch_id) != 0) { - // insert thread info if it doesn't already exist - get_thread_id(itr.thread_id); - - auto kind = tool_metadata.buffer_names.at(itr.kind); - auto info = itr.dispatch_info; - auto kernel_id = info.kernel_id; - auto dispatch_id = info.dispatch_id; - auto corr_id = itr.correlation_id; - auto grid = info.grid_size; - auto workgroup = info.workgroup_size; - auto kern_name = tool_metadata.get_kernel_symbol(kernel_id)->formatted_kernel_name; - auto stream_id = get_stream_id(itr.stream_id); - auto queue_id = get_queue_id(info.queue_id); - auto region_name = - (corr_id.external.value > 0) - ? tool_metadata.get_kernel_name(kernel_id, corr_id.external.value) - : std::string_view{}; - - auto evt_id = - create_event(conn, - { - insert_value("category_id", string_entries.at(kind)), - insert_value("stack_id", corr_id.internal), - insert_value("parent_stack_id", corr_id.internal), - insert_value("correlation_id", corr_id.external.value), - }); - - if(dispatch_evt_ids.size() < dispatch_id + 1) - common::container::resize(dispatch_evt_ids, dispatch_id + 1, 0UL); - - ROCP_CI_LOG_IF(WARNING, dispatch_evt_ids.at(dispatch_id) != 0) + ROCP_CI_LOG(WARNING) << fmt::format("duplicate kernel dispatch id {} :: event_id={}, kernel_id={}, " "corr_id={}, name='{}'", dispatch_id, @@ -1017,45 +1016,114 @@ write_rocpd( kernel_id, corr_id.internal, kern_name); + } - dispatch_evt_ids.at(dispatch_id) = evt_id; + dispatch_evt_ids.at(dispatch_id) = evt_id; - auto stmt = get_insert_statement( - "rocpd_kernel_dispatch{{uuid}}", - { - insert_value("id", dispatch_id), - insert_value("nid", node_id), - insert_value("pid", this_pid), - insert_value("tid", itr.thread_id), - insert_value("agent_id", tool_metadata.get_agent(info.agent_id)->node_id), - insert_value("kernel_id", kernel_id), - insert_value("dispatch_id", dispatch_id), - insert_value("queue_id", queue_id), - insert_value("stream_id", stream_id), - insert_value("start", itr.start_timestamp), - insert_value("end", itr.end_timestamp), - insert_value("private_segment_size", info.private_segment_size), - insert_value("group_segment_size", info.group_segment_size), - insert_value("workgroup_size_x", workgroup.x), - insert_value("workgroup_size_y", workgroup.y), - insert_value("workgroup_size_z", workgroup.z), - insert_value("grid_size_x", grid.x), - insert_value("grid_size_y", grid.y), - insert_value("grid_size_z", grid.z), - insert_value("region_name_id", string_entries.at(region_name)), - insert_value("event_id", evt_id), - }); + auto region_name = + (corr_id.external.value > 0 && (enable_duplicate_check || kernel_id > 0)) + ? tool_metadata.get_kernel_name(kernel_id, corr_id.external.value) + : std::string_view{}; - execute_raw_sql_statements(conn, stmt); + auto agent_node_id = tool_metadata.get_agent(info.agent_id)->node_id; + + // Insert into kernel dispatch table + auto stmt = get_insert_statement( + "rocpd_kernel_dispatch{{uuid}}", + { + insert_value("id", dispatch_id), + insert_value("nid", node_id), + insert_value("pid", this_pid), + insert_value("tid", thread_id), + insert_value("agent_id", agent_node_id), + insert_value("kernel_id", kernel_id), + insert_value("dispatch_id", dispatch_id), + insert_value("queue_id", queue_id), + insert_value("stream_id", stream_id), + insert_value("start", start_timestamp), + insert_value("end", end_timestamp), + insert_value("private_segment_size", info.private_segment_size), + insert_value("group_segment_size", info.group_segment_size), + insert_value("workgroup_size_x", workgroup.x), + insert_value("workgroup_size_y", workgroup.y), + insert_value("workgroup_size_z", workgroup.z), + insert_value("grid_size_x", grid.x), + insert_value("grid_size_y", grid.y), + insert_value("grid_size_z", grid.z), + insert_value("region_name_id", string_entries.at(region_name)), + insert_value("event_id", evt_id), + }); + + execute_raw_sql_statements(conn, stmt); + }; + + if(kernel_dispatch_gen.empty()) + { + for(auto pctr : counter_collection_gen) + { + auto _deferred = sql::deferred_transaction{conn}; + for(const auto& record : counter_collection_gen.get(pctr)) + { + const auto& dispatch_data = record.dispatch_data; + const auto& info = dispatch_data.dispatch_info; + + // Register thread ID + get_thread_id(record.thread_id); + + // Use buffer category for kernel dispatches + auto kind = + tool_metadata.buffer_names.at(ROCPROFILER_BUFFER_TRACING_KERNEL_DISPATCH); + + // Process this dispatch + process_dispatch(info.dispatch_id, // dispatch_id + info.kernel_id, // kernel_id + dispatch_data.correlation_id, // corr_id + info, // info + kind, // kind + record.thread_id, // thread_id + get_queue_id(info.queue_id), // queue_id + get_stream_id(record.stream_id), // stream_id + dispatch_data.start_timestamp, // start_timestamp + dispatch_data.end_timestamp, // end_timestamp + info.grid_size, // grid + info.workgroup_size, // workgroup + false // enable_duplicate_check + ); + } + } + } + else + { + for(auto pitr : kernel_dispatch_gen) + { + auto _deferred = sql::deferred_transaction{conn}; + for(auto itr : kernel_dispatch_gen.get(pitr)) + { + // Register thread ID + get_thread_id(itr.thread_id); + + // Process this dispatch + process_dispatch(itr.dispatch_info.dispatch_id, // dispatch_id + itr.dispatch_info.kernel_id, // kernel_id + itr.correlation_id, // corr_id + itr.dispatch_info, // info + tool_metadata.buffer_names.at(itr.kind), // kind + itr.thread_id, // thread_id + get_queue_id(itr.dispatch_info.queue_id), // queue_id + get_stream_id(itr.stream_id), // stream_id + itr.start_timestamp, // start_timestamp + itr.end_timestamp, // end_timestamp + itr.dispatch_info.grid_size, // grid + itr.dispatch_info.workgroup_size, // workgroup + true // enable_duplicate_check + ); + } } } }; - auto insert_pmc_event_data = [&conn, - &tool_metadata, - &string_entries, - &counter_collection_gen, - &kernel_dispatch_gen](auto& dispatch_evt_ids) { + auto insert_pmc_event_data = [&conn, &tool_metadata, &counter_collection_gen]( + auto& dispatch_evt_ids) { auto _sqlgenperf_rocpd = get_simple_timer("rocpd_pmc_event"); size_t idx = tool_metadata.pmc_event_offset; for(auto ditr : counter_collection_gen) @@ -1066,39 +1134,6 @@ write_rocpd( const auto& info = record.dispatch_data.dispatch_info; auto dispatch_id = info.dispatch_id; - if(kernel_dispatch_gen.empty()) - { - auto kind = - tool_metadata.buffer_names.at(ROCPROFILER_BUFFER_TRACING_KERNEL_DISPATCH); - auto kernel_id = info.kernel_id; - auto corr_id = record.dispatch_data.correlation_id; - auto kern_name = - tool_metadata.get_kernel_symbol(kernel_id)->formatted_kernel_name; - - auto evt_id = - create_event(conn, - { - insert_value("category_id", string_entries.at(kind)), - insert_value("stack_id", corr_id.internal), - insert_value("parent_stack_id", corr_id.internal), - insert_value("correlation_id", corr_id.external.value), - }); - - if(dispatch_evt_ids.size() < dispatch_id + 1) - common::container::resize(dispatch_evt_ids, dispatch_id + 1, 0UL); - - ROCP_CI_LOG_IF(WARNING, dispatch_evt_ids.at(dispatch_id) != 0) << fmt::format( - "duplicate kernel dispatch id {} :: event_id={}, kernel_id={}, " - "corr_id={}, name='{}'", - dispatch_id, - evt_id, - kernel_id, - corr_id.internal, - kern_name); - - dispatch_evt_ids.at(dispatch_id) = evt_id; - } - auto evt_id = dispatch_evt_ids.at(dispatch_id); for(const auto& count : record.read()) { @@ -1377,7 +1412,7 @@ write_rocpd( insert_api_data(rocdecode_api_gen); } - insert_kernel_dispatch_data(kernel_dispatch_gen, dispatch_to_evt_id); + insert_kernel_dispatch_data(dispatch_to_evt_id); insert_pmc_event_data(dispatch_to_evt_id); insert_memory_copy_data(memory_copy_gen); diff --git a/source/lib/output/metadata.cpp b/source/lib/output/metadata.cpp index 507beefc9e..e7ec5cebc7 100644 --- a/source/lib/output/metadata.cpp +++ b/source/lib/output/metadata.cpp @@ -46,8 +46,10 @@ #include #include +#include #include #include +#include #include namespace rocprofiler @@ -71,6 +73,81 @@ query_pc_sampling_configuration(const rocprofiler_pc_sampling_configuration_t* c } return ROCPROFILER_STATUS_SUCCESS; } + +/** + * @brief Processes and stores the supported counters for a given agent. + * + * This function iterates over all counters supported by the given agent. + * If a filter set is provided, only counters present in the filter set are processed. + * Otherwise, all counters are collected. + * + * @param agent_id The ID of the agent whose counters are being processed. + * @param filter_set Optional set of counter names to filter which counters to load. If null, + * all supported counters will be loaded. + * @param output_map A reference to the map where the processed counter information will be stored. + * + * @return ROCPROFILER_STATUS_SUCCESS on success, or an appropriate error status on failure. + */ +rocprofiler_status_t +process_agent_counters(rocprofiler_agent_id_t agent_id, + std::set* filter_set, + agent_counter_info_map_t& output_map) +{ + struct callback_data_t + { + std::set* counters_set = nullptr; + agent_counter_info_map_t* agent_counter_info = nullptr; + }; + + auto cb_data = callback_data_t{filter_set, &output_map}; + + return rocprofiler_iterate_agent_supported_counters( + agent_id, + [](rocprofiler_agent_id_t id, + rocprofiler_counter_id_t* counters, + size_t num_counters, + void* user_data) { + auto* data = static_cast(user_data); + auto* counters_set_data = data->counters_set; + auto* agent_counter_info_data = data->agent_counter_info; + + agent_counter_info_data->emplace(id, counter_info_vec_t{}); + + for(size_t i = 0; i < num_counters; ++i) + { + auto _info = rocprofiler_counter_info_v1_t{}; + auto _dim_ids = std::vector{}; + auto _dim_info = std::vector{}; + + ROCPROFILER_CHECK(rocprofiler_query_counter_info( + counters[i], ROCPROFILER_COUNTER_INFO_VERSION_1, &_info)); + + if(counters_set_data != nullptr && counters_set_data->count(_info.name) == 0) + continue; + + for(uint64_t j = 0; j < _info.dimensions_count; ++j) + { + if(_info.dimensions[j] == nullptr) + { + ROCP_WARNING << fmt::format( + "nullptr dimension encountered for counter '{}' at index {}", + _info.name, + j); + continue; + } + + _dim_ids.emplace_back(_info.dimensions[j]->id); + _dim_info.emplace_back(*_info.dimensions[j]); + } + + agent_counter_info_data->at(id).emplace_back( + id, _info, std::move(_dim_ids), std::move(_dim_info)); + } + + return ROCPROFILER_STATUS_SUCCESS; + }, + &cb_data); +} } // namespace kernel_symbol_info::kernel_symbol_info() @@ -155,52 +232,105 @@ metadata::metadata(inprocess) add_kernel_symbol(std::move(info)); } +/** + * @brief Initializes the metadata by loading all counters supported on GPU agents. + * + * This method is used by the `rocprofv3-avail` tool to enumerate all available counters + * for each GPU agent in the system. + * + * For each non-CPU agent, this function calls `process_agent_counters` with a null filter, + * ensuring that all counters supported by that agent are queried and stored in metadata. + */ void metadata::init(inprocess) { if(inprocess_init) return; + inprocess_init = true; + + for(const auto& agent : agents) + { + if(agent.type == ROCPROFILER_AGENT_TYPE_CPU) continue; + auto status = process_agent_counters(agent.id, nullptr, agent_counter_info); + ROCP_WARNING_IF(status != ROCPROFILER_STATUS_SUCCESS) << fmt::format( + "rocprofiler_iterate_agent_supported_counters failed for agent {} ({}) :: {}", + agent.node_id, + agent.name, + rocprofiler_get_status_string(status)); + } +} + +/** + * @brief Initializes the metadata by loading only selected counters for GPU agents. + * + * This method is used by the `rocprofv3` tool when profiling with a list + * of performance counters which are profiled by user. + * + * This filtering reduces runtime overhead and significantly shrinks the size of + * generated JSON metadata in profiling output. + */ +void +metadata::init(inprocess_with_counters&& data) +{ + if(inprocess_init) return; inprocess_init = true; - for(auto itr : agents) + + // No counters to process, exit early. Kernel trace doesn't have to iterate counters. + if(data.counters.empty()) return; + + auto gpu_index_to_counters_map = std::map>{}; + for(const auto& agent : agents) { - if(itr.type == ROCPROFILER_AGENT_TYPE_CPU) continue; - - auto status = rocprofiler_iterate_agent_supported_counters( - itr.id, - [](rocprofiler_agent_id_t id, - rocprofiler_counter_id_t* counters, - size_t num_counters, - void* user_data) { - auto* data_v = static_cast(user_data); - data_v->emplace(id, counter_info_vec_t{}); - - for(size_t i = 0; i < num_counters; ++i) - { - auto _info = rocprofiler_counter_info_v1_t{}; - auto _dim_ids = std::vector{}; - auto _dim_info = std::vector{}; + gpu_index_to_counters_map[agent.gpu_index] = {}; + } - ROCPROFILER_CHECK(rocprofiler_query_counter_info( - counters[i], - ROCPROFILER_COUNTER_INFO_VERSION_1, - &static_cast(_info))); + // Used to parse counters like "SQ_WAVES:device=0". + constexpr auto device_qualifier = std::string_view{":device="}; - for(uint64_t j = 0; j < _info.dimensions_count; ++j) - { - _dim_ids.emplace_back(_info.dimensions[j].id); - _dim_info.emplace_back(_info.dimensions[j]); - } - data_v->at(id).emplace_back( - id, _info, std::move(_dim_ids), std::move(_dim_info)); - } - return ROCPROFILER_STATUS_SUCCESS; - }, - &agent_counter_info); + for(const auto& pmc_counter : data.counters) + { + auto name_v = pmc_counter; + + if(auto pos = pmc_counter.find(device_qualifier); pos != std::string::npos) + { + name_v = pmc_counter.substr(0, pos); + auto device_id_s = pmc_counter.substr(pos + device_qualifier.length()); + + ROCP_FATAL_IF(device_id_s.empty() || + device_id_s.find_first_not_of("0123456789") != std::string::npos) + << fmt::format("Invalid device qualifier format (expected ':device=N') in '{}'", + pmc_counter); + + auto device_id_l = std::stol(device_id_s); + if(gpu_index_to_counters_map.find(device_id_l) == gpu_index_to_counters_map.end()) + { + ROCP_WARNING << fmt::format("Device ID not found for PMC counter '{}'", + pmc_counter); + continue; + } + + // Add the counter to the corresponding device. + gpu_index_to_counters_map[device_id_l].emplace(name_v); + } + else + { + // No device qualifier — add to all devices. + for(auto& [_, counters] : gpu_index_to_counters_map) + counters.emplace(name_v); + } + } + + // Process selected counters for each GPU agent. + for(const auto& agent : agents) + { + if(agent.type == ROCPROFILER_AGENT_TYPE_CPU) continue; + + auto* filter = &gpu_index_to_counters_map[agent.gpu_index]; + auto status = process_agent_counters(agent.id, filter, agent_counter_info); ROCP_WARNING_IF(status != ROCPROFILER_STATUS_SUCCESS) << fmt::format( - "rocprofiler_iterate_agent_supported_counters returned {} for agent {} ({}) :: {}", - rocprofiler_get_status_name(status), - itr.node_id, - itr.name, + "rocprofiler_iterate_agent_supported_counters failed for agent {} ({}) :: {}", + agent.node_id, + agent.name, rocprofiler_get_status_string(status)); } } diff --git a/source/lib/output/metadata.hpp b/source/lib/output/metadata.hpp index de67082a66..300b4324c4 100644 --- a/source/lib/output/metadata.hpp +++ b/source/lib/output/metadata.hpp @@ -139,6 +139,11 @@ struct metadata struct inprocess {}; + // Tag type for initializing with specific counters for rocprofv3 tool. + struct inprocess_with_counters + { + std::set counters = {}; + }; pid_t process_id = 0; pid_t parent_process_id = 0; @@ -177,8 +182,12 @@ struct metadata metadata& operator=(const metadata&) = delete; metadata& operator=(metadata&&) noexcept = delete; + // Loads all counters supported on agents. Used by the 'rocprofv3-avail' tool. void init(inprocess); + // Loads only selected counters into metadata to reduce JSON size. Used by the 'rocprofv3' tool. + void init(inprocess_with_counters&&); + const agent_info* get_agent(rocprofiler_agent_id_t _val) const; const code_object_info* get_code_object(uint64_t code_obj_id) const; const kernel_symbol_info* get_kernel_symbol(uint64_t kernel_id) const; diff --git a/source/lib/python/rocpd/__init__.py b/source/lib/python/rocpd/__init__.py index bd8b171833..dc9f798560 100644 --- a/source/lib/python/rocpd/__init__.py +++ b/source/lib/python/rocpd/__init__.py @@ -46,8 +46,24 @@ "write_csv", "write_otf2", "RocpdImportData", + "version_info", ] +version_info = { + "version": "@PROJECT_VERSION@", + "major": int("@PROJECT_VERSION_MAJOR@"), + "minor": int("@PROJECT_VERSION_MINOR@"), + "patch": int("@PROJECT_VERSION_PATCH@"), + "git_revision": "@ROCPROFILER_SDK_GIT_REVISION@", + "library_arch": "@CMAKE_LIBRARY_ARCHITECTURE@", + "system_name": "@CMAKE_SYSTEM_NAME@", + "system_processor": "@CMAKE_SYSTEM_PROCESSOR@", + "system_version": "@CMAKE_SYSTEM_VERSION@", + "compiler_id": "@CMAKE_CXX_COMPILER_ID@", + "compiler_version": "@CMAKE_CXX_COMPILER_VERSION@", + "rocm_version": "@rocm_version_FULL_VERSION@", +} + def format_path(path, tag=os.path.basename(sys.executable)): return libpyrocpd.format_path(path, tag) diff --git a/source/lib/python/rocpd/__main__.py b/source/lib/python/rocpd/__main__.py index a056d497dd..92f5eaf4e9 100644 --- a/source/lib/python/rocpd/__main__.py +++ b/source/lib/python/rocpd/__main__.py @@ -38,11 +38,14 @@ def main(argv=None, config=None): """ import argparse - from . import time_window - from . import output_config - from . import pftrace from . import csv from . import otf2 + from . import output_config + from . import pftrace + from . import query + from . import summary + from . import time_window + from . import version_info from .importer import RocpdImportData convert_examples = """ @@ -50,25 +53,62 @@ def main(argv=None, config=None): Example usage: Convert 1 database, output perfetto trace - $ python3 -m rocpd convert -i db1.db --output-format pftrace + $ rocpd convert -i db1.db --output-format pftrace Convert 2 databases, output perfetto trace to path and filename, reduce time window to omit the first 30% - $ python3 -m rocpd convert -i db1.db db2.db --output-format pftrace -d "./output/" -o "twoFileTraces" --start 30% --end 100% + $ rocpd convert -i db1.db db2.db --output-format pftrace -d "./output/" -o "twoFileTraces" --start 30% --end 100% Convert 6 databases, output CSV and perfetto trace formats - $ python3 -m rocpd convert -i db{0..5}.db --output-format csv pftrace -d "~/output_folder/" -o "sixFileTraces" + $ rocpd convert -i db{0..5}.db --output-format csv pftrace -d "~/output_folder/" -o "sixFileTraces" Convert 2 databases, output CSV, OTF2, and perfetto trace formats - $ python3 -m rocpd convert -i db{3,4}.db --output-format csv otf2 pftrace + $ rocpd convert -i db{3,4}.db --output-format csv otf2 pftrace + +""" + query_examples = """ + +Example usage: + + Query the first 5 rows of the 'rocpd_info_agents' view and output to console + $ rocpd query -i db0.db --query "SELECT * FROM rocpd_info_agents LIMIT 5" + + Combine 4 databases and query the first 10 rows of the 'top_kernels' view and output to CSV file + $ rocpd query -i db{0..3}.db --query "SELECT * FROM top_kernels LIMIT 10" --format csv """ + summary_examples = """ + +Example usage: + + Output all summaries to console and include domain summary for 1 database + $ rocpd summary -i db1.db --domain-summary + + Aggregate 3 databases and output all summary files and include summary by rank/process ID, to csv file output + $ rocpd summary -i db{1..3}.db --summary-by-rank --format csv + + Output all summaries to console and exlude all regions to save processing time + $ rocpd summary -i db0.db --region-categories NONE + + Aggregate 2 databases and output all summary files to HTML, only include HIP and MARKER regions, include domain summary + $ rocpd summary -i db{0,1}.db --region-categories HIP MARKERS --domain-summary --format html + +""" + + # Add the subparsers parser = argparse.ArgumentParser( prog="rocpd", description="Aggregate and/or analyze ROCm Profiling Data (rocpd)", allow_abbrev=False, ) + parser.add_argument( + "-v", + "--version", + action="store_true", + help="Print the version information and exit", + ) + subparsers = parser.add_subparsers(dest="command") converter = subparsers.add_parser( "convert", @@ -78,20 +118,36 @@ def main(argv=None, config=None): epilog=convert_examples, ) + query_reporter = subparsers.add_parser( + "query", + description="Generate output on a query", + allow_abbrev=False, + formatter_class=argparse.RawTextHelpFormatter, + epilog=query_examples, + ) + + generate_summary = subparsers.add_parser( + "summary", + description="Generate summary views from rocPD data", + allow_abbrev=False, + formatter_class=argparse.RawTextHelpFormatter, + epilog=summary_examples, + ) + def get_output_type(val): return val.lower().replace("perfetto", "pftrace") - required_params = converter.add_argument_group("Required arguments") - - required_params.add_argument( + # add required options for each subparser + converter_required_params = converter.add_argument_group("Required options") + converter_required_params.add_argument( "-i", "--input", required=True, type=output_config.check_file_exists, nargs="+", - help="Input path and filename to one or more database(s), separated by spaces", + help="Input path and filename to one or more database(s)", ) - required_params.add_argument( + converter_required_params.add_argument( "-f", "--output-format", help="For adding output format (supported formats: csv, pftrace, otf2)", @@ -102,7 +158,27 @@ def get_output_type(val): required=True, ) - # add args from any sub-modules + query_required_params = query_reporter.add_argument_group("Required options") + query_required_params.add_argument( + "-i", + "--input", + required=True, + type=output_config.check_file_exists, + nargs="+", + help="Input path and filename to one or more database(s)", + ) + + summary_required_params = generate_summary.add_argument_group("Required options") + summary_required_params.add_argument( + "-i", + "--input", + required=True, + type=output_config.check_file_exists, + nargs="+", + help="Input path and filename to one or more database(s)", + ) + + # converter: add args from any sub-modules valid_out_config_args = output_config.add_args(converter) valid_generic_args = output_config.add_generic_args(converter) valid_pftrace_args = pftrace.add_args(converter) @@ -110,51 +186,110 @@ def get_output_type(val): valid_otf2_args = otf2.add_args(converter) valid_time_window_args = time_window.add_args(converter) + # query: subparser args + valid_out_config_args = output_config.add_args(query_reporter) + valid_query_args = query.add_args(query_reporter) + valid_time_window_args = time_window.add_args(query_reporter) + + # summary: subparser args + valid_io_args = summary.add_io_args(generate_summary) + valid_summary_args = summary.add_args(generate_summary) + valid_time_window_args = time_window.add_args(generate_summary) + # parse the command line arguments args = parser.parse_args(argv) - # process the args - out_cfg_args = output_config.process_args(args, valid_out_config_args) - generic_out_cfg_args = output_config.process_generic_args(args, valid_generic_args) - pftrace_args = pftrace.process_args(args, valid_pftrace_args) - csv_args = csv.process_args(args, valid_csv_args) - otf2_args = otf2.process_args(args, valid_otf2_args) - window_args = time_window.process_args(args, valid_time_window_args) - - # now start processing the data. Import the data and merge the views - importData = RocpdImportData(args.input) - - # adjust the time window view of the data - if window_args is not None: - time_window.apply_time_window(importData, **window_args) - - all_args = { - **out_cfg_args, - **generic_out_cfg_args, - **pftrace_args, - **csv_args, - **otf2_args, - } - # setup the config args - config = ( - output_config.output_config(**all_args) - if config is None - else config.update(**all_args) - ) + if args.version: + for key, itr in version_info.items(): + if key in ["major", "minor", "patch"]: + continue + print(f" {key:>16}: {itr}") + return 0 + + # error check the command line arguments, if no subparser command is given, print the help message + if args.command is None: + parser.print_help() + return + + # if the user requested converter, process the conversion + if args.command == "convert": + # process the args + out_cfg_args = output_config.process_args(args, valid_out_config_args) + generic_out_cfg_args = output_config.process_generic_args( + args, valid_generic_args + ) + pftrace_args = pftrace.process_args(args, valid_pftrace_args) + csv_args = csv.process_args(args, valid_csv_args) + otf2_args = otf2.process_args(args, valid_otf2_args) + window_args = time_window.process_args(args, valid_time_window_args) + + # now start processing the data. Import the data and merge the views + importData = RocpdImportData(args.input) + + # adjust the time window view of the data + if window_args is not None: + time_window.apply_time_window(importData, **window_args) + + all_args = { + **out_cfg_args, + **generic_out_cfg_args, + **pftrace_args, + **csv_args, + **otf2_args, + } + # setup the config args + config = ( + output_config.output_config(**all_args) + if config is None + else config.update(**all_args) + ) + + # process each requested output format + format_handlers = { + "pftrace": pftrace.write_pftrace, + "csv": csv.write_csv, + "otf2": otf2.write_otf2, + } + + for out_format in args.output_format: + if out_format in format_handlers: + print(f"Converting database(s) to {out_format} format:") + format_handlers[out_format](importData, config) + else: + print(f"Warning: Unsupported output format '{out_format}'") + + # if the user requested query module, execute the query + elif args.command == "query": + # query subparser args + query_args = query.process_args(args, valid_query_args) + out_cfg_args = output_config.process_args(args, valid_out_config_args) + window_args = time_window.process_args(args, valid_time_window_args) + + all_args = {**query_args, **out_cfg_args} + + query.execute( + args.input, + args, + window_args=window_args, + **all_args, + ) + + # if the user requested a summary, generate the views + elif args.command == "summary": + # summary subparser args + summary_args = summary.process_args(args, valid_summary_args) + io_args = output_config.process_args(args, valid_io_args) + window_args = time_window.process_args(args, valid_time_window_args) + + # now start processing the data. Import the data and merge the views + importData = RocpdImportData(args.input) + + # adjust the time window view of the data + if window_args is not None: + time_window.apply_time_window(importData, **window_args) - # process each requested output format - format_handlers = { - "pftrace": pftrace.write_pftrace, - "csv": csv.write_csv, - "otf2": otf2.write_otf2, - } - - for out_format in args.output_format: - if out_format in format_handlers: - print(f"Converting database(s) to {out_format} format:") - format_handlers[out_format](importData, config) - else: - print(f"Warning: Unsupported output format '{out_format}'") + all_args = {**summary_args, **io_args} + summary.generate_all_summaries(importData, **all_args) print("Done. Exiting...") diff --git a/source/lib/python/rocpd/libpyrocpd.cpp b/source/lib/python/rocpd/libpyrocpd.cpp index 0f62325724..f436288f0f 100644 --- a/source/lib/python/rocpd/libpyrocpd.cpp +++ b/source/lib/python/rocpd/libpyrocpd.cpp @@ -67,6 +67,29 @@ #include #include +template +struct EmplaceHelper +{ + EmplaceHelper(F&& f) + : m_f(std::forward(f)) + {} + + operator rocprofiler::tool::csv_output_file() && { return m_f(); } + +private: + F m_f; +}; + +// Deduction guide +template +EmplaceHelper(F&&) -> EmplaceHelper; + +#define EMPLACE_OFS_IF_NEEDED(data_var, ofs_var, generator_func, ...) \ + if(!data_var.empty() && !ofs_var) \ + { \ + ofs_var.emplace(EmplaceHelper([&] { return generator_func(output_cfg, ##__VA_ARGS__); })); \ + } + namespace py = ::pybind11; namespace rocpd @@ -513,7 +536,18 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd) if(data.empty()) return; - auto csv_manager = rocpd::output::CsvManager{output_cfg}; + std::optional agent_ofs; + std::optional kernels_ofs; + std::optional memory_copy_ofs; + std::optional memory_allocation_ofs; + std::optional scratch_memory_ofs; + std::optional counter_ofs; + std::optional hip_ofs; + std::optional hsa_ofs; + std::optional marker_ofs; + std::optional rccl_ofs; + std::optional rocdecode_ofs; + std::optional rocjpeg_ofs; for(auto obj : {data.connection}) { @@ -551,8 +585,6 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd) where_extra_condition); }; - rocpd::output::write_agent_info_csv(csv_manager, agents); - constexpr auto region_order_by = "start ASC, end DESC"; auto kernels = rocpd::sql_generator{ @@ -593,18 +625,99 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd) select_guid_nid_pid("regions", "AND category LIKE 'ROCJPEG_%'"), region_order_by}; - rocpd::output::write_csvs(csv_manager, - kernels, - memory_copies, - memory_allocations, - hip_api_calls, - hsa_api_calls, - marker_api_calls, - counters_calls, - scratch_memory_calls, - rccl_calls, - rocdecode_calls, - rocjpeg_calls); + // Agents csv + EMPLACE_OFS_IF_NEEDED(agents, agent_ofs, rocpd::output::generate_agent_ofs); + if(agent_ofs) rocpd::output::generate_csv(*agent_ofs, agents); + + // Kernels csv + EMPLACE_OFS_IF_NEEDED( + kernels, kernels_ofs, rocpd::output::generate_kernel_ofs); + if(kernels_ofs) + rocpd::output::generate_csv(output_cfg, *kernels_ofs, kernels); + + // Memory copy csv + EMPLACE_OFS_IF_NEEDED(memory_copies, + memory_copy_ofs, + rocpd::output::generate_memory_copy_ofs); + if(memory_copy_ofs) + rocpd::output::generate_csv( + output_cfg, *memory_copy_ofs, memory_copies); + + // Memory allocations csv + EMPLACE_OFS_IF_NEEDED(memory_allocations, + memory_allocation_ofs, + rocpd::output::generate_memory_allocation_ofs); + if(memory_allocation_ofs) + rocpd::output::generate_csv( + output_cfg, *memory_allocation_ofs, memory_allocations); + + // Scratch memory csv + EMPLACE_OFS_IF_NEEDED(scratch_memory_calls, + scratch_memory_ofs, + rocpd::output::generate_scratch_memory_ofs); + if(scratch_memory_ofs) + rocpd::output::generate_csv( + output_cfg, *scratch_memory_ofs, scratch_memory_calls); + + // Counter csv + EMPLACE_OFS_IF_NEEDED( + counters_calls, counter_ofs, rocpd::output::generate_counter_ofs); + if(counter_ofs) + rocpd::output::generate_csv(output_cfg, *counter_ofs, counters_calls); + + // HIP csv + EMPLACE_OFS_IF_NEEDED(hip_api_calls, + hip_ofs, + rocpd::output::generate_region_ofs, + domain_type::HIP); + if(hip_ofs) + rocpd::output::generate_csv(*hip_ofs, hip_api_calls, domain_type::HIP); + + // HSA csv + EMPLACE_OFS_IF_NEEDED(hsa_api_calls, + hsa_ofs, + rocpd::output::generate_region_ofs, + domain_type::HSA); + if(hsa_ofs) + rocpd::output::generate_csv(*hsa_ofs, hsa_api_calls, domain_type::HSA); + + // Markers csv + EMPLACE_OFS_IF_NEEDED(marker_api_calls, + marker_ofs, + rocpd::output::generate_region_ofs, + domain_type::MARKER); + + if(marker_ofs) + rocpd::output::generate_csv( + *marker_ofs, marker_api_calls, domain_type::MARKER); + + // RCCL csv + EMPLACE_OFS_IF_NEEDED(rccl_calls, + rccl_ofs, + rocpd::output::generate_region_ofs, + domain_type::RCCL); + if(rccl_ofs) + rocpd::output::generate_csv(*rccl_ofs, rccl_calls, domain_type::RCCL); + + // ROCDECODE csv + EMPLACE_OFS_IF_NEEDED(rocdecode_calls, + rocdecode_ofs, + rocpd::output::generate_region_ofs, + domain_type::ROCDECODE); + + if(rocdecode_ofs) + rocpd::output::generate_csv( + *rocdecode_ofs, rocdecode_calls, domain_type::ROCDECODE); + + // ROCJPEG csv + EMPLACE_OFS_IF_NEEDED(rocjpeg_calls, + rocjpeg_ofs, + rocpd::output::generate_region_ofs, + domain_type::ROCJPEG); + + if(rocjpeg_ofs) + rocpd::output::generate_csv( + *rocjpeg_ofs, rocjpeg_calls, domain_type::ROCJPEG); } } } diff --git a/source/lib/python/rocpd/query.py b/source/lib/python/rocpd/query.py new file mode 100644 index 0000000000..ab369326db --- /dev/null +++ b/source/lib/python/rocpd/query.py @@ -0,0 +1,560 @@ +#!/usr/bin/env python3 +############################################################################### +# MIT License +# +# Copyright (c) 2025 Advanced Micro Devices, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +############################################################################### + +import os +import sys + +from typing import Union, Tuple, List, Optional +from datetime import datetime + +from . import output_config +from . import libpyrocpd +from .importer import RocpdImportData +from .time_window import apply_time_window + + +def export_sqlite_query( + conn: RocpdImportData, + query: str, + params: Union[Tuple, List] = (), + export_format: Optional[str] = None, + export_path: Optional[str] = None, + dashboard_template_path: Optional[str] = None, +) -> Optional[str]: + """ + Execute a SQLite query and print it to console. + Then, if export_format is specified, write the results to a file. + Returns the path to the exported file (or None if nothing was exported). + + Supported export_format values (case-insensitive): + - "csv" + - "html" + - "md" (markdown) + - "pdf" + - "dashboard" (templated HTML dashboard) + - "clipboard" + + If export_format == "dashboard", you may optionally pass a + dashboard_template_path (a Jinja2 template file). If omitted, + a built-in default template is used. + """ + + try: + import pandas as pd + + conn = conn.connection if isinstance(conn, RocpdImportData) else conn + + # 1) Run the query via pandas + df = pd.read_sql_query(query, conn, params=params) + + if df.empty: + sys.stderr.write(f"No results found for query: {query}\n") + sys.stderr.flush() + return None + + if export_format == "console" or export_format is None: + # 2) Print to console + print(df.to_string(index=False)) + return None + + elif export_format == "clipboard": + df.to_clipboard(excel=False) + return None + + export_format = export_format.lower() + ext = export_format + export_path = export_path or f"query_output.{ext}" + if not export_path.endswith(f".{ext}"): + export_path = f"{export_path}.{ext}" + export_path = os.path.abspath(libpyrocpd.format_path(export_path, "rocpd")) + + os.makedirs(os.path.dirname(export_path), exist_ok=True) + + def write_export(content): + with open(export_path, "w") as ofs: + ofs.write(f"{content}\n") + ofs.flush() + + # 3) Export based on format + if export_format == "csv": + df.to_csv(export_path, index=False) + + elif export_format == "html": + write_export(df.to_html(index=False)) + + elif export_format == "md": + # pandas 1.0+ has to_markdown + try: + write_export(df.to_markdown(index=False)) + except AttributeError: + # fallback: manually write markdown table + _df_to_markdown_fallback(df, export_path) + + elif export_format == "pdf": + _export_df_to_pdf(df, export_path) + + elif export_format == "dashboard": + _export_dashboard( + df, export_path=export_path, template_path=dashboard_template_path + ) + + elif export_format == "json": + df.to_json(export_path, index=False, indent=2, orient="records") + + else: + print(f"Unsupported export format: {export_format}") + return None + + print(f"Exported to: {export_path}\n") + return export_path + + except Exception as e: + print(f"Error: {e}") + return None + + +def _df_to_markdown_fallback(df, path: str): + """ + Simple fallback if pandas.DataFrame.to_markdown(...) is unavailable. + """ + headers = list(df.columns) + with open(path, "w", encoding="utf-8") as f: + # Header row + f.write("| " + " | ".join(headers) + " |\n") + # Separator + f.write("|" + "|".join("---" for _ in headers) + "|\n") + # Data rows + for row in df.itertuples(index=False): + line = "| " + " | ".join(str(v) for v in row) + " |\n" + f.write(line) + + +def _export_df_to_pdf(df, path: str): + """ + Render a DataFrame into a monospaced text table inside a PDF. + """ + from reportlab.lib.pagesizes import letter + from reportlab.pdfgen import canvas + from reportlab.lib.units import inch + + c = canvas.Canvas(path, pagesize=letter) + width, height = letter + x = 0.5 * inch + y = height - 1 * inch + row_height = 14 + + c.setFont("Courier", 9) + headers = list(df.columns) + header_line = " | ".join(headers) + c.drawString(x, y, header_line) + y -= row_height + c.drawString(x, y, "-" * len(header_line)) + y -= row_height + + for _, row in df.iterrows(): + row_line = " | ".join(str(v) for v in row) + # Clip at ~160 characters so it doesn’t overflow the page width + c.drawString(x, y, row_line[:160]) + y -= row_height + if y < 1 * inch: + c.showPage() + c.setFont("Courier", 9) + y = height - 1 * inch + + c.save() + + +def _export_dashboard(df, export_path: str, template_path: Optional[str] = None): + """ + Generate a templated HTML “dashboard” from df. If template_path is None, + use a built-in template. Otherwise, load the Jinja2 template from that path. + """ + from jinja2 import Environment, FileSystemLoader, select_autoescape + + # 1) Prepare Jinja2 environment + if template_path: + # User provided a .html (Jinja2) file + env = Environment( + loader=FileSystemLoader(os.path.dirname(template_path)), + autoescape=select_autoescape(["html", "xml"]), + ) + template = env.get_template(os.path.basename(template_path)) + else: + # Built-in default template + builtin_html = """ + + + + + Dashboard Report + + + +

{{ title }}

+

Generated on {{ timestamp }}

+
+ {{ table_html | safe }} +
+ + + """ + env = Environment(autoescape=select_autoescape(["html", "xml"])) + template = env.from_string(builtin_html) + + # 2) Render template with context + context = { + "title": "SQLite Query Dashboard", + "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), + "table_html": df.to_html(index=False, classes="dashboard-table"), + } + rendered = template.render(**context) + + # 3) Write to export_path + with open(export_path, "w", encoding="utf-8") as f: + f.write(rendered) + + +def zip_files(file_paths: List[str], zip_path: str) -> str: + """ + Zip up one or more files into zip_path. Overwrites existing zip if present. + Returns the path to the created zip. + """ + import zipfile + + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: + for fp in file_paths: + if os.path.isfile(fp): + zf.write(fp, arcname=os.path.basename(fp)) + else: + raise FileNotFoundError(f"Cannot find file to zip: {fp}") + print(f"Created ZIP archive: {zip_path}") + return zip_path + + +def send_report_email( + file_paths: List[str], + to: Union[str, List[str]], + sender: str, + subject: str = "rocpd query Report", + inline_preview: bool = False, + smtp_server: str = "localhost", + smtp_port: int = 25, + smtp_user: Optional[str] = None, + smtp_password: Optional[str] = None, + zip_attachments: bool = False, +) -> None: + """ + Send an email with one or more attachments, optionally zipped, + and optionally with an inline preview (if the primary attachment is HTML). + + Args: + file_paths: List of file paths to attach (each must exist). + to: Recipient email address, or list of addresses. + sender: Sender email address. + subject: Subject line. + inline_preview: If True, and one of the attachments is HTML, use that + HTML as the email body (and still attach files). + smtp_server / smtp_port / smtp_user / smtp_password: SMTP credentials. + zip_attachments: If True, bundle all file_paths into a single ZIP named + "_attachments.zip" and attach that ZIP only. + """ + import smtplib + from email.message import EmailMessage + + # 1) Validate that files exist + for fp in file_paths: + if not os.path.isfile(fp): + raise FileNotFoundError(f"Attachment not found: {fp}") + + # 2) If zip_attachments is True, zip everything into one archive + actual_attachments: List[str] + if zip_attachments: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + zip_path = f"attachments_{timestamp}.zip" + zip_files(file_paths, zip_path) + actual_attachments = [zip_path] + else: + actual_attachments = file_paths.copy() + + # 3) Build the EmailMessage + msg = EmailMessage() + msg["Subject"] = subject + msg["From"] = sender + msg["To"] = ", ".join(to) if isinstance(to, list) else to + + # 4) If inline_preview is True, look for the first HTML attachment, + # read its content, and set as an HTML alternative in the email body. + if inline_preview: + html_body_found = False + for fp in actual_attachments: + if fp.lower().endswith(".html"): + with open(fp, "r", encoding="utf-8") as f: + html_content = f.read() + msg.set_content( + "This email contains an inline HTML preview. If your mail client " + "doesn’t display HTML, see the attachment." + ) + msg.add_alternative(html_content, subtype="html") + html_body_found = True + break + if not html_body_found: + # No HTML attachment found; create a simple text body + msg.set_content("Please see attached report file(s).") + + else: + # No inline preview desired; use a simple text body + msg.set_content("Please see attached report file(s).") + + # 5) Attach each file (or the single ZIP) + for fp in actual_attachments: + with open(fp, "rb") as f: + data = f.read() + ctype = "application" + subtype = "octet-stream" + filename = os.path.basename(fp) + msg.add_attachment(data, maintype=ctype, subtype=subtype, filename=filename) + + # 6) Connect to SMTP and send + with smtplib.SMTP(smtp_server, smtp_port) as server: + server.ehlo() + if smtp_user and smtp_password: + server.starttls() + server.login(smtp_user, smtp_password) + server.send_message(msg) + + print(f"Email sent to {msg['To']} with subject '{subject}'") + + +def add_args(parser): + """Add query arguments""" + + query_options = parser.add_argument_group("Query Options") + + # Common arguments + query_options.add_argument( + "--query", required=True, help="SQL SELECT query to execute (enclose in quotes)." + ) + + query_options.add_argument( + "--script", + required=False, + type=str, + help="Input SQL script which should be read before query (e.g. defines views)", + ) + + query_options.add_argument( + "--format", + help="Export format", + choices=("console", "csv", "html", "json", "md", "pdf", "dashboard", "clipboard"), + type=str.lower, + ) + + email_options = parser.add_argument_group("Query Email Options") + + # Email options (optional) + email_options.add_argument( + "--email-to", help="Recipient email address (or comma-separated list)." + ) + email_options.add_argument( + "--email-from", help="Sender email address (required if --email-to is used)." + ) + email_options.add_argument( + "--email-subject", + default="SQLite Query Report", + help="Subject line for the email (default: %(default)s).", + ) + email_options.add_argument( + "--smtp-server", + default="localhost", + help="SMTP server hostname (default: %(default)s).", + ) + email_options.add_argument( + "--smtp-port", + type=int, + default=25, + help="SMTP server port (default: %(default)d).", + ) + email_options.add_argument("--smtp-user", help="SMTP login username (if required).") + email_options.add_argument( + "--smtp-password", help="SMTP login password (if required)." + ) + email_options.add_argument( + "--zip-attachments", + action="store_true", + help="Zip all attachments into a single .zip file before sending.", + ) + email_options.add_argument( + "--inline-preview", + action="store_true", + help="Embed HTML report as inline body if an HTML attachment is present.", + ) + + dashboard_options = parser.add_argument_group("Query Dashboard Options") + + dashboard_options.add_argument( + "--template-path", help="Path to a Jinja2 HTML template for the dashboard" + ) + + return [ + "query", + "script", + "email_to", + "email_from", + "email_subject", + "smtp_server", + "smtp_port", + "smtp_user", + "smtp_password", + "inline_preview", + "zip_attachments", + "format", + "template_path", + ] + + +def process_args(args, valid_args): + # do not add any of the arguments to the output config dict + ret = {} + return ret + + +def execute(input, args, config=None, window_args=None, **kwargs): + + importData = RocpdImportData(input) + + apply_time_window(importData, **window_args) + + config = ( + output_config.output_config(**kwargs) + if config is None + else config.update(**kwargs) + ) + + if args.script: + # read script and execute statements + with open(args.script, "r") as ifs: + for itr in ifs.read().split(";"): + importData.execute(f"{itr}") + + # Prepare parameters for export + query = args.query + db = importData + export_format = args.format + export_path = os.path.join(config.output_path, config.output_file) + + # Dashboard-only extra + dashboard_template = kwargs.get("template_path", None) + + # 1) Run and export + exported_file = export_sqlite_query( + db, + query=query, + params=(), + export_format=export_format, + export_path=export_path, + dashboard_template_path=dashboard_template, + ) + + # 2) If --email-to was provided and we have a file, send it + if args.email_to: + if not args.email_from: + raise ValueError("--email-from is required when --email-to is used.") + if not exported_file: + print("No file was exported; skipping email.") + return + + recipients = [addr.strip() for addr in args.email_to.split(",")] + send_report_email( + file_paths=[exported_file], + to=recipients, + sender=args.email_from, + subject=args.email_subject, + inline_preview=args.inline_preview, + smtp_server=args.smtp_server, + smtp_port=args.smtp_port, + smtp_user=args.smtp_user, + smtp_password=args.smtp_password, + zip_attachments=args.zip_attachments, + ) + + +def main(argv=None): + import argparse + from .time_window import add_args as add_args_time_window + from .time_window import process_args as process_args_time_window + from .output_config import add_args as add_args_output_config + from .output_config import process_args as process_args_output_config + from .output_config import add_generic_args, process_generic_args + + parser = argparse.ArgumentParser( + description="Generate report for rocpd query", allow_abbrev=False + ) + + required_params = parser.add_argument_group("Required options") + + required_params.add_argument( + "-i", + "--input", + required=True, + type=output_config.check_file_exists, + nargs="+", + help="Input path and filename to one or more database(s), separated by spaces", + ) + + valid_out_config_args = add_args_output_config(parser) + valid_generic_args = add_generic_args(parser) + valid_time_window_args = add_args_time_window(parser) + valid_query_args = add_args(parser) + + args = parser.parse_args(argv) + + out_cfg_args = process_args_output_config(args, valid_out_config_args) + generic_out_cfg_args = process_generic_args(args, valid_generic_args) + window_args = process_args_time_window(args, valid_time_window_args) + query_args = process_args(args, valid_query_args) + + all_args = { + **query_args, + **out_cfg_args, + **generic_out_cfg_args, + } + + execute( + args.input, + args, + window_args=window_args, + **all_args, + ) + + +if __name__ == "__main__": + main() diff --git a/source/lib/python/rocpd/source/csv.cpp b/source/lib/python/rocpd/source/csv.cpp index 5be3f97977..b49d906e75 100644 --- a/source/lib/python/rocpd/source/csv.cpp +++ b/source/lib/python/rocpd/source/csv.cpp @@ -24,8 +24,6 @@ #include "lib/common/defines.hpp" #include "lib/common/hasher.hpp" -#include "lib/common/mpl.hpp" -#include "lib/output/csv.hpp" #include "lib/output/csv_output_file.hpp" #include "lib/output/generator.hpp" #include "lib/output/metadata.hpp" @@ -36,436 +34,457 @@ #include "lib/output/stream_info.hpp" #include "lib/rocprofiler-sdk-tool/config.hpp" -#include - #include +#include +#include + #include #include #include #include #include +#include #include #include -namespace fs = std::filesystem; - -namespace -{ -const std::string STATS_HEADER = "\"Name\",\"Calls\",\"TotalDurationNs\"," - "\"AverageNs\",\"Percentage\",\"MinNs\",\"MaxNs\",\"StdDev\""; -const std::string API_TRACE_HEADER = - "\"Guid\",\"Domain\",\"Function\",\"Process_Id\",\"Thread_Id\"," - "\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\""; -} // namespace - namespace rocpd { namespace output { -CsvManager::CsvManager(rocprofiler::tool::output_config output_cfg) -: config{std::move(output_cfg)} -{ - if(!ensure_output_directory()) - { - ROCP_ERROR << "Failed to create csv output directory: " << config.output_path; - return; - } - - this->csv_configs = { - {CsvType::KERNEL_DISPATCH, - {"kernel_trace.csv", - "\"Guid\",\"Kind\",\"Agent_Id\",\"Queue_Id\"," - "\"Stream_Id\",\"Thread_Id\",\"Dispatch_Id\"," - "\"Kernel_Id\",\"Kernel_Name\",\"Correlation_Id\"," - "\"Start_Timestamp\",\"End_" - "Timestamp\",\"Private_Segment_Size\",\"Group_" - "Segment_Size\",\"Workgroup_Size_X\"," - "\"Workgroup_Size_Y\",\"Workgroup_Size_Z\",\"Grid_" - "Size_X\",\"Grid_Size_Y\",\"Grid_Size_" - "Z\""}}, - {CsvType::MEMORY_COPY, - {"memory_copy_trace.csv", - "\"Guid\",\"Kind\",\"Direction\",\"Stream_Id\",\"Source_Agent_Id\"," - "\"Destination_Agent_" - "Id\"," - "\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\""}}, - {CsvType::MEMORY_ALLOCATION, - {"memory_allocation_trace.csv", - "\"Guid\",\"Kind\",\"Operation\",\"Agent_Id\",\"Allocation_Size\"," - "\"Address\"," - "\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\""}}, - {CsvType::SCRATCH_MEMORY, - {"scratch_memory_trace.csv", - "\"Kind\",\"Operation\",\"Agent_Id\",\"Queue_Id\",\"Thread_Id\"," - "\"Alloc_Flags\",\"Start_" - "Timestamp\",\"End_Timestamp\""}}, - - {CsvType::HIP_API, {"hip_api_trace.csv", API_TRACE_HEADER}}, - {CsvType::HSA_CSV_API, {"hsa_api_trace.csv", API_TRACE_HEADER}}, - {CsvType::MARKER, {"marker_api_trace.csv", API_TRACE_HEADER}}, - {CsvType::RCCL_API, {"rccl_api_trace.csv", API_TRACE_HEADER}}, - {CsvType::ROCDECODE_API, {"rocdecode_api_trace.csv", API_TRACE_HEADER}}, - {CsvType::ROCJPEG_API, {"rocjpeg_api_trace.csv", API_TRACE_HEADER}}, - - {CsvType::COUNTER, - {"counter_trace.csv", - "\"Pid\",\"Correlation_Id\",\"Dispatch_Id\",\"Agent_Id\",\"Queue_Id\"," - "\"Process_Id\"," - "\"Thread_Id\"," - "\"Grid_Size\",\"Kernel_Id\",\"Kernel_Name\",\"Workgroup_Size\",\"LDS_" - "Block_Size\"," - "\"Scratch_Size\",\"VGPR_Count\",\"Accum_VGPR_Count\",\"SGPR_Count\"," - "\"Counter_Name\",\"Counter_Value\",\"Start_Timestamp\",\"End_" - "Timestamp\""}}, - }; -} - -bool -CsvManager::ensure_output_directory() const +std::string +extract_field_from_extdata(const std::string& extdata, const std::string& field) { try { - fs::create_directories(config.output_path); - return true; - } catch(const std::exception& e) + std::stringstream ss(extdata); + cereal::JSONInputArchive archive(ss); + std::string value; + archive(cereal::make_nvp(field.c_str(), value)); + return value; + } catch(...) { - ROCP_ERROR << "Failed to create directory: " << e.what(); - return false; + return {}; } } -CsvManager::~CsvManager() +rocprofiler::tool::csv_output_file +generate_agent_ofs(const rocprofiler::tool::output_config& cfg) { - for(auto& [type, stream] : streams) - { - if(stream.is_open()) - { - stream.flush(); - stream.close(); - } - } + return rocprofiler::tool::csv_output_file{cfg, + "agent_info", + rocprofiler::tool::csv::agent_info_csv_encoder_guid{}, + {"Guid", + "Node_Id", + "Logical_Node_Id", + "Agent_Type", + "Cpu_Cores_Count", + "Simd_Count", + "Cpu_Core_Id_Base", + "Simd_Id_Base", + "Max_Waves_Per_Simd", + "Lds_Size_In_Kb", + "Gds_Size_In_Kb", + "Num_Gws", + "Wave_Front_Size", + "Num_Xcc", + "Cu_Count", + "Array_Count", + "Num_Shader_Banks", + "Simd_Arrays_Per_Engine", + "Cu_Per_Simd_Array", + "Simd_Per_Cu", + "Max_Slots_Scratch_Cu", + "Gfx_Target_Version", + "Vendor_Id", + "Device_Id", + "Location_Id", + "Domain", + "Drm_Render_Minor", + "Num_Sdma_Engines", + "Num_Sdma_Xgmi_Engines", + "Num_Sdma_Queues_Per_Engine", + "Num_Cp_Queues", + "Max_Engine_Clk_Ccompute", + "Max_Engine_Clk_Fcompute", + "Sdma_Fw_Version", + "Fw_Version", + "Capability", + "Cu_Per_Engine", + "Max_Waves_Per_Cu", + "Family_Id", + "Workgroup_Max_Size", + "Grid_Max_Size", + "Local_Mem_Size", + "Hive_Id", + "Gpu_Id", + "Workgroup_Max_Dim_X", + "Workgroup_Max_Dim_Y", + "Workgroup_Max_Dim_Z", + "Grid_Max_Dim_X", + "Grid_Max_Dim_Y", + "Grid_Max_Dim_Z", + "Name", + "Vendor_Name", + "Product_Name", + "Model_Name"}}; } -std::ofstream& -CsvManager::get_stream(CsvType type) +rocprofiler::tool::csv_output_file +generate_kernel_ofs(const rocprofiler::tool::output_config& cfg) { - return streams[type]; + return rocprofiler::tool::csv_output_file{ + cfg, + domain_type::KERNEL_DISPATCH, + rocprofiler::tool::csv::kernel_trace_with_stream_csv_encoder_guid{}, + {"Guid", + "Kind", + "Agent_Id", + "Queue_Id", + "Stream_Id", + "Thread_Id", + "Dispatch_Id", + "Kernel_Id", + "Kernel_Name", + "Correlation_Id", + "Start_Timestamp", + "End_Timestamp", + "LDS_Block_Size", + "Scratch_Size", + "VGPR_Count", + "Accum_VGPR_Count", + "SGPR_Count", + "Workgroup_Size_X", + "Workgroup_Size_Y", + "Workgroup_Size_Z", + "Grid_Size_X", + "Grid_Size_Y", + "Grid_Size_Z"}}; } -bool -CsvManager::has_stream(CsvType type) const +rocprofiler::tool::csv_output_file +generate_memory_copy_ofs(const rocprofiler::tool::output_config& cfg) { - return streams.count(type) != 0u && streams.at(type).is_open(); + return rocprofiler::tool::csv_output_file{ + cfg, + domain_type::MEMORY_COPY, + rocprofiler::tool::csv::memory_copy_with_stream_csv_encoder_guid{}, + {"Guid", + "Kind", + "Direction", + "Stream_Id", + "Source_Agent_Id", + "Destination_Agent_Id", + "Correlation_Id", + "Start_Timestamp", + "End_Timestamp"}}; } -bool -CsvManager::initialize_csv_file(CsvType type) +rocprofiler::tool::csv_output_file +generate_memory_allocation_ofs(const rocprofiler::tool::output_config& cfg) { - if(has_stream(type)) return true; - - if(csv_configs.find(type) == csv_configs.end()) - { - ROCP_ERROR << "No CSV configuration found for type: " << static_cast(type); - return false; - } - - const auto& cfg = csv_configs[type]; + return rocprofiler::tool::csv_output_file{ + cfg, + domain_type::MEMORY_ALLOCATION, + rocprofiler::tool::csv::memory_allocation_csv_encoder_guid{}, + {"Guid", + "Kind", + "Operation", + "Agent_Id", + "Allocation_Size", + "Address", + "Correlation_Id", + "Start_Timestamp", + "End_Timestamp"}}; +} - fs::path output_dir = config.output_path; - fs::path filename = - config.output_file.empty() ? cfg.filename : config.output_file + "_" + cfg.filename; +rocprofiler::tool::csv_output_file +generate_scratch_memory_ofs(const rocprofiler::tool::output_config& cfg) +{ + return rocprofiler::tool::csv_output_file{cfg, + domain_type::SCRATCH_MEMORY, + rocprofiler::tool::csv::scratch_memory_encoder_guid{}, + { + "Guid", + "Kind", + "Operation", + "Agent_Id", + "Allocation_Size", + "Queue_Id", + "Thread_Id", + "Alloc_Flags", + "Start_Timestamp", + "End_Timestamp", + }}; +} - file_paths[type] = (output_dir / filename).string(); +rocprofiler::tool::csv_output_file +generate_counter_ofs(const rocprofiler::tool::output_config& cfg) +{ + return rocprofiler::tool::csv_output_file{ + cfg, + domain_type::COUNTER_COLLECTION, + rocprofiler::tool::csv::counter_collection_csv_encoder_guid{}, + {"Guid", "Correlation_Id", "Dispatch_Id", "Agent_Id", "Queue_Id", + "Process_Id", "Thread_Id", "Grid_Size", "Kernel_Id", "Kernel_Name", + "Workgroup_Size", "LDS_Block_Size", "Scratch_Size", "VGPR_Count", "Accum_VGPR_Count", + "SGPR_Count", "Counter_Name", "Counter_Value", "Start_Timestamp", "End_Timestamp"}}; +} +rocprofiler::tool::csv_output_file +generate_region_ofs(const rocprofiler::tool::output_config& cfg, domain_type domain) +{ + return rocprofiler::tool::csv_output_file{cfg, + domain, + rocprofiler::tool::csv::api_csv_encoder_guid{}, + {"Guid", + "Domain", + "Function", + "Process_Id", + "Thread_Id", + "Correlation_Id", + "Start_Timestamp", + "End_Timestamp"}}; +} +void +generate_csv(rocprofiler::tool::csv_output_file& ofs, const std::vector& data) +{ + if(data.empty()) return; - auto& path = file_paths[type]; - auto& stream = streams[type]; + auto sorted_data = data; + std::sort(sorted_data.begin(), + sorted_data.end(), + [](const rocpd::types::agent& lhs, const rocpd::types::agent& rhs) { + return lhs.node_id < rhs.node_id; + }); - stream.open(path, std::ios::out); - if(!stream.is_open()) + for(auto& itr : sorted_data) { - ROCP_ERROR << "Failed to open CSV output file: " << path; - return false; + auto row_ss = std::stringstream{}; + rocprofiler::tool::csv::agent_info_csv_encoder_guid::write_row( + row_ss, + itr.guid, + itr.node_id, + itr.logical_node_id, + itr.type, + itr.cpu_cores_count, + itr.simd_count, + itr.cpu_core_id_base, + itr.simd_id_base, + itr.max_waves_per_simd, + itr.lds_size_in_kb, + itr.gds_size_in_kb, + itr.num_gws, + itr.wave_front_size, + itr.num_xcc, + itr.cu_count, + itr.array_count, + itr.num_shader_banks, + itr.simd_arrays_per_engine, + itr.cu_per_simd_array, + itr.simd_per_cu, + itr.max_slots_scratch_cu, + itr.gfx_target_version, + itr.vendor_id, + itr.device_id, + itr.location_id, + itr.domain, + itr.drm_render_minor, + itr.num_sdma_engines, + itr.num_sdma_xgmi_engines, + itr.num_sdma_queues_per_engine, + itr.num_cp_queues, + itr.max_engine_clk_ccompute, + itr.max_engine_clk_fcompute, + itr.sdma_fw_version.Value, + itr.fw_version.Value, + itr.capability.Value, + itr.cu_per_engine, + itr.max_waves_per_cu, + itr.family_id, + itr.workgroup_max_size, + itr.grid_max_size, + itr.local_mem_size, + itr.hive_id, + itr.gpu_id, + itr.workgroup_max_dim.x, + itr.workgroup_max_dim.y, + itr.workgroup_max_dim.z, + itr.grid_max_dim.x, + itr.grid_max_dim.y, + itr.grid_max_dim.z, + itr.name, + itr.vendor_name, + itr.product_name, + itr.model_name); + ofs << row_ss.str(); } - - stream << cfg.header << '\n'; - return true; } -template -bool -has_any_data(const rocprofiler::tool::generator& data_gen) +void +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data) { - for(auto ditr : data_gen) + if(data.empty()) return; + + for(const auto& ditr : data) { - auto gen = data_gen.get(ditr); - if(begin(gen) != end(gen)) + for(const auto& record : data.get(ditr)) { - return true; + std::string kernel_identifier = cfg.kernel_rename ? record.region : record.name; + std::string agent_identifier = create_agent_index(cfg.agent_index_value, + record.agent_abs_index, + record.agent_log_index, + record.agent_type_index, + std::string_view(record.agent_type)) + .as_string(); + + auto row_ss = std::stringstream{}; + rocprofiler::tool::csv::kernel_trace_with_stream_csv_encoder_guid::write_row( + row_ss, + record.guid, + record.category, + agent_identifier, + record.queue_id, + record.stream_id, + record.tid, + record.dispatch_id, + record.kernel_id, + kernel_identifier, + record.stack_id, + record.start, + record.end, + record.lds_size, + record.scratch_size, + record.arch_vgpr_count, + record.accum_vgpr_count, + record.sgpr_count, + record.workgroup_size.x, + record.workgroup_size.y, + record.workgroup_size.z, + record.grid_size.x, + record.grid_size.y, + record.grid_size.z); + ofs << row_ss.str(); } } - return false; } -template void -process_data_to_csv(CsvManager& csv_manager, - CsvType csv_type, - const rocprofiler::tool::generator& data_gen, - Processor process_func) +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data) { - if(!has_any_data(data_gen)) return; + if(data.empty()) return; - if(!csv_manager.initialize_csv_file(csv_type)) return; - - for(auto ditr : data_gen) + for(const auto& ditr : data) { - auto gen = data_gen.get(ditr); - for(auto it = begin(gen); it != end(gen); ++it) + for(const auto& record : data.get(ditr)) { - process_func(csv_manager, csv_type, *it); - } - } -} + auto row_ss = std::stringstream{}; -void -write_kernel_csv( - CsvManager& csv_manager, - const rocprofiler::tool::generator& kernel_dispatch_gen) -{ - process_data_to_csv( - csv_manager, - CsvType::KERNEL_DISPATCH, - kernel_dispatch_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::kernel_dispatch& kernel) { - std::string kernel_identifier = cm.config.kernel_rename ? kernel.region : kernel.name; - - std::string agent_identifier = create_agent_index(cm.config.agent_index_value, - kernel.agent_abs_index, - kernel.agent_log_index, - kernel.agent_type_index, - std::string_view(kernel.agent_type)) - .as_string(); + std::string src_agent_identifier = + create_agent_index(cfg.agent_index_value, + record.src_agent_abs_index, + record.src_agent_log_index, + record.src_agent_type_index, + std::string_view(record.src_agent_type)) + .as_string(); - cm.write_line(type, - fmt::format("\"{}\"", kernel.guid), - fmt::format("\"{}\"", "KERNEL_DISPATCH"), - fmt::format("\"{}\"", agent_identifier), - kernel.queue_id, - kernel.stream_id, - kernel.tid, - kernel.dispatch_id, - kernel.kernel_id, - fmt::format("\"{}\"", kernel_identifier), - kernel.stack_id, - kernel.start, - kernel.end, - kernel.scratch_size, - kernel.lds_size, - kernel.workgroup_size.x, - kernel.workgroup_size.y, - kernel.workgroup_size.z, - kernel.grid_size.x, - kernel.grid_size.y, - kernel.grid_size.z); - }); -} + std::string dst_agent_identifier = + create_agent_index(cfg.agent_index_value, + record.dst_agent_abs_index, + record.dst_agent_log_index, + record.dst_agent_type_index, + std::string_view(record.dst_agent_type)) + .as_string(); -void -write_memory_copy_csv( - CsvManager& csv_manager, - const rocprofiler::tool::generator& memory_copies_gen) -{ - process_data_to_csv(csv_manager, - CsvType::MEMORY_COPY, - memory_copies_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::memory_copies& mcopy) { - std::string src_agent_identifier = - create_agent_index(cm.config.agent_index_value, - mcopy.src_agent_abs_index, - mcopy.src_agent_log_index, - mcopy.src_agent_type_index, - std::string_view(mcopy.src_agent_type)) - .as_string(); - - std::string dst_agent_identifier = - create_agent_index(cm.config.agent_index_value, - mcopy.dst_agent_abs_index, - mcopy.dst_agent_log_index, - mcopy.dst_agent_type_index, - std::string_view(mcopy.dst_agent_type)) - .as_string(); - - cm.write_line(type, - fmt::format("\"{}\"", mcopy.guid), - fmt::format("\"{}\"", "MEMORY_COPY"), - fmt::format("\"{}\"", mcopy.name), - mcopy.stream_id, - fmt::format("\"{}\"", src_agent_identifier), - fmt::format("\"{}\"", dst_agent_identifier), - mcopy.stack_id, - mcopy.start, - mcopy.end); - }); + rocprofiler::tool::csv::memory_copy_with_stream_csv_encoder_guid::write_row( + row_ss, + record.guid, + record.category, + record.name, + record.stream_id, + src_agent_identifier, + dst_agent_identifier, + record.stack_id, + record.start, + record.end); + ofs << row_ss.str(); + } + } } void -write_memory_allocation_csv( - CsvManager& csv_manager, - const rocprofiler::tool::generator& memory_alloc_gen) +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data) { - process_data_to_csv( - csv_manager, - CsvType::MEMORY_ALLOCATION, - memory_alloc_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::memory_allocation& malloc) { - std::string operation = fmt::format("MEMORY_ALLOCATION_{}", malloc.type); - - std::string agent_identifier = create_agent_index(cm.config.agent_index_value, - malloc.agent_abs_index, - malloc.agent_log_index, - malloc.agent_type_index, - std::string_view(malloc.agent_type)) + if(data.empty()) return; + + for(const auto& ditr : data) + { + for(const auto& record : data.get(ditr)) + { + std::string agent_identifier = create_agent_index(cfg.agent_index_value, + record.agent_abs_index, + record.agent_log_index, + record.agent_type_index, + std::string_view(record.agent_type)) .as_string(); - std::string agent_id = - operation != "MEMORY_ALLOCATION_FREE" ? agent_identifier : "\"\""; - std::string address = fmt::format("\"0x{:016x}\"", malloc.address); - - cm.write_line(type, - fmt::format("\"{}\"", malloc.guid), - fmt::format("\"{}\"", "MEMORY_ALLOCATION"), - fmt::format("\"{}\"", operation), - fmt::format("\"{}\"", agent_id), - malloc.size, - address, - malloc.stack_id, - malloc.start, - malloc.end); - }); -} + std::string agent_id = record.type != "FREE" ? agent_identifier : ""; -void -write_scratch_memory_csv( - CsvManager& csv_manager, - const rocprofiler::tool::generator& scratch_memory_gen) -{ - process_data_to_csv( - csv_manager, - CsvType::SCRATCH_MEMORY, - scratch_memory_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::scratch_memory& scratch_mem) { - std::string agent_identifier = - create_agent_index(cm.config.agent_index_value, - scratch_mem.agent_abs_index, - scratch_mem.agent_log_index, - scratch_mem.agent_type_index, - std::string_view(scratch_mem.agent_type)) - .as_string(); + auto row_ss = std::stringstream{}; - cm.write_line(type, - fmt::format("\"{}\"", "SCRATCH_MEMORY"), - fmt::format("\"SCRATCH_MEMORY_{}\"", scratch_mem.operation), - fmt::format("\"{}\"", agent_identifier), - scratch_mem.queue_id, - scratch_mem.tid, - scratch_mem.alloc_flags, - scratch_mem.start, - scratch_mem.end); - }); -} + rocprofiler::tool::csv::memory_allocation_csv_encoder_guid::write_row( + row_ss, + record.guid, + record.category, + record.type, + agent_identifier, + record.size, + rocprofiler::sdk::utility::as_hex(record.address, 16), + record.stack_id, + record.start, + record.end); -void -write_hip_api_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& hip_api_gen) -{ - process_data_to_csv(csv_manager, - CsvType::HIP_API, - hip_api_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::region& api) { - // Skip entries that are not HIP API calls - if(api.category.find("HIP_") != 0) return; - - cm.write_line(type, - fmt::format("\"{}\"", api.guid), - fmt::format("\"{}\"", api.category), - fmt::format("\"{}\"", api.name), - api.pid, - api.tid, - api.stack_id, - api.start, - api.end); - }); + ofs << row_ss.str(); + } + } } void -write_hsa_api_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& hsa_api_gen) +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data) { - process_data_to_csv(csv_manager, - CsvType::HSA_CSV_API, - hsa_api_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::region& api) { - // Skip entries that are not HSA API calls - if(api.category.find("HSA_") != 0) return; - - cm.write_line(type, - fmt::format("\"{}\"", api.guid), - fmt::format("\"{}\"", api.category), - fmt::format("\"{}\"", api.name), - api.pid, - api.tid, - api.stack_id, - api.start, - api.end); - }); -} + if(data.empty()) return; -void -write_marker_api_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& marker_api_gen) -{ - namespace tool = ::rocprofiler::tool; - - if(marker_api_gen.empty()) return; - - using marker_csv_encoder = tool::csv::csv_encoder<8>; - - auto ofs = tool::csv_output_file{csv_manager.config, - domain_type::MARKER, - marker_csv_encoder{}, - {"Guid", - "Domain", - "Function", - "Process_Id", - "Thread_Id", - "Correlation_Id", - "Start_Timestamp", - "End_Timestamp"}}; - - // write samples first and ignore the timestamp ordering w.r.t. regions for now - for(auto ditr : marker_api_gen) + for(const auto& ditr : data) { - for(const auto& record : marker_api_gen.get(ditr)) + for(const auto& record : data.get(ditr)) { - auto row_ss = std::stringstream{}; - auto _name = record.name; + std::string agent_identifier = create_agent_index(cfg.agent_index_value, + record.agent_abs_index, + record.agent_log_index, + record.agent_type_index, + std::string_view(record.agent_type)) + .as_string(); - if(record.has_extdata()) - { - if(auto _extdata = record.get_extdata(); !_extdata.message.empty()) - _name = _extdata.message; - } + auto row_ss = std::stringstream{}; - marker_csv_encoder::write_row(row_ss, - record.guid, - record.category, - _name, - record.pid, - record.tid, - record.stack_id, - record.start, - record.end); + rocprofiler::tool::csv::scratch_memory_encoder_guid::write_row(row_ss, + record.guid, + record.category, + record.operation, + agent_identifier, + record.size, + record.queue_id, + record.tid, + record.alloc_flags, + record.start, + record.end); ofs << row_ss.str(); } @@ -473,266 +492,87 @@ write_marker_api_csv(CsvManager& c } void -write_rccl_api_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& rccl_api_gen) +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data) { - process_data_to_csv(csv_manager, - CsvType::RCCL_API, - rccl_api_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::region& api) { - if(api.category.find("RCCL_") != 0) return; - - cm.write_line(type, - fmt::format("\"{}\"", api.guid), - fmt::format("\"{}\"", api.category), - fmt::format("\"{}\"", api.name), - api.pid, - api.tid, - api.stack_id, - api.start, - api.end); - }); -} + if(data.empty()) return; -void -write_rocdecode_api_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& rocdecode_api_gen) -{ - process_data_to_csv(csv_manager, - CsvType::ROCDECODE_API, - rocdecode_api_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::region& api) { - if(api.category.find("ROCDECODE_") != 0) return; - - cm.write_line(type, - fmt::format("\"{}\"", api.guid), - fmt::format("\"{}\"", api.category), - fmt::format("\"{}\"", api.name), - api.pid, - api.tid, - api.stack_id, - api.start, - api.end); - }); -} + for(const auto& ditr : data) + { + for(const auto& record : data.get(ditr)) + { + std::string agent_identifier = create_agent_index(cfg.agent_index_value, + record.agent_abs_index, + record.agent_log_index, + record.agent_type_index, + std::string_view(record.agent_type)) + .as_string(); -void -write_rocjpeg_api_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& rocjpeg_api_gen) -{ - process_data_to_csv(csv_manager, - CsvType::ROCJPEG_API, - rocjpeg_api_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::region& api) { - if(api.category.find("ROCJPEG_") != 0) return; - - cm.write_line(type, - fmt::format("\"{}\"", api.guid), - fmt::format("\"{}\"", api.category), - fmt::format("\"{}\"", api.name), - api.pid, - api.tid, - api.stack_id, - api.start, - api.end); - }); -} + auto row_ss = std::stringstream{}; + rocprofiler::tool::csv::counter_collection_csv_encoder_guid::write_row( + row_ss, + record.guid, + record.stack_id, + record.dispatch_id, + agent_identifier, + record.queue_id, + record.pid, + record.tid, + record.grid_size, + record.kernel_id, + record.kernel_name, + record.workgroup_size, + record.lds_block_size, + record.scratch_size, + record.vgpr_count, + record.accum_vgpr_count, + record.sgpr_count, + record.counter_name, + record.value, + record.start, + record.end); -void -write_agent_info_csv(CsvManager& csv_manager, const std::vector& agents) -{ - if(agents.empty()) return; - - namespace tool = ::rocprofiler::tool; - using agent_info_csv_encoder = tool::csv::csv_encoder<54>; - - auto ofs = tool::csv_output_file{csv_manager.config, - "agent_info", - agent_info_csv_encoder{}, - {"Guid", - "Node_Id", - "Logical_Node_Id", - "Agent_Type", - "Cpu_Cores_Count", - "Simd_Count", - "Cpu_Core_Id_Base", - "Simd_Id_Base", - "Max_Waves_Per_Simd", - "Lds_Size_In_Kb", - "Gds_Size_In_Kb", - "Num_Gws", - "Wave_Front_Size", - "Num_Xcc", - "Cu_Count", - "Array_Count", - "Num_Shader_Banks", - "Simd_Arrays_Per_Engine", - "Cu_Per_Simd_Array", - "Simd_Per_Cu", - "Max_Slots_Scratch_Cu", - "Gfx_Target_Version", - "Vendor_Id", - "Device_Id", - "Location_Id", - "Domain", - "Drm_Render_Minor", - "Num_Sdma_Engines", - "Num_Sdma_Xgmi_Engines", - "Num_Sdma_Queues_Per_Engine", - "Num_Cp_Queues", - "Max_Engine_Clk_Ccompute", - "Max_Engine_Clk_Fcompute", - "Sdma_Fw_Version", - "Fw_Version", - "Capability", - "Cu_Per_Engine", - "Max_Waves_Per_Cu", - "Family_Id", - "Workgroup_Max_Size", - "Grid_Max_Size", - "Local_Mem_Size", - "Hive_Id", - "Gpu_Id", - "Workgroup_Max_Dim_X", - "Workgroup_Max_Dim_Y", - "Workgroup_Max_Dim_Z", - "Grid_Max_Dim_X", - "Grid_Max_Dim_Y", - "Grid_Max_Dim_Z", - "Name", - "Vendor_Name", - "Product_Name", - "Model_Name"}}; - - for(const auto& itr : agents) - { - auto row_ss = std::stringstream{}; - agent_info_csv_encoder::write_row(row_ss, - itr.guid, - itr.node_id, - itr.logical_node_id, - itr.type, - itr.cpu_cores_count, - itr.simd_count, - itr.cpu_core_id_base, - itr.simd_id_base, - itr.max_waves_per_simd, - itr.lds_size_in_kb, - itr.gds_size_in_kb, - itr.num_gws, - itr.wave_front_size, - itr.num_xcc, - itr.cu_count, - itr.array_count, - itr.num_shader_banks, - itr.simd_arrays_per_engine, - itr.cu_per_simd_array, - itr.simd_per_cu, - itr.max_slots_scratch_cu, - itr.gfx_target_version, - itr.vendor_id, - itr.device_id, - itr.location_id, - itr.domain, - itr.drm_render_minor, - itr.num_sdma_engines, - itr.num_sdma_xgmi_engines, - itr.num_sdma_queues_per_engine, - itr.num_cp_queues, - itr.max_engine_clk_ccompute, - itr.max_engine_clk_fcompute, - itr.sdma_fw_version.Value, - itr.fw_version.Value, - itr.capability.Value, - itr.cu_per_engine, - itr.max_waves_per_cu, - itr.family_id, - itr.workgroup_max_size, - itr.grid_max_size, - itr.local_mem_size, - itr.hive_id, - itr.gpu_id, - itr.workgroup_max_dim.x, - itr.workgroup_max_dim.y, - itr.workgroup_max_dim.z, - itr.grid_max_dim.x, - itr.grid_max_dim.y, - itr.grid_max_dim.z, - itr.name, - itr.vendor_name, - itr.product_name, - itr.model_name); - ofs << row_ss.str(); + ofs << row_ss.str(); + } } } void -write_counters_csv(CsvManager& csv_manager, - const rocprofiler::tool::generator& counter_gen) +generate_csv(rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data, + const domain_type domain) { - process_data_to_csv(csv_manager, - CsvType::COUNTER, - counter_gen, - [](CsvManager& cm, CsvType type, const rocpd::types::counter& counter) { - std::string agent_identifier = - create_agent_index(cm.config.agent_index_value, - counter.agent_abs_index, - counter.agent_log_index, - counter.agent_type_index, - std::string_view(counter.agent_type)) - .as_string(); - - cm.write_line(type, - counter.guid, - counter.stack_id, - counter.dispatch_id, - fmt::format("\"{}\"", agent_identifier), - counter.queue_id, - counter.pid, - counter.tid, - counter.grid_size, - counter.kernel_id, - fmt::format("\"{}\"", counter.kernel_name), - counter.workgroup_size, - counter.lds_block_size, - counter.scratch_size, - counter.vgpr_count, - counter.accum_vgpr_count, - counter.sgpr_count, - fmt::format("\"{}\"", counter.counter_name), - counter.value, - counter.start, - counter.end); - }); -} + if(data.empty()) return; -void -write_csvs(CsvManager& csv_manager, - const rocprofiler::tool::generator& kernel_dispatch, - const rocprofiler::tool::generator& memory_copies, - const rocprofiler::tool::generator& memory_allocations, - const rocprofiler::tool::generator& hip_api_calls, - const rocprofiler::tool::generator& hsa_api_calls, - const rocprofiler::tool::generator& marker_api_calls, - const rocprofiler::tool::generator& counters_calls, - const rocprofiler::tool::generator& scratch_memory_calls, - const rocprofiler::tool::generator& rccl_calls, - const rocprofiler::tool::generator& rocdecode_calls, - const rocprofiler::tool::generator& rocjpeg_calls) -{ - rocpd::output::write_kernel_csv(csv_manager, kernel_dispatch); - rocpd::output::write_memory_copy_csv(csv_manager, memory_copies); - rocpd::output::write_memory_allocation_csv(csv_manager, memory_allocations); - rocpd::output::write_hip_api_csv(csv_manager, hip_api_calls); - rocpd::output::write_hsa_api_csv(csv_manager, hsa_api_calls); - rocpd::output::write_marker_api_csv(csv_manager, marker_api_calls); - - rocpd::output::write_counters_csv(csv_manager, counters_calls); - rocpd::output::write_scratch_memory_csv(csv_manager, scratch_memory_calls); - rocpd::output::write_rccl_api_csv(csv_manager, rccl_calls); - - rocpd::output::write_rocdecode_api_csv(csv_manager, rocdecode_calls); - rocpd::output::write_rocjpeg_api_csv(csv_manager, rocjpeg_calls); + for(const auto& ditr : data) + { + for(const auto& record : data.get(ditr)) + { + auto row_ss = std::stringstream{}; + + auto name = record.name; + + if(domain == domain_type::MARKER) + { + std::string message = extract_field_from_extdata(record.extdata, "message"); + if(!message.empty()) name = message; + } + + rocprofiler::tool::csv::api_csv_encoder_guid::write_row(row_ss, + record.guid, + record.category, + name, + record.pid, + record.tid, + record.stack_id, + record.start, + record.end); + + ofs << row_ss.str(); + } + } } + } // namespace output -} // namespace rocpd +} // namespace rocpd \ No newline at end of file diff --git a/source/lib/python/rocpd/source/csv.hpp b/source/lib/python/rocpd/source/csv.hpp index 7cabdcaf10..dffa8d3eee 100644 --- a/source/lib/python/rocpd/source/csv.hpp +++ b/source/lib/python/rocpd/source/csv.hpp @@ -1,6 +1,7 @@ + // MIT License // -// Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved. +// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -9,8 +10,8 @@ // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -22,9 +23,13 @@ #pragma once +#include +#include + #include "lib/python/rocpd/source/types.hpp" #include "lib/common/defines.hpp" +#include "lib/output/csv_output_file.hpp" #include "lib/output/generateStats.hpp" #include "lib/output/generator.hpp" #include "lib/output/metadata.hpp" @@ -45,76 +50,59 @@ namespace rocpd { namespace output { -using rocprofiler::tool::float_type; +rocprofiler::tool::csv_output_file +generate_agent_ofs(const rocprofiler::tool::output_config& cfg); -struct CsvFileConfig -{ - std::string filename; - std::string header; -}; +rocprofiler::tool::csv_output_file +generate_kernel_ofs(const rocprofiler::tool::output_config& cfg); -enum class CsvType -{ - KERNEL_DISPATCH, - MEMORY_COPY, - MEMORY_ALLOCATION, - SCRATCH_MEMORY, - HIP_API, - HSA_CSV_API, - MARKER, - COUNTER, - RCCL_API, - ROCDECODE_API, - ROCJPEG_API, -}; - -class CsvManager -{ -public: - CsvManager(rocprofiler::tool::output_config output_cfg); - ~CsvManager(); +rocprofiler::tool::csv_output_file +generate_memory_copy_ofs(const rocprofiler::tool::output_config& cfg); - rocprofiler::tool::output_config config; - std::map csv_configs; +rocprofiler::tool::csv_output_file +generate_memory_allocation_ofs(const rocprofiler::tool::output_config& cfg); - std::ofstream& get_stream(CsvType type); +rocprofiler::tool::csv_output_file +generate_scratch_memory_ofs(const rocprofiler::tool::output_config& cfg); - bool has_stream(CsvType type) const; - bool initialize_csv_file(CsvType type); +rocprofiler::tool::csv_output_file +generate_counter_ofs(const rocprofiler::tool::output_config& cfg); - template - void write_line(CsvType type, Args&&... args) - { - auto& stream = get_stream(type); - if(!stream.is_open()) return; +rocprofiler::tool::csv_output_file +generate_region_ofs(const rocprofiler::tool::output_config& cfg, domain_type domain); - std::vector items; - (items.push_back(fmt::format("{}", std::forward(args))), ...); - stream << fmt::format("{}\n", fmt::join(items, ",")); - } +void +generate_csv(rocprofiler::tool::csv_output_file& ofs, const std::vector& data); -private: - std::map streams; - std::map file_paths; +void +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data); - bool ensure_output_directory() const; -}; +void +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data); + +void +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data); void -write_agent_info_csv(CsvManager& csv_manager, const std::vector& agents); +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data); void -write_csvs(CsvManager& csv_manager, - const rocprofiler::tool::generator& kernel_dispatch, - const rocprofiler::tool::generator& memory_copies, - const rocprofiler::tool::generator& memory_allocations, - const rocprofiler::tool::generator& hip_api_calls, - const rocprofiler::tool::generator& hsa_api_calls, - const rocprofiler::tool::generator& marker_api_calls, - const rocprofiler::tool::generator& counters_calls, - const rocprofiler::tool::generator& scratch_memory_calls, - const rocprofiler::tool::generator& rccl_calls, - const rocprofiler::tool::generator& rocdecode_calls, - const rocprofiler::tool::generator& rocjpeg_calls); +generate_csv(const rocprofiler::tool::output_config& cfg, + rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data); + +void +generate_csv(rocprofiler::tool::csv_output_file& ofs, + const rocprofiler::tool::generator& data, + const domain_type domain); + } // namespace output } // namespace rocpd diff --git a/source/lib/python/rocpd/source/types.hpp b/source/lib/python/rocpd/source/types.hpp index 165c68c36e..1bbe906b8e 100644 --- a/source/lib/python/rocpd/source/types.hpp +++ b/source/lib/python/rocpd/source/types.hpp @@ -286,6 +286,9 @@ struct kernel_dispatch uint64_t stack_id = 0; uint64_t parent_stack_id = 0; uint64_t corr_id = 0; + uint64_t sgpr_count = 0; + uint64_t arch_vgpr_count = 0; + uint64_t accum_vgpr_count = 0; }; struct memory_allocation @@ -714,6 +717,9 @@ load(ArchiveT& ar, rocpd::types::kernel_dispatch& data) LOAD_DATA_FIELD(stack_id); LOAD_DATA_FIELD(parent_stack_id); LOAD_DATA_FIELD(corr_id); + LOAD_DATA_FIELD(sgpr_count); + LOAD_DATA_FIELD(arch_vgpr_count); + LOAD_DATA_FIELD(accum_vgpr_count); } template diff --git a/source/lib/python/rocpd/summary.py b/source/lib/python/rocpd/summary.py new file mode 100644 index 0000000000..e9295cb638 --- /dev/null +++ b/source/lib/python/rocpd/summary.py @@ -0,0 +1,532 @@ +#!/usr/bin/env python3 +############################################################################### +# MIT License +# +# Copyright (c) 2025 Advanced Micro Devices, Inc. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +############################################################################### + +import argparse +import os + +from typing import Any, List, Tuple +from .importer import RocpdImportData, execute_statement +from .query import export_sqlite_query +from . import output_config + + +def get_temp_view_names(connection: RocpdImportData) -> List[str]: + """Return the names of all temporary views in the SQLite connection.""" + return [ + v[0] + for v in execute_statement( + connection, "SELECT name FROM sqlite_temp_master WHERE type='view';" + ).fetchall() + ] + + +def get_temp_view_columns(connection: RocpdImportData, view_name: str) -> List[str]: + """Return the column names of a given temporary view.""" + cursor = connection.cursor() + cursor.execute(f"PRAGMA table_xinfo('{view_name}')") + return [row[1] for row in cursor.fetchall()] + + +def make_temp_view_query(view_name, query) -> str: + return "CREATE TEMPORARY VIEW IF NOT EXISTS `{}` AS {}".format(view_name, query) + + +def export_view( + connection: RocpdImportData, view_name, output_format, output_path, filename="" +) -> None: + """Write the contents of a SQL view to an output format.""" + + query = "SELECT * FROM `{}`".format(view_name) + query_one = "SELECT * FROM `{}` LIMIT 1".format(view_name) + + # just return if view is empty + if not connection.execute(query_one).fetchone(): + return + + # prepare the output filename + if not filename: + output_filename = view_name + else: + output_filename = f"{filename}_{view_name}" + + if output_format == "console": + print(f"\n{view_name.upper()}:") + + # call query module to export. query will append the extension + export_path = os.path.join(output_path, output_filename) + export_sqlite_query( + connection, query, export_format=output_format, export_path=export_path + ) + + +def generate_summary_query( + view_name: str, + name_column="name", + by_rank=False, +) -> Tuple[str, str]: + """Generate the SQL statement to create a summary view.""" + + if by_rank: + view_suffix = "_summary_by_rank" + group_by_columns = "guid, {name_column}".format(name_column=name_column) + aggregation_group_by = "T.guid, T.nid, T.{name_column}".format( + name_column=name_column + ) + total_duration_group_by = "guid" + additional_select_columns = "AD.pid AS ProcessID, P.hostname AS Hostname," + additional_aggregated_columns = """ + T.guid, + T.nid, + T.pid,""" + join_condition = "T.guid = A.guid AND T.{name_column} = A.name".format( + name_column=name_column + ) + total_duration_join = "JOIN total_duration TD ON AD.guid = TD.guid JOIN processes P ON AD.pid = P.pid" + else: + view_suffix = "_summary" + group_by_columns = name_column + aggregation_group_by = "T.{name_column}".format(name_column=name_column) + total_duration_group_by = "" + additional_select_columns = "" + additional_aggregated_columns = "" + join_condition = "T.{name_column} = A.name".format(name_column=name_column) + total_duration_join = "CROSS JOIN total_duration TD" + + full_view_name = f"{view_name}{view_suffix}" + + summary_query = f""" + WITH + avg_data AS ( + SELECT + {group_by_columns.replace(name_column, f"{name_column} AS name")}, + AVG(duration) AS avg_duration + FROM {view_name} + GROUP BY {group_by_columns} + ), + aggregated_data AS ( + SELECT{additional_aggregated_columns} + T.{name_column} as name, + COUNT(*) AS calls, + SUM(T.duration) AS total_duration, + A.avg_duration AS average_duration, + MIN(T.duration) AS min_duration, + MAX(T.duration) AS max_duration, + SQRT(SUM(CAST((T.duration - A.avg_duration) AS REAL) * CAST((T.duration - A.avg_duration) AS REAL)) / (COUNT(*) - 1)) AS std_dev_duration + FROM {view_name} T + JOIN avg_data A ON {join_condition} + GROUP BY {aggregation_group_by} + ), + total_duration AS ( + SELECT + {f"{total_duration_group_by}," if total_duration_group_by else ""} + SUM(total_duration) AS grand_total_duration + FROM + aggregated_data + {f"GROUP BY {total_duration_group_by}" if total_duration_group_by else ""} + ) + SELECT + {additional_select_columns} + AD.name AS Name, + AD.calls AS Calls, + AD.total_duration AS "DURATION (nsec)", + AD.average_duration AS "AVERAGE (nsec)", + (CAST(AD.total_duration AS REAL) / TD.grand_total_duration) * 100 AS "PERCENT (INC)", + AD.min_duration AS "MIN (nsec)", + AD.max_duration AS "MAX (nsec)", + AD.std_dev_duration AS "STD_DEV" + FROM + aggregated_data AD + {total_duration_join} + ORDER BY + {"AD.pid," if by_rank else ""} AD.total_duration DESC; + """ + + return (full_view_name, summary_query) + + +def generate_domain_query(connection: RocpdImportData, by_rank=False) -> Tuple[str, str]: + """Generate the SQL statement for domain summary by doing union over all summary views.""" + + if by_rank: + view_suffix = "_summary_by_rank" + view_name = "domain_summary_by_rank" + additional_group_columns = "ProcessID, Hostname," + additional_select_columns = "GD.ProcessID, GD.Hostname," + total_duration_group_by = "GROUP BY ProcessID" + join_condition = "JOIN total_duration TD ON GD.ProcessID = TD.ProcessID" + order_by = "ORDER BY GD.ProcessID" + else: + view_suffix = "_summary" + view_name = "domain_summary" + additional_group_columns = "" + additional_select_columns = "" + total_duration_group_by = "" + join_condition = "CROSS JOIN total_duration TD" + order_by = 'ORDER BY GD."DURATION (nsec)" DESC' + + summary_views = [ + itr for itr in get_temp_view_names(connection) if itr.endswith(view_suffix) + ] + + if len(summary_views) < 1: + return view_name + + union_selects = [ + f" SELECT '{s.replace(view_suffix, '').upper()}' as domain, * FROM {s} " + for s in summary_views + ] + + domain_select = f""" + WITH + all_domains AS ( + {f" UNION ALL ".join(union_selects)} + ), + grouped_domains AS ( + SELECT + domain, + {additional_group_columns} + SUM(calls) AS calls, + SUM("DURATION (nsec)") AS "DURATION (nsec)", + SUM("AVERAGE (nsec)") AS "AVERAGE (nsec)", + MIN("MIN (nsec)") AS "MIN (nsec)", + MAX("MAX (nsec)") AS "MAX (nsec)", + SUM("STD_DEV") AS "STD_DEV" + FROM all_domains + GROUP BY domain{", ProcessID" if by_rank else ""} + ), + total_duration AS ( + SELECT + {additional_group_columns} + SUM("DURATION (nsec)") AS grand_total_duration + FROM grouped_domains + {total_duration_group_by} + ) + SELECT + {additional_select_columns} + GD.domain AS Name, + GD.calls AS Calls, + GD."DURATION (nsec)", + GD."AVERAGE (nsec)", + (CAST(GD."DURATION (nsec)" AS REAL) / TD.grand_total_duration) * 100 AS "PERCENT (INC)", + GD."MIN (nsec)", + GD."MAX (nsec)", + GD."STD_DEV" + FROM + grouped_domains GD + {join_condition} + {order_by}; + """ + + return (view_name, domain_select) + + +def create_summary_views(connection: RocpdImportData, by_rank=False) -> None: + """Create summary views for eligible temporary views in the database.""" + + NAME_COLUMN_MAP = { + "memory_allocations": "type", + "scratch_memory": "operation", + } + + avoid_view_pattern = ("rocpd", "region", "counter", "pmc") + required_columns = {"duration"} + + views = get_temp_view_names(connection) + + for view_name in views: + if any(pattern in view_name for pattern in avoid_view_pattern): + continue + + columns = get_temp_view_columns(connection, view_name) + if not required_columns.issubset(columns): + continue + + # Create regular summary view + summary_view_name, summary_query = generate_summary_query( + view_name, name_column=NAME_COLUMN_MAP.get(view_name, "name") + ) + connection.execute(make_temp_view_query(summary_view_name, summary_query)) + + # Create per-rank summary + if by_rank: + per_rank_view_name, summary_by_rank_query = generate_summary_query( + view_name, + name_column=NAME_COLUMN_MAP.get(view_name, "name"), + by_rank=True, + ) + connection.execute( + make_temp_view_query(per_rank_view_name, summary_by_rank_query) + ) + + +def create_summary_region_views( + connection: RocpdImportData, by_rank=False, region_categories=None +) -> None: + """Create summary and region views""" + + query = "SELECT DISTINCT(category) FROM regions_and_samples;" + categories = execute_statement(connection, query).fetchall() + + if region_categories is None: + # Automatically retrieve region categories from the database + region_categories = set([cat[0].split("_")[0] for cat in categories]) + + category_map = { + cat.lower(): [c[0] for c in categories if c[0].startswith(cat + "_")] + for cat in region_categories + if "MARKER" not in cat.upper() + } + + for k, v in category_map.items(): + if len(v) > 0: + conditions = [f"category LIKE '{c}'" for c in v] + temp_region_view = f""" + CREATE TEMPORARY VIEW IF NOT EXISTS `{k}` AS + SELECT * + FROM regions_and_samples + WHERE {" OR ".join(conditions)}; + """ + + connection.execute(temp_region_view) + + # Create regular summary view + summary_view_name, summary_query = generate_summary_query(k) + connection.execute(make_temp_view_query(summary_view_name, summary_query)) + + # Create per-rank summary view + if by_rank: + per_rank_view_name, summary_by_rank_query = generate_summary_query( + k, by_rank=True + ) + connection.execute( + make_temp_view_query(per_rank_view_name, summary_by_rank_query) + ) + + # Markers + if "MARKER" not in region_categories: + return + + view_name = "markers" + markers_create = f""" + CREATE TEMPORARY VIEW IF NOT EXISTS `{view_name}` AS + SELECT JSON_EXTRACT(extdata, '$.message') AS marker_name, * + FROM regions_and_samples + WHERE category LIKE 'MARKER_%' + """ + connection.execute(markers_create) + + # Create regular summary view + summary_view_name, summary_query = generate_summary_query( + view_name, name_column="marker_name" + ) + connection.execute(make_temp_view_query(summary_view_name, summary_query)) + + # Create per-rank summary view + if by_rank: + per_rank_view_name, summary_by_rank_query = generate_summary_query( + view_name, name_column="marker_name", by_rank=True + ) + connection.execute( + make_temp_view_query(per_rank_view_name, summary_by_rank_query) + ) + + +def create_domain_view(connection: RocpdImportData, by_rank=False) -> str: + """Create a domain summary view by aggregating all summary views.""" + + view_name, domain_query = generate_domain_query(connection, by_rank=by_rank) + + # Create the domain summary view + connection.execute(make_temp_view_query(view_name, domain_query)) + + return view_name + + +def generate_all_summaries(connection: RocpdImportData, **kwargs: Any) -> None: + """Generate all summary views and write them to CSV files.""" + + domain_summary = kwargs.get("domain_summary", False) + by_rank = kwargs.get("summary_by_rank", False) + filename = kwargs.get("output_file", "") + output_path = kwargs.get("output_path", "./rocpd-output-data") + region_categories = kwargs.get("region_categories", None) + output_format = kwargs.get("format", "console") + + # create the temporary summary views + create_summary_views(connection, by_rank) + create_summary_region_views(connection, by_rank, region_categories=region_categories) + + if domain_summary: + create_domain_view(connection) + # Create domain summary per rank only if both domain_summary and summary_by_rank are enabled + if by_rank: + create_domain_view(connection, by_rank=True) + + # Write regular summary views + print("\nSummary files:") + summary_views = [ + itr for itr in get_temp_view_names(connection) if itr.endswith("_summary") + ] + for v in summary_views: + export_view(connection, v, output_format, output_path, filename) + + # Write per-rank summary views if flag is set + if by_rank: + print("\nSummary files by rank:") + summary_by_rank_views = [ + itr + for itr in get_temp_view_names(connection) + if itr.endswith("_summary_by_rank") + ] + for v in summary_by_rank_views: + export_view(connection, v, output_format, output_path, filename) + + +# +# Command-line interface functions +# + + +def add_io_args(parser): + """Add input/output arguments for summary.""" + io_options = parser.add_argument_group("I/O options") + + io_options.add_argument( + "-f", + "--format", + help="Sets the format the summaries are output to (default: console)", + choices=("console", "csv", "html", "json", "md", "pdf"), + default="console", + type=str, + required=False, + ) + io_options.add_argument( + "-o", + "--output-file", + help="Sets the base output file name", + default=os.environ.get("ROCPD_OUTPUT_NAME", ""), + type=str, + required=False, + ) + io_options.add_argument( + "-d", + "--output-path", + help="Sets the output path where the output files will be saved (default path: `./rocpd-output-data`)", + default=os.environ.get("ROCPD_OUTPUT_PATH", "./rocpd-output-data"), + type=str, + required=False, + ) + + return ["format", "output_file", "output_path"] + + +def add_args(parser): + """Add arguments for summary.""" + summary_options = parser.add_argument_group("Summary options") + summary_options.add_argument( + "--domain-summary", + action="store_true", + default=False, + help="Generate domain summary view", + ) + summary_options.add_argument( + "--summary-by-rank", + action="store_true", + default=False, + help="Generate summary views by-rank (or Process ID)", + ) + summary_options.add_argument( + "--region-categories", + nargs="+", + default=None, + help="Specify region categories to include in the summary (example: HIP, HSA, RCCL, ROCDECODE, ROCJPEG, MARKER). If not specified, categories will be automatically retrieved from the database.", + ) + + return ["domain_summary", "summary_by_rank", "region_categories"] + + +def process_args(args, valid_args): + + ret = {} + for itr in valid_args: + if hasattr(args, itr): + val = getattr(args, itr) + if val is not None: + ret[itr] = val + return ret + + +def execute(input, window_args=None, **kwargs: Any) -> RocpdImportData: + from .time_window import apply_time_window + + importData = RocpdImportData(input) + + apply_time_window(importData, **window_args) + + generate_all_summaries(importData, **kwargs) + + return importData + + +def main(argv=None) -> int: + """Main entry point for command line execution.""" + from .time_window import add_args as add_args_time_window + from .time_window import process_args as process_args_time_window + + parser = argparse.ArgumentParser( + description="Create ROCpd database summary region views" + ) + required_params = parser.add_argument_group("Required options") + + required_params.add_argument( + "-i", + "--input", + required=True, + type=output_config.check_file_exists, + nargs="+", + help="Input path and filename to one or more database(s), separated by spaces", + ) + + valid_io_args = add_io_args(parser) + valid_summary_args = add_args(parser) + valid_time_window_args = add_args_time_window(parser) + + args = parser.parse_args(argv) + + summary_args = process_args(args, valid_summary_args) + io_args = output_config.process_args(args, valid_io_args) + window_args = process_args_time_window(args, valid_time_window_args) + + all_args = {**summary_args, **io_args} + + execute( + args.input, + window_args=window_args, + **all_args, + ) + + +if __name__ == "__main__": + main() diff --git a/source/lib/python/roctx/__init__.py b/source/lib/python/roctx/__init__.py index 701121babb..64b874be83 100644 --- a/source/lib/python/roctx/__init__.py +++ b/source/lib/python/roctx/__init__.py @@ -38,8 +38,24 @@ "nameOsThread", "nameHipDevice", "context_decorators", + "version_info", ] +version_info = { + "version": "@PROJECT_VERSION@", + "major": int("@PROJECT_VERSION_MAJOR@"), + "minor": int("@PROJECT_VERSION_MINOR@"), + "patch": int("@PROJECT_VERSION_PATCH@"), + "git_revision": "@ROCPROFILER_SDK_GIT_REVISION@", + "library_arch": "@CMAKE_LIBRARY_ARCHITECTURE@", + "system_name": "@CMAKE_SYSTEM_NAME@", + "system_processor": "@CMAKE_SYSTEM_PROCESSOR@", + "system_version": "@CMAKE_SYSTEM_VERSION@", + "compiler_id": "@CMAKE_CXX_COMPILER_ID@", + "compiler_version": "@CMAKE_CXX_COMPILER_VERSION@", + "rocm_version": "@rocm_version_FULL_VERSION@", +} + def mark(msg): return libpyroctx.roctxMark(msg) if msg is not None else None diff --git a/source/lib/python/utilities.cmake b/source/lib/python/utilities.cmake index 6bae034df2..9c8a987d76 100644 --- a/source/lib/python/utilities.cmake +++ b/source/lib/python/utilities.cmake @@ -97,7 +97,7 @@ function(rocprofiler_roctx_python_bindings _VERSION) foreach(_SOURCE ${roctx_PYTHON_SOURCES}) configure_file(${CMAKE_CURRENT_LIST_DIR}/${_SOURCE} - ${roctx_PYTHON_OUTPUT_DIRECTORY}/${_SOURCE} COPYONLY) + ${roctx_PYTHON_OUTPUT_DIRECTORY}/${_SOURCE} @ONLY) install( FILES ${roctx_PYTHON_OUTPUT_DIRECTORY}/${_SOURCE} DESTINATION ${roctx_PYTHON_INSTALL_DIRECTORY} @@ -154,12 +154,14 @@ function(rocprofiler_rocpd_python_bindings _VERSION) output_config.py otf2.py pftrace.py + query.py schema.py + summary.py time_window.py) foreach(_SOURCE ${rocpd_PYTHON_SOURCES}) configure_file(${CMAKE_CURRENT_LIST_DIR}/${_SOURCE} - ${rocpd_PYTHON_OUTPUT_DIRECTORY}/${_SOURCE} COPYONLY) + ${rocpd_PYTHON_OUTPUT_DIRECTORY}/${_SOURCE} @ONLY) install( FILES ${rocpd_PYTHON_OUTPUT_DIRECTORY}/${_SOURCE} DESTINATION ${rocpd_PYTHON_INSTALL_DIRECTORY} diff --git a/source/lib/rocprofiler-sdk-tool/config.hpp b/source/lib/rocprofiler-sdk-tool/config.hpp index a253439542..7da04a9a9c 100644 --- a/source/lib/rocprofiler-sdk-tool/config.hpp +++ b/source/lib/rocprofiler-sdk-tool/config.hpp @@ -139,7 +139,8 @@ struct config : output_config int mpi_rank = get_mpi_rank(); uint64_t att_param_shader_engine_mask = get_env("ROCPROF_ATT_PARAM_SHADER_ENGINE_MASK", 0x1); - uint64_t att_param_buffer_size = get_env("ROCPROF_ATT_PARAM_BUFFER_SIZE", 0x6000000); + // 256MB + uint64_t att_param_buffer_size = get_env("ROCPROF_ATT_PARAM_BUFFER_SIZE", 0x10000000); uint64_t att_param_simd_select = get_env("ROCPROF_ATT_PARAM_SIMD_SELECT", 0xF); uint64_t att_param_target_cu = get_env("ROCPROF_ATT_PARAM_TARGET_CU", 1); uint64_t att_param_perf_ctrl = get_env("ROCPROF_ATT_PARAM_PERFCOUNTER_CTRL", 0); diff --git a/source/lib/rocprofiler-sdk-tool/tool.cpp b/source/lib/rocprofiler-sdk-tool/tool.cpp index aaab7bf80b..bf0cd3e6f5 100644 --- a/source/lib/rocprofiler-sdk-tool/tool.cpp +++ b/source/lib/rocprofiler-sdk-tool/tool.cpp @@ -1128,6 +1128,7 @@ construct_counter_collection_profile(rocprofiler_agent_id_t agent_id, auto profile = std::optional{}; auto counters_v = counter_vec_t{}; auto found_v = std::vector{}; + auto not_found_counters_v = std::vector{}; const auto* agent_v = tool_metadata->get_agent(agent_id); auto expected_v = counters.size(); @@ -1158,24 +1159,31 @@ construct_counter_collection_profile(rocprofiler_agent_id_t agent_id, } // search the gpu agent counter info for a counter with a matching name + bool counter_found = false; for(const auto& citr : gpu_agents_counter_info.at(agent_id)) { if(name_v == std::string_view{citr.name}) { counters_v.emplace_back(citr.id); found_v.emplace_back(itr); + counter_found = true; } } + + if(!counter_found) not_found_counters_v.emplace_back(itr); } if(expected_v != counters_v.size()) { auto requested_counters = fmt::format("{}", fmt::join(counters.begin(), counters.end(), ", ")); - auto found_counters = fmt::format("{}", fmt::join(found_v.begin(), found_v.end(), ", ")); + auto found_counters = fmt::format("{}", fmt::join(found_v.begin(), found_v.end(), ", ")); + auto missing_counters = fmt::format( + "{}", fmt::join(not_found_counters_v.begin(), not_found_counters_v.end(), ", ")); ROCP_WARNING << "Unable to find all counters for agent " << agent_v->node_id << " (gpu-" << agent_v->gpu_index << ", " << agent_v->name << ") in [" - << requested_counters << "]. Found: [" << found_counters << "]"; + << requested_counters << "]. Found: [" << found_counters << "]. Missing: [" + << missing_counters << "]"; } if(!counters_v.empty()) @@ -1246,6 +1254,22 @@ get_instruction_index(rocprofiler_pc_t pc) return CHECK_NOTNULL(tool_metadata)->get_instruction_index(pc); } +std::set +get_config_perf_counters() +{ + auto tool_pmc_counters = std::set{}; + for(const auto& counters_group : tool::config().counters) + { + for(const auto& counter : counters_group) + tool_pmc_counters.emplace(counter); + } + for(const auto& att_counter : tool::config().att_param_perfcounters) + { + tool_pmc_counters.emplace(att_counter.counter_name); + } + return tool_pmc_counters; +} + } // namespace std::vector @@ -1461,7 +1485,7 @@ initialize_logging() { auto logging_cfg = rocprofiler::common::logging_config{.install_failure_handler = true}; common::init_logging("ROCPROF", logging_cfg); - FLAGS_colorlogtostderr = true; + FLAGS_colorlogtostderr = isatty(fileno(stderr)) == 1 ? true : false; } } @@ -1679,7 +1703,7 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data) const uint64_t buffer_size = 16 * common::units::get_page_size(); const uint64_t buffer_watermark = 15 * common::units::get_page_size(); - tool_metadata->init(tool::metadata::inprocess{}); + tool_metadata->init(tool::metadata::inprocess_with_counters{get_config_perf_counters()}); ROCPROFILER_CALL(rocprofiler_create_context(&get_client_ctx()), "create context failed"); diff --git a/source/lib/rocprofiler-sdk/agent.cpp b/source/lib/rocprofiler-sdk/agent.cpp index 056af77a00..821fca31f4 100644 --- a/source/lib/rocprofiler-sdk/agent.cpp +++ b/source/lib/rocprofiler-sdk/agent.cpp @@ -503,7 +503,8 @@ update_agent_runtime_visibility(rocprofiler_agent_t& agent_info) { auto _uuid = std::strtoull(itr.substr(uuid_prefix.length()).c_str(), nullptr, 16); - if(_uuid == agent_info.uuid.value) return parse_result{true, _idx_v}; + auto uuid_view = uuid_view_t{agent_info.uuid}; + if(_uuid == uuid_view.value64[0]) return parse_result{true, _idx_v}; } else { @@ -720,7 +721,7 @@ read_topology() agent_info.name = ""; agent_info.product_name = ""; agent_info.vendor_name = ""; - agent_info.uuid = {.value = 0}; + memset(&agent_info.uuid.bytes, 0, sizeof(agent_info.uuid.bytes)); if(agent_info.type == ROCPROFILER_AGENT_TYPE_GPU) { constexpr auto workgrp_max = 1024; @@ -729,7 +730,10 @@ read_topology() constexpr auto grid_max_y = std::numeric_limits::max(); constexpr auto grid_max_z = std::numeric_limits::max(); - read_property(properties, "unique_id", agent_info.uuid.value); + auto _uuid = uuid_view_t{}; + uint64_t uuid_val = 0; + read_property(properties, "unique_id", uuid_val); + _uuid.value64[0] = uuid_val; read_property( properties, "max_engine_clk_fcompute", agent_info.max_engine_clk_fcompute); read_property(properties, "local_mem_size", agent_info.local_mem_size); @@ -744,6 +748,7 @@ read_topology() agent_info.grid_max_dim = {grid_max_x, grid_max_y, grid_max_z}; agent_info.cu_count = agent_info.simd_count / agent_info.simd_per_cu; + agent_info.uuid = static_cast(_uuid); if(int drm_fd = 0; (drm_fd = drmOpenRender(agent_info.drm_render_minor)) >= 0) { uint32_t major_version = 0; diff --git a/source/lib/rocprofiler-sdk/agent.hpp b/source/lib/rocprofiler-sdk/agent.hpp index 0edf81d62e..d1325280b3 100644 --- a/source/lib/rocprofiler-sdk/agent.hpp +++ b/source/lib/rocprofiler-sdk/agent.hpp @@ -37,6 +37,45 @@ namespace rocprofiler { namespace agent { +struct uuid_view_t +{ + union + { + uint8_t bytes[16]; ///< raw bytes + uint64_t value64[2]; /// view as 64 bit chunks + }; + + constexpr uuid_view_t() + : bytes() + { + for(uint8_t& byte : bytes) + { + byte = 0; + } + } + + explicit uuid_view_t(rocprofiler_uuid_t _uuid) + { + static_assert(sizeof(bytes) == sizeof(_uuid.bytes)); + for(size_t i = 0; i < sizeof(bytes); i++) + { + bytes[i] = _uuid.bytes[i]; + } + } + + explicit constexpr operator rocprofiler_uuid_t() const + { + auto _uuid = rocprofiler_uuid_t{}; + static_assert(std::is_same_v, + std::remove_extent_t> == true); + for(size_t i = 0; i < sizeof(bytes); i++) + { + _uuid.bytes[i] = bytes[i]; + } + return _uuid; + } +}; + std::vector get_agents(); diff --git a/source/lib/rocprofiler-sdk/aql/aql_profile_v2.h b/source/lib/rocprofiler-sdk/aql/aql_profile_v2.h index 3dcacbb3df..4771f1f204 100644 --- a/source/lib/rocprofiler-sdk/aql/aql_profile_v2.h +++ b/source/lib/rocprofiler-sdk/aql/aql_profile_v2.h @@ -184,6 +184,14 @@ aqlprofile_get_pmc_info(const aqlprofile_pmc_profile_t* profile, aqlprofile_pmc_info_type_t attribute, void* value); +typedef enum aqlprofile_att_parameter_name_ext_t +{ + /** + * HSA_VEN_AMD_AQLPROFILE_PARAMETER_NAME_ATT_BUFFER_SIZE + 1 + */ + AQLPROFILE_ATT_PARAMETER_NAME_BUFFER_SIZE_HIGH = 11, +} aqlprofile_att_parameter_name_ext_t; + // Profile parameter object typedef struct { diff --git a/source/lib/rocprofiler-sdk/aql/packet_construct.cpp b/source/lib/rocprofiler-sdk/aql/packet_construct.cpp index 4c739adb83..6425bd3884 100644 --- a/source/lib/rocprofiler-sdk/aql/packet_construct.cpp +++ b/source/lib/rocprofiler-sdk/aql/packet_construct.cpp @@ -145,7 +145,8 @@ ThreadTraceAQLPacketFactory::ThreadTraceAQLPacketFactory(const hsa::AgentCache& uint32_t cu = static_cast(params.target_cu); uint32_t shader_engine_mask = static_cast(params.shader_engine_mask); uint32_t simd = static_cast(params.simd_select); - uint32_t buffer_size = static_cast(params.buffer_size); + uint32_t buffer_size_lo = static_cast(params.buffer_size); + uint32_t buffer_size_hi = static_cast(params.buffer_size >> 32); uint32_t perf_ctrl = static_cast(params.perfcounter_ctrl); aql_params.clear(); @@ -153,7 +154,10 @@ ThreadTraceAQLPacketFactory::ThreadTraceAQLPacketFactory(const hsa::AgentCache& aql_params.push_back({HSA_VEN_AMD_AQLPROFILE_PARAMETER_NAME_COMPUTE_UNIT_TARGET, {cu}}); aql_params.push_back({HSA_VEN_AMD_AQLPROFILE_PARAMETER_NAME_SE_MASK, {shader_engine_mask}}); aql_params.push_back({HSA_VEN_AMD_AQLPROFILE_PARAMETER_NAME_SIMD_SELECTION, {simd}}); - aql_params.push_back({HSA_VEN_AMD_AQLPROFILE_PARAMETER_NAME_ATT_BUFFER_SIZE, {buffer_size}}); + aql_params.push_back({HSA_VEN_AMD_AQLPROFILE_PARAMETER_NAME_ATT_BUFFER_SIZE, {buffer_size_lo}}); + + if(buffer_size_hi != 0) aql_params.push_back({static_cast( + AQLPROFILE_ATT_PARAMETER_NAME_BUFFER_SIZE_HIGH), {buffer_size_hi}}); if(perf_ctrl != 0 && !params.perfcounters.empty()) { diff --git a/source/lib/rocprofiler-sdk/buffer_tracing.cpp b/source/lib/rocprofiler-sdk/buffer_tracing.cpp index a5104517f5..1e72623ab2 100644 --- a/source/lib/rocprofiler-sdk/buffer_tracing.cpp +++ b/source/lib/rocprofiler-sdk/buffer_tracing.cpp @@ -159,8 +159,7 @@ rocprofiler_configure_buffer_tracing_service(rocprofiler_context_id_t if(buffer_id.handle == 0) return ROCPROFILER_STATUS_ERROR_BUFFER_NOT_FOUND; - constexpr auto invalid_buffer_id = - rocprofiler_buffer_id_t{std::numeric_limits::max()}; + constexpr auto invalid_buffer_id = rocprofiler_buffer_id_t{0}; if(!ctx->buffered_tracer) { @@ -168,7 +167,7 @@ rocprofiler_configure_buffer_tracing_service(rocprofiler_context_id_t ctx->buffered_tracer->buffer_data.fill(invalid_buffer_id); } - if(ctx->buffered_tracer->buffer_data.at(kind).handle != invalid_buffer_id.handle) + if(ctx->buffered_tracer->buffer_data.at(kind) != invalid_buffer_id) return ROCPROFILER_STATUS_ERROR_SERVICE_ALREADY_CONFIGURED; RETURN_STATUS_ON_FAIL(rocprofiler::context::add_domain(ctx->buffered_tracer->domains, kind)); diff --git a/source/lib/rocprofiler-sdk/context.cpp b/source/lib/rocprofiler-sdk/context.cpp index e04e530581..8397eef5d0 100644 --- a/source/lib/rocprofiler-sdk/context.cpp +++ b/source/lib/rocprofiler-sdk/context.cpp @@ -20,14 +20,15 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include -#include - #include "lib/rocprofiler-sdk/context/context.hpp" #include "lib/rocprofiler-sdk/context/domain.hpp" #include "lib/rocprofiler-sdk/hsa/hsa.hpp" #include "lib/rocprofiler-sdk/registration.hpp" +#include +#include +#include + #include #include @@ -60,7 +61,7 @@ rocprofiler_create_context(rocprofiler_context_id_t* context_id) rocprofiler_status_t rocprofiler_start_context(rocprofiler_context_id_t context_id) { - if(context_id.handle == rocprofiler_context_none.handle || + if(context_id == rocprofiler_context_none || !rocprofiler::context::get_registered_context(context_id)) return ROCPROFILER_STATUS_ERROR_CONTEXT_NOT_FOUND; @@ -74,7 +75,7 @@ rocprofiler_start_context(rocprofiler_context_id_t context_id) rocprofiler_status_t rocprofiler_stop_context(rocprofiler_context_id_t context_id) { - if(context_id.handle == rocprofiler_context_none.handle || + if(context_id == rocprofiler_context_none || !rocprofiler::context::get_registered_context(context_id)) return ROCPROFILER_STATUS_ERROR_CONTEXT_NOT_FOUND; @@ -91,7 +92,7 @@ rocprofiler_context_is_active(rocprofiler_context_id_t context_id, int* status) *status = 0; // return context not found if not registered - if(context_id.handle == rocprofiler_context_none.handle || + if(context_id == rocprofiler_context_none || !rocprofiler::context::get_registered_context(context_id)) return ROCPROFILER_STATUS_ERROR_CONTEXT_NOT_FOUND; @@ -114,7 +115,7 @@ rocprofiler_context_is_valid(rocprofiler_context_id_t context_id, int* status) { *status = 0; - if(context_id.handle == rocprofiler_context_none.handle || + if(context_id == rocprofiler_context_none || !rocprofiler::context::get_registered_context(context_id)) return ROCPROFILER_STATUS_ERROR_CONTEXT_NOT_FOUND; diff --git a/source/lib/rocprofiler-sdk/context/domain.cpp b/source/lib/rocprofiler-sdk/context/domain.cpp index d0a97e45ec..6ca94bbec8 100644 --- a/source/lib/rocprofiler-sdk/context/domain.cpp +++ b/source/lib/rocprofiler-sdk/context/domain.cpp @@ -68,7 +68,7 @@ add_domain(domain_context& _cfg, DomainT _domain) if(_didx >= _cfg.array_size) return ROCPROFILER_STATUS_ERROR_KIND_NOT_FOUND; - _cfg.domains |= (1 << _didx); + _cfg.domains |= (1UL << _didx); return ROCPROFILER_STATUS_SUCCESS; } diff --git a/source/lib/rocprofiler-sdk/counters.cpp b/source/lib/rocprofiler-sdk/counters.cpp index 02aa9671c2..d8a3af966b 100644 --- a/source/lib/rocprofiler-sdk/counters.cpp +++ b/source/lib/rocprofiler-sdk/counters.cpp @@ -25,6 +25,7 @@ #include "lib/common/static_object.hpp" #include "lib/common/string_entry.hpp" #include "lib/common/synchronized.hpp" +#include "lib/common/utility.hpp" #include "lib/rocprofiler-sdk/agent.hpp" #include "lib/rocprofiler-sdk/counters/dimensions.hpp" #include "lib/rocprofiler-sdk/counters/evaluate_ast.hpp" @@ -53,25 +54,37 @@ get_static_string(std::string_view str) return common::get_string_entry(str)->c_str(); } -template -const std::vector* -get_static_ptr(const std::vector& vec) +template +const Tp** +get_static_ptr_array(const std::vector& vec) { // The use of std::map is purposeful. Keys can be vectors in map and cannot be in unordered_map. // Simplifying the code to create these static objects. Given that they are not created often ( // or looked up often), the performance difference between map and unordered_map is negligible. - using static_ptr_map = std::map, std::unique_ptr>>; + using static_ptr_map = std::map, std::vector>; static auto*& static_ptrs = common::static_object>::construct(); - return static_ptrs->wlock([&](auto& data) { + + return static_ptrs->wlock([&](auto& data) -> const Tp** { if(auto it = data.find(vec); it != data.end()) { - return it->second.get(); + return it->second.data(); + } + + auto [inserted_it, success] = data.emplace(vec, std::vector{}); + const std::vector& stored_vec = inserted_it->first; + + auto& ptr_vec = inserted_it->second; + ptr_vec.reserve(stored_vec.size()); + for(const auto& item : stored_vec) + { + ptr_vec.push_back(&item); } - data[vec] = std::make_unique>(vec); - return data[vec].get(); + + return ptr_vec.data(); }); } + } // namespace } // namespace counters } // namespace rocprofiler @@ -139,7 +152,7 @@ rocprofiler_query_counter_info(rocprofiler_counter_id_t counter_id, return true; } - out_struct.dimensions = counters::get_static_ptr(_dim_info)->data(); + out_struct.dimensions = counters::get_static_ptr_array(_dim_info); out_struct.dimensions_count = _dim_info.size(); return true; }; @@ -152,20 +165,33 @@ rocprofiler_query_counter_info(rocprofiler_counter_id_t counter_id, const auto* dims = common::get_val(dim_ptr->id_to_dim, counter_id.handle); if(!dims) return false; - std::vector instances; + auto instances = std::vector{}; + auto _dim_info = std::vector{}; + + constexpr auto rocprofiler_counter_record_dimension_instance_v1_info_t_rt_size = + common::compute_runtime_sizeof(); + + constexpr auto rocprofiler_counter_dimension_info_v1_t_rt_size = + common::compute_runtime_sizeof(); for(const auto& metric_dim : *dims) { if(metric_dim.size() == 0) continue; - std::vector tmp; + + _dim_info.emplace_back(rocprofiler_counter_record_dimension_info_t{ + .name = counters::get_static_string(metric_dim.name()), + .instance_size = metric_dim.size(), + .id = static_cast(metric_dim.type())}); + + auto tmp = std::vector{}; // If no instances are found, create the first set of instances if(instances.empty()) { for(size_t i = 0; i < metric_dim.size(); i++) { auto& rec = instances.emplace_back(); - counters::set_dim_in_rec(rec, metric_dim.type(), i); - counters::set_counter_in_rec(rec, counter_id); + counters::set_dim_in_rec(rec.instance_id, metric_dim.type(), i); + counters::set_counter_in_rec(rec.instance_id, counter_id); } } else @@ -177,8 +203,8 @@ rocprofiler_query_counter_info(rocprofiler_counter_id_t counter_id, for(const auto& instance : instances) { auto& rec = tmp.emplace_back(instance); - counters::set_dim_in_rec(rec, metric_dim.type(), i); - counters::set_counter_in_rec(rec, counter_id); + counters::set_dim_in_rec(rec.instance_id, metric_dim.type(), i); + counters::set_counter_in_rec(rec.instance_id, counter_id); } } instances = tmp; @@ -186,13 +212,33 @@ rocprofiler_query_counter_info(rocprofiler_counter_id_t counter_id, } if(instances.empty()) { - out_struct.instance_ids = nullptr; - out_struct.instance_ids_count = 0; + out_struct.dimensions_instances = nullptr; + out_struct.dimensions_instances_count = 0; return true; } - out_struct.instance_ids = counters::get_static_ptr(instances)->data(); - out_struct.instance_ids_count = instances.size(); + for(auto& instance : instances) + { + auto dimensions = std::vector{}; + auto instance_id = instance.instance_id; + for(const auto& dimension : _dim_info) + { + auto& curr = dimensions.emplace_back(rocprofiler_counter_dimension_info_t{}); + curr.index = counters::rec_to_dim_pos( + instance_id, + static_cast( + dimension.id)); + curr.dimension_name = dimension.name; + curr.size = rocprofiler_counter_dimension_info_v1_t_rt_size; + } + instance.dimensions = counters::get_static_ptr_array(dimensions); + instance.dimensions_count = std::size(_dim_info); + instance.counter_id = counters::rec_to_counter_id(instance_id).handle; + instance.size = rocprofiler_counter_record_dimension_instance_v1_info_t_rt_size; + } + out_struct.dimensions_instances = counters::get_static_ptr_array(instances); + out_struct.dimensions_instances_count = instances.size(); + out_struct.size = sizeof(rocprofiler_counter_info_v1_t); return true; }; diff --git a/source/lib/rocprofiler-sdk/counters/tests/metrics_test.cpp b/source/lib/rocprofiler-sdk/counters/tests/metrics_test.cpp index 6e952506aa..b0b978d4ec 100644 --- a/source/lib/rocprofiler-sdk/counters/tests/metrics_test.cpp +++ b/source/lib/rocprofiler-sdk/counters/tests/metrics_test.cpp @@ -241,9 +241,9 @@ TEST(metrics, check_public_api_query) for(size_t i = 0; i < info.dimensions_count; i++) { const auto& dim = dims->at(i); - EXPECT_EQ(dim.size(), info.dimensions[i].instance_size); - EXPECT_EQ(dim.type(), info.dimensions[i].id); - EXPECT_EQ(std::string(info.dimensions[i].name), dim.name()); + EXPECT_EQ(dim.size(), info.dimensions[i]->instance_size); + EXPECT_EQ(dim.type(), info.dimensions[i]->id); + EXPECT_EQ(std::string(info.dimensions[i]->name), dim.name()); } size_t instance_count = 0; @@ -256,18 +256,20 @@ TEST(metrics, check_public_api_query) instance_count = metric_dim.size() * instance_count; } - EXPECT_EQ(info.instance_ids_count, instance_count); + EXPECT_EQ(info.dimensions_instances_count, instance_count); std::set> dim_permutations; - for(size_t i = 0; i < info.instance_ids_count; i++) + for(size_t i = 0; i < info.dimensions_instances_count; i++) { std::vector dim_ids; - ASSERT_EQ(rocprofiler::counters::rec_to_counter_id(info.instance_ids[i]).handle, - metric.id()); + ASSERT_EQ( + rocprofiler::counters::rec_to_counter_id(info.dimensions_instances[i]->instance_id) + .handle, + metric.id()); for(const auto& metric_dim : *dims) { - dim_ids.push_back( - rocprofiler::counters::rec_to_dim_pos(info.instance_ids[i], metric_dim.type())); + dim_ids.push_back(rocprofiler::counters::rec_to_dim_pos( + info.dimensions_instances[i]->instance_id, metric_dim.type())); } // Ensure that the premutation is unique ASSERT_EQ(dim_permutations.insert(dim_ids).second, true); diff --git a/source/lib/rocprofiler-sdk/counters/tests/metrics_test.h b/source/lib/rocprofiler-sdk/counters/tests/metrics_test.h index 9cea346493..58626b60c5 100644 --- a/source/lib/rocprofiler-sdk/counters/tests/metrics_test.h +++ b/source/lib/rocprofiler-sdk/counters/tests/metrics_test.h @@ -184,7 +184,1287 @@ static const std::unordered_map", - "TCP stalls TA data interface. Now Windowed."}}}}; + "TCP stalls TA data interface. Now Windowed."}, + {"SQ_WAVES_LT_32", + "SQ", + "9", + "", + "Count number of waves sent <32 active threads sent to SQs. " + "This value represents the number of waves that an each individual SIMD has enqueued during " + "the collection timeframe (for dispatch profiling this is the timeframe of kernel execution, " + "for agent profiling it is the timeframe between start_context and read counter data) with " + "less than 32 threads. A sum of all SQ_WAVES_LT_32 values will give the total number of " + "waves with 32 threads enqueued during the collection timeframe by the application. " + "Returns one value per-SE (aggregates of SIMD values). " + "Useful for checking for wavefront occupancy."}, + {"TCC_ALL_TC_OP_WB_WRITEBACK", + "TCC", + "73", + "", + "Number of writebacks due to all TC_OP writeback requests."}, + {"GRBM_UTCL2_BUSY", + "GRBM", + "34", + "", + "The Unified Translation Cache Level-2 (UTCL2) block is busy."}, + {"SPI_RA_SGPR_SIMD_FULL_CSN", + "SPI", + "115", + "", + "Sum of SIMD where SGPR can't take csn wave when !fits. Source is RA0"}, + {"TCP_TCC_NC_ATOMIC_REQ", + "TCP", + "77", + "", + "Total atomic requests with NC mtype from this TCP to all TCCs"}, + {"SQC_DCACHE_HITS", + "SQ", + "291", + "", + "Number of cache hits. (per-SQ, per-Bank, nondeterministic)"}, + {"CPC_CPC_TCIU_IDLE", + "CPC", + "29", + "", + "CPC TCIU interface Idle."}, + {"SPI_CSN_WAVE", + "SPI", + "52", + "", + "Number of waves. Requires SPI_DEBUG_CNTL.DEBUG_PIPE_SEL to select source, " + "DEBUG_PIPE_SEL = 1, source is CS1; DEBUG_PIPE_SEL = 2, source is CS2; " + "DEBUG_PIPE_SEL = 3, source is CS3; default, source is CS0;"}, + {"SQ_INST_CYCLES_VMEM_WR", + "SQ", + "77", + "", + "The number of cycles needed to send addr and cmd data for VMEM write instructions. " + "This value is returned on a per-SE (aggregate of values in SIMDs in the SE) basis " + "with units in quad-cycles(4 cycles)."}, + {"TCC_EA0_WR_UNCACHED_32B", + "TCC", + "29", + "", + "Number of 32-byte write/atomic going over the TC_EA_wrreq interface due to uncached " + "traffic. Note that CC mtypes can produce uncached requests, and those are included " + "in this. A 64-byte request will be counted as 2"}, + {"TA_ADDR_STALLED_BY_TD_CYCLES", + "TA", + "55", + "", + "Number of cycles addr path stalled by TD. Perf_Windowing not supported for this " + "counter."}, + {"TCP_TOTAL_WRITEBACK_INVALIDATES", + "TCP", + "45", + "", + "Total number of cache invalidates. Equals TCP_PERF_SEL_TOTAL_WBINVL1+ " + "TCP_PERF_SEL_TOTAL_WBINVL1_VOL+ TCP_PERF_SEL_CP_TCP_INVALIDATE+ " + "TCP_PERF_SEL_SQ_TCP_INVALIDATE_VOL. Not Windowed."}, + {"TCC_PROBE_ALL", + "TCC", + "10", + "", + "Number of external probe requests with with EA_TCC_preq_all== 1. Not windowable."}, + {"TCC_CC_REQ", + "TCC", + "7", + "", + "The number of coherently cached requests. This is measured at the tag block."}, + {"SPI_RA_REQ_NO_ALLOC_CSN", + "SPI", + "85", + "", + "Arb cycles with CSn req and no CSn alloc. Source is RA0"}, + {"CPC_ME1_DC0_SPI_BUSY", + "CPC", + "33", + "", + "CPC Me1 Processor Busy."}, + {"SQ_WAVES_RESTORED", + "SQ", + "159", + "", + "Count number of context-restored waves sent to SQs. This value represents the number " + "of waves whos current register state has been restored from a register bank during " + "the collection timeframe (for dispatch profiling this is the timeframe of kernel " + "execution, for agent profiling it is the timeframe between start_context and read " + "counter data). Context saving/restoring is a slow operation and should be limited. " + "High values can also indicate that stalling may be taking place (waiting for free " + "register space). Returns one value per-SE (aggregates of SIMD values)."}, + {"CPF_CPF_TCIU_IDLE", + "CPF", + "27", + "", + "CPF TCIU interface Idle."}, + {"TCP_TCC_ATOMIC_WITH_RET_REQ", + "TCP", + "71", + "", + "Total atomic with return requests from TCP to all TCCs"}, + {"SQC_DCACHE_REQ_READ_8", + "SQ", + "326", + "", + "Number of constant cache 8 dw read requests. (per-SQ)"}, + {"TCC_STREAMING_REQ", + "TCC", + "4", + "", + "Number of streaming requests. This is measured at the tag block."}, + {"SQ_INSTS_SMEM_NORM", + "SQ", + "161", + "", + "Number of SMEM instructions issued normalized to match the level of memory accessed " + "(i.e. scratch, global, etc). This normalized value is designed to give a hint of " + "high cost memory actions being used. The formula used to calculate this value is " + "the following (INST_COUNT *2 for load/store; INST_COUNT*2 atomic; INST_COUNT*2 " + "memtime; INST_COUNT*4 wb/inv). This value is returned per-SE (aggregate of values " + "in SIMDs in the SE)."}, + {"SQC_ICACHE_MISSES", + "SQ", + "272", + "", + "Number of cache misses, includes uncached requests. (per-SQ, per-Bank, " + "nondeterministic)"}, + {"SQ_WAVES_LT_64", + "SQ", + "7", + "", + "Count number of waves with <64 active threads sent to SQs. This value represents " + "the number of waves that an each individual SIMD has enqueued during the collection " + "timeframe (for dispatch profiling this is the timeframe of kernel execution, for " + "agent profiling it is the timeframe between start_context and read counter data) " + "with less than 64 threads. A sum of all SQ_WAVES_LT_64 values will give the total " + "number of waves with 64 threads enqueued during the collection timeframe by the " + "application. Returns one value per-SE (aggregates of SIMD values). Useful for " + "checking for wavefront occupancy."}, + {"TCP_TCC_NC_WRITE_REQ", + "TCP", + "76", + "", + "Total write requests with NC mtype from this TCP to all TCCs"}, + {"SQ_LDS_ATOMIC_RETURN", + "SQ", + "98", + "", + "The number of atomic return cycles in LDS (local data store). This value is returned " + "on a per-SE (aggregate of values in SIMDs in the SE) basis."}, + {"CPF_CMP_UTCL1_STALL_ON_TRANSLATION", + "CPF", + "20", + "", + "One of the Compute UTCL1s is stalled waiting on translation, XNACK or PENDING " + "response."}, + {"TCC_CYCLE", + "TCC", + "1", + "", + "Number of cycles. Not windowable."}, + {"TCP_GATE_EN2", + "TCP", + "1", + "", + "TCP core clocks are turned on. Not Windowed."}, + {"TCC_WRITEBACK", + "TCC", + "22", + "", + "Number of lines written back to main memory. This includes writebacks of dirty lines " + "and uncached write/atomic requests."}, + {"SPI_CSN_WINDOW_VALID", + "SPI", + "47", + "", + "Clock count enabled by perfcounter_start event. Requires " + "SPI_DEBUG_CNTL.DEBUG_PIPE_SEL to select source, DEBUG_PIPE_SEL = 1, source is CS1; " + "DEBUG_PIPE_SEL = 2, source is CS2; DEBUG_PIPE_SEL = 3, source is CS3; default, " + "source is CS0;"}, + {"SQ_ACCUM_PREV_HIRES", + "SQ", + "158", + "", + "This is a hardware register that can be used for accumulating values for other " + "counters. This is useful in expressions where you want to integrate over time. " + "This counter is primarily for use with derived counters supplied by rocprof."}, + {"TCP_TCC_UC_WRITE_REQ", + "TCP", + "79", + "", + "Total write requests with UC mtype from this TCP to all TCCs"}, + {"TCP_UTCL1_TRANSLATION_MISS", + "TCP", + "48", + "", + "Total utcl1 translation misses"}, + {"GRBM_TA_BUSY", + "GRBM", + "13", + "", + "Any of the Texture Pipes (TA) are busy in the shader engine(s)."}, + {"TCC_EA0_ATOMIC_LEVEL", + "TCC", + "37", + "", + "The sum of the number of EA atomics in flight. This is primarily meant for measure " + "average EA atomic latency. Average atomic latency = " + "TCC_PERF_SEL_EA_WRREQ_ATOMIC_LEVEL/TCC_PERF_SEL_EA_WRREQ_ATOMIC."}, + {"SQ_IFETCH_LEVEL", + "SQ", + "89", + "", + "Number of inflight instruction fetch requests from the cache. This is a value " + "returned per-sharder engine. Best used with accumlate() functions as part of a " + "derived counter."}, + {"TCC_RW_REQ", + "TCC", + "8", + "", + "The number of RW requests. This is measured at the tag block."}, + {"TCP_TOTAL_WRITE", + "TCP", + "32", + "", + "Total number of local write pixels/buffers from TA. Equals " + "TCP_PERF_SEL_TOTAL_MISS_LRU_WRITE+ TCP_PERF_SEL_TOTAL_MISS_EVICT_WRITE"}, + {"TCP_TOTAL_ACCESSES", + "TCP", + "29", + "", + "Total number of pixels/buffers from TA. Equals " + "TCP_PERF_SEL_TOTAL_READ+TCP_PERF_SEL_TOTAL_NONREAD"}, + {"SQC_DCACHE_REQ_READ_16", + "SQ", + "327", + "", + "Number of constant cache 16 dw read requests. (per-SQ)"}, + {"SQ_WAIT_ANY", + "SQ", + "58", + "", + "Number of wave-cycles spent waiting for anything (per-simd, nondeterministic). " + "Units in quad-cycles(4 cycles)"}, + {"SQ_CYCLES", + "SQ", + "2", + "", + "Clock cycles. Value is returned per-SIMD."}, + {"GRBM_SPI_BUSY", + "GRBM", + "11", + "", + "Any of the Shader Pipe Interpolators (SPI) are busy in the shader engine(s)."}, + {"SQ_INSTS_MFMA", + "SQ", + "27", + "", + "Total number of MFMA (Matrix-Fused-Multiply-Add) instructions issued. This value is " + "returned per-SE (aggregate of values in SIMDs in the SE). See AMD ISAs for more " + "information on MFMA instructions."}, + {"GRBM_CP_BUSY", + "GRBM", + "3", + "", + "Any of the Command Processor (CPG/CPC/CPF) blocks are busy."}, + {"SQ_ACCUM_PREV", + "SQ", + "1", + "", + "This is a hardware register that can be used for accumulating values for other " + "counters. This is useful in expressions where you want to integrate over time. " + "Only accumulates once every 4 cycles. This counter is primarily for use with " + "derived counters supplied by rocprof."}, + {"TCP_TCC_CC_WRITE_REQ", + "TCP", + "82", + "", + "Total write requests with CC mtype from this TCP to all TCCs"}, + {"SPI_CSN_NUM_THREADGROUPS", + "SPI", + "49", + "", + "Number of threadgroups launched. Requires SPI_DEBUG_CNTL.DEBUG_PIPE_SEL to select " + "source, DEBUG_PIPE_SEL = 1, source is CS1; DEBUG_PIPE_SEL = 2, source is CS2; " + "DEBUG_PIPE_SEL = 3, source is CS3; default, source is CS0;"}, + {"TCP_TCC_NC_READ_REQ", + "TCP", + "75", + "", + "Total read requests with NC mtype from this TCP to all TCCs"}, + {"TCP_TD_TCP_STALL_CYCLES", + "TCP", + "7", + "", + "TD stalls TCP"}, + {"SQ_INSTS_SENDMSG", + "SQ", + "40", + "", + "Total number of Sendmsg (typically an interrupt to the CPU host) instructions " + "issued. This value is returned per-SE (aggregate of values in SIMDs in the SE). " + "See AMD ISAs for more information on Sendmsg instructions."}, + {"TA_ADDR_STALLED_BY_TC_CYCLES", + "TA", + "54", + "", + "Number of cycles addr path stalled by TC. Perf_Windowing not supported for this " + "counter."}, + {"TA_BUFFER_WRITE_WAVEFRONTS", + "TA", + "46", + "", + "Number of buffer write wavefronts processed by TA."}, + {"SQ_ACTIVE_INST_EXP_GDS", + "SQ", + "74", + "", + "Number of cycles each wave spends working on EXPORT or GDS instructions. This value " + "represents the number of cycles each wave spends executing instructions " + "synchronizing workgroups across the device (global data sync). High values " + "indicates large amounts of time spent waiting on communication between CUs. This " + "value is returned on a per-SE (aggregate of values in SIMDs in the SE) basis with " + "units in quad-cycles(4 cycles). See AMD ISAs for more information on GDS " + "instructions."}, + {"TCC_EA0_WRREQ_DRAM_CREDIT_STALL", + "TCC", + "33", + "", + "Number of cycles a EA write request was stalled because the interface was out of " + "DRAM credits."}, + {"TCC_WRITE", + "TCC", + "13", + "", + "Number of write requests."}, + {"SPI_RA_VGPR_SIMD_FULL_CSN", + "SPI", + "109", + "", + "Sum of SIMD where VGPR can't take csn wave when !fits. Source is RA0"}, + {"TCP_TCC_UC_READ_REQ", + "TCP", + "78", + "", + "Total read requests with UC mtype from this TCP to all TCCs"}, + {"TCC_EA0_RDREQ_DRAM", + "TCC", + "43", + "", + "Number of TCC/EA read requests (either 32-byte or 64-byte) destined for DRAM (MC)."}, + {"TCC_EA0_WRREQ_IO_CREDIT_STALL", + "TCC", + "31", + "", + "Number of cycles a EA write request was stalled because the interface was out of IO " + "credits."}, + {"TCC_TOO_MANY_EA_WRREQS_STALL", + "TCC", + "34", + "", + "Number of cycles the TCC could not send a EA write request because it already " + "reached its maximum number of pending EA write requests."}, + {"TCP_TOTAL_READ", + "TCP", + "30", + "", + "Total number of read pixels/buffers from TA. Equals " + "TCP_PERF_SEL_TOTAL_HIT_LRU_READ + TCP_PERF_SEL_TOTAL_MISS_LRU_READ + " + "TCP_PERF_SEL_TOTAL_MISS_EVICT_READ"}, + {"SQ_INSTS_VMEM", + "SQ", + "30", + "", + "The number of VMEM (GPU Memory) instructions issued. The value is returned per-SE " + "(aggregate of values in SIMDs in the SE)."}, + {"SPI_RA_WAVE_SIMD_FULL_CSN", + "SPI", + "103", + "", + "Sum of SIMD where WAVE can't take csn wave when !fits. Source is RA0"}, + {"SQ_INSTS_VSKIPPED", + "SQ", + "41", + "", + "The number of vector instructions skipped. This can occur when the S_SETVSKIP bit " + "is enabled on certain instructions. Often this is used as an alturnative to " + "branching (a compiler may replace a branch with setting this bit to skip the " + "operation, typically as a performance optimization). The value is returned per-SE " + "(aggregate of values in SIMDs in the SE)."}, + {"SQ_ITEMS", + "SQ", + "14", + "", + "Number of valid items per wave. This value is returned on a per-SE (aggregate of " + "values in SIMDs in the SE) basis."}, + {"SQ_LEVEL_WAVES", + "SQ", + "5", + "", + "Track the number of waves. Set ACCUM_PREV for the next counter to use this. This " + "value is returned on a per-SIMD basis."}, + {"SQC_TC_DATA_WRITE_REQ", + "SQ", + "265", + "", + "Number of data write requests to the TC (No-Masking, nondeterministic)"}, + {"SQ_INST_LEVEL_LDS", + "SQ", + "44", + "", + "Number of in-flight LDS instructions. This value represents the number of " + "instructions each wave spends executing instructions accessing the local data store " + "(data shared between SIMDs on the same CU). Set next counter to ACCUM_PREV and " + "divide by INSTS_LDS for average latency. Includes FLAT instructions. This value is " + "returned on a per-SE (aggregate of values in SIMDs in the SE) basis."}, + {"TCP_TOTAL_CACHE_ACCESSES", + "TCP", + "60", + "", + "Count of total cache line (tag) accesses (includes hits and misses)."}, + {"TA_BUFFER_WAVEFRONTS", + "TA", + "44", + "", + "Number of buffer wavefronts processed by TA."}, + {"SQ_WAVES_EQ_64", + "SQ", + "6", + "", + "Count number of waves with exactly 64 active threads sent to SQs. This value " + "represents the number of waves that an each individual SIMD has enqueued during " + "the collection timeframe (for dispatch profiling this is the timeframe of kernel " + "execution, for agent profiling it is the timeframe between start_context and read " + "counter data) with exactly 64 threads. A sum of all SQ_WAVES_EQ_64 values will " + "give the total number of waves with 64 threads enqueued during the collection " + "timeframe by the application. Returns one value per-SE (aggregates of SIMD values). " + "Useful for checking for wavefront occupancy."}, + {"TCP_WRITE_TAGCONFLICT_STALL_CYCLES", + "TCP", + "12", + "", + "Tagram conflict stall on a write"}, + {"SQC_TC_INST_REQ", + "SQ", + "263", + "", + "Number of insruction requests to the TC (No-Masking, nondeterministic)"}, + {"SQC_TC_DATA_ATOMIC_REQ", + "SQ", + "266", + "", + "Number of data atomic requests to the TC (No-Masking, nondeterministic)"}, + {"TCC_EA0_RDREQ_IO_CREDIT_STALL", + "TCC", + "41", + "", + "Number of cycles there was a stall because the read request interface was out of IO " + "credits. Stalls occur regardless of whether a read needed to be performed or not."}, + {"TCP_TCC_CC_ATOMIC_REQ", + "TCP", + "83", + "", + "Total atomic requests with CC mtype from this TCP to all TCCs"}, + {"TCP_TCC_UC_ATOMIC_REQ", + "TCP", + "80", + "", + "Total atomic requests with UC mtype from this TCP to all TCCs"}, + {"SQC_ICACHE_MISSES_DUPLICATE", + "SQ", + "273", + "", + "Number of misses that were duplicates (access to a non-resident, miss pending CL). " + "(per-SQ, per-Bank, nondeterministic)"}, + {"TCC_EA0_RDREQ_LEVEL", + "TCC", + "44", + "", + "The sum of the number of TCC/EA read requests in flight. This is primarily meant " + "for measure average EA read latency. Average read latency = " + "TCC_PERF_SEL_EA_RDREQ_LEVEL/TCC_PERF_SEL_EA_RDREQ."}, + {"TA_BUFFER_TOTAL_CYCLES", + "TA", + "49", + "", + "Number of buffer cycles issued to TC."}, + {"SQ_WAIT_INST_ANY", + "SQ", + "61", + "", + "Number of wave-cycles spent waiting for any instruction issue. Units in " + "quad-cycles(4 cycles)."}, + {"SQ_WAVE_CYCLES", + "SQ", + "47", + "", + "The cycles spent executing waves in the CUs. This value is reported per-SE " + "(aggregates of SIMD values) and is nondeterministic. Units are in quad-cycles (4 " + "cycles). Useful for determining how much time is spent executing wave code vs " + "overhead/waiting. Low cycle count relative to actual number of cycles processed by " + "the CU can indicate that the CU is stalling or is overloaded."}, + {"TCC_EA0_WRREQ_DRAM", + "TCC", + "103", + "", + "Number of TCC/EA write requests (either 32-byte of 64-byte) destined for DRAM (MC)."}, + {"TCP_TCR_TCP_STALL_CYCLES", + "TCP", + "8", + "", + "TCR stalls TCP_TCR_req interface"}, + {"TCP_TCC_RW_READ_REQ", + "TCP", + "85", + "", + "Total write requests with RW mtype from this TCP to all TCCs"}, + {"SQ_INST_LEVEL_VMEM", + "SQ", + "42", + "", + "Number of in-flight VMEM instructions. Set next counter to ACCUM_PREV and divide by " + "INSTS_VMEM for average latency. Includes FLAT instructions. This value is returned " + "on a per-SE (aggregate of values in SIMDs in the SE) basis."}, + {"TCP_TCC_CC_READ_REQ", + "TCP", + "81", + "", + "Total write requests with CC mtype from this TCP to all TCCs"}, + {"SQ_ACTIVE_INST_VMEM", + "SQ", + "70", + "", + "Number of cycles each wave spends working on a VMEM instructions. This value " + "represents the number of cycles each wave spends executing vector memory " + "instructions. High values indicates a large amount of time spent executing vector " + "memory operations. This value is returned on a per-SE (aggregate of values in " + "SIMDs in the SE) basis with units in quad-cycles(4 cycles)."}, + {"SQ_IFETCH", + "SQ", + "88", + "", + "Number of instruction fetch requests from L1I (instruction) cache. This is a value " + "returned per-SIMD."}, + {"TCP_TCC_READ_REQ", + "TCP", + "69", + "", + "Total read requests from TCP to all TCCs"}, + {"SQC_DCACHE_REQ", + "SQ", + "290", + "", + "Number of requests (post-bank-serialization). (per-SQ, per-Bank)"}, + {"CPC_CPC_STAT_STALL", + "CPC", + "27", + "", + "CPC Stalled."}, + {"TCP_GATE_EN1", + "TCP", + "0", + "", + "TCP interface clocks are turned on. Not Windowed."}, + {"TCP_PENDING_STALL_CYCLES", + "TCP", + "22", + "", + "Stall due to data pending from L2"}, + {"SQC_DCACHE_MISSES_DUPLICATE", + "SQ", + "293", + "", + "Number of misses that were duplicates (access to a non-resident, miss pending CL). " + "(per-SQ, per-Bank, nondeterministic)"}, + {"CPF_CPF_STAT_IDLE", + "CPF", + "24", + "", + "CPF Idle."}, + {"TCP_VOLATILE", + "TCP", + "28", + "", + "Total number of L1 volatile pixels/buffers from TA"}, + {"CPC_CPC_TCIU_BUSY", + "CPC", + "28", + "", + "CPC TCIU interface Busy."}, + {"SQC_DCACHE_REQ_READ_2", + "SQ", + "324", + "", + "Number of constant cache 2 dw read requests. (per-SQ)"}, + {"CPC_CPC_STAT_BUSY", + "CPC", + "25", + "", + "CPC Busy."}, + {"TCP_TCP_LATENCY", + "TCP", + "65", + "", + "Total TCP wave latency (from first clock of wave entering to first clock of wave " + "leaving), divide by TA_TCP_STATE_READ to avg wave latency"}, + {"TCP_UTCL1_TRANSLATION_HIT", + "TCP", + "49", + "", + "Total utcl1 translation hits"}, + {"SQ_INST_LEVEL_SMEM", + "SQ", + "43", + "", + "Number of in-flight SMEM instructions (*2 load/store; *2 atomic; *2 memtime; *4 " + "wb/inv). Set next counter to ACCUM_PREV and divide by INSTS_SMEM for average " + "latency per smem request. Falls slightly short of total request latency because " + "some fetches are divided into two requests that may finish at different times and " + "this counter collects the average latency of the two. This value is returned on a " + "per-SE (aggregate of values in SIMDs in the SE) basis."}, + {"SPI_SWC_CSC_WR", + "SPI", + "189", + "", + "Number of clocks to write CSC waves to SGPRs (need to multiply this value by 4) " + "Requires SPI_DEBUG_CNTL.DEBUG_PIPE_SEL to select source, DEBUG_PIPE_SEL = 1, " + "source is CS1; DEBUG_PIPE_SEL = 2, source is CS2; DEBUG_PIPE_SEL = 3, source is " + "CS3; default, source is CS0;"}, + {"TCC_READ", + "TCC", + "12", + "", + "Number of read requests. Compressed reads are included in this, but metadata reads " + "are not included."}, + {"TD_LOAD_WAVEFRONT", + "TD", + "25", + "", + "Count the wavefronts with opcode = load, include atomics and store."}, + {"GRBM_EA_BUSY", + "GRBM", + "35", + "", + "The Efficiency Arbiter (EA) block is busy."}, + {"SPI_RA_WVLIM_STALL_CSN", + "SPI", + "133", + "", + "Number of clocks csn is stalled due to WAVE LIMIT."}, + {"SPI_RA_BAR_CU_FULL_CSN", + "SPI", + "123", + "", + "Sum of CU where BARRIER can't take csn wave when !fits. Source is RA0"}, + {"TD_TD_BUSY", + "TD", + "1", + "", + "TD is processing or waiting for data. Perf_Windowing not supported for this " + "counter."}, + {"SQC_ICACHE_REQ", + "SQ", + "270", + "", + "Number of requests. (per-SQ, per-Bank)"}, + {"TCC_ATOMIC", + "TCC", + "14", + "", + "Number of atomic requests of all types."}, + {"TCP_ATOMIC_TAGCONFLICT_STALL_CYCLES", + "TCP", + "13", + "", + "Tagram conflict stall on an atomic"}, + {"CPF_CPF_STAT_BUSY", + "CPF", + "23", + "", + "CPF Busy."}, + {"TCC_EA0_WRREQ_LEVEL", + "TCC", + "35", + "", + "The sum of the number of EA write requests in flight. This is primarily meant for " + "measure average EA write latency. Average write latency = " + "TCC_PERF_SEL_EA_WRREQ_LEVEL/TCC_PERF_SEL_EA_WRREQ."}, + {"SPI_RA_RES_STALL_CSN", + "SPI", + "91", + "", + "Arb cycles with CSn req and no CSn fits. Source is RA0"}, + {"TCC_BUSY", + "TCC", + "2", + "", + "Number of cycles we have a request pending. Not windowable."}, + {"SQC_DCACHE_INPUT_VALID_READYB", + "SQ", + "260", + "", + "Input stalled by SQC (per-SQ, nondeterministic, unwindowed)"}, + {"SQ_WAVES_SAVED", + "SQ", + "160", + "", + "Count number of context-saved waves sent to SQs. This value represents the number " + "of waves whos current register state has been saved to a register bank during the " + "collection timeframe (for dispatch profiling this is the timeframe of kernel " + "execution, for agent profiling it is the timeframe between start_context and read " + "counter data) . Context saving/restoring is a slow operation and should be limited. " + "High values can also indicate that stalling may be taking place (waiting for free " + "register space). Returns one value per-SE (aggregates of SIMD values)."}, + {"SQ_ACTIVE_INST_LDS", + "SQ", + "71", + "", + "Number of cycles each wave spends working on LDS instructions. This value represents " + "the number of cycles each wave spends executing instructions accessing the local " + "data store (data shared between SIMDs on the same CU). High values indicates a " + "large amount of reading/writing to this shared memory space. This value is returned " + "on a per-SE (aggregate of values in SIMDs in the SE) basis with units in " + "quad-cycles(4 cycles). See AMD ISAs for more information on LDS instructions."}, + {"TCC_EA0_RD_UNCACHED_32B", + "TCC", + "40", + "", + "Number of 32-byte TCC/EA read due to uncached traffic. A 64-byte request will be " + "counted as 2"}, + {"TCP_TCC_RW_WRITE_REQ", + "TCP", + "86", + "", + "Total write requests with RW mtype from this TCP to all TCCs"}, + {"TCC_EA0_RDREQ_DRAM_CREDIT_STALL", + "TCC", + "43", + "", + "Number of cycles there was a stall because the read request interface was out of " + "DRAM credits. Stalls occur regardless of whether a read needed to be performed or " + "not."}, + {"SPI_VWC_CSC_WR", + "SPI", + "195", + "", + "Number of clocks to write CSC waves to VGPRs (need to multiply this value by 4) " + "Requires SPI_DEBUG_CNTL.DEBUG_PIPE_SEL to select source, DEBUG_PIPE_SEL = 1, " + "source is CS1; DEBUG_PIPE_SEL = 2, source is CS2; DEBUG_PIPE_SEL = 3, source is " + "CS3; default, source is CS0;"}, + {"SQC_DCACHE_REQ_READ_4", + "SQ", + "325", + "", + "Number of constant cache 4 dw read requests. (per-SQ)"}, + {"SQ_WAVES_LT_16", + "SQ", + "10", + "", + "Count number of waves sent <16 active threads sent to SQs. (per-simd, emulated, " + "global). This value represents the number of waves that an each individual SIMD " + "has enqueued during the collection timeframe (for dispatch profiling this is the " + "timeframe of kernel execution, for agent profiling it is the timeframe between " + "start_context and read counter data) with less than 16 threads. A sum of all " + "SQ_WAVES_LT_16 values will give the total number of waves with 16 threads enqueued " + "during the collection timeframe by the application. Returns one value per-SE " + "(aggregates of SIMD values). Useful for checking for wavefront occupancy."}, + {"SQC_DCACHE_ATOMIC", + "SQ", + "298", + "", + "Number of atomic requests. (per-SQ, per-Bank)"}, + {"TCC_EA0_RDREQ_GMI_CREDIT_STALL", + "TCC", + "42", + "", + "Number of cycles there was a stall because the read request interface was out of " + "GMI credits. Stalls occur regardless of whether a read needed to be performed or " + "not."}, + {"SPI_RA_REQ_NO_ALLOC", + "SPI", + "79", + "", + "Arb cycles with requests but no allocation. Source is RA0"}, + {"SQC_DCACHE_MISSES", + "SQ", + "292", + "", + "Number of cache misses, includes uncached requests. (per-SQ, per-Bank, " + "nondeterministic)"}, + {"TCC_NC_REQ", + "TCC", + "5", + "", + "The number of noncoherently cached requests. This is measured at the tag block."}, + {"SQ_ACTIVE_INST_FLAT", + "SQ", + "76", + "", + "Number of cycles each wave spends working on FLAT instructions. This value " + "represents the number of cycles each wave spends executing instructions accessing " + "flat scratch memory locations. High values indicates a large amount of " + "reading/writing to scratch memory on the device. This value is returned on a " + "per-SE (aggregate of values in SIMDs in the SE) basis with units in quad-cycles(4 " + "cycles). See AMD ISAs for more information on FLAT instructions."}, + {"SQC_TC_DATA_READ_REQ", + "SQ", + "264", + "", + "Number of data read requests to the TC (No-Masking, nondeterministic)"}, + {"TCP_TCC_READ_REQ_LATENCY", + "TCP", + "66", + "", + "Total TCP->TCC request latency for reads and atomics with return. Not Windowed."}, + {"CPC_UTCL1_STALL_ON_TRANSLATION", + "CPC", + "24", + "", + "One of the UTCL1s is stalled waiting on translation, XNACK or PENDING response."}, + {"TCC_PROBE", + "TCC", + "9", + "", + "Number of probe requests. Not windowable."}, + {"TA_BUFFER_ATOMIC_WAVEFRONTS", + "TA", + "47", + "", + "Number of buffer atomic wavefronts processed by TA."}, + {"TA_DATA_STALLED_BY_TC_CYCLES", + "TA", + "56", + "", + "Number of cycles data path stalled by TC. Perf_Windowing not supported for this " + "counter."}, + {"TA_FLAT_WAVEFRONTS", + "TA", + "100", + "", + "Number of flat opcode wavfronts processed by the TA."}, + {"TA_TOTAL_WAVEFRONTS", + "TA", + "32", + "", + "Total number of wavefronts processed by TA."}, + {"CPC_CPC_STAT_IDLE", + "CPC", + "26", + "", + "CPC Idle."}, + {"CPC_CPC_UTCL2IU_STALL", + "CPC", + "32", + "", + "CPC UTCL2 interface Stalled waiting on Free, Tags or Translation."}, + {"TCC_NORMAL_WRITEBACK", + "TCC", + "68", + "", + "Number of writebacks due to requests that are not writeback requests."}, + {"SQ_INST_CYCLES_SMEM", + "SQ", + "84", + "", + "The number of cycles needed to execute scalar memory reads (SMEM). This value is " + "returned on a per-SE (aggregate of values in SIMDs in the SE) basis with units in " + "quad-cycles(4 cycles)."}, + {"SQ_BUSY_CYCLES", + "SQ", + "3", + "", + "Number of clock cycles there are active waves in a shader engine (as reported by " + "the distributed sequencer). This value does not denote the number of active waves, " + "only the clock cycle in which any wave is present in a SE. This value is returned " + "on a per-shader engine basis in clock cycles."}, + {"SQ_LDS_UNALIGNED_STALL", + "SQ", + "96", + "", + "Number of cycles LDS (local data store) is stalled processing flat unaligned " + "load/store ops. This value is returned on a per-SE (aggregate of values in SIMDs " + "in the SE) basis."}, + {"TA_BUFFER_READ_WAVEFRONTS", + "TA", + "45", + "", + "Number of buffer read wavefronts processed by TA."}, + {"TA_FLAT_ATOMIC_WAVEFRONTS", + "TA", + "103", + "", + "Number of flat opcode atomics processed by the TA."}, + {"GRBM_TC_BUSY", + "GRBM", + "28", + "", + "Any of the Texture Cache Blocks (TCP/TCI/TCA/TCC) are busy."}, + {"GRBM_CPC_BUSY", + "GRBM", + "30", + "", + "The Command Processor Compute (CPC) is busy."}, + {"TCP_UTCL1_PERMISSION_MISS", + "TCP", + "50", + "", + "Total utcl1 permission misses"}, + {"SPI_RA_BULKY_CU_FULL_CSN", + "SPI", + "125", + "", + "Sum of CU where BULKY can't take csn wave when !fits. Source is RA0"}, + {"TCP_TA_TCP_STATE_READ", + "TCP", + "27", + "", + "Number of state reads"}, + {"TCP_TCC_WRITE_REQ", + "TCP", + "70", + "", + "Total write requests from TCP to all TCCs"}, + {"TCP_TCC_RW_ATOMIC_REQ", + "TCP", + "87", + "", + "Total atomic requests with RW mtype from this TCP to all TCCs"}, + {"SQ_ACTIVE_INST_MISC", + "SQ", + "75", + "", + "Number of cycles each wave spends working on a BRANCH or SENDMSG instructions. This " + "value represents the number of cycles each wave spends executing instructions " + "performing control flow branching and message sending. This value is returned on a " + "per-SE (aggregate of values in SIMDs in the SE) basis with units in quad-cycles(4 " + "cycles). See AMD ISAs for more information on BRANCH and SENDMSG instructions."}, + {"SQ_ACTIVE_INST_SCA", + "SQ", + "73", + "", + "Number of cycles each wave spends working on a SALU or SMEM instructions. This " + "value represents the number of cycles each wave spends executing scalar ALU or " + "scalar memory instructions. On MI200/300 platforms, there is a single ALU per CU. " + "High values indicates a large amount of time spent executing scalar instructions. " + "This value is returned on a per-SE (aggregate of values in SIMDs in the SE) basis " + "with units in quad-cycles(4 cycles). See AMD ISAs for more information on SALU " + "and SMEM instructions."}, + {"TD_COALESCABLE_WAVEFRONT", + "TD", + "32", + "", + "Count wavefronts that TA finds coalescable."}, + {"SPI_RA_TMP_STALL_CSN", + "SPI", + "97", + "", + "Cycles where csn wants to req but does not fit in temp space."}, + {"SQ_BUSY_CU_CYCLES", + "SQ", + "13", + "", + "Number of quad-cycles each CU is busy. Can be used to calculate the percentage of " + "time each CU is busy. This value is returned on a per-SE (aggregate of values in " + "SIMDs in the SE) basis with units in quad-cycles(4 cycles)."}, + {"TCA_BUSY", + "TCA", + "2", + "", + "Number of cycles we have a request pending. Not windowable."}, + {"TCP_TOTAL_ATOMIC_WITH_RET", + "TCP", + "38", + "", + "Total number of atomic with return pixels/buffers from TA"}, + {"SQC_ICACHE_HITS", + "SQ", + "271", + "", + "Number of cache hits. (per-SQ, per-Bank, nondeterministic)"}, + {"TCA_CYCLE", + "TCA", + "1", + "", + "Number of cycles. Not windowable."}, + {"SPI_CSN_BUSY", + "SPI", + "48", + "", + "Number of clocks with outstanding waves (SPI or SH). Requires " + "SPI_DEBUG_CNTL.DEBUG_PIPE_SEL to select source, DEBUG_PIPE_SEL = 1, source is " + "CS1; DEBUG_PIPE_SEL = 2, source is CS2; DEBUG_PIPE_SEL = 3, source is CS3; " + "default, source is CS0;"}, + {"TCC_UC_REQ", + "TCC", + "6", + "", + "The number of uncached requests. This is measured at the tag block."}, + {"SQC_DCACHE_REQ_READ_1", + "SQ", + "323", + "", + "Number of constant cache 1 dw read requests. (per-SQ)"}, + {"SQ_ACTIVE_INST_ANY", + "SQ", + "69", + "", + "Number of cycles each wave spends working on any type of instruction. Useful in " + "determining percentage of time spend executing wave workloads (see WaveExec). This " + "value is returned on a per-SE (aggregate of values in SIMDs in the SE) basis with " + "units in quad-cycles(4 cycles)."}, + {"TCP_TCC_ATOMIC_WITHOUT_RET_REQ", + "TCP", + "72", + "", + "Total atomic without return requests from TCP to all TCCs"}, + {"TCP_TCC_WRITE_REQ_LATENCY", + "TCP", + "67", + "", + "Total TCP->TCC request latency for writes and atomics without return. Not Windowed."}, + {"TCP_READ_TAGCONFLICT_STALL_CYCLES", + "TCP", + "11", + "", + "Tagram conflict stall on a read"}, + {"TCC_EA0_WRREQ_STALL", + "TCC", + "30", + "", + "Number of cycles a write request was stalled."}, + {"SQC_TC_REQ", + "SQ", + "262", + "", + "Total number of TC requests that were issued by instruction and constant caches. " + "(No-Masking, nondeterministic)"}, + {"CPF_CPF_STAT_STALL", + "CPF", + "25", + "", + "CPF Stalled."}, + {"TCC_ALL_TC_OP_INV_EVICT", + "TCC", + "80", + "", + "Number of evictions due to all TC_OP invalidate requests."}, + {"SQ_LDS_ADDR_CONFLICT", + "SQ", + "95", + "", + "Number of cycles LDS (local data store) is stalled by address conflicts. This value " + "is returned on a per-SE (aggregate of values in SIMDs in the SE) basis."}, + {"SQ_LDS_IDX_ACTIVE", + "SQ", + "99", + "", + "Number of cycles LDS (local data store) is used for indexed (non-direct," + "non-interpolation) operations. This value is returned on a per-SE (aggregate of " + "values in SIMDs in the SE) basis."}, + {"SQ_INST_CYCLES_VMEM_RD", + "SQ", + "78", + "", + "The number of cycles needed to send addr and cmd data for VMEM read instructions. " + "This value is returned on a per-SE (aggregate of values in SIMDs in the SE) basis " + "with units in quad-cycles(4 cycles)."}, + {"CPC_CPC_UTCL2IU_BUSY", + "CPC", + "30", + "", + "CPC UTCL2 interface Busy."}, + {"TCP_UTCL1_REQUEST", + "TCP", + "47", + "", + "Total CLIENT_UTCL1 NORMAL requests"}, + {"CPF_CPF_TCIU_STALL", + "CPF", + "28", + "", + "CPF TCIU interface Stalled waiting on Free, Tags."}, + {"SQ_INSTS_BRANCH", + "SQ", + "39", + "", + "Total number of BRANCH instructions issued. This value is returned per-SE " + "(aggregate of values in SIMDs in the SE). This value SHOULD NOT be used in " + "combination with SQ_ACTIVE_INST_MISC to calculate latency. SQ_ACTIVE_INST_MISC " + "includes both BRANCH and SENDMSG instructions while this is only BRANCH."}, + {"SPI_RA_LDS_CU_FULL_CSN", + "SPI", + "120", + "", + "Sum of CU where LDS can't take csn wave when !fits. Source is RA0"}, + {"TD_ATOMIC_WAVEFRONT", + "TD", + "26", + "", + "Count the wavefronts with opcode = atomic."}, + {"SQ_INSTS_EXP_GDS", + "SQ", + "38", + "", + "Total number of EXPORT or GDS (global wave state) instructions issued. When used in " + "combination with SQ_ACTIVE_INST_EXP_GDS (cycle count for executing instructions) " + "the average latency of EXPORT/GDS instruction execution can be calculated " + "(SQ_ACTIVE_INST_EXP_GDS / SQ_INSTS_EXP_GDS). This value is returned per-SE " + "(aggregate of values in SIMDs in the SE)."}, + {"SQC_TC_STALL", + "SQ", + "267", + "", + "Valid request stalled TC request interface (no-credits). (No-Masking, " + "nondeterministic, unwindowed)"}, + {"CPF_CPF_TCIU_BUSY", + "CPF", + "26", + "", + "CPF TCIU interface Busy."}, + {"TCC_EA0_WRREQ_GMI_CREDIT_STALL", + "TCC", + "32", + "", + "Number of cycles a EA write request was stalled because the interface was out of " + "GMI credits."}, + {"GRBM_CPF_BUSY", + "GRBM", + "31", + "", + "The Command Processor Fetchers (CPF) is busy."}, + {"SQ_WAVES_LT_48", + "SQ", + "8", + "", + "Count number of waves with <48 active threads sent to SQs. This value represents " + "the number of waves that an each individual SIMD has enqueued during the collection " + "timeframe (for dispatch profiling this is the timeframe of kernel execution, for " + "agent profiling it is the timeframe between start_context and read counter data) " + "with less than 48 threads. A sum of all SQ_WAVES_LT_48 values will give the total " + "number of waves with 48 threads enqueued during the collection timeframe by the " + "application. Returns one value per-SE (aggregates of SIMD values). Useful for " + "checking for wavefront occupancy."}, + {"TCC_EA0_ATOMIC", + "TCC", + "36", + "", + "Number of transactions going over the TC_EA_wrreq interface that are actually " + "atomic requests."}, + {"TD_TC_STALL", + "TD", + "15", + "", + "TD is stalled waiting for TC data."}, + {"SPI_RA_TGLIM_CU_FULL_CSN", + "SPI", + "127", + "", + "Cycles where csn wants to req but all CU are at tg_limit"}, + {"TA_BUFFER_COALESCED_WRITE_CYCLES", + "TA", + "53", + "", + "Number of buffer coalesced write cycles issued to TC."}, + {"TCP_TOTAL_ATOMIC_WITHOUT_RET", + "TCP", + "39", + "", + "Total number of atomic without return pixels/buffers from TA"}, + {"CPC_ME1_BUSY_FOR_PACKET_DECODE", + "CPC", + "13", + "", + "Me1 busy for packet decode."}, + {"SQ_INSTS", + "SQ", + "25", + "", + "Total number of instructions issued. When used in combination with " + "SQ_ACTIVE_INST_ANY (cycle count for executing instructions) the average latency of " + "instruction execution can be calculated (SQ_ACTIVE_INST_ANY / SQ_INSTS). This " + "value is returned per-SE (aggregate of values in SIMDs in the SE)."}, + {"TCC_NORMAL_EVICT", + "TCC", + "74", + "", + "Number of evictions due to requests that are not invalidate or probe requests."}, + {"CPC_CPC_UTCL2IU_IDLE", + "CPC", + "31", + "", + "CPC UTCL2 interface Idle."}, + {"TCC_REQ", + "TCC", + "3", + "", + "Number of requests of all types. This is measured at the tag block. This may be " + "more than the number of requests arriving at the TCC, but it is a good indication " + "of the total amount of work that needs to be performed."}, + {"TCC_TAG_STALL", + "TCC", + "45", + "", + "Number of cycles the normal request pipeline in the tag was stalled for any reason. " + "Normally, stalls of this nature are measured exactly from one point the pipeline, " + "but that is not the case for this counter. Probes can stall the pipeline at a " + "variety of places, and there is no single point that can reasonably measure the " + "total stalls accurately."}, + {"TD_STORE_WAVEFRONT", + "TD", + "27", + "", + "Count the wavefronts with opcode = store."}, + {"TA_BUFFER_COALESCED_READ_CYCLES", + "TA", + "52", + "", + "Number of buffer coalesced read cycles issued to TC."}, + {"SQ_LDS_MEM_VIOLATIONS", + "SQ", + "97", + "", + "Number of threads that have a memory violation in the LDS (local data store). This " + "value is returned on a per-SE (aggregate of values in SIMDs in the SE) basis."}, + {"TCC_CLIENT184_REQ", + "TCC", + "312", + "", + "Number of cycles client184 sent a request to this TCC."}, + {"TCC_CLIENT185_REQ", + "TCC", + "313", + "", + "Number of cycles client185 sent a request to this TCC."}, + {"TCC_CLIENT186_REQ", + "TCC", + "314", + "", + "Number of cycles client186 sent a request to this TCC."}, + {"TCC_CLIENT187_REQ", + "TCC", + "315", + "", + "Number of cycles client187 sent a request to this TCC."}, + {"TCC_CLIENT188_REQ", + "TCC", + "316", + "", + "Number of cycles client188 sent a request to this TCC."}, + {"TCC_CLIENT189_REQ", + "TCC", + "317", + "", + "Number of cycles client189 sent a request to this TCC."}, + {"TCC_CLIENT190_REQ", + "TCC", + "318", + "", + "Number of cycles client190 sent a request to this TCC."}, + {"TCC_CLIENT191_REQ", + "TCC", + "319", + "", + "Number of cycles client191 sent a request to this TCC."}}}}; static const std::unordered_map>> derived_gfx908 = {{"gfx908", @@ -431,4 +1711,533 @@ static const std::unordered_mapTCC request latency for reads and atomics with return. Not Windowed. Sum over " + "TCP instances."}, + {"TCC_EA0_RDREQ_GMI_CREDIT_STALL_sum", + "", + "", + "reduce(TCC_EA0_RDREQ_GMI_CREDIT_STALL,sum)", + "Number of cycles there was a stall because the read request interface was out of GMI " + "credits. Stalls occur regardless of whether a read needed to be performed or not. Sum " + "over TCC instances."}, + {"TCC_TAG_STALL_sum", + "", + "", + "reduce(TCC_TAG_STALL,sum)", + "Total number of cycles the normal request pipeline in the tag is stalled for any reason."}, + {"TCP_TA_TCP_STATE_READ_sum", + "", + "", + "reduce(TCP_TA_TCP_STATE_READ,sum)", + "Number of state reads Sum over TCP instances."}, + {"TCC_ATOMIC_sum", + "", + "", + "reduce(TCC_ATOMIC,sum)", + "Number of atomic requests of all types. Sum over TCC instances."}, + {"TCP_TCC_READ_REQ_sum", + "", + "", + "reduce(TCP_TCC_READ_REQ,sum)", + "Total read requests from TCP to all TCCs Sum over TCP instances."}, + {"TCP_TCC_NC_ATOMIC_REQ_sum", + "", + "", + "reduce(TCP_TCC_NC_ATOMIC_REQ,sum)", + "Total atomic requests with NC mtype from this TCP to all TCCs Sum over TCP instances."}, + {"TCC_READ_sum", + "", + "", + "reduce(TCC_READ,sum)", + "Number of read requests. Compressed reads are included in this, but metadata reads are " + "not included. Sum over TCC instances."}, + {"TA_BUFFER_TOTAL_CYCLES_sum", + "", + "", + "reduce(TA_BUFFER_TOTAL_CYCLES,sum)", + "Number of buffer cycles issued to TC. Sum over TA instances."}, + {"TCP_TOTAL_WRITEBACK_INVALIDATES_sum", + "", + "", + "reduce(TCP_TOTAL_WRITEBACK_INVALIDATES,sum)", + "Total number of cache invalidates. Equals TCP_PERF_SEL_TOTAL_WBINVL1+ " + "TCP_PERF_SEL_TOTAL_WBINVL1_VOL+ TCP_PERF_SEL_CP_TCP_INVALIDATE+ " + "TCP_PERF_SEL_SQ_TCP_INVALIDATE_VOL. Not Windowed. Sum over TCP instances."}, + {"TCC_EA0_ATOMIC_sum", + "", + "", + "reduce(TCC_EA0_ATOMIC,sum)", + "Number of transactions going over the TC_EA_wrreq interface that are actually atomic " + "requests. Sum over TCC instances."}, + {"TCC_STREAMING_REQ_sum", + "", + "", + "reduce(TCC_STREAMING_REQ,sum)", + "Number of streaming requests. This is measured at the tag block. Sum over TCC instances."}, + {"TA_ADDR_STALLED_BY_TC_CYCLES_sum", + "", + "", + "reduce(TA_ADDR_STALLED_BY_TC_CYCLES,sum)", + "Number of cycles addr path stalled by TC. Perf_Windowing not supported for this counter. " + "Sum over TA instances."}, + {"TCC_EA0_WRREQ_DRAM_sum", + "", + "", + "reduce(TCC_EA0_WRREQ_DRAM,sum)", + "Number of TCC/EA write requests (either 32-byte of 64-byte) destined for DRAM (MC). Sum " + "over TCC instances."}, + {"TCC_EA0_RDREQ_DRAM_sum", + "", + "", + "reduce(TCC_EA0_RDREQ_DRAM,sum)", + "Number of TCC/EA read requests (either 32-byte or 64-byte) destined for DRAM (MC). Sum " + "over TCC instances."}, + {"TD_ATOMIC_WAVEFRONT_sum", + "", + "", + "reduce(TD_ATOMIC_WAVEFRONT,sum)", + "Count the wavefronts with opcode = atomic. Sum over TD instances."}, + {"TCC_EA0_RDREQ_DRAM_CREDIT_STALL_sum", + "", + "", + "reduce(TCC_EA0_RDREQ_DRAM_CREDIT_STALL,sum)", + "Number of cycles there was a stall because the read request interface was out of DRAM " + "credits. Stalls occur regardless of whether a read needed to be performed or not. Sum " + "over TCC instances."}, + {"TCC_ALL_TC_OP_INV_EVICT_sum", + "", + "", + "reduce(TCC_ALL_TC_OP_INV_EVICT,sum)", + "Number of evictions due to all TC_OP invalidate requests. Sum over TCC instances."}, + {"TCP_TD_TCP_STALL_CYCLES_sum", + "", + "", + "reduce(TCP_TD_TCP_STALL_CYCLES,sum)", + "TD stalls TCP. Sum over TCP instances."}, + {"TCP_WRITE_TAGCONFLICT_STALL_CYCLES_sum", + "", + "", + "reduce(TCP_WRITE_TAGCONFLICT_STALL_CYCLES,sum)", + "Tagram conflict stall on a write. Sum over TCP instances."}, + {"TCC_EA0_WRREQ_STALL_sum", + "", + "", + "reduce(TCC_EA0_WRREQ_STALL,sum)", + "Number of cycles a write request was stalled. Sum over TCC instances."}, + {"TCP_TCC_UC_ATOMIC_REQ_sum", + "", + "", + "reduce(TCP_TCC_UC_ATOMIC_REQ,sum)", + "Total atomic requests with UC mtype from this TCP to all TCCs Sum over TCP instances."}, + {"TCP_ATOMIC_TAGCONFLICT_STALL_CYCLES_sum", + "", + "", + "reduce(TCP_ATOMIC_TAGCONFLICT_STALL_CYCLES,sum)", + "Tagram conflict stall on an atomic. Sum over TCP instances."}, + {"TCP_GATE_EN1_sum", + "", + "", + "reduce(TCP_GATE_EN1,sum)", + "TCP interface clocks are turned on. Not Windowed. Sum over TCP instances."}, + {"TD_TC_STALL_sum", + "", + "", + "reduce(TD_TC_STALL,sum)", + "TD is stalled waiting for TC data. Sum over TD instances."}, + {"TCC_ALL_TC_OP_WB_WRITEBACK_sum", + "", + "", + "reduce(TCC_ALL_TC_OP_WB_WRITEBACK,sum)", + "Number of writebacks due to all TC_OP writeback requests. Sum over TCC instances."}, + {"TCC_UC_REQ_sum", + "", + "", + "reduce(TCC_UC_REQ,sum)", + "The number of uncached requests. This is measured at the tag block. Sum over TCC " + "instances."}, + {"TCC_EA0_RD_UNCACHED_32B_sum", + "", + "", + "reduce(TCC_EA0_RD_UNCACHED_32B,sum)", + "Number of 32-byte TCC/EA read due to uncached traffic. A 64-byte request will be counted " + "as 2 Sum over TCC instances."}, + {"TCP_UTCL1_PERMISSION_MISS_sum", + "", + "", + "reduce(TCP_UTCL1_PERMISSION_MISS,sum)", + "Total utcl1 permission misses Sum over TCP instances."}, + {"TCC_TOO_MANY_EA_WRREQS_STALL_sum", + "", + "", + "reduce(TCC_TOO_MANY_EA_WRREQS_STALL,sum)", + "Number of cycles the TCC could not send a EA write request because it already reached its " + "maximum number of pending EA write requests. Sum over TCC instances."}, + {"TCP_TCC_WRITE_REQ_LATENCY_sum", + "", + "", + "reduce(TCP_TCC_WRITE_REQ_LATENCY,sum)", + "Total TCP->TCC request latency for writes and atomics without return. Not Windowed. Sum " + "over TCP instances."}, + {"TCC_REQ_sum", + "", + "", + "reduce(TCC_REQ,sum)", + "Number of requests of all types. This is measured at the tag block. This may be more " + "than the number of requests arriving at the TCC, but it is a good indication of the " + "total amount of work that needs to be performed. Sum over TCC instances."}, + {"TCP_TCR_TCP_STALL_CYCLES_sum", + "", + "", + "reduce(TCP_TCR_TCP_STALL_CYCLES,sum)", + "TCR stalls TCP_TCR_req interface. Sum over TCP instances."}, + {"TCP_UTCL1_REQUEST_sum", + "", + "", + "reduce(TCP_UTCL1_REQUEST,sum)", + "Total CLIENT_UTCL1 NORMAL requests Sum over TCP instances."}, + {"TCP_TCC_ATOMIC_WITHOUT_RET_REQ_sum", + "", + "", + "reduce(TCP_TCC_ATOMIC_WITHOUT_RET_REQ,sum)", + "Total atomic without return requests from TCP to all TCCs Sum over TCP instances."}, + {"TA_ADDR_STALLED_BY_TD_CYCLES_sum", + "", + "", + "reduce(TA_ADDR_STALLED_BY_TD_CYCLES,sum)", + "Number of cycles addr path stalled by TD. Perf_Windowing not supported for this counter. " + "Sum over TA instances."}, + {"TCP_TCC_NC_WRITE_REQ_sum", + "", + "", + "reduce(TCP_TCC_NC_WRITE_REQ,sum)", + "Total write requests with NC mtype from this TCP to all TCCs Sum over TCP instances."}, + {"TCC_EA0_RDREQ_LEVEL_sum", + "", + "", + "reduce(TCC_EA0_RDREQ_LEVEL,sum)", + "The sum of the number of TCC/EA read requests in flight. This is primarily meant for " + "measure average EA read latency. Average read latency = " + "TCC_PERF_SEL_EA_RDREQ_LEVEL/TCC_PERF_SEL_EA_RDREQ. Sum over TCC instances."}, + {"TCC_RW_REQ_sum", + "", + "", + "reduce(TCC_RW_REQ,sum)", + "The number of RW requests. This is measured at the tag block. Sum over TCC instances."}, + {"TCC_EA0_WRREQ_IO_CREDIT_STALL_sum", + "", + "", + "reduce(TCC_EA0_WRREQ_IO_CREDIT_STALL,sum)", + "Number of cycles a EA write request was stalled because the interface was out of IO " + "credits. Sum over TCC instances."}, + {"TCP_READ_TAGCONFLICT_STALL_CYCLES_sum", + "", + "", + "reduce(TCP_READ_TAGCONFLICT_STALL_CYCLES,sum)", + "Tagram conflict stall on a read. Sum over TCP instances."}, + {"TCC_BUSY_sum", + "", + "", + "reduce(TCC_BUSY,sum)", + "Number of cycles we have a request pending. Not windowable. Sum over TCC instances."}, + {"TCC_BUSY_avr", + "", + "", + "reduce(TCC_BUSY,avr)", + "TCC_BUSY avr over all memory channels."}, + {"TCC_PROBE_sum", + "", + "", + "reduce(TCC_PROBE,sum)", + "Number of probe requests. Not windowable. Sum over TCC instances."}, + {"TCC_PROBE_ALL_sum", + "", + "", + "reduce(TCC_PROBE_ALL,sum)", + "Number of external probe requests with with EA_TCC_preq_all== 1. Not windowable. Sum " + "over TCC instances."}, + {"TA_BUFFER_READ_WAVEFRONTS_sum", + "", + "", + "reduce(TA_BUFFER_READ_WAVEFRONTS,sum)", + "Number of buffer read wavefronts processed by TA. Sum over TA instances."}, + {"TCP_TOTAL_READ_sum", + "", + "", + "reduce(TCP_TOTAL_READ,sum)", + "Total number of read pixels/buffers from TA. Equals TCP_PERF_SEL_TOTAL_HIT_LRU_READ + " + "TCP_PERF_SEL_TOTAL_MISS_LRU_READ + TCP_PERF_SEL_TOTAL_MISS_EVICT_READ. Sum over TCP " + "instances."}, + {"TCC_WRITE_sum", + "", + "", + "reduce(TCC_WRITE,sum)", + "Number of write requests. Sum over TCC instances."}, + {"TCC_EA0_RDREQ_IO_CREDIT_STALL_sum", + "", + "", + "reduce(TCC_EA0_RDREQ_IO_CREDIT_STALL,sum)", + "Number of cycles there was a stall because the read request interface was out of IO " + "credits. Stalls occur regardless of whether a read needed to be performed or not. Sum " + "over TCC instances."}, + {"TCC_EA0_WRREQ_GMI_CREDIT_STALL_sum", + "", + "", + "reduce(TCC_EA0_WRREQ_GMI_CREDIT_STALL,sum)", + "Number of cycles a EA write request was stalled because the interface was out of GMI " + "credits. Sum over TCC instances."}, + {"TCP_UTCL1_TRANSLATION_HIT_sum", + "", + "", + "reduce(TCP_UTCL1_TRANSLATION_HIT,sum)", + "Total utcl1 translation hits Sum over TCP instances."}, + {"TCC_CYCLE_sum", + "", + "", + "reduce(TCC_CYCLE,sum)", + "Number of cycles. Not windowable. Sum over TCC instances."}, + {"TCC_EA0_WRREQ_DRAM_CREDIT_STALL_sum", + "", + "", + "reduce(TCC_EA0_WRREQ_DRAM_CREDIT_STALL,sum)", + "Number of cycles a EA write request was stalled because the interface was out of DRAM " + "credits. Sum over TCC instances."}, + {"TCP_TCC_RW_ATOMIC_REQ_sum", + "", + "", + "reduce(TCP_TCC_RW_ATOMIC_REQ,sum)", + "Total atomic requests with RW mtype from this TCP to all TCCs. Sum over TCP instances."}, + {"TCP_TCC_RW_READ_REQ_sum", + "", + "", + "reduce(TCP_TCC_RW_READ_REQ,sum)", + "Total write requests with RW mtype from this TCP to all TCCs. Sum over TCP instances."}, + {"TCP_UTCL1_TRANSLATION_MISS_sum", + "", + "", + "reduce(TCP_UTCL1_TRANSLATION_MISS,sum)", + "Total utcl1 translation misses Sum over TCP instances."}, + {"TA_TOTAL_WAVEFRONTS_sum", + "", + "", + "reduce(TA_TOTAL_WAVEFRONTS,sum)", + "Total number of wavefronts processed by TA. Sum over TA instances."}, + {"TCP_TOTAL_ATOMIC_WITHOUT_RET_sum", + "", + "", + "reduce(TCP_TOTAL_ATOMIC_WITHOUT_RET,sum)", + "Total number of atomic without return pixels/buffers from TA Sum over TCP instances."}, + {"TCC_NORMAL_WRITEBACK_sum", + "", + "", + "reduce(TCC_NORMAL_WRITEBACK,sum)", + "Number of writebacks due to requests that are not writeback requests. Sum over TCC " + "instances."}}}}; diff --git a/source/lib/rocprofiler-sdk/hsa/async_copy.cpp b/source/lib/rocprofiler-sdk/hsa/async_copy.cpp index 50e7da6bb9..ab81c2d958 100644 --- a/source/lib/rocprofiler-sdk/hsa/async_copy.cpp +++ b/source/lib/rocprofiler-sdk/hsa/async_copy.cpp @@ -142,8 +142,7 @@ context_filter(const context::context* ctx) return (has_buffered || has_callback); } -constexpr auto null_rocp_agent_id = - rocprofiler_agent_id_t{.handle = std::numeric_limits::max()}; +constexpr auto null_rocp_agent_id = rocprofiler_agent_id_t{.handle = 0}; struct async_copy_data { diff --git a/source/lib/rocprofiler-sdk/hsa/memory_allocation.cpp b/source/lib/rocprofiler-sdk/hsa/memory_allocation.cpp index 16f0320899..4a51ae1a57 100644 --- a/source/lib/rocprofiler-sdk/hsa/memory_allocation.cpp +++ b/source/lib/rocprofiler-sdk/hsa/memory_allocation.cpp @@ -324,8 +324,7 @@ get_next_dispatch() return _v; } -constexpr auto null_rocp_agent_id = - rocprofiler_agent_id_t{.handle = std::numeric_limits::max()}; +constexpr auto null_rocp_agent_id = rocprofiler_agent_id_t{.handle = 0}; struct memory_allocation_data { diff --git a/source/lib/rocprofiler-sdk/hsa/queue_controller.cpp b/source/lib/rocprofiler-sdk/hsa/queue_controller.cpp index b6742feb24..557cfd1dd5 100644 --- a/source/lib/rocprofiler-sdk/hsa/queue_controller.cpp +++ b/source/lib/rocprofiler-sdk/hsa/queue_controller.cpp @@ -83,9 +83,9 @@ destroy_queue(hsa_queue_t* hsa_queue) } constexpr rocprofiler_agent_t default_agent = - rocprofiler_agent_t{.size = sizeof(rocprofiler_agent_t), - .id = rocprofiler_agent_id_t{std::numeric_limits::max()}, - .type = ROCPROFILER_AGENT_TYPE_NONE, + rocprofiler_agent_t{.size = sizeof(rocprofiler_agent_t), + .id = rocprofiler_agent_id_t{.handle = 0}, + .type = ROCPROFILER_AGENT_TYPE_NONE, .cpu_cores_count = 0, .simd_count = 0, .mem_banks_count = 0, @@ -129,8 +129,8 @@ constexpr rocprofiler_agent_t default_agent = .local_mem_size = 0, .hive_id = 0, .gpu_id = 0, - .workgroup_max_dim = {0, 0, 0}, - .grid_max_dim = {0, 0, 0}, + .workgroup_max_dim = {.x = 0, .y = 0, .z = 0}, + .grid_max_dim = {.x = 0, .y = 0, .z = 0}, .mem_banks = nullptr, .caches = nullptr, .io_links = nullptr, @@ -142,7 +142,7 @@ constexpr rocprofiler_agent_t default_agent = .logical_node_id = 0, .logical_node_type_id = 0, .runtime_visibility = {0, 0, 0, 0, 0}, - .uuid = {.value = 0}}; + .uuid = static_cast(agent::uuid_view_t{})}; } // namespace void diff --git a/source/lib/rocprofiler-sdk/hsa/scratch_memory.cpp b/source/lib/rocprofiler-sdk/hsa/scratch_memory.cpp index 2eb3fd36e0..e7a5ba063c 100644 --- a/source/lib/rocprofiler-sdk/hsa/scratch_memory.cpp +++ b/source/lib/rocprofiler-sdk/hsa/scratch_memory.cpp @@ -462,7 +462,7 @@ impl(Args... args) [[maybe_unused]] const auto get_agent_id = [](const hsa_queue_t* hsa_queue) -> rocprofiler_agent_id_t { - rocprofiler_agent_id_t _agent_id{static_cast(-1)}; + rocprofiler_agent_id_t _agent_id{.handle = 0}; bool found_agent{false}; rocprofiler::hsa::get_queue_controller()->iterate_queues( diff --git a/source/lib/rocprofiler-sdk/pc_sampling/utils.hpp b/source/lib/rocprofiler-sdk/pc_sampling/utils.hpp index 2443766ef9..8b1ff92934 100644 --- a/source/lib/rocprofiler-sdk/pc_sampling/utils.hpp +++ b/source/lib/rocprofiler-sdk/pc_sampling/utils.hpp @@ -58,7 +58,7 @@ constexpr size_t get_hsa_pcs_buffer_size() { // TODO: Find the minimum size of all buffers and use that. - return 1024 * sizeof(perf_sample_hosttrap_v1_t); + return 64 * 1024 * sizeof(perf_sample_hosttrap_v1_t); // 4MB } } // namespace utils } // namespace pc_sampling diff --git a/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp b/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp index 065f481469..396b8a3c04 100644 --- a/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp +++ b/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp @@ -78,7 +78,7 @@ initialize_logging() { auto logging_cfg = rocprofiler::common::logging_config{.install_failure_handler = true}; common::init_logging("ROCPROF", logging_cfg); - FLAGS_colorlogtostderr = true; + FLAGS_colorlogtostderr = isatty(fileno(stderr)) == 1 ? true : false; } tool::metadata& @@ -262,7 +262,7 @@ pc_sample_config(uint64_t agent_handle, } bool -is_counter_set(uint64_t* counter_handles, uint64_t agent_handle, size_t num_counters) +is_counter_set(const uint64_t* counter_handles, uint64_t agent_handle, size_t num_counters) { rocprofiler_profile_config_id_t cfg_id = {.handle = 0}; for(size_t itr = 0; itr < num_counters; itr++) diff --git a/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp b/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp index 664142b02d..f599201345 100644 --- a/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp +++ b/source/lib/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp @@ -74,9 +74,9 @@ pc_sample_config(uint64_t agent_handle, uint64_t* max_interval, uint64_t* flags) ROCPROFILER_EXPORT; bool -is_counter_set(uint64_t* counter_handles, - uint64_t agent_handle, - size_t num_counters) ROCPROFILER_EXPORT; +is_counter_set(const uint64_t* counter_handles, + uint64_t agent_handle, + size_t num_counters) ROCPROFILER_EXPORT; void agent_info(uint64_t agent_handle, const char** agent_info_str) ROCPROFILER_EXPORT; diff --git a/source/lib/rocprofiler-sdk/tests/agent.cpp b/source/lib/rocprofiler-sdk/tests/agent.cpp index 42e651310f..e951160899 100644 --- a/source/lib/rocprofiler-sdk/tests/agent.cpp +++ b/source/lib/rocprofiler-sdk/tests/agent.cpp @@ -111,7 +111,7 @@ TEST(rocprofiler_lib, agent_abi) EXPECT_EQ(offsetof(rocprofiler_agent_t, uuid), 296) << msg; // Add test for offset of new field above this. Do NOT change any existing values! - constexpr auto expected_rocp_agent_size = 304; + constexpr auto expected_rocp_agent_size = 312; // If a new field is added, increase this value by the size of the new field(s) EXPECT_EQ(sizeof(rocprofiler_agent_t), expected_rocp_agent_size) << "ABI break. If you added a new field, make sure that this is the only new check that " @@ -450,7 +450,9 @@ TEST(rocprofiler_lib, agent_visibility_multigpu) for(const auto* itr : _agents) { ordinals.emplace(itr->id, itr->logical_node_type_id); - uuids.emplace(itr->id, fmt::format("GPU-{:X}", itr->uuid.value)); + auto uuid_view = rocprofiler::agent::uuid_view_t{itr->uuid}; + auto uuid_value = uuid_view.value64[0]; + uuids.emplace(itr->id, fmt::format("GPU-{:X}", uuid_value)); ROCP_WARNING << ordinals.at(itr->id) << " :: " << uuids.at(itr->id); all_ordinals = fmt::format("{},{}", all_ordinals, ordinals.at(itr->id)); @@ -637,7 +639,10 @@ TEST(rocprofiler_lib, agent_visibility_inverted_multigpu) for(const auto* itr : _agents) { - auto _uuid = fmt::format("GPU-{:X}", itr->uuid.value); + auto uuid_view = rocprofiler::agent::uuid_view_t{itr->uuid}; + auto uuid_value = uuid_view.value64[0]; + auto _uuid = fmt::format("GPU-{:X}", uuid_value); + if(ordinals.empty()) { devices_reversed_with_ordinal_0_uuid = fmt::format("1,{}", _uuid); @@ -761,7 +766,9 @@ TEST(rocprofiler_lib, agent_visibility_inverted_multigpu) for(const auto* itr : get_gpu_agents()) { - const auto curr_uuid = fmt::format("GPU-{:X}", itr->uuid.value); + auto uuid_view = rocprofiler::agent::uuid_view_t{itr->uuid}; + auto uuid_value = uuid_view.value64[0]; + const auto curr_uuid = fmt::format("GPU-{:X}", uuid_value); if(ordinals.at(itr->id) == 0) { ASSERT_EQ(itr->runtime_visibility.hsa, 1) << "agent-" << itr->node_id; diff --git a/source/lib/rocprofiler-sdk/thread_trace/core.cpp b/source/lib/rocprofiler-sdk/thread_trace/core.cpp index 16360ad5b2..8f50e08f00 100644 --- a/source/lib/rocprofiler-sdk/thread_trace/core.cpp +++ b/source/lib/rocprofiler-sdk/thread_trace/core.cpp @@ -57,8 +57,7 @@ namespace rocprofiler namespace thread_trace { constexpr size_t QUEUE_SIZE = 128; -constexpr uint64_t MIN_BUFFER_SIZE = 1 << 18; // 2 pages per SE -constexpr uint64_t MAX_BUFFER_SIZE = std::numeric_limits::max(); // aqlprofile limit +constexpr uint64_t MIN_BUFFER_SIZE = 1 << 20; // 1MB struct cbdata_t { @@ -74,15 +73,15 @@ thread_trace_parameter_pack::are_params_valid() const { if(shader_cb_fn == nullptr) { - ROCP_WARNING << "Callback cannot be null!"; + ROCP_CI_LOG(WARNING) << "Callback cannot be null!"; return false; } if(shader_engine_mask == 0) return false; - if(buffer_size > MAX_BUFFER_SIZE || buffer_size < MIN_BUFFER_SIZE) + if(buffer_size < MIN_BUFFER_SIZE) { - ROCP_WARNING << "Invalid buffer size: " << buffer_size; + ROCP_CI_LOG(WARNING) << "Invalid buffer size: " << buffer_size; return false; } diff --git a/source/lib/rocprofiler-sdk/thread_trace/decode.cpp b/source/lib/rocprofiler-sdk/thread_trace/decode.cpp index 16ef7bfd15..f0e0980d7e 100644 --- a/source/lib/rocprofiler-sdk/thread_trace/decode.cpp +++ b/source/lib/rocprofiler-sdk/thread_trace/decode.cpp @@ -21,6 +21,7 @@ // SOFTWARE. #include "lib/common/static_object.hpp" +#include "lib/common/synchronized.hpp" #include "lib/rocprofiler-sdk/aql/helpers.hpp" #include "lib/rocprofiler-sdk/context/context.hpp" #include "lib/rocprofiler-sdk/hsa/agent_cache.hpp" @@ -29,15 +30,19 @@ #include #include +#include +#include #include +#include #include namespace { using DL = rocprofiler::thread_trace::DL; using AddressTable = rocprofiler::sdk::codeobj::disassembly::CodeobjAddressTranslate; +using LockedTable = rocprofiler::common::Synchronized; class DecoderInstance { @@ -46,72 +51,72 @@ class DecoderInstance : dl(std::move(_dl)) {} - std::unique_ptr
dl{nullptr}; - AddressTable table{}; + const std::unique_ptr dl{nullptr}; + + LockedTable table{}; }; -std::mutex map_mut; +using DecoderMap = + std::unordered_map>; +using LockedMap = rocprofiler::common::Synchronized; auto& -get_dlopens() +get_dlmap() { - static auto*& _v = rocprofiler::common::static_object< - std::unordered_map>>::construct(); + static auto*& _v = rocprofiler::common::static_object::construct(); return *CHECK_NOTNULL(_v); } std::shared_ptr -get_dl(rocprofiler_thread_trace_decoder_handle_t handle) +get_dl(rocprofiler_thread_trace_decoder_id_t handle) { - auto lk = std::unique_lock{map_mut}; - auto it = get_dlopens().find(handle.handle); - if(it == get_dlopens().end()) return nullptr; - - return it->second; + return get_dlmap().rlock([&](const DecoderMap& map) -> std::shared_ptr { + if(auto it = map.find(handle); it != map.end()) return it->second; + return nullptr; + }); } } // namespace extern "C" { rocprofiler_status_t -rocprofiler_thread_trace_decoder_create(rocprofiler_thread_trace_decoder_handle_t* handle, - const char* path) +rocprofiler_thread_trace_decoder_create(rocprofiler_thread_trace_decoder_id_t* handle, + const char* path) { auto dl = std::make_unique
(path); if(dl->handle == nullptr) return ROCPROFILER_STATUS_ERROR_NOT_AVAILABLE; if(!dl->valid()) return ROCPROFILER_STATUS_ERROR_INCOMPATIBLE_ABI; - auto lk = std::unique_lock{map_mut}; - static uint64_t count = 1; + static std::atomic count{1}; + handle->handle = count.fetch_add(1); - auto instance = std::make_shared(std::move(dl)); - - handle->handle = count++; - get_dlopens()[handle->handle] = std::move(instance); + get_dlmap().wlock( + [&](DecoderMap& map) { map[*handle] = std::make_shared(std::move(dl)); }); return ROCPROFILER_STATUS_SUCCESS; } void -rocprofiler_thread_trace_decoder_destroy(rocprofiler_thread_trace_decoder_handle_t handle) +rocprofiler_thread_trace_decoder_destroy(rocprofiler_thread_trace_decoder_id_t handle) { - auto lk = std::unique_lock{map_mut}; - get_dlopens().erase(handle.handle); + get_dlmap().wlock([&](DecoderMap& map) { map.erase(handle); }); } rocprofiler_status_t -rocprofiler_thread_trace_decoder_codeobj_load(rocprofiler_thread_trace_decoder_handle_t handle, - uint64_t load_id, - uint64_t load_addr, - uint64_t load_size, - const void* data, - uint64_t size) +rocprofiler_thread_trace_decoder_codeobj_load(rocprofiler_thread_trace_decoder_id_t handle, + uint64_t load_id, + uint64_t load_addr, + uint64_t load_size, + const void* data, + uint64_t size) { auto decoder = get_dl(handle); if(decoder == nullptr) return ROCPROFILER_STATUS_ERROR_INVALID_ARGUMENT; try { - decoder->table.addDecoder(data, size, load_id, load_addr, load_size); + decoder->table.wlock([&](AddressTable& table) { + table.addDecoder(data, size, load_id, load_addr, load_size); + }); } catch(...) { return ROCPROFILER_STATUS_ERROR; @@ -120,15 +125,17 @@ rocprofiler_thread_trace_decoder_codeobj_load(rocprofiler_thread_trace_decoder_h } rocprofiler_status_t -rocprofiler_thread_trace_decoder_codeobj_unload(rocprofiler_thread_trace_decoder_handle_t handle, - uint64_t load_id) +rocprofiler_thread_trace_decoder_codeobj_unload(rocprofiler_thread_trace_decoder_id_t handle, + uint64_t load_id) { auto decoder = get_dl(handle); if(decoder == nullptr) return ROCPROFILER_STATUS_ERROR_INVALID_ARGUMENT; try { - if(decoder->table.removeDecoder(load_id)) return ROCPROFILER_STATUS_SUCCESS; + bool result = + decoder->table.wlock([&](AddressTable& table) { return table.removeDecoder(load_id); }); + if(result) return ROCPROFILER_STATUS_SUCCESS; } catch(std::exception&) {} @@ -169,32 +176,32 @@ isa_callback(char* isa_instruction, void* userdata) { ROCP_FATAL_IF(userdata == nullptr) << "Userdata is null!"; - auto& table = static_cast(userdata)->decoder->table; - - std::unique_ptr instruction{nullptr}; + auto decoder = static_cast(userdata)->decoder; + ROCP_FATAL_IF(decoder == nullptr) << "decoder is null"; try { - instruction = table.get(pc.marker_id, pc.addr); - } catch(std::exception& e) - { - ROCP_WARNING << pc.marker_id << ":" << pc.addr << ' ' << e.what(); - return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR; - } + auto instruction = decoder->table.wlock( + [&](AddressTable& table) { return table.get(pc.marker_id, pc.addr); }); - if(!instruction) return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR_INVALID_ARGUMENT; + if(!instruction) return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR_INVALID_ARGUMENT; - { - size_t tmp_isa_size = *isa_size; - *isa_size = instruction->inst.size(); + { + size_t tmp_isa_size = *isa_size; + *isa_size = instruction->inst.size(); - if(*isa_size > tmp_isa_size) - return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR_OUT_OF_RESOURCES; - } + if(*isa_size > tmp_isa_size) + return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR_OUT_OF_RESOURCES; + } - memcpy(isa_instruction, instruction->inst.data(), *isa_size); - *isa_memory_size = instruction->size; + memcpy(isa_instruction, instruction->inst.data(), *isa_size); + *isa_memory_size = instruction->size; + } catch(std::exception& e) + { + ROCP_CI_LOG(INFO) << pc.marker_id << ":" << pc.addr << ' ' << e.what(); + return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_ERROR; + } return ROCPROFILER_THREAD_TRACE_DECODER_STATUS_SUCCESS; } @@ -214,7 +221,7 @@ trace_callback(rocprofiler_thread_trace_decoder_record_type_t record_type_id, extern "C" { rocprofiler_status_t -rocprofiler_trace_decode(rocprofiler_thread_trace_decoder_handle_t handle, +rocprofiler_trace_decode(rocprofiler_thread_trace_decoder_id_t handle, rocprofiler_thread_trace_decoder_callback_t user_callback, void* data, uint64_t size, @@ -249,8 +256,8 @@ rocprofiler_trace_decode(rocprofiler_thread_trace_decoder_handle_t handle, } const char* -rocprofiler_thread_trace_decoder_info_string(rocprofiler_thread_trace_decoder_handle_t handle, - rocprofiler_thread_trace_decoder_info_t info) +rocprofiler_thread_trace_decoder_info_string(rocprofiler_thread_trace_decoder_id_t handle, + rocprofiler_thread_trace_decoder_info_t info) { auto decoder = get_dl(handle); if(decoder == nullptr) return nullptr; diff --git a/source/lib/tests/common/CMakeLists.txt b/source/lib/tests/common/CMakeLists.txt index 08075a8bcf..63008a9875 100644 --- a/source/lib/tests/common/CMakeLists.txt +++ b/source/lib/tests/common/CMakeLists.txt @@ -5,8 +5,8 @@ project(rocprofiler-sdk-tests-common LANGUAGES C CXX) include(GoogleTest) -set(common_sources demangling.cpp environment.cpp md5sum.cpp mpl.cpp parse.cpp sha256.cpp - uuid_v7.cpp) +set(common_sources c_array.cpp demangling.cpp environment.cpp md5sum.cpp mpl.cpp + parse.cpp sha256.cpp uuid_v7.cpp) add_executable(common-tests) target_sources(common-tests PRIVATE ${common_sources}) diff --git a/source/lib/tests/common/c_array.cpp b/source/lib/tests/common/c_array.cpp new file mode 100644 index 0000000000..e37d2fe031 --- /dev/null +++ b/source/lib/tests/common/c_array.cpp @@ -0,0 +1,138 @@ +// MIT License +// +// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include + +#include + +#include +#include + +namespace +{ +template +void +compare_serialization_of_vector_and_c_array(std::vector& vec) +{ + std::stringstream ss_vec; + { + cereal::BinaryOutputArchive oarchive(ss_vec); + save(oarchive, vec); + } + + std::stringstream ss_arr; + { + cereal::BinaryOutputArchive oarchive(ss_arr); + auto arr = rocprofiler::sdk::container::make_c_array(vec.data(), vec.size()); + save(oarchive, arr); + } + + ASSERT_EQ(ss_vec.str(), ss_arr.str()); +} + +template +void +compare_serialization_of_vector_and_c_array_of_pointers(std::vector& vec) +{ + std::vector ptrs; + ptrs.reserve(vec.size()); + for(auto& val : vec) + ptrs.emplace_back(&val); + + std::stringstream ss_vec; + { + cereal::BinaryOutputArchive oarchive(ss_vec); + save(oarchive, vec); + } + + std::stringstream ss_arr; + { + cereal::BinaryOutputArchive oarchive(ss_arr); + auto arr = rocprofiler::sdk::container::make_c_array(ptrs.data(), ptrs.size()); + save(oarchive, arr); + } + + ASSERT_EQ(ss_vec.str(), ss_arr.str()); +} + +} // namespace + +TEST(common, c_array) +{ + std::vector vec{1, 2, 3, 4, 5}; + auto arr = rocprofiler::sdk::container::make_c_array(vec.data(), vec.size()); + + // Validate size + ASSERT_EQ(arr.size(), vec.size()); + + // Test operator[] and at() + for(size_t i = 0; i < arr.size(); ++i) + { + EXPECT_EQ(arr[i], vec[i]); + EXPECT_EQ(arr.at(i), vec.at(i)); + } + + // Bounds checking on at() + EXPECT_THROW(arr.at(arr.size()), std::out_of_range); + + // Test slice + auto sub_arr = arr.slice(1, 4); // should contain 2, 3, 4 + ASSERT_EQ(sub_arr.size(), 3); + EXPECT_EQ(sub_arr[0], 2); + EXPECT_EQ(sub_arr[2], 4); + + // Test pop_front() + arr.pop_front(); // drops 1 + ASSERT_EQ(arr.size(), 4); + EXPECT_EQ(arr[0], 2); + + // Test pop_back() + arr.pop_back(); // drops 5 + ASSERT_EQ(arr.size(), 3); + EXPECT_EQ(arr[2], 4); + + // Test iterators + int expected[] = {2, 3, 4}; + int idx = 0; + for(auto v : arr) + { + EXPECT_EQ(v, expected[idx++]); + } + + // Const iterator + const auto& const_arr = arr; + idx = 0; + for(auto v : const_arr) + { + EXPECT_EQ(v, expected[idx++]); + } + + // Test implicit cast to pointer + int* ptr = arr; + EXPECT_EQ(ptr[0], 2); + EXPECT_EQ(ptr[2], 4); + + // Test Serialization. + compare_serialization_of_vector_and_c_array(vec); + compare_serialization_of_vector_and_c_array_of_pointers(vec); +} diff --git a/source/share/rocprofiler-sdk-rocpd/data_views.sql b/source/share/rocprofiler-sdk-rocpd/data_views.sql index 868542c99f..c4c8b1d245 100644 --- a/source/share/rocprofiler-sdk-rocpd/data_views.sql +++ b/source/share/rocprofiler-sdk-rocpd/data_views.sql @@ -319,7 +319,10 @@ SELECT S.private_segment_size AS static_scratch_size, E.stack_id, E.parent_stack_id, - E.correlation_id AS corr_id + E.correlation_id AS corr_id, + S.sgpr_count, + S.arch_vgpr_count, + S.accum_vgpr_count FROM `rocpd_kernel_dispatch` K INNER JOIN `rocpd_info_agent` A ON A.id = K.agent_id @@ -596,6 +599,7 @@ SELECT JSON_EXTRACT(M.extdata, '$.flags') AS alloc_flags, M.start, M.end, + (M.end - M.start) AS duration, M.size, M.address, E.correlation_id, diff --git a/source/share/rocprofiler-sdk-rocpd/summary_views.sql b/source/share/rocprofiler-sdk-rocpd/summary_views.sql index 3575bf6f55..357eecec0e 100644 --- a/source/share/rocprofiler-sdk-rocpd/summary_views.sql +++ b/source/share/rocprofiler-sdk-rocpd/summary_views.sql @@ -151,226 +151,3 @@ GROUP BY name ORDER BY total_duration DESC; - --- Kernel summary by name -CREATE VIEW - `kernel_summary` AS -WITH - avg_data AS ( - SELECT - name, - AVG(duration) AS avg_duration - FROM - `kernels` - GROUP BY - name - ), - aggregated_data AS ( - SELECT - K.name, - COUNT(*) AS calls, - SUM(K.duration) AS total_duration, - SUM(CAST(K.duration AS REAL) * CAST(K.duration AS REAL)) AS sqr_duration, - A.avg_duration AS average_duration, - MIN(K.duration) AS min_duration, - MAX(K.duration) AS max_duration, - SUM(CAST((K.duration - A.avg_duration) AS REAL) * CAST((K.duration - A.avg_duration) AS REAL)) / (COUNT(*) - 1) AS variance_duration, - SQRT( - SUM(CAST((K.duration - A.avg_duration) AS REAL) * CAST((K.duration - A.avg_duration) AS REAL)) / (COUNT(*) - 1) - ) AS std_dev_duration - FROM - `kernels` K - JOIN avg_data A ON K.name = A.name - GROUP BY - K.name - ), - total_duration AS ( - SELECT - SUM(total_duration) AS grand_total_duration - FROM - aggregated_data - ) -SELECT - AD.name AS name, - AD.calls, - AD.total_duration AS "DURATION (nsec)", - AD.sqr_duration AS "SQR (nsec)", - AD.average_duration AS "AVERAGE (nsec)", - (CAST(AD.total_duration AS REAL) / TD.grand_total_duration) * 100 AS "PERCENT (INC)", - AD.min_duration AS "MIN (nsec)", - AD.max_duration AS "MAX (nsec)", - AD.variance_duration AS "VARIANCE", - AD.std_dev_duration AS "STD_DEV" -FROM - aggregated_data AD - CROSS JOIN total_duration TD; - --- --- Kernel summary by region name -CREATE VIEW - `kernel_summary_region` AS -WITH - avg_data AS ( - SELECT - region, - AVG(duration) AS avg_duration - FROM - `kernels` - GROUP BY - region - ), - aggregated_data AS ( - SELECT - K.region AS name, - COUNT(*) AS calls, - SUM(K.duration) AS total_duration, - SUM(CAST(K.duration AS REAL) * CAST(K.duration AS REAL)) AS sqr_duration, - A.avg_duration AS average_duration, - MIN(K.duration) AS min_duration, - MAX(K.duration) AS max_duration, - SUM(CAST((K.duration - A.avg_duration) AS REAL) * CAST((K.duration - A.avg_duration) AS REAL)) / (COUNT(*) - 1) AS variance_duration, - SQRT( - SUM(CAST((K.duration - A.avg_duration) AS REAL) * CAST((K.duration - A.avg_duration) AS REAL)) / (COUNT(*) - 1) - ) AS std_dev_duration - FROM - `kernels` K - JOIN avg_data A ON K.region = A.region - GROUP BY - K.region - ), - total_duration AS ( - SELECT - SUM(total_duration) AS grand_total_duration - FROM - aggregated_data - ) -SELECT - AD.name AS name, - AD.calls, - AD.total_duration AS "DURATION (nsec)", - AD.sqr_duration AS "SQR (nsec)", - AD.average_duration AS "AVERAGE (nsec)", - (CAST(AD.total_duration AS REAL) / TD.grand_total_duration) * 100 AS "PERCENT (INC)", - AD.min_duration AS "MIN (nsec)", - AD.max_duration AS "MAX (nsec)", - AD.variance_duration AS "VARIANCE", - AD.std_dev_duration AS "STD_DEV" -FROM - aggregated_data AD - CROSS JOIN total_duration TD; - --- --- Memory copy summary -CREATE VIEW - `memory_copy_summary` AS -WITH - avg_data AS ( - SELECT - name, - AVG(duration) AS avg_duration - FROM - `memory_copies` - GROUP BY - name - ), - aggregated_data AS ( - SELECT - MC.name, - COUNT(*) AS calls, - SUM(MC.duration) AS total_duration, - SUM(CAST(MC.duration AS REAL) * CAST(MC.duration AS REAL)) AS sqr_duration, - A.avg_duration AS average_duration, - MIN(MC.duration) AS min_duration, - MAX(MC.duration) AS max_duration, - SUM( - CAST((MC.duration - A.avg_duration) AS REAL) * CAST((MC.duration - A.avg_duration) AS REAL) - ) / (COUNT(*) - 1) AS variance_duration, - SQRT( - SUM( - CAST((MC.duration - A.avg_duration) AS REAL) * CAST((MC.duration - A.avg_duration) AS REAL) - ) / (COUNT(*) - 1) - ) AS std_dev_duration - FROM - `memory_copies` MC - JOIN avg_data A ON MC.name = A.name - GROUP BY - MC.name - ), - total_duration AS ( - SELECT - SUM(total_duration) AS grand_total_duration - FROM - aggregated_data - ) -SELECT - AD.name AS name, - AD.calls, - AD.total_duration AS "DURATION (nsec)", - AD.sqr_duration AS "SQR (nsec)", - AD.average_duration AS "AVERAGE (nsec)", - (CAST(AD.total_duration AS REAL) / TD.grand_total_duration) * 100 AS "PERCENT (INC)", - AD.min_duration AS "MIN (nsec)", - AD.max_duration AS "MAX (nsec)", - AD.variance_duration AS "VARIANCE", - AD.std_dev_duration AS "STD_DEV" -FROM - aggregated_data AD - CROSS JOIN total_duration TD; - --- --- Memory allocation summary -CREATE VIEW - `memory_allocation_summary` AS -WITH - avg_data AS ( - SELECT - type AS name, - AVG(duration) AS avg_duration - FROM - `memory_allocations` - GROUP BY - type - ), - aggregated_data AS ( - SELECT - MA.type AS name, - COUNT(*) AS calls, - SUM(MA.duration) AS total_duration, - SUM(CAST(MA.duration AS REAL) * CAST(MA.duration AS REAL)) AS sqr_duration, - A.avg_duration AS average_duration, - MIN(MA.duration) AS min_duration, - MAX(MA.duration) AS max_duration, - SUM( - CAST((MA.duration - A.avg_duration) AS REAL) * CAST((MA.duration - A.avg_duration) AS REAL) - ) / (COUNT(*) - 1) AS variance_duration, - SQRT( - SUM( - CAST((MA.duration - A.avg_duration) AS REAL) * CAST((MA.duration - A.avg_duration) AS REAL) - ) / (COUNT(*) - 1) - ) AS std_dev_duration - FROM - `memory_allocations` MA - JOIN avg_data A ON MA.type = A.name - GROUP BY - MA.type - ), - total_duration AS ( - SELECT - SUM(total_duration) AS grand_total_duration - FROM - aggregated_data - ) -SELECT - 'MEMORY_ALLOCATION_' || AD.name AS name, - AD.calls, - AD.total_duration AS "DURATION (nsec)", - AD.sqr_duration AS "SQR (nsec)", - AD.average_duration AS "AVERAGE (nsec)", - (CAST(AD.total_duration AS REAL) / TD.grand_total_duration) * 100 AS "PERCENT (INC)", - AD.min_duration AS "MIN (nsec)", - AD.max_duration AS "MAX (nsec)", - AD.variance_duration AS "VARIANCE", - AD.std_dev_duration AS "STD_DEV" -FROM - aggregated_data AD - CROSS JOIN total_duration TD; diff --git a/source/share/rocprofiler-sdk/counter_defs.yaml b/source/share/rocprofiler-sdk/counter_defs.yaml index 82243e87c6..4072c46437 100644 --- a/source/share/rocprofiler-sdk/counter_defs.yaml +++ b/source/share/rocprofiler-sdk/counter_defs.yaml @@ -42,6 +42,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -54,6 +55,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -66,6 +68,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -78,6 +81,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -90,6 +94,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -102,6 +107,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -114,6 +120,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -126,6 +133,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -138,6 +146,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -150,6 +159,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -162,6 +172,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -310,6 +321,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -322,6 +334,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -334,6 +347,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -346,6 +360,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -358,6 +373,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -370,6 +386,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -382,6 +399,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1171,6 +1189,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1183,6 +1202,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1200,6 +1220,7 @@ rocprofiler-sdk: - gfx1031 - gfx1032 - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1217,6 +1238,7 @@ rocprofiler-sdk: - gfx1031 - gfx1032 - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1286,6 +1308,7 @@ rocprofiler-sdk: - gfx1031 - gfx1032 - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1303,6 +1326,7 @@ rocprofiler-sdk: - gfx1031 - gfx1032 - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1315,6 +1339,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1327,6 +1352,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1769,6 +1795,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1782,6 +1809,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1795,6 +1823,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1809,6 +1838,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1821,6 +1851,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1833,6 +1864,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1845,6 +1877,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1857,6 +1890,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1869,6 +1903,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1881,6 +1916,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1893,6 +1929,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1905,6 +1942,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1917,6 +1955,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1929,6 +1968,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1941,6 +1981,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1953,6 +1994,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1967,6 +2009,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -1993,6 +2036,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2509,6 +2553,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2532,6 +2577,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2544,6 +2590,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2556,6 +2603,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2568,6 +2616,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2580,6 +2629,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2592,6 +2642,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2604,6 +2655,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2616,6 +2668,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2628,6 +2681,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2640,6 +2694,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2663,6 +2718,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2686,6 +2742,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2698,6 +2755,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2710,6 +2768,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2773,6 +2832,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2785,6 +2845,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2797,6 +2858,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2809,6 +2871,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2821,6 +2884,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2833,6 +2897,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2859,6 +2924,7 @@ rocprofiler-sdk: - gfx1200 - gfx1201 - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -2875,6 +2941,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 185 + - architectures: + - gfx908 + block: SQ + event: 158 - architectures: - gfx940 - gfx941 @@ -2895,6 +2965,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 96 + - architectures: + - gfx908 + block: SQ + event: 69 - architectures: - gfx940 - gfx941 @@ -2917,6 +2991,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 101 + - architectures: + - gfx908 + block: SQ + event: 74 - architectures: - gfx940 - gfx941 @@ -2938,6 +3016,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 103 + - architectures: + - gfx908 + block: SQ + event: 76 - architectures: - gfx940 - gfx941 @@ -2959,6 +3041,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 98 + - architectures: + - gfx908 + block: SQ + event: 71 - architectures: - gfx940 - gfx941 @@ -2980,6 +3066,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 102 + - architectures: + - gfx908 + block: SQ + event: 75 - architectures: - gfx940 - gfx941 @@ -3002,6 +3092,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 100 + - architectures: + - gfx908 + block: SQ + event: 73 - architectures: - gfx940 - gfx941 @@ -3054,6 +3148,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 97 + - architectures: + - gfx908 + block: SQ + event: 70 - architectures: - gfx940 - gfx941 @@ -3071,6 +3169,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -3097,6 +3196,7 @@ rocprofiler-sdk: - gfx1200 - gfx1201 - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -3109,6 +3209,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -3123,6 +3224,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 115 + - architectures: + - gfx908 + block: SQ + event: 88 - architectures: - gfx940 - gfx941 @@ -3142,6 +3247,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 116 + - architectures: + - gfx908 + block: SQ + event: 89 - architectures: - gfx940 - gfx941 @@ -3160,6 +3269,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -3176,6 +3286,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 64 + - architectures: + - gfx908 + block: SQ + event: 39 - architectures: - gfx940 - gfx941 @@ -3196,6 +3310,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 63 + - architectures: + - gfx908 + block: SQ + event: 38 - architectures: - gfx940 - gfx941 @@ -3379,6 +3497,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 52 + - architectures: + - gfx908 + block: SQ + event: 27 - architectures: - gfx940 - gfx941 @@ -3445,6 +3567,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 65 + - architectures: + - gfx908 + block: SQ + event: 40 - architectures: - gfx940 - gfx941 @@ -3513,6 +3639,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 188 + - architectures: + - gfx908 + block: SQ + event: 161 - architectures: - gfx940 - gfx941 @@ -4039,6 +4169,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 55 + - architectures: + - gfx908 + block: SQ + event: 30 - architectures: - gfx940 - gfx941 @@ -4118,6 +4252,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 66 + - architectures: + - gfx908 + block: SQ + event: 41 - architectures: - gfx940 - gfx941 @@ -4243,6 +4381,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 111 + - architectures: + - gfx908 + block: SQ + event: 84 - architectures: - gfx940 - gfx941 @@ -4289,6 +4431,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 105 + - architectures: + - gfx908 + block: SQ + event: 78 - architectures: - gfx940 - gfx941 @@ -4308,6 +4454,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 104 + - architectures: + - gfx908 + block: SQ + event: 77 - architectures: - gfx940 - gfx941 @@ -4358,6 +4508,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 69 + - architectures: + - gfx908 + block: SQ + event: 44 - architectures: - gfx940 - gfx941 @@ -4392,6 +4546,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 68 + - architectures: + - gfx908 + block: SQ + event: 43 - architectures: - gfx940 - gfx941 @@ -4411,6 +4569,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 67 + - architectures: + - gfx908 + block: SQ + event: 42 - architectures: - gfx940 - gfx941 @@ -4428,6 +4590,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4443,6 +4606,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 122 + - architectures: + - gfx908 + block: SQ + event: 95 - architectures: - gfx940 - gfx941 @@ -4462,6 +4629,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 125 + - architectures: + - gfx908 + block: SQ + event: 98 - architectures: - gfx940 - gfx941 @@ -4510,6 +4681,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 126 + - architectures: + - gfx908 + block: SQ + event: 99 - architectures: - gfx940 - gfx941 @@ -4529,6 +4704,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 124 + - architectures: + - gfx908 + block: SQ + event: 97 - architectures: - gfx940 - gfx941 @@ -4548,6 +4727,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 123 + - architectures: + - gfx908 + block: SQ + event: 96 - architectures: - gfx940 - gfx941 @@ -4573,6 +4756,7 @@ rocprofiler-sdk: event: 7 - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4643,6 +4827,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 85 + - architectures: + - gfx908 + block: SQ + event: 58 - architectures: - gfx940 - gfx941 @@ -4682,6 +4870,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 88 + - architectures: + - gfx908 + block: SQ + event: 61 - architectures: - gfx940 - gfx941 @@ -4838,6 +5030,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4855,6 +5048,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4872,6 +5066,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4889,6 +5084,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4906,6 +5102,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -4924,6 +5121,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 186 + - architectures: + - gfx908 + block: SQ + event: 159 - architectures: - gfx940 - gfx941 @@ -4946,6 +5147,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 187 + - architectures: + - gfx908 + block: SQ + event: 160 - architectures: - gfx940 - gfx941 @@ -5004,6 +5209,10 @@ rocprofiler-sdk: - gfx90a block: SQ event: 74 + - architectures: + - gfx908 + block: SQ + event: 47 - architectures: - gfx940 - gfx941 @@ -5256,6 +5465,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 54 - architectures: @@ -5271,6 +5481,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5282,6 +5493,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 55 - architectures: @@ -5297,6 +5509,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5308,6 +5521,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 47 - architectures: @@ -5323,6 +5537,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5334,6 +5549,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 52 - architectures: @@ -5349,6 +5565,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5360,6 +5577,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 53 - architectures: @@ -5375,6 +5593,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5413,6 +5632,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 45 - architectures: @@ -5428,6 +5648,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5466,6 +5687,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 49 - architectures: @@ -5481,6 +5703,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5492,6 +5715,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 44 - architectures: @@ -5507,6 +5731,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5518,6 +5743,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 46 - architectures: @@ -5533,6 +5759,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5625,6 +5852,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 56 - architectures: @@ -5640,6 +5868,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5651,6 +5880,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 103 - architectures: @@ -5666,6 +5896,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5757,6 +5988,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 100 - architectures: @@ -5772,6 +6004,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5848,6 +6081,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5859,6 +6093,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TA event: 32 - architectures: @@ -5874,6 +6109,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5956,6 +6192,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -5979,6 +6216,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6002,6 +6240,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6017,6 +6256,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6028,6 +6268,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6043,6 +6284,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6054,6 +6296,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6069,6 +6312,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6080,6 +6324,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6092,6 +6337,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6103,6 +6349,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6114,6 +6361,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6129,6 +6377,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6140,6 +6389,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6152,6 +6402,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6162,6 +6413,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6177,6 +6429,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6192,6 +6445,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6203,6 +6457,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6250,6 +6505,10 @@ rocprofiler-sdk: description: Number of TCC/EA read requests (either 32-byte or 64-byte) destined for DRAM (MC). properties: [] definitions: + - architectures: + - gfx908 + block: TCC + event: 43 - architectures: - gfx940 - gfx941 @@ -6266,6 +6525,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6281,6 +6541,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6291,6 +6552,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6302,6 +6564,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6317,6 +6580,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6328,6 +6592,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6343,6 +6608,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6354,6 +6620,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6369,6 +6636,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6389,6 +6657,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6404,6 +6673,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6454,6 +6724,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6468,6 +6739,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6483,6 +6755,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6493,6 +6766,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6503,6 +6777,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6518,6 +6793,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6528,6 +6804,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6543,6 +6820,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6554,6 +6832,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6569,6 +6848,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6593,6 +6873,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6607,6 +6888,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6630,6 +6912,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -6646,6 +6929,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7172,6 +7456,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx90a - gfx940 - gfx941 @@ -7188,6 +7473,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7199,6 +7485,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7214,6 +7501,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7224,6 +7512,7 @@ rocprofiler-sdk: properties: [] definitions: - architectures: + - gfx908 - gfx90a - gfx940 - gfx941 @@ -7240,6 +7529,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7251,6 +7541,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7266,6 +7557,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7281,6 +7573,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7306,6 +7599,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7317,6 +7611,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7333,6 +7628,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7345,6 +7641,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7362,6 +7659,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7373,6 +7671,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7388,6 +7687,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7399,6 +7699,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7414,6 +7715,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7427,6 +7729,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7442,6 +7745,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7454,6 +7758,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7470,6 +7775,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7481,6 +7787,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7496,6 +7803,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7507,6 +7815,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7523,6 +7832,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7539,6 +7849,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7550,6 +7861,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7783,6 +8095,70 @@ rocprofiler-sdk: - gfx950 block: TCC event: 116 + - name: TCC_CLIENT184_REQ + description: 'Number of cycles client184 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 312 + - name: TCC_CLIENT185_REQ + description: 'Number of cycles client185 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 313 + - name: TCC_CLIENT186_REQ + description: 'Number of cycles client186 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 314 + - name: TCC_CLIENT187_REQ + description: 'Number of cycles client187 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 315 + - name: TCC_CLIENT188_REQ + description: 'Number of cycles client188 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 316 + - name: TCC_CLIENT189_REQ + description: 'Number of cycles client189 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 317 + - name: TCC_CLIENT190_REQ + description: 'Number of cycles client190 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 318 + - name: TCC_CLIENT191_REQ + description: 'Number of cycles client191 sent a request to this TCC.' + properties: [] + definitions: + - architectures: + - gfx908 + block: TCC + event: 319 - name: TCC_EA0_RDREQ_64B_sum description: Number of 64-byte TCC/EA read requests. Sum over TCP instances. properties: [] @@ -7954,6 +8330,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 13 - architectures: @@ -7969,6 +8346,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7980,6 +8358,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -7992,6 +8371,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8003,6 +8383,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8015,6 +8396,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8026,6 +8408,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 22 - architectures: @@ -8041,6 +8424,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8052,6 +8436,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 11 - architectures: @@ -8067,6 +8452,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8078,6 +8464,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 27 - architectures: @@ -8093,6 +8480,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8104,6 +8492,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 72 - architectures: @@ -8122,6 +8511,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8133,6 +8523,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 71 - architectures: @@ -8151,6 +8542,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8162,6 +8554,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 83 - architectures: @@ -8180,6 +8573,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8191,6 +8585,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 81 - architectures: @@ -8209,6 +8604,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8220,6 +8616,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 82 - architectures: @@ -8238,6 +8635,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8249,6 +8647,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 77 - architectures: @@ -8267,6 +8666,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8278,6 +8678,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 75 - architectures: @@ -8296,6 +8697,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8307,6 +8709,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 76 - architectures: @@ -8325,6 +8728,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8336,6 +8740,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 69 - architectures: @@ -8354,6 +8759,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 66 - architectures: @@ -8366,6 +8772,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx950 expression: reduce(TCP_TCC_READ_REQ_LATENCY,sum) - name: TCP_TCC_READ_REQ_sum @@ -8374,6 +8781,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8385,6 +8793,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 87 - architectures: @@ -8403,6 +8812,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8414,6 +8824,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 85 - architectures: @@ -8432,6 +8843,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8443,6 +8855,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 86 - architectures: @@ -8461,6 +8874,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8472,6 +8886,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 80 - architectures: @@ -8490,6 +8905,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8501,6 +8917,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 78 - architectures: @@ -8519,6 +8936,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8530,6 +8948,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 79 - architectures: @@ -8548,6 +8967,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8559,6 +8979,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 70 - architectures: @@ -8577,6 +8998,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 67 - architectures: @@ -8589,6 +9011,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx950 expression: reduce(TCP_TCC_WRITE_REQ_LATENCY,sum) - name: TCP_TCC_WRITE_REQ_sum @@ -8597,6 +9020,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8609,6 +9033,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 65 - architectures: @@ -8622,6 +9047,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx950 expression: reduce(TCP_TCP_LATENCY,sum) - name: TCP_TCP_TA_DATA_STALL_CYCLES @@ -8676,6 +9102,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8688,6 +9115,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8699,6 +9127,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8711,6 +9140,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8722,6 +9152,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 29 - architectures: @@ -8738,6 +9169,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8749,6 +9181,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 39 - architectures: @@ -8764,6 +9197,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8775,6 +9209,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 38 - architectures: @@ -8790,6 +9225,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8801,6 +9237,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8816,6 +9253,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8828,6 +9266,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 30 - architectures: @@ -8844,6 +9283,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8855,6 +9295,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 32 - architectures: @@ -8871,6 +9312,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 45 - architectures: @@ -8890,6 +9332,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8902,6 +9345,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8913,6 +9357,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 50 - architectures: @@ -8931,6 +9376,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8942,6 +9388,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 47 - architectures: @@ -8960,6 +9407,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -8971,6 +9419,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 49 - architectures: @@ -8989,6 +9438,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9000,6 +9450,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 48 - architectures: @@ -9018,6 +9469,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9029,6 +9481,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 28 - architectures: @@ -9044,6 +9497,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9055,6 +9509,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TCP event: 12 - architectures: @@ -9070,6 +9525,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9396,6 +9852,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TD event: 26 - architectures: @@ -9411,6 +9868,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9422,6 +9880,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TD event: 32 - architectures: @@ -9437,6 +9896,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9448,6 +9908,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TD event: 25 - architectures: @@ -9463,6 +9924,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9500,6 +9962,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TD event: 27 - architectures: @@ -9515,6 +9978,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9526,6 +9990,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 block: TD event: 15 - architectures: @@ -9541,6 +10006,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9552,6 +10018,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 @@ -9564,6 +10031,7 @@ rocprofiler-sdk: definitions: - architectures: - gfx90a + - gfx908 - gfx940 - gfx941 - gfx942 diff --git a/tests/pytest-packages/tests/rocprofv3.py b/tests/pytest-packages/tests/rocprofv3.py index 48f018eb0d..7143cba509 100644 --- a/tests/pytest-packages/tests/rocprofv3.py +++ b/tests/pytest-packages/tests/rocprofv3.py @@ -231,3 +231,190 @@ def test_rocpd_data( assert len(_rpd_data) == len( _js_data ), f"query: {_rpd_query}\n{rpd_category} ({len(_rpd_data)}):\n\t{_rpd_data}\n{js_category} ({len(_js_data)}):\n\t{_js_data}" + + +def _perform_time_sanity_checks(data): + """Helper function to perform time sanity checks on data.""" + columns = data[0].keys() + start_columns = [c for c in columns if "start" in c.lower()] + end_columns = [c for c in columns if "end" in c.lower()] + + if not start_columns or not end_columns: + return None, None + + for record in data: + start_time = record[start_columns[0]] + end_time = record[end_columns[0]] + assert int(start_time) >= 0, f"Time error: Start time ({start_time}) < 0)." + assert int(end_time) >= 0, f"Time error: End time ({end_time}) < 0)." + assert int(end_time) >= int( + start_time + ), f"Time error: End time ({end_time}) < Start time ({start_time})." + + return start_columns[0], end_columns[0] + + +def _perform_csv_json_match(csv_row, json_row, mapping, json_data): + + def get_nested(d, path): + """Helper to get nested dict values using dot notation.""" + keys = path.split(".") + for k in keys: + if isinstance(d, dict): + d = d.get(k) + else: + return None + return d + + for csv_key, json_info in mapping.items(): + if json_info is None: + continue + + csv_value = csv_row[csv_key] + + if csv_key == "Operation": + json_value = json_data["rocprofiler-sdk-tool"]["strings"]["buffer_records"][ + json_row["kind"] + ]["operations"][json_row["operation"]] + + assert str(csv_value) in str( + json_value + ), f"Mismatch for {csv_key}: CSV={csv_value} JSON={json_value}" + continue + + if csv_key == "Function": + json_value = json_data["rocprofiler-sdk-tool"]["strings"]["buffer_records"][ + json_row["kind"] + ]["operations"][json_row["operation"]] + else: + json_path, subkey = json_info + json_value = get_nested(json_row, json_path) + if subkey: + json_value = ( + json_value.get(subkey) if isinstance(json_value, dict) else None + ) + + assert str(csv_value) == str( + json_value + ), f"Mismatch for {csv_key}: CSV={csv_value} JSON={json_value}" + + +def test_csv_data( + csv_data, + json_data, + categories=( + "agent", + "hip", + "hsa", + "marker", + "kernel", + "memory_copy", + "memory_allocation", + "rocdecode_api", + "rocjpeg_api", + "counter_collection", + ), +): + + mapping = { + "hip": "hip_api", + "hsa": "hsa_api", + "marker": "marker_api", + "kernel": "kernel_dispatch", + "memory_copy": "memory_copy", + "memory_allocation": "memory_allocation", + "rocdecode_api": "rocdecode_api", + "rocjpeg_api": "rocjpeg_api", + "counter_collection": "counter_collection", + } + + keys_mapping = { + "kernel": { + "Thread_Id": ("thread_id", None), + "Correlation_Id": ("correlation_id", "internal"), + "Start_Timestamp": ("start_timestamp", None), + "End_Timestamp": ("end_timestamp", None), + "Queue_Id": ("dispatch_info.queue_id.handle", None), + "Kernel_Id": ("dispatch_info.kernel_id", None), + "Dispatch_Id": ("dispatch_info.dispatch_id", None), + "Stream_Id": ("stream_id.handle", None), + "Workgroup_Size_X": ("dispatch_info.workgroup_size.x", None), + "Workgroup_Size_Y": ("dispatch_info.workgroup_size.y", None), + "Workgroup_Size_Z": ("dispatch_info.workgroup_size.z", None), + "Grid_Size_X": ("dispatch_info.grid_size.x", None), + "Grid_Size_Y": ("dispatch_info.grid_size.y", None), + "Grid_Size_Z": ("dispatch_info.grid_size.z", None), + }, + "hip": { + "Function": (), # Special case + "Thread_Id": ("thread_id", None), + "Correlation_Id": ("correlation_id", "internal"), + "Start_Timestamp": ("start_timestamp", None), + "End_Timestamp": ("end_timestamp", None), + }, + "hsa": { + "Function": (), # Special case + "Thread_Id": ("thread_id", None), + "Correlation_Id": ("correlation_id", "internal"), + "Start_Timestamp": ("start_timestamp", None), + "End_Timestamp": ("end_timestamp", None), + }, + "memory_copy": { + "Correlation_Id": ("correlation_id", "internal"), + "Start_Timestamp": ("start_timestamp", None), + "End_Timestamp": ("end_timestamp", None), + }, + "memory_allocation": { + "Operation": (), # Special case + "Correlation_Id": ("correlation_id", "internal"), + "Start_Timestamp": ("start_timestamp", None), + "End_Timestamp": ("end_timestamp", None), + }, + "marker": { + "Thread_Id": ("thread_id", None), + "Correlation_Id": ("correlation_id", "internal"), + "Start_Timestamp": ("start_timestamp", None), + "End_Timestamp": ("end_timestamp", None), + }, + } + + for data in csv_data: + filename, _csv_data = data + + file_category = [category for category in categories if category in filename] + assert len(file_category) > 0, f"{filename} is not a valid csv filename" + + category = file_category[0] + + if category == "counter_collection": + _js_data = json_data["rocprofiler-sdk-tool"]["callback_records"][category] + elif category == "agent": + _js_data = json_data["rocprofiler-sdk-tool"]["agents"] + else: + json_records_key = mapping[category] + _js_data = json_data["rocprofiler-sdk-tool"]["buffer_records"][ + json_records_key + ] + + assert len(_js_data) == len( + _csv_data + ), f"Size mismatch for {category}: JSON size= {len(_js_data)} rows, CSV size= {len(_csv_data)} rows." + + if not _csv_data: + continue # Exit if there is no data to validate + + csv_start_col, csv_end_col = _perform_time_sanity_checks(_csv_data) + json_start_col, json_end_col = _perform_time_sanity_checks(_js_data) + + if None in (csv_start_col, json_start_col, csv_end_col, json_end_col): + continue + + _csv_data_sorted = sorted( + _csv_data, key=lambda x: (int(x[csv_start_col]), int(x[csv_end_col])) + ) + _js_data_sorted = sorted( + _js_data, key=lambda x: (int(x[json_start_col]), int(x[json_end_col])) + ) + + for a, b in zip(_csv_data_sorted, _js_data_sorted): + _perform_csv_json_match(a, b, keys_mapping[category], json_data) diff --git a/tests/rocpd/CMakeLists.txt b/tests/rocpd/CMakeLists.txt index 1d365006cf..8b55f1f145 100644 --- a/tests/rocpd/CMakeLists.txt +++ b/tests/rocpd/CMakeLists.txt @@ -22,7 +22,7 @@ set(rocpd-env # ######################################################################################### -foreach(_SUBPARSER "" "-convert") +foreach(_SUBPARSER "" "-convert" "-query" "-summary") string(REPLACE "-" "" _CMD "${_SUBPARSER}") add_test(NAME rocpd${_SUBPARSER}-help COMMAND ${Python3_EXECUTABLE} -m rocpd ${_CMD} --help) @@ -34,7 +34,7 @@ foreach(_SUBPARSER "" "-convert") "${ROCPROFILER_DEFAULT_FAIL_REGEX}") endforeach() -foreach(_MODULE "csv" "pftrace" "otf2") +foreach(_MODULE "csv" "pftrace" "otf2" "query" "summary") add_test(NAME rocpd-module-${_MODULE}-help COMMAND ${Python3_EXECUTABLE} -m rocpd.${_MODULE} --help) diff --git a/tests/rocprofv3/advanced-thread-trace/CMakeLists.txt b/tests/rocprofv3/advanced-thread-trace/CMakeLists.txt index d48dc04315..7a938b5dde 100644 --- a/tests/rocprofv3/advanced-thread-trace/CMakeLists.txt +++ b/tests/rocprofv3/advanced-thread-trace/CMakeLists.txt @@ -42,8 +42,6 @@ rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY validate.py conftest.p find_package(rocprofiler-sdk REQUIRED) -set(IS_DISABLED ON) - find_library( attdecoder_LIBRARY NAMES rocprof-trace-decoder @@ -58,43 +56,43 @@ endif() find_package_handle_standard_args(attdecoder REQUIRED_VARS attdecoder_LIB_DIR attdecoder_LIBRARY) +set(COMMON_PARAMS_NO_LIB ${PRELOAD_ARGS} --att --output-format json --log-level env -d + ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace/) + +set(IS_DISABLED ON) if(attdecoder_FOUND) set(IS_DISABLED OFF) - set(LIB_PATH_ENV "ROCPROF_ATT_LIBRARY_PATH=${attdecoder_LIB_DIR}") + set(COMMON_PARAMS --att-library-path ${attdecoder_LIB_DIR} ${COMMON_PARAMS_NO_LIB}) endif() # hsa multiqueue dependency test with lib path add_test( - NAME rocprofv3-test-hsa-multiqueue-att-cmd-env-att-lib-path-execute + NAME rocprofv3-test-att-hsa-multiqueue-cmd-env-att-lib-path-execute COMMAND - $ --log-level env --advanced-thread-trace - 1 --att-target-cu 1 --att-shader-engine-mask 0x11 --kernel-include-regex copyD - --att-buffer-size 0x6000000 --att-simd-select 0x3 --att-serialize-all 1 -d - ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace/cmd_input -o out --output-format json - ${PRELOAD_ARGS} --att-library-path ${attdecoder_LIB_DIR} --att-activity 8 -- + $ ${COMMON_PARAMS}/cmd_input + --att-target-cu 1 --att-shader-engine-mask 0x11 --kernel-include-regex copyD + --att-buffer-size 0x6000000 --att-simd-select 0x3 --att-serialize-all 1 -o out -- $) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-cmd-env-att-lib-path-execute + rocprofv3-test-att-hsa-multiqueue-cmd-env-att-lib-path-execute PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED}) # hsa multiqueue dependency test with json input add_test( - NAME rocprofv3-test-hsa-multiqueue-att-json-execute + NAME rocprofv3-test-att-hsa-multiqueue-json-execute COMMAND - $ --log-level env --att-library-path - ${attdecoder_LIB_DIR} -d ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace/json_input -i - ${CMAKE_CURRENT_BINARY_DIR}/att_input.json ${PRELOAD_ARGS} -- + $ ${COMMON_PARAMS}/json_input -i + ${CMAKE_CURRENT_BINARY_DIR}/att_input.json -- $) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-json-execute - PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED} ENVIRONMENT - "${LIB_PATH_ENV}") + rocprofv3-test-att-hsa-multiqueue-json-execute + PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED}) # validate output add_test( - NAME rocprofv3-test-hsa-multiqueue-att-cmd-validate + NAME rocprofv3-test-att-hsa-multiqueue-cmd-validate COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --input ${CMAKE_CURRENT_BINARY_DIR}/hsa_code_object_testapp-trace/cmd_input/out_results.json @@ -102,7 +100,7 @@ add_test( ${CMAKE_CURRENT_BINARY_DIR}/hsa_code_object_testapp-trace/cmd_input) add_test( - NAME rocprofv3-test-hsa-multiqueue-att-json-validate + NAME rocprofv3-test-att-hsa-multiqueue-json-validate COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --input ${CMAKE_CURRENT_BINARY_DIR}/hsa_code_object_testapp-trace/json_input/out_results.json @@ -110,46 +108,46 @@ add_test( ${CMAKE_CURRENT_BINARY_DIR}/hsa_code_object_testapp-trace/json_input) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-cmd-validate + rocprofv3-test-att-hsa-multiqueue-cmd-validate PROPERTIES TIMEOUT 45 LABELS "integration-tests" DEPENDS - "rocprofv3-test-hsa-multiqueue-att-cmd-ld-lib-path-execute" + "rocprofv3-test-att-hsa-multiqueue-cmd-ld-lib-path-execute" FAIL_REGULAR_EXPRESSION "AssertionError" DISABLED ${IS_DISABLED}) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-json-validate + rocprofv3-test-att-hsa-multiqueue-json-validate PROPERTIES TIMEOUT 45 LABELS "integration-tests" DEPENDS - "rocprofv3-test-hsa-multiqueue-att-json-execute" + "rocprofv3-test-att-hsa-multiqueue-json-execute" FAIL_REGULAR_EXPRESSION "AssertionError" DISABLED ${IS_DISABLED}) -function(configure_att_input _FILENAME _OUTDIR) - set(LIBRARY_OUTPUT_DIR ${_OUTDIR}) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/att_input.yml.in +function(configure_att_input _FILENAME) + set(LIB_PATH_LOC "${attdecoder_LIB_DIR}") + configure_file(${CMAKE_CURRENT_SOURCE_DIR}//${_FILENAME}.in ${CMAKE_CURRENT_BINARY_DIR}/${_FILENAME} @ONLY) endfunction() -configure_att_input(att_input.yml "${attdecoder_LIB_DIR}") -configure_att_input(att_input_will_fail.yml "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") +configure_att_input(att_input.yml) +configure_att_input(att_input_will_fail.yml) # test yaml input add_test( NAME rocprofv3-test-att-yaml-input COMMAND - $ -i - ${CMAKE_CURRENT_BINARY_DIR}/att_input.yml --log-level env --echo -- + $ ${COMMON_PARAMS_NO_LIB}/yaml -i + ${CMAKE_CURRENT_BINARY_DIR}/att_input.yml --echo -- $) set_tests_properties( @@ -166,7 +164,7 @@ add_test( set_tests_properties( rocprofv3-test-att-yaml-input-will-fail - PROPERTIES TIMEOUT 45 LABELS "integration-tests" WILL_FAIL ON DISABLED True) + PROPERTIES TIMEOUT 45 LABELS "integration-tests" WILL_FAIL ON DISABLED ${IS_DISABLED}) add_test(NAME rocprofv3-test-att-library-path-cmd-line-will-fail COMMAND $ --att --att-library-path . @@ -174,11 +172,12 @@ add_test(NAME rocprofv3-test-att-library-path-cmd-line-will-fail set_tests_properties( rocprofv3-test-att-library-path-cmd-line-will-fail - PROPERTIES TIMEOUT 45 LABELS "integration-tests" WILL_FAIL ON DISABLED True) + PROPERTIES TIMEOUT 45 LABELS "integration-tests" WILL_FAIL ON DISABLED ${IS_DISABLED}) -add_test(NAME rocprofv3-test-att-library-path-env-var-will-fail - COMMAND $ --att --log-level env --echo - -- $) +add_test( + NAME rocprofv3-test-att-library-path-env-var-will-fail + COMMAND $ ${COMMON_PARAMS_NO_LIB}/envfail + --att-library-path . --echo -- $) set_tests_properties( rocprofv3-test-att-library-path-env-var-will-fail @@ -187,65 +186,70 @@ set_tests_properties( LABELS "integration-tests" ENVIRONMENT - "ROCPROF_ATT_LIBRARY_PATH=." + "ROCPROF_ATT_LIBRARY_PATH=${attdecoder_LIB_DIR}" WILL_FAIL ON DISABLED - True) + ${IS_DISABLED}) + +add_test(NAME rocprofv3-test-att-env-var + COMMAND $ ${COMMON_PARAMS_NO_LIB}/envvar + -- $) + +set_tests_properties( + rocprofv3-test-att-env-var + PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT + "ROCPROF_ATT_LIBRARY_PATH=${attdecoder_LIB_DIR}" DISABLED ${IS_DISABLED}) # # Uses ATT and Counter Collection at the same time # -add_test( - NAME rocprofv3-test-hsa-multiqueue-att-plus-pmc-execute - COMMAND - $ --log-level env --pmc SQ_WAVES - --advanced-thread-trace -d ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace/cmd_input -o - out --output-format json ${PRELOAD_ARGS} -- $) +add_test(NAME rocprofv3-test-att-hsa-multiqueue-plus-pmc-execute + COMMAND $ ${COMMON_PARAMS}/cmd_input + --pmc SQ_WAVES -o out -- $) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-plus-pmc-execute - PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED} ENVIRONMENT - "${LIB_PATH_ENV}") + rocprofv3-test-att-hsa-multiqueue-plus-pmc-execute + PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED}) # Check for conflict PMC + activity -add_test( - NAME rocprofv3-test-hsa-multiqueue-att-activity-pmc-will-fail - COMMAND - $ --log-level env --pmc SQ_WAVES - --advanced-thread-trace -d ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace/cmd_input -o - out --att-activity 8 ${PRELOAD_ARGS} -- $) +add_test(NAME rocprofv3-test-att-hsa-multiqueue-activity-pmc-will-fail + COMMAND $ ${COMMON_PARAMS}/cmd_input + --pmc SQ_WAVES -o out --att-activity 8 -- $) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-activity-pmc-will-fail - PROPERTIES TIMEOUT - 45 - LABELS - "integration-tests" - DISABLED - ${IS_DISABLED} - ENVIRONMENT - "${LIB_PATH_ENV}" - WILL_FAIL - ON) + rocprofv3-test-att-hsa-multiqueue-activity-pmc-will-fail + PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED} WILL_FAIL ON) # Check for conflict Perfcounters + activity add_test( - NAME rocprofv3-test-hsa-multiqueue-att-activity-perf-will-fail - COMMAND - $ --log-level env --att - --att-perfcounter-ctrl 8 -d ${CMAKE_CURRENT_BINARY_DIR}/%argt%-trace/cmd_input -o - out --att-activity 8 ${PRELOAD_ARGS} -- $) + NAME rocprofv3-test-att-hsa-multiqueue-activity-perf-will-fail + COMMAND $ ${COMMON_PARAMS}/cmd_input + --att-perfcounter-ctrl 8 -o out --att-activity 8 -- $) set_tests_properties( - rocprofv3-test-hsa-multiqueue-att-activity-perf-will-fail - PROPERTIES TIMEOUT - 45 - LABELS - "integration-tests" - DISABLED - ${IS_DISABLED} - ENVIRONMENT - "${LIB_PATH_ENV}" - WILL_FAIL - ON) + rocprofv3-test-att-hsa-multiqueue-activity-perf-will-fail + PROPERTIES TIMEOUT 45 LABELS "integration-tests" DISABLED ${IS_DISABLED} WILL_FAIL ON) + +# ATT plus PC sampling + +rocprofiler_sdk_pc_sampling_stochastic_disabled(IS_PC_SAMPLING_STOCHASTIC_DISABLED) + +set(ATT_PLUS_PCS_DISABLE ${IS_DISABLED}) +if(${IS_PC_SAMPLING_STOCHASTIC_DISABLED}) + set(ATT_PLUS_PCS_DISABLE ON) +endif() +if(${ROCPROFILER_DISABLE_UNSTABLE_CTESTS}) + set(ATT_PLUS_PCS_DISABLE ON) +endif() +set(PCS_ARGS --pc-sampling-beta-enabled 1 --pc-sampling-unit cycles + --pc-sampling-interval 16384 --pc-sampling-method stochastic) + +add_test(NAME rocprofv3-test-att-plus-pc-sampling + COMMAND $ ${COMMON_PARAMS}/cmd_input + ${PCS_ARGS} -o out -- $) + +set_tests_properties( + rocprofv3-test-att-plus-pc-sampling + PROPERTIES TIMEOUT 90 LABELS "integration-tests;thread-trace;pc-sampling" DISABLED + ${ATT_PLUS_PCS_DISABLE}) diff --git a/tests/rocprofv3/advanced-thread-trace/att_input.yml.in b/tests/rocprofv3/advanced-thread-trace/att_input.yml.in index d80dd3e1f6..e46bfc1c48 100644 --- a/tests/rocprofv3/advanced-thread-trace/att_input.yml.in +++ b/tests/rocprofv3/advanced-thread-trace/att_input.yml.in @@ -23,9 +23,9 @@ jobs: - advanced_thread_trace: True att_library_path: - - @LIBRARY_OUTPUT_DIR@/att - - @LIBRARY_OUTPUT_DIR@ + - @LIB_PATH_LOC@/att + - @LIB_PATH_LOC@ - advanced_thread_trace: True att_library_path: - - @LIBRARY_OUTPUT_DIR@ + - @LIB_PATH_LOC@ diff --git a/tests/rocprofv3/advanced-thread-trace/att_input_will_fail.yml.in b/tests/rocprofv3/advanced-thread-trace/att_input_will_fail.yml.in new file mode 100644 index 0000000000..b3bc337db3 --- /dev/null +++ b/tests/rocprofv3/advanced-thread-trace/att_input_will_fail.yml.in @@ -0,0 +1,26 @@ +# MIT License +# +# Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +jobs: + - advanced_thread_trace: True + att_library_path: + - @LIB_PATH_LOC@/att diff --git a/tests/rocprofv3/memory-allocation/validate.py b/tests/rocprofv3/memory-allocation/validate.py index 62a7474c79..f3cb03cb64 100755 --- a/tests/rocprofv3/memory-allocation/validate.py +++ b/tests/rocprofv3/memory-allocation/validate.py @@ -59,7 +59,18 @@ def test_memory_allocation(json_data): assert len(bf_op_names) == 5 - allocation_reported_agent_ids = set() + # Op values: + UNKNOWN_OP = 0 + HSA_MEMORY_ALLOCATE_OP = 1 + HSA_AMD_VMEM_HANDLE_CREATE_OP = 2 + HSA_MEMORY_FREE_OP = 3 + HSA_AMD_VMEM_HANDLE_RELEASE = 4 + + valid_agent_ids = set() + for row in data["agents"]: + if "id" in row and "handle" in row.id: + valid_agent_ids.add(row.id.handle) + # check buffering data for node in memory_allocation_data: assert "size" in node @@ -78,7 +89,14 @@ def test_memory_allocation(json_data): assert node.allocation_size >= 0 assert len(node.address) > 0 assert node.thread_id > 0 - assert node.agent_id.handle > 0 + + op_id = node.operation + assert op_id != UNKNOWN_OP + if op_id == HSA_MEMORY_ALLOCATE_OP or op_id == HSA_AMD_VMEM_HANDLE_CREATE_OP: + assert node.agent_id.handle in valid_agent_ids + else: + assert node.agent_id.handle == 0 # free ops record agent id as null + assert node.start_timestamp > 0 assert node.end_timestamp > 0 assert node.start_timestamp < node.end_timestamp @@ -89,8 +107,6 @@ def test_memory_allocation(json_data): in bf_op_names ) - allocation_reported_agent_ids.add(node["agent_id"]["handle"]) - def test_otf2_data(otf2_data, json_data): import rocprofiler_sdk.tests.rocprofv3 as rocprofv3 diff --git a/tests/rocprofv3/pc-sampling/host-trap/transpose-multiple-agents/conftest.py b/tests/rocprofv3/pc-sampling/host-trap/transpose-multiple-agents/conftest.py index 9a19c5768f..d88b9bc4fc 100644 --- a/tests/rocprofv3/pc-sampling/host-trap/transpose-multiple-agents/conftest.py +++ b/tests/rocprofv3/pc-sampling/host-trap/transpose-multiple-agents/conftest.py @@ -84,4 +84,11 @@ def input_kernel_trace_csv(request): def input_agent_info_csv(request): filename = request.config.getoption("--input-agent-info-csv") with open(filename, "r") as inp: - return pd.read_csv(inp) + return pd.read_csv( + inp, + na_filter=False, # parse empty fields as "" + keep_default_na=False, # parse empty fields as "" + dtype={ + "Name": str, + }, + ) diff --git a/tests/rocprofv3/pc-sampling/stochastic/transpose-multiple-agents/conftest.py b/tests/rocprofv3/pc-sampling/stochastic/transpose-multiple-agents/conftest.py index 7472a41c13..56b39cca61 100644 --- a/tests/rocprofv3/pc-sampling/stochastic/transpose-multiple-agents/conftest.py +++ b/tests/rocprofv3/pc-sampling/stochastic/transpose-multiple-agents/conftest.py @@ -93,7 +93,14 @@ def input_kernel_trace_csv(request): def input_agent_info_csv(request): filename = request.config.getoption("--input-agent-info-csv") with open(filename, "r") as inp: - return pd.read_csv(inp) + return pd.read_csv( + inp, + na_filter=False, # parse empty fields as "" + keep_default_na=False, # parse empty fields as "" + dtype={ + "Name": str, + }, + ) @pytest.fixture diff --git a/tests/rocprofv3/rocpd/CMakeLists.txt b/tests/rocprofv3/rocpd/CMakeLists.txt index 1bafc1730c..2c434fcccb 100644 --- a/tests/rocprofv3/rocpd/CMakeLists.txt +++ b/tests/rocprofv3/rocpd/CMakeLists.txt @@ -222,6 +222,58 @@ set_tests_properties( FIXTURES_REQUIRED rocprofv3-test-rocpd) +######################################################################################### +# +# Summary generate +# +######################################################################################### +add_test( + NAME rocprofv3-test-rocpd-summary-generation + COMMAND + ${Python3_EXECUTABLE} -m rocpd summary --domain-summary --summary-by-rank -f csv + -d ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/summary -i + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data/out_results.db) + +set_tests_properties( + rocprofv3-test-rocpd-summary-generation + PROPERTIES TIMEOUT + 45 + LABELS + "integration-tests;rocpd" + ENVIRONMENT + "${rocprofv3-rocpd-env}" + DEPENDS + "rocprofv3-test-rocpd-execute" + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + FIXTURES_SETUP + rocprofv3-test-rocpd-generation + FIXTURES_REQUIRED + rocprofv3-test-rocpd) + +add_test( + NAME rocprofv3-test-rocpd-summary-generation-multiproc + COMMAND + ${Python3_EXECUTABLE} -m rocpd summary --domain-summary --summary-by-rank -f csv + -d ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-test/summary -o out_mp -i + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data-multiproc/out_mp_0_results.db + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data-multiproc/out_mp_1_results.db) + +set_tests_properties( + rocprofv3-test-rocpd-summary-generation-multiproc + PROPERTIES TIMEOUT + 120 + LABELS + "integration-tests;rocpd" + ENVIRONMENT + "${rocprofv3-rocpd-env}" + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + DISABLED + "${MULTIPROC_IS_DISABLED}" + FIXTURES_REQUIRED + rocprofv3-test-rocpd-multiproc) + ######################################################################################### # # Validation @@ -238,7 +290,13 @@ add_test( ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data/out_results.json --otf2-input ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.otf2 --pftrace-input - ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.pftrace) + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.pftrace --csv-input + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_agent_info.csv + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_kernel_trace.csv + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_hip_api_trace.csv + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_marker_api_trace.csv + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_memory_allocation_trace.csv + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_counter_collection.csv) set_tests_properties( rocprofv3-test-rocpd-validation diff --git a/tests/rocprofv3/rocpd/conftest.py b/tests/rocprofv3/rocpd/conftest.py index 73faaed82e..c35d196792 100644 --- a/tests/rocprofv3/rocpd/conftest.py +++ b/tests/rocprofv3/rocpd/conftest.py @@ -22,6 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +import csv import pandas as pd import pytest import json @@ -50,6 +51,12 @@ def pytest_addoption(parser): action="store", help="Path to OTF2 trace file.", ) + parser.addoption( + "--csv-input", + action="store", + nargs="+", + help="Paths to CSV files.", + ) parser.addoption( "--summary-input", action="store", @@ -83,6 +90,14 @@ def otf2_data(request): return OTF2Reader(filename).read()[0] +@pytest.fixture +def csv_data(request): + filenames = request.config.getoption("--csv-input") + return [ + (filename, list(csv.DictReader(open(filename, "r")))) for filename in filenames + ] + + @pytest.fixture def summary_data(request): filename = request.config.getoption("--summary-input") diff --git a/tests/rocprofv3/rocpd/validate.py b/tests/rocprofv3/rocpd/validate.py index a47c56d538..4555c55374 100644 --- a/tests/rocprofv3/rocpd/validate.py +++ b/tests/rocprofv3/rocpd/validate.py @@ -46,6 +46,25 @@ def test_otf2_data(otf2_data, json_data): ) +def test_csv_data(csv_data, json_data): + import rocprofiler_sdk.tests.rocprofv3 as rocprofv3 + + rocprofv3.test_csv_data( + csv_data, + json_data, + ( + "agent", + "hip", + "hsa", + "marker", + "kernel", + "memory_copy", + "memory_allocation", + "counter_collection", + ), + ) + + if __name__ == "__main__": exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) sys.exit(exit_code) diff --git a/tests/thread-trace/CMakeLists.txt b/tests/thread-trace/CMakeLists.txt index 6bf784e5ba..4c43dc9f67 100644 --- a/tests/thread-trace/CMakeLists.txt +++ b/tests/thread-trace/CMakeLists.txt @@ -97,3 +97,20 @@ set_tests_properties( PROPERTIES TIMEOUT 10 LABELS "integration-tests" ENVIRONMENT "${ROCPROFILER_MEMCHECK_PRELOAD_ENV}" FAIL_REGULAR_EXPRESSION "${ROCPROFILER_DEFAULT_FAIL_REGEX}") + +# Test large buffer sizes. 5120 == 5GB +add_test(NAME thread-trace-api-large-buffer-test + COMMAND $) + +set_tests_properties( + thread-trace-api-large-buffer-test + PROPERTIES TIMEOUT + 10 + LABELS + "integration-tests" + ENVIRONMENT + "${PRELOAD_ENV};ATT_BUFFER_SIZE_MB=5120" + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + DISABLED + ${ROCPROFILER_DISABLE_UNSTABLE_CTESTS}) diff --git a/tests/thread-trace/agent.cpp b/tests/thread-trace/agent.cpp index 4d57af5945..ccc2004995 100644 --- a/tests/thread-trace/agent.cpp +++ b/tests/thread-trace/agent.cpp @@ -29,17 +29,6 @@ #include -#define C_API_BEGIN \ - try \ - { -#define C_API_END \ - } \ - catch(std::exception & e) \ - { \ - std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << ' ' << e.what() << std::endl; \ - } \ - catch(...) { std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << std::endl; } - namespace ATTTest { namespace Agent @@ -58,19 +47,16 @@ dispatch_tracing_callback(rocprofiler_callback_tracing_record_t record, assert(record.payload); auto* rdata = static_cast(record.payload); - int dispatch_id = (int) rdata->dispatch_info.dispatch_id; - - auto get_int_var = [](const char* var_name, int def) { - const char* var = getenv(var_name); - if(var) return atoi(var); - return def; - }; - static int begin_dispatch = get_int_var("ROCPROFILER_THREAD_TRACE_BEGIN", 1); - static int end_dispatch = get_int_var("ROCPROFILER_THREAD_TRACE_END", 4); + auto dispatch_id = rdata->dispatch_info.dispatch_id; + + // Choose two dispatches to begin(6) and end(10) the trace + constexpr uint64_t begin_dispatch = 6; + constexpr uint64_t end_dispatch = 10; static std::atomic isprofiling{false}; + static std::atomic stop_profiling{false}; - static std::mutex mut; - static std::set captured_ids; + static std::mutex mut{}; + static std::set captured_ids{}; if(record.phase == ROCPROFILER_CALLBACK_PHASE_ENTER) { @@ -84,6 +70,7 @@ dispatch_tracing_callback(rocprofiler_callback_tracing_record_t record, std::unique_lock lk(mut); captured_ids.insert(dispatch_id); } + if(dispatch_id > end_dispatch) stop_profiling.store(true); return; } @@ -93,7 +80,7 @@ dispatch_tracing_callback(rocprofiler_callback_tracing_record_t record, std::unique_lock lk(mut); captured_ids.erase(dispatch_id); - if(!captured_ids.empty()) return; + if(!captured_ids.empty() || stop_profiling == false) return; bool _exp = true; if(!isprofiling.compare_exchange_strong(_exp, false, std::memory_order_relaxed)) return; @@ -115,11 +102,15 @@ query_available_agents(rocprofiler_agent_version_t /* version */, const auto* agent = static_cast(agents[idx]); if(agent->type != ROCPROFILER_AGENT_TYPE_GPU) continue; + // Check if we are testing for large buffers + static const char* var = getenv("ATT_BUFFER_SIZE_MB"); + static uint64_t buffer_size_mb = (var ? atoi(var) : 96) * 1024ul * 1024ul; + std::vector parameters; parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_TARGET_CU, 1}); parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SIMD_SELECT, 0xF}); - parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_BUFFER_SIZE, 0x6000000}); - parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SHADER_ENGINE_MASK, 0x11}); + parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_BUFFER_SIZE, buffer_size_mb}); + parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SHADER_ENGINE_MASK, 0x1}); parameters.push_back({ROCPROFILER_THREAD_TRACE_PARAMETER_SERIALIZE_ALL, 0}); ROCPROFILER_CALL( @@ -135,9 +126,10 @@ query_available_agents(rocprofiler_agent_version_t /* version */, } int -tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data) +tool_init(rocprofiler_client_finalize_t /* fini_func */, void* /* tool_data */) { - (void) fini_func; + Callbacks::init(); + ROCPROFILER_CALL(rocprofiler_create_context(&tracing_ctx), "context creation"); ROCPROFILER_CALL(rocprofiler_create_context(&agent_ctx), "context creation"); @@ -147,7 +139,7 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data) nullptr, 0, Callbacks::tool_codeobj_tracing_callback, - tool_data), + nullptr), "code object tracing service configure"); ROCPROFILER_CALL( @@ -156,13 +148,13 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data) nullptr, 0, dispatch_tracing_callback, - tool_data), + nullptr), "dispatch tracing service configure"); ROCPROFILER_CALL(rocprofiler_query_available_agents(ROCPROFILER_AGENT_INFO_VERSION_0, &query_available_agents, sizeof(rocprofiler_agent_t), - tool_data), + nullptr), "Failed to find GPU agents"); int valid_ctx = 0; @@ -177,13 +169,6 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data) return 0; } -void -tool_fini(void* tool_data) -{ - Callbacks::finalize_json(tool_data); - delete static_cast(tool_data); -} - } // namespace Agent } // namespace ATTTest @@ -206,8 +191,8 @@ rocprofiler_configure(uint32_t /* version */, static auto cfg = rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t), &ATTTest::Agent::tool_init, - &ATTTest::Agent::tool_fini, - new Callbacks::ToolData{"att_agent_test/"}}; + &Callbacks::finalize, + nullptr}; // return pointer to configure data return &cfg; diff --git a/tests/thread-trace/multi_dispatch.cpp b/tests/thread-trace/multi_dispatch.cpp index bf47704b5e..234024e9d7 100644 --- a/tests/thread-trace/multi_dispatch.cpp +++ b/tests/thread-trace/multi_dispatch.cpp @@ -48,15 +48,17 @@ dispatch_callback(rocprofiler_agent_id_t /* agent */, static std::atomic count{0}; if(count.fetch_add(1) > NUM_KERNELS) return ROCPROFILER_THREAD_TRACE_CONTROL_NONE; - assert(userdata && "Dispatch callback passed null!"); + assert(dispatch_userdata && "Dispatch callback passed null!"); dispatch_userdata->ptr = userdata; return ROCPROFILER_THREAD_TRACE_CONTROL_START_AND_STOP; } int -tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) +tool_init(rocprofiler_client_finalize_t /* fini_func */, void* /* tool_data */) { + Callbacks::init(); + static rocprofiler_context_id_t client_ctx = {0}; ROCPROFILER_CALL(rocprofiler_create_context(&client_ctx), "context creation"); @@ -67,7 +69,7 @@ tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) nullptr, 0, Callbacks::tool_codeobj_tracing_callback, - tool_data), + nullptr), "code object tracing service configure"); std::vector params{}; @@ -100,7 +102,7 @@ tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) params.size(), dispatch_callback, Callbacks::shader_data_callback, - tool_data), + nullptr), "thread trace service configure"); } @@ -121,13 +123,6 @@ tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) return 0; } -void -tool_fini(void* tool_data) -{ - Callbacks::finalize_json(tool_data); - delete static_cast(tool_data); -} - } // namespace Multi } // namespace ATTTest @@ -147,11 +142,11 @@ rocprofiler_configure(uint32_t /* version */, ATTTest::Multi::client_id = id; // create configure data - static auto cfg = rocprofiler_tool_configure_result_t{ - sizeof(rocprofiler_tool_configure_result_t), - &ATTTest::Multi::tool_init, - &ATTTest::Multi::tool_fini, - reinterpret_cast(new Callbacks::ToolData{"att_multi_test/"})}; + static auto cfg = + rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t), + &ATTTest::Multi::tool_init, + &Callbacks::finalize, + nullptr}; // return pointer to configure data return &cfg; diff --git a/tests/thread-trace/single_dispatch.cpp b/tests/thread-trace/single_dispatch.cpp index 4feb8b44d1..f09e9c500d 100644 --- a/tests/thread-trace/single_dispatch.cpp +++ b/tests/thread-trace/single_dispatch.cpp @@ -41,35 +41,20 @@ dispatch_callback(rocprofiler_agent_id_t /* agent */, rocprofiler_async_correlation_id_t /* correlation_id */, rocprofiler_kernel_id_t kernel_id, rocprofiler_dispatch_id_t /* dispatch_id */, - void* userdata, - rocprofiler_user_data_t* dispatch_userdata) + void* /* userdata */, + rocprofiler_user_data_t* /* dispatch_userdata */) { - C_API_BEGIN - assert(userdata && "Dispatch callback passed null!"); - auto& tool = *reinterpret_cast(userdata); - dispatch_userdata->ptr = userdata; + static rocprofiler_kernel_id_t target_kernel_id = kernel_id; + if(target_kernel_id == kernel_id) return ROCPROFILER_THREAD_TRACE_CONTROL_START_AND_STOP; - static std::string_view desired_func_name = "branching_kernel"; - - try - { - auto& kernel_name = tool.kernel_id_to_kernel_name.at(kernel_id); - if(kernel_name.find(desired_func_name) == std::string::npos) - return ROCPROFILER_THREAD_TRACE_CONTROL_NONE; - - return ROCPROFILER_THREAD_TRACE_CONTROL_START_AND_STOP; - } catch(...) - { - std::cerr << "Could not find kernel id: " << kernel_id << std::endl; - } - - C_API_END return ROCPROFILER_THREAD_TRACE_CONTROL_NONE; } int -tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) +tool_init(rocprofiler_client_finalize_t /* fini_func */, void* /* tool_data */) { + Callbacks::init(); + static rocprofiler_context_id_t client_ctx = {0}; ROCPROFILER_CALL(rocprofiler_create_context(&client_ctx), "context creation"); @@ -80,7 +65,7 @@ tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) nullptr, 0, Callbacks::tool_codeobj_tracing_callback, - tool_data), + nullptr), "code object tracing service configure"); std::vector agents{}; @@ -110,7 +95,7 @@ tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) 0, dispatch_callback, Callbacks::shader_data_callback, - tool_data), + nullptr), "thread trace service configure"); } @@ -131,13 +116,6 @@ tool_init(rocprofiler_client_finalize_t /* fini_func */, void* tool_data) return 0; } -void -tool_fini(void* tool_data) -{ - Callbacks::finalize_json(tool_data); - delete static_cast(tool_data); -} - } // namespace Single } // namespace ATTTest @@ -157,11 +135,11 @@ rocprofiler_configure(uint32_t /* version */, ATTTest::Single::client_id = id; // create configure data - static auto cfg = rocprofiler_tool_configure_result_t{ - sizeof(rocprofiler_tool_configure_result_t), - &ATTTest::Single::tool_init, - &ATTTest::Single::tool_fini, - reinterpret_cast(new Callbacks::ToolData{"att_single_test/"})}; + static auto cfg = + rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t), + &ATTTest::Single::tool_init, + &Callbacks::finalize, + nullptr}; // return pointer to configure data return &cfg; diff --git a/tests/thread-trace/trace_callbacks.cpp b/tests/thread-trace/trace_callbacks.cpp index 3224c57b4c..d40377badb 100644 --- a/tests/thread-trace/trace_callbacks.cpp +++ b/tests/thread-trace/trace_callbacks.cpp @@ -26,11 +26,6 @@ #endif #include "trace_callbacks.hpp" -#include - -#ifdef ENABLE_ATT_FILES -# include -#endif #include #include @@ -38,135 +33,78 @@ namespace Callbacks { -using code_obj_load_data_t = rocprofiler_callback_tracing_code_object_load_data_t; -using kernel_symbol_data_t = rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t; +rocprofiler_thread_trace_decoder_id_t decoder{}; +std::atomic latency{0}; void tool_codeobj_tracing_callback(rocprofiler_callback_tracing_record_t record, rocprofiler_user_data_t* /* user_data */, - void* userdata) + void* /* userdata */) { - C_API_BEGIN if(record.kind != ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT) return; - if(record.phase != ROCPROFILER_CALLBACK_PHASE_LOAD) return; - - assert(userdata && "Dispatch callback passed null!"); - auto& tool = *reinterpret_cast(userdata); - - if(record.operation == ROCPROFILER_CODE_OBJECT_DEVICE_KERNEL_SYMBOL_REGISTER) - { - auto* data = static_cast(record.payload); - tool.kernel_id_to_kernel_name.emplace(data->kernel_id, data->kernel_name); - } - if(record.operation != ROCPROFILER_CODE_OBJECT_LOAD) return; - auto* data = static_cast(record.payload); + auto* data = static_cast(record.payload); + if(data->storage_type == ROCPROFILER_CODE_OBJECT_STORAGE_TYPE_FILE) return; - static std::atomic filecnt{0}; - std::string name = "codeobj_" + std::to_string(filecnt.fetch_add(1)) + ".out"; - -#ifdef ENABLE_ATT_FILES - if(std::string_view(data->uri).find("file:///") == 0) + if(record.phase != ROCPROFILER_CALLBACK_PHASE_LOAD) { - rocprofiler::sdk::codeobj::disassembly::CodeObjectBinary binary(data->uri); - - std::ofstream file(tool.out_dir + name, std::ios::binary); - assert(file.is_open() && "Could not open codeobj file for writing"); - file.write((char*) binary.buffer.data(), binary.buffer.size()); + DECODER_CALL( + rocprofiler_thread_trace_decoder_codeobj_unload(decoder, data->code_object_id)); + return; } - else - { - std::ofstream file(tool.out_dir + name, std::ios::binary); - file.write((char*) data->memory_base, data->memory_size); - } -#endif - - auto _lk = std::unique_lock{tool.mut}; - tool.codeobjs.push_back( - {data->load_delta, data->load_size, data->code_object_id, name, data->uri}); - C_API_END + DECODER_CALL(rocprofiler_thread_trace_decoder_codeobj_load( + decoder, + data->code_object_id, + data->load_delta, + data->load_size, + reinterpret_cast(data->memory_base), + data->memory_size)); } +typedef void (*rocprofiler_thread_trace_decoder_callback_t)( + rocprofiler_thread_trace_decoder_record_type_t record_type_id, + void* trace_events, + uint64_t trace_size, + void* userdata); + void -shader_data_callback(rocprofiler_agent_id_t agent, - int64_t se_id, - void* se_data, - size_t data_size, - rocprofiler_user_data_t userdata) +shader_data_callback(rocprofiler_agent_id_t /* agent */, + int64_t /* se_id */, + void* se_data, + size_t data_size, + rocprofiler_user_data_t /* userdata */) { - C_API_BEGIN - - assert(userdata.ptr && "Dispatch callback passed null!"); - auto& tool = *reinterpret_cast(userdata.ptr); - - std::string name = "agent_" + std::to_string(agent.handle) + "_shader_engine_" + - std::to_string(se_id) + "_" + std::to_string(agent.handle) + ".att"; - -#ifdef ENABLE_ATT_FILES - { - std::ofstream file(tool.out_dir + name, std::ios::binary); - assert(file.is_open() && "Could not open ATT file for writing"); - file.write((char*) se_data, data_size); - } -#endif - - assert(se_data); - assert(data_size); - - auto _lk = std::unique_lock{tool.mut}; - tool.att_files.push_back(name); - - C_API_END + auto parse = [](rocprofiler_thread_trace_decoder_record_type_t record_type_id, + void* trace_events, + uint64_t trace_size, + void* /* userdata */) { + if(record_type_id != ROCPROFILER_THREAD_TRACE_DECODER_RECORD_WAVE) return; + + for(size_t w = 0; w < trace_size; w++) + { + auto* wave = static_cast(trace_events); + for(size_t i = 0; i < wave->instructions_size; i++) + latency += wave->instructions_array[i].duration; + } + }; + DECODER_CALL(rocprofiler_trace_decode(decoder, parse, se_data, data_size, nullptr)); } void -finalize_json(void* userdata) +init() { - assert(userdata && "Dispatch callback passed null!"); - - auto& tool = *reinterpret_cast(userdata); - auto _lk = std::unique_lock{tool.mut}; - assert(!tool.att_files.empty()); - -#ifdef ENABLE_ATT_FILES - nlohmann::json att_json; - for(auto& file : tool.att_files) - att_json.push_back(file); - - nlohmann::json codeobj_json; - nlohmann::json snapshot_json; - for(auto& file : tool.codeobjs) - { - nlohmann::json codeobj; - codeobj["code_object_id"] = file.id; - codeobj["load_delta"] = file.addr; - codeobj["load_size"] = file.size; - codeobj["uri"] = file.uri; - codeobj["filename"] = file.filename; - codeobj_json.push_back(codeobj); - - nlohmann::json pair_json; - pair_json["key"] = file.id; - pair_json["value"] = file.filename; - snapshot_json.push_back(pair_json); - } - nlohmann::json tool_json; - tool_json["strings"]["att_files"] = att_json; - tool_json["code_objects"] = codeobj_json; - tool_json["strings"]["code_object_snapshot_files"] = snapshot_json; - - nlohmann::json array; - array.push_back(tool_json); + // const char* decoder_lib = std::getenv("ROCPROF_TRACE_DECODER_PATH"); + DECODER_CALL(rocprofiler_thread_trace_decoder_create(&decoder, "/opt/rocm/lib")); +} - nlohmann::json sdk_json; - sdk_json["rocprofiler-sdk-tool"] = array; +void +finalize(void* /* tool_data */) +{ + rocprofiler_thread_trace_decoder_destroy(decoder); - std::ofstream json_file(tool.out_dir + (std::to_string(getpid()) + "_results.json")); - assert(json_file.is_open() && "Could not open json file for writing!"); - json_file << sdk_json; -#endif + if(latency.load() == 0) std::cerr << "Error: No latency was assigned to the trace!"; } } // namespace Callbacks diff --git a/tests/thread-trace/trace_callbacks.hpp b/tests/thread-trace/trace_callbacks.hpp index 414d6c984a..32afefe053 100644 --- a/tests/thread-trace/trace_callbacks.hpp +++ b/tests/thread-trace/trace_callbacks.hpp @@ -30,7 +30,6 @@ #include #include -#include #include #include #include @@ -40,60 +39,21 @@ #include #include -#define ROCPROFILER_VAR_NAME_COMBINE(X, Y) X##Y -#define ROCPROFILER_VARIABLE(X, Y) ROCPROFILER_VAR_NAME_COMBINE(X, Y) - -#define C_API_BEGIN \ - try \ - { -#define C_API_END \ - } \ - catch(std::exception & e) \ +#define ROCPROFILER_CALL(result, msg) \ + if((result) != ROCPROFILER_STATUS_SUCCESS) \ { \ - std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << ' ' << e.what() << std::endl; \ - } \ - catch(...) { std::cerr << "Error in " << __FILE__ << ':' << __LINE__ << std::endl; } + std::cerr << "Error: " << msg << std::endl; \ + abort(); \ + } -#define ROCPROFILER_CALL(result, msg) \ +#define DECODER_CALL(result) \ + if((result) != ROCPROFILER_STATUS_SUCCESS) \ { \ - rocprofiler_status_t CHECKSTATUS = result; \ - if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \ - { \ - std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \ - std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \ - << " failed with error code " << CHECKSTATUS << ": " << status_msg \ - << std::endl; \ - std::stringstream errmsg{}; \ - errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \ - << status_msg << ")"; \ - throw std::runtime_error(errmsg.str()); \ - } \ + std::cerr << "Error: Generic decoder error" << std::endl; \ } namespace Callbacks { -struct CodeobjInfo -{ - int64_t addr = 0; - size_t size = 0; - size_t id = 0; - std::string filename{}; - std::string uri{}; -}; - -struct ToolData -{ - ToolData(const char* out) - : out_dir(out){}; - - std::string out_dir{}; - std::mutex mut{}; - std::vector codeobjs{}; - std::vector att_files{}; - - std::unordered_map kernel_id_to_kernel_name = {}; -}; - void tool_codeobj_tracing_callback(rocprofiler_callback_tracing_record_t record, rocprofiler_user_data_t*, @@ -107,6 +67,9 @@ shader_data_callback(rocprofiler_agent_id_t agent, rocprofiler_user_data_t userdata); void -finalize_json(void* userdata); +init(); + +void +finalize(void* /* tool_data */); } // namespace Callbacks