[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 <Marko.Crnobrnja@amd.com>
Co-authored-by: Aleksei Tumakaev <atumakae@amd.com>
Co-authored-by: Jonathan R. Madsen <jrmadsen@users.noreply.github.com>
Cette révision appartient à :
systems-assistant[bot]
2025-09-23 09:55:30 +02:00
révisé par GitHub
Parent 93cfcb1a4e
révision 1e9d8abbf6
11 fichiers modifiés avec 494 ajouts et 54 suppressions
+48 -6
Voir le fichier
@@ -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 <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
@@ -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<rocprofiler_address_t, rocprofiler::agent::index_and_size>{};
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<uint64_t>{};
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<uint64_t>{};
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,
{
+4
Voir le fichier
@@ -460,6 +460,9 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd)
auto memory_copies = rocpd::sql_generator<rocpd::types::memory_copies>{
conn, select_guid_nid_pid("memory_copies")};
auto scratch_memory = rocpd::sql_generator<rocpd::types::scratch_memory>{
conn, select_guid_nid_pid("scratch_memory")};
auto counters = rocpd::sql_generator<rocpd::types::counter>{
conn, select_guid_nid_pid("counters_collection")};
@@ -495,6 +498,7 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd)
samples,
kernels,
memory_copies,
scratch_memory,
memory_allocations,
counters);
}
+135 -28
Voir le fichier
@@ -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 <fmt/format.h>
@@ -174,6 +175,7 @@ write_perfetto(
const tool::generator<types::sample>& sample_gen,
const tool::generator<types::kernel_dispatch>& kernel_dispatch_gen,
const tool::generator<types::memory_copies>& memory_copy_gen,
const tool::generator<types::scratch_memory>& scratch_memory_gen,
const tool::generator<types::memory_allocation>& memory_allocation_gen,
const tool::generator<types::counter>& 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<uint64_t, uint64_t>{
std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::min()};
auto address_to_agent_and_size =
std::unordered_map<rocprofiler_address_t, agent_and_size>{};
std::unordered_map<rocprofiler_address_t, rocprofiler::agent::index_and_size>{};
auto queue_to_agent_and_size =
std::unordered_map<rocprofiler_queue_id_t, rocprofiler::agent::index_and_size>{};
auto free_mem_info = std::vector<free_memory_information>{};
// 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<uint64_t, std::map<rocprofiler_timestamp_t, uint64_t>>{};
// 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<uint64_t, ::perfetto::CounterTrack>{};
auto scratch_mem_names = std::vector<std::string>{};
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<sdk::category::scratch_memory>::name,
scratch_mem_tracks.at(mitr.first),
itr.first,
itr.second / bytes_multiplier);
tracing_session->FlushBlocking();
}
}
}
tracing_session->FlushBlocking();
}
// Create counter tracks per agent
+1
Voir le fichier
@@ -63,6 +63,7 @@ write_perfetto(
const tool::generator<types::sample>& sample_gen,
const tool::generator<types::kernel_dispatch>& kernel_dispatch_gen,
const tool::generator<types::memory_copies>& memory_copy_gen,
const tool::generator<types::scratch_memory>& scratch_memory_gen,
const tool::generator<types::memory_allocation>& memory_allocation_gen,
const tool::generator<types::counter>& counter_collection_gen);
} // namespace output
+6
Voir le fichier
@@ -76,6 +76,12 @@ struct uuid_view_t
}
};
struct index_and_size
{
uint64_t agent_abs_index = {};
uint64_t size = {0};
};
std::vector<const rocprofiler_agent_t*>
get_agents();
+81 -20
Voir le fichier
@@ -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
+1
Voir le fichier
@@ -49,3 +49,4 @@ add_subdirectory(python-bindings)
add_subdirectory(rocpd)
add_subdirectory(rocpd-kernel-rename)
add_subdirectory(attachment)
add_subdirectory(rocpd-scratch)
+107
Voir le fichier
@@ -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
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --scratch-memory-trace -d
${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data -o out --output-format rocpd json
--runtime-trace --kernel-rename -- $<TARGET_FILE:scratch-memory>)
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)
+65
Voir le fichier
@@ -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]
+5
Voir le fichier
@@ -0,0 +1,5 @@
[pytest]
addopts = --durations=20 -rA -s -vv
testpaths = validate.py
pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages
+41
Voir le fichier
@@ -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)