From 1e9d8abbf6894f185a4b90faa0dc5c14ef496f20 Mon Sep 17 00:00:00 2001 From: "systems-assistant[bot]" <221163467+systems-assistant[bot]@users.noreply.github.com> Date: Tue, 23 Sep 2025 09:55:30 +0200 Subject: [PATCH] [rocpd] Convert to perfetto does not display scratch_memory correctly - SWDEV-542550 (#168) Add scratch memory to pftrace generated with rocpd ---- Co-authored-by: Marko Crnobrnja Co-authored-by: Aleksei Tumakaev Co-authored-by: Jonathan R. Madsen --- .../source/lib/output/generateRocpd.cpp | 54 +++++- .../source/lib/python/rocpd/libpyrocpd.cpp | 4 + .../lib/python/rocpd/source/perfetto.cpp | 163 +++++++++++++++--- .../lib/python/rocpd/source/perfetto.hpp | 1 + .../source/lib/rocprofiler-sdk/agent.hpp | 6 + .../pytest_utils/perfetto_reader.py | 101 ++++++++--- .../tests/rocprofv3/CMakeLists.txt | 1 + .../rocprofv3/rocpd-scratch/CMakeLists.txt | 107 ++++++++++++ .../tests/rocprofv3/rocpd-scratch/conftest.py | 65 +++++++ .../tests/rocprofv3/rocpd-scratch/pytest.ini | 5 + .../tests/rocprofv3/rocpd-scratch/validate.py | 41 +++++ 11 files changed, 494 insertions(+), 54 deletions(-) create mode 100644 projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/CMakeLists.txt create mode 100644 projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/conftest.py create mode 100644 projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/pytest.ini create mode 100644 projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/validate.py diff --git a/projects/rocprofiler-sdk/source/lib/output/generateRocpd.cpp b/projects/rocprofiler-sdk/source/lib/output/generateRocpd.cpp index e3106c57dc..f50cebefce 100644 --- a/projects/rocprofiler-sdk/source/lib/output/generateRocpd.cpp +++ b/projects/rocprofiler-sdk/source/lib/output/generateRocpd.cpp @@ -41,6 +41,7 @@ #include "lib/common/utility.hpp" #include "lib/output/sql/common.hpp" #include "lib/output/sql/deferred_transaction.hpp" +#include "lib/rocprofiler-sdk/agent.hpp" #include #include @@ -1212,6 +1213,9 @@ write_rocpd( auto insert_memory_alloc_data = [&conn, &tool_metadata, &string_entries, node_id, this_pid](const auto& _gen) { + auto address_to_agent_and_size = + std::unordered_map{}; + for(auto pitr : _gen) { auto _deferred = sql::deferred_transaction{conn}; @@ -1231,17 +1235,55 @@ write_rocpd( ROCP_FATAL_IF(_level != "REAL" && _level != "VIRTUAL" && _level != "SCRATCH") << "erroneous db level: " << _level; - auto _node_id = std::optional{}; - if(_type == "ALLOC") - { - _node_id = tool_metadata.get_agent(itr.agent_id)->node_id; - } - auto _stream_id = get_stream_id(extract_stream_field(itr)); auto _queue_id = get_queue_id(extract_queue_field(itr)); auto _address = extract_address_field(itr); auto _allocation_size = extract_allocation_size_field(itr); + // memory allocation counter track + struct free_memory_information + { + rocprofiler_timestamp_t start_timestamp = 0; + rocprofiler_timestamp_t end_timestamp = 0; + rocprofiler_address_t address = {.handle = 0}; + }; + + auto _node_id = std::optional{}; + if(_type == "ALLOC") + { + _node_id = tool_metadata.get_agent(itr.agent_id)->node_id; + address_to_agent_and_size.emplace( + rocprofiler_address_t{.handle = _address.handle}, + rocprofiler::agent::index_and_size{_node_id.value(), _allocation_size}); + } + else if(_type == "FREE") + { + if(address_to_agent_and_size.count(_address) == 0) + { + if(_address.handle == 0) + { + // Freeing null pointers is expected behavior and is occurs in HSA + // functions like hipStreamDestroy + ROCP_INFO << "null pointer freed due to HSA operation"; + } + else + { + // Following should not occur + ROCP_INFO << "Unpaired free operation occurred"; + } + } + else + { + auto [agent_abs_index, size] = address_to_agent_and_size[_address]; + _node_id = agent_abs_index; + _allocation_size = 0; + } + } + else + { + ROCP_CI_LOG(WARNING) << "unhandled memory allocation type " << _type; + } + auto evt_id = create_event( conn, { diff --git a/projects/rocprofiler-sdk/source/lib/python/rocpd/libpyrocpd.cpp b/projects/rocprofiler-sdk/source/lib/python/rocpd/libpyrocpd.cpp index 02e8e60691..7031fe8eaf 100644 --- a/projects/rocprofiler-sdk/source/lib/python/rocpd/libpyrocpd.cpp +++ b/projects/rocprofiler-sdk/source/lib/python/rocpd/libpyrocpd.cpp @@ -460,6 +460,9 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd) auto memory_copies = rocpd::sql_generator{ conn, select_guid_nid_pid("memory_copies")}; + auto scratch_memory = rocpd::sql_generator{ + conn, select_guid_nid_pid("scratch_memory")}; + auto counters = rocpd::sql_generator{ conn, select_guid_nid_pid("counters_collection")}; @@ -495,6 +498,7 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd) samples, kernels, memory_copies, + scratch_memory, memory_allocations, counters); } diff --git a/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp b/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp index 95d08b7719..8790296301 100644 --- a/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp +++ b/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp @@ -33,6 +33,7 @@ #include "lib/output/sql/common.hpp" #include "lib/output/stream_info.hpp" #include "lib/rocprofiler-sdk-tool/config.hpp" +#include "lib/rocprofiler-sdk/agent.hpp" #include @@ -174,6 +175,7 @@ write_perfetto( const tool::generator& sample_gen, const tool::generator& kernel_dispatch_gen, const tool::generator& memory_copy_gen, + const tool::generator& scratch_memory_gen, const tool::generator& memory_allocation_gen, const tool::generator& counter_collection_gen) { @@ -687,19 +689,15 @@ write_perfetto( rocprofiler_timestamp_t start_timestamp = 0; rocprofiler_timestamp_t end_timestamp = 0; rocprofiler_address_t address = {.handle = 0}; + rocprofiler_queue_id_t queue = {.handle = 0}; }; struct memory_information { - uint64_t alloc_size = {0}; - rocprofiler_address_t address = {.handle = 0}; - bool is_alloc_op = {false}; - }; - - struct agent_and_size - { - uint64_t agent_abs_index = {}; - uint64_t size = {0}; + uint64_t alloc_size = {0}; + rocprofiler_address_t address = {.handle = 0}; + rocprofiler_queue_id_t queue = {.handle = 0}; + bool is_alloc_op = {false}; }; auto mem_alloc_endpoints = @@ -707,7 +705,9 @@ write_perfetto( auto mem_alloc_extremes = std::pair{ std::numeric_limits::max(), std::numeric_limits::min()}; auto address_to_agent_and_size = - std::unordered_map{}; + std::unordered_map{}; + auto queue_to_agent_and_size = + std::unordered_map{}; auto free_mem_info = std::vector{}; // Load memory allocation endpoints @@ -719,24 +719,47 @@ write_perfetto( { LOG_IF(FATAL, itr.agent_name.empty()) << "Missing agent id for memory allocation trace"; - mem_alloc_endpoints[itr.agent_abs_index].emplace( - itr.start, - memory_information{ - itr.size, rocprofiler_address_t{.handle = itr.address}, true}); - mem_alloc_endpoints[itr.agent_abs_index].emplace( - itr.end, - memory_information{ - itr.size, rocprofiler_address_t{.handle = itr.address}, true}); - address_to_agent_and_size.emplace( - rocprofiler_address_t{.handle = itr.address}, - agent_and_size{itr.agent_abs_index, itr.size}); + + if(itr.level == "REAL") + { + mem_alloc_endpoints[itr.agent_abs_index].emplace( + itr.start, + memory_information{itr.size, + rocprofiler_address_t{.handle = itr.address}, + rocprofiler_queue_id_t{.handle = itr.queue_id}, + true}); + mem_alloc_endpoints[itr.agent_abs_index].emplace( + itr.end, + memory_information{itr.size, + rocprofiler_address_t{.handle = itr.address}, + rocprofiler_queue_id_t{.handle = itr.queue_id}, + true}); + + address_to_agent_and_size.emplace( + rocprofiler_address_t{.handle = itr.address}, + rocprofiler::agent::index_and_size{itr.agent_abs_index, itr.size}); + } + // Scratch memory operations are indexed by queue id as agent + // id is not available + else if(itr.level == "SCRATCH") + { + queue_to_agent_and_size.emplace( + rocprofiler_queue_id_t{.handle = itr.queue_id}, + rocprofiler::agent::index_and_size{itr.agent_abs_index, itr.size}); + } } else if(itr.type == "FREE") { // Store free memory operations in seperate vector to pair with agent // and allocation size in following loop - free_mem_info.push_back(free_memory_information{ - itr.start, itr.end, rocprofiler_address_t{.handle = itr.address}}); + if(itr.level == "REAL") + { + free_mem_info.push_back(free_memory_information{ + itr.start, + itr.end, + rocprofiler_address_t{.handle = itr.address}, + rocprofiler_queue_id_t{.handle = itr.queue_id}}); + } } else { @@ -765,9 +788,9 @@ write_perfetto( } auto [agent_abs_index, size] = address_to_agent_and_size[itr.address]; mem_alloc_endpoints[agent_abs_index].emplace( - itr.start_timestamp, memory_information{size, itr.address, false}); + itr.start_timestamp, memory_information{size, itr.address, itr.queue, false}); mem_alloc_endpoints[agent_abs_index].emplace( - itr.end_timestamp, memory_information{size, itr.address, false}); + itr.end_timestamp, memory_information{size, itr.address, itr.queue, false}); } // Create running sum of allocated memory for(auto& [_, endpoint_map] : mem_alloc_endpoints) @@ -817,10 +840,10 @@ write_perfetto( { mem_alloc_endpoints[abs_index].emplace( mem_alloc_extremes.first - extremes_endpoint_buffer, - memory_information{0, {0}, false}); + memory_information{0, {0}, {0}, false}); mem_alloc_endpoints[abs_index].emplace( mem_alloc_extremes.second + extremes_endpoint_buffer, - memory_information{0, {0}, false}); + memory_information{0, {0}, {0}, false}); auto _track_name = std::stringstream{}; @@ -853,9 +876,93 @@ write_perfetto( mem_alloc_tracks.at(alloc_itr.first), itr.first, itr.second.alloc_size / bytes_multiplier); + tracing_session->FlushBlocking(); + } + } + + // scratch memory counter track + auto scratch_mem_endpoints = + std::unordered_map>{}; + + // Load scratch memory usage endpoints + for(const auto& ditr : scratch_memory_gen) + for(const auto& itr : scratch_memory_gen.get(ditr)) + { + auto agent_abs_index = itr.agent_abs_index; + if(itr.operation == "FREE") + { + auto [agent_index, size] = + queue_to_agent_and_size[rocprofiler_queue_id_t{.handle = itr.queue_id}]; + agent_abs_index = agent_index; + } + + // Track start and end timestamps for this scratch memory record + scratch_mem_endpoints[agent_abs_index].emplace(itr.start, 0); + scratch_mem_endpoints[agent_abs_index].emplace(itr.end, 0); + } + + // Load values at each endpoint + for(const auto& ditr : scratch_memory_gen) + for(const auto& itr : scratch_memory_gen.get(ditr)) + { + if(itr.operation == "ALLOC") + { + auto agent_abs_index = itr.agent_abs_index; + + // For each timestamp in the range of this record including intervening + // deallocations write in allocation size + auto begin = scratch_mem_endpoints.at(agent_abs_index).lower_bound(itr.start); + auto end = scratch_mem_endpoints.at(agent_abs_index).upper_bound(itr.end); + + for(auto mitr = begin; mitr != end; ++mitr) + { + mitr->second = itr.size; + } + } + } + + // Create counter tracks for visualization + auto scratch_mem_tracks = std::unordered_map{}; + auto scratch_mem_names = std::vector{}; + scratch_mem_names.reserve(scratch_mem_endpoints.size()); + + for(auto& [abs_index, ts_map] : scratch_mem_endpoints) + { + // Add buffer timestamps for better visualization + if(!ts_map.empty()) + { + auto _track_name = std::stringstream{}; + const auto _agent = agent_data.at(abs_index).first; + auto agent_index_info = agent_data.at(abs_index).second; + + _track_name << "SCRATCH MEMORY on " << agent_index_info.label << " [" + << agent_index_info.index << "] (" << agent_index_info.type << ")"; + + constexpr auto _unit = ::perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES; + auto& _name = scratch_mem_names.emplace_back(_track_name.str()); + scratch_mem_tracks.emplace(abs_index, + ::perfetto::CounterTrack{_name.c_str(), this_pid_track} + .set_unit(_unit) + .set_unit_multiplier(bytes_multiplier) + .set_is_incremental(false)); + } + } + + // Write counter values to perfetto trace + for(const auto& mitr : scratch_mem_endpoints) + { + if(scratch_mem_tracks.count(mitr.first) > 0) + { + for(const auto& itr : mitr.second) + { + TRACE_COUNTER(sdk::perfetto_category::name, + scratch_mem_tracks.at(mitr.first), + itr.first, + itr.second / bytes_multiplier); + tracing_session->FlushBlocking(); + } } } - tracing_session->FlushBlocking(); } // Create counter tracks per agent diff --git a/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.hpp b/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.hpp index 601a83edd9..79394239b1 100644 --- a/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.hpp +++ b/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.hpp @@ -63,6 +63,7 @@ write_perfetto( const tool::generator& sample_gen, const tool::generator& kernel_dispatch_gen, const tool::generator& memory_copy_gen, + const tool::generator& scratch_memory_gen, const tool::generator& memory_allocation_gen, const tool::generator& counter_collection_gen); } // namespace output diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.hpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.hpp index d1325280b3..d40ea492ff 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.hpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.hpp @@ -76,6 +76,12 @@ struct uuid_view_t } }; +struct index_and_size +{ + uint64_t agent_abs_index = {}; + uint64_t size = {0}; +}; + std::vector get_agents(); diff --git a/projects/rocprofiler-sdk/tests/pytest-packages/pytest_utils/perfetto_reader.py b/projects/rocprofiler-sdk/tests/pytest-packages/pytest_utils/perfetto_reader.py index 22b7ac6611..73cd25d6c8 100644 --- a/projects/rocprofiler-sdk/tests/pytest-packages/pytest_utils/perfetto_reader.py +++ b/projects/rocprofiler-sdk/tests/pytest-packages/pytest_utils/perfetto_reader.py @@ -343,6 +343,55 @@ class PerfettoReader: [self.dataframe, counter_collection_df], ignore_index=True ) + scratch_df = self.query_tp( + """WITH Pairs AS( + SELECT + counter.id as slice_id, + track_id, + ts, + (LEAD(counter.ts) OVER window) - counter.ts AS dur, + counter_track.name as track_name, + ROW_NUMBER() OVER window AS rn + FROM counter JOIN counter_track ON counter.track_id = counter_track.id + WHERE counter_track.name LIKE '%SCRATCH MEMORY%' + WINDOW window AS (PARTITION BY counter.value, track_id ORDER BY counter.ts) + ) + SELECT + slice_id, + track_id, + 'scratch_memory' as category, + 0 as depth, + 0 as stack_id, + 0 as parent_stack_id, + ts, + dur, + Pairs.track_name as name + FROM Pairs WHERE (rn % 2 == 1) ORDER BY slice_id""" + ) + + # Transform scratch memory data to match the main dataframe schema + if not scratch_df.empty: + # Register counter track IDs in self.track_ids before adding to dataframe + for row in scratch_df.itertuples(): + if ( + row.tp_index < len(self.track_ids) + and row.track_id not in self.track_ids[row.tp_index] + ): + # Add the counter track to track_ids with reasonable default values + self.track_ids[row.tp_index][row.track_id] = { + "tp_index": row.tp_index, + "pid": 0, + "tid": 0, + "rank": 0, + "thread": 0, + "prio": 2, + "process_name": "scratch_process", + "thread_name": f"scratch_track_{row.category}", + } + + # Concatenate with main dataframe + self.dataframe = pd.concat([self.dataframe, scratch_df], ignore_index=True) + self.df_categories = sorted(list(self.dataframe["category"].unique())) # check for update to include/exclude category @@ -420,26 +469,38 @@ class PerfettoReader: _thread_name = ( thread.thread_name if thread.track_name is None else thread.track_name ) - for process in self.process.itertuples(): - if process.tp_index != thread.tp_index: - continue - _process_name = ( - process.process_name - if process.track_name is None - else process.track_name - ) - if process.track_id == thread.track_parent_id: - self.track_ids[thread.tp_index][thread.track_id] = { - "tp_index": thread.tp_index, - "pid": process.pid, - "tid": thread.tid, - "rank": -1, - "thread": -1, - "prio": 0 if thread.is_main_thread else 1, - "process_name": _process_name, - "thread_name": _thread_name, - } - break + if thread.track_parent_id == 0: + self.track_ids[thread.tp_index][thread.track_id] = { + "tp_index": thread.tp_index, + "pid": 0, + "tid": thread.tid, + "rank": -1, + "thread": -1, + "prio": 0 if thread.is_main_thread else 1, + "process_name": "", + "thread_name": _thread_name, + } + else: + for process in self.process.itertuples(): + if process.tp_index != thread.tp_index: + continue + _process_name = ( + process.process_name + if process.track_name is None + else process.track_name + ) + if process.track_id == thread.track_parent_id: + self.track_ids[thread.tp_index][thread.track_id] = { + "tp_index": thread.tp_index, + "pid": process.pid, + "tid": thread.tid, + "rank": -1, + "thread": -1, + "prio": 0 if thread.is_main_thread else 1, + "process_name": _process_name, + "thread_name": _thread_name, + } + break # some track ids do not have an associated system thread id so handle them here. # for example, omnitrace post-processes sampling data collected on a thread diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/CMakeLists.txt b/projects/rocprofiler-sdk/tests/rocprofv3/CMakeLists.txt index f0e4fd631a..e18ce67333 100644 --- a/projects/rocprofiler-sdk/tests/rocprofv3/CMakeLists.txt +++ b/projects/rocprofiler-sdk/tests/rocprofv3/CMakeLists.txt @@ -49,3 +49,4 @@ add_subdirectory(python-bindings) add_subdirectory(rocpd) add_subdirectory(rocpd-kernel-rename) add_subdirectory(attachment) +add_subdirectory(rocpd-scratch) diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/CMakeLists.txt b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/CMakeLists.txt new file mode 100644 index 0000000000..3a70fde388 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/CMakeLists.txt @@ -0,0 +1,107 @@ +# +# rocprofv3 rocpd tests +# +cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR) + +project( + rocprofiler-sdk-tests-rocprofv3-rocpd-scratch + LANGUAGES CXX + VERSION 0.0.0) + +find_package(rocprofiler-sdk REQUIRED) + +set(rocprofv3-rocpd-scratch-env + "${ROCPROFILER_MEMCHECK_PRELOAD_ENV}" + "PYTHONPATH=${rocprofiler-sdk_LIB_DIR}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages" + "OMPI_ALLOW_RUN_AS_ROOT=1" + "OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1") + +rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY conftest.py validate.py) + +find_package(Python3 REQUIRED) + +######################################################################################### +# +# generate rocpd database and json for comparison +# +######################################################################################### + +add_test( + NAME rocprofv3-test-rocpd-scratch-execute + COMMAND + $ --scratch-memory-trace -d + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data -o out --output-format rocpd json + --runtime-trace --kernel-rename -- $) + +set_tests_properties( + rocprofv3-test-rocpd-scratch-execute + PROPERTIES TIMEOUT + 120 + LABELS + "integration-tests;rocpd" + ENVIRONMENT + "${rocprofv3-rocpd-scratch-env}" + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + FIXTURES_SETUP + rocprofv3-test-rocpd-scratch) + +######################################################################################### +# +# perfetto generate +# +######################################################################################### + +add_test( + NAME rocprofv3-test-rocpd-scratch-perfetto-generation + COMMAND + ${Python3_EXECUTABLE} -m rocpd convert -f pftrace --kernel-rename -d + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data -i + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data/out_results.db) + +set_tests_properties( + rocprofv3-test-rocpd-scratch-perfetto-generation + PROPERTIES TIMEOUT + 120 + LABELS + "integration-tests;rocpd" + ENVIRONMENT + "${rocprofv3-rocpd-scratch-env}" + DEPENDS + "rocprofv3-test-rocpd-scratch-execute" + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + FIXTURES_SETUP + rocprofv3-test-rocpd-scratch-generation + FIXTURES_REQUIRED + rocprofv3-test-rocpd-scratch) + +######################################################################################### +# +# Validation +# +######################################################################################### + +set(VALIDATION_DEPENDS rocprofv3-test-rocpd-scratch-perfetto-generation) + +add_test( + NAME rocprofv3-test-rocpd-scratch-validation + COMMAND + ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data/out_results.json --pftrace-input + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.pftrace) + +set_tests_properties( + rocprofv3-test-rocpd-scratch-validation + PROPERTIES TIMEOUT + 120 + LABELS + "integration-tests;rocpd" + ENVIRONMENT + "${rocprofv3-rocpd-scratch-env}" + DEPENDS + rocprofv3-test-rocpd-scratch-perfetto-generation + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + FIXTURES_REQUIRED + rocprofv3-test-rocpd-scratch-generation) diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/conftest.py b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/conftest.py new file mode 100644 index 0000000000..1d1d139095 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/conftest.py @@ -0,0 +1,65 @@ +#!/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 pandas as pd +import pytest +import json +import os +import io + +from rocprofiler_sdk.pytest_utils.dotdict import dotdict +from rocprofiler_sdk.pytest_utils import collapse_dict_list +from rocprofiler_sdk.pytest_utils.perfetto_reader import PerfettoReader +from rocprofiler_sdk.pytest_utils.otf2_reader import OTF2Reader + + +def pytest_addoption(parser): + parser.addoption( + "--json-input", + action="store", + help="Path to JSON file.", + ) + parser.addoption( + "--pftrace-input", + action="store", + help="Path to Perfetto trace file.", + ) + + pd.set_option("display.width", 2000) + # increase debug display of pandas dataframes + for itr in ["rows", "columns", "colwidth"]: + pd.set_option(f"display.max_{itr}", None) + + +@pytest.fixture +def json_data(request): + filename = request.config.getoption("--json-input") + with open(filename, "r") as inp: + return dotdict(collapse_dict_list(json.load(inp))) + + +@pytest.fixture +def pftrace_data(request): + filename = request.config.getoption("--pftrace-input") + return PerfettoReader(filename).read()[0] diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/pytest.ini b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/pytest.ini new file mode 100644 index 0000000000..5e1e1c14a0 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/pytest.ini @@ -0,0 +1,5 @@ + +[pytest] +addopts = --durations=20 -rA -s -vv +testpaths = validate.py +pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/validate.py b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/validate.py new file mode 100644 index 0000000000..ebd691b033 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd-scratch/validate.py @@ -0,0 +1,41 @@ +#!/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 sys +import pytest + + +def test_perfetto_data(pftrace_data, json_data): + import rocprofiler_sdk.tests.rocprofv3 as rocprofv3 + + rocprofv3.test_perfetto_data( + pftrace_data, + json_data, + ("scratch_memory",), + ) + + +if __name__ == "__main__": + exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) + sys.exit(exit_code)