Public C++ header files and samples updates (#819)

* Public C++ header files (source/include/rocprofiler-sdk/cxx)

* Update samples/api_buffered_tracing

- scratch memory and page migration
- README

* Update samples/api_buffered_tracing

- page migration component in sample

* Update tests/page-migration/validate.py

- fix checks for page migration operation names

* Update tests/page-migration/validate.py

- fix get_allocated_pages

* Update scratch memory and page migration validations

* Fix include/rocprofiler-sdk/cxx installation

* Rework include/rocprofiler-sdk/cxx

- Improve name_info to support const char*, string_view, string

* Update samples/api_{buffered,callback}_tracing

* External correlation ID request sample

- includes correlation ID retirement demo

* Update samples/api_buffered_tracing/README.md

* Update lib/rocprofiler-sdk/hsa/queue.cpp

- generate correlation ID for kernel launch if one doesn't exist

* Remove priority check from tool libraries (samples/tests)

- if(priority > 0) return nullptr check in rocprofiler_configure has proliferated beyond its intended use

* Apply suggestions from code review
Этот коммит содержится в:
Jonathan R. Madsen
2024-04-25 20:09:11 -05:00
коммит произвёл GitHub
родитель e2bce49655
Коммит de13d2ac5d
37 изменённых файлов: 2232 добавлений и 512 удалений
+11 -10
Просмотреть файл
@@ -26,13 +26,14 @@ set(c-tool-env
set_tests_properties(
test-c-tool-execute
PROPERTIES TIMEOUT
45
LABELS
"integration-tests"
ENVIRONMENT
"${c-tool-env}"
PASS_REGULAR_EXPRESSION
"Test C tool is using rocprofiler-sdk v([0-9]+\\.[0-9]+\\.[0-9]+)"
FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
PROPERTIES
TIMEOUT
45
LABELS
"integration-tests"
ENVIRONMENT
"${c-tool-env}"
PASS_REGULAR_EXPRESSION
"Test C tool \\(priority=0\\) is using rocprofiler-sdk v([0-9]+\\.[0-9]+\\.[0-9]+)"
FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
+27
Просмотреть файл
@@ -0,0 +1,27 @@
// MIT License
//
// Copyright (c) 2023 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 <rocprofiler-sdk/cxx/name_info.hpp>
#include <rocprofiler-sdk/cxx/serialization.hpp>
+28 -30
Просмотреть файл
@@ -21,17 +21,17 @@ def to_dict(key_values):
return a
def op_name(op_name, record):
found_op = False
op_key = None
def get_operation(record, kind_name, op_name=None):
for idx, itr in enumerate(record["names"]):
if kind_name == itr["kind"]:
if op_name is None:
return idx, itr["operations"]
else:
for oidx, oname in enumerate(itr["operations"]):
if op_name == oname:
return oidx
for kind_node in record["names"]["kind_names"]:
if kind_node["value"] == op_name:
op_key = kind_node["key"]
for op_node in record["names"]["operation_names"]:
if op_node["key"] == op_key:
return op_key, to_dict(op_node["value"])
return None
def dict_from_value_key(d):
@@ -259,18 +259,17 @@ def test_retired_correlation_ids(input_data):
def get_allocated_pages(callback_records):
# Get how many pages we allocated
hip_api_traces = callback_records["hip_api_traces"]
_, op_dict = op_name("HIP_RUNTIME_API", callback_records)
op_key = [k for k, v in op_dict.items() if v == "hipHostRegister"][0]
op_idx = get_operation(callback_records, "HIP_RUNTIME_API", "hipHostRegister")
rt_idx, rt_data = get_operation(callback_records, "HIP_RUNTIME_API")
assert op_idx is not None, f"{rt_idx}:\n{rt_data}"
host_register_record = []
for r in hip_api_traces:
if (
r["operation"] == op_key
and "sizeBytes" in r["args"]
and "hostPtr" in r["args"]
):
host_register_record.append(r)
for itr in callback_records["hip_api_traces"]:
if itr["kind"] == rt_idx and itr["operation"] == op_idx and itr["phase"] == 2:
assert "sizeBytes" in itr["args"].keys(), f"{itr}"
assert "hostPtr" in itr["args"].keys(), f"{itr}"
host_register_record.append(itr)
assert len(host_register_record) == 1
alloc_size = int(host_register_record[0]["args"]["sizeBytes"], 10)
@@ -285,11 +284,11 @@ def test_page_migration_data(input_data):
sdk_data = data["rocprofiler-sdk-json-tool"]
buffer_records = sdk_data["buffer_records"]
callback_records = sdk_data["callback_records"]
page_migtation_buffers = buffer_records["page_migration"]
page_migration_buffers = buffer_records["page_migration"]
bf_op_id, bf_op_names = op_name("PAGE_MIGRATION", buffer_records)
assert bf_op_names[0] == "NONE"
assert "PAGE_MIGRATE" in str(bf_op_names)
_, bf_op_names = get_operation(buffer_records, "PAGE_MIGRATION")
assert bf_op_names[0] == "PAGE_MIGRATION_NONE"
assert "PAGE_MIGRATION_PAGE_MIGRATE" in bf_op_names
assert len(bf_op_names) == 5
node_ids = set(x["gpu_id"] for x in sdk_data["agents"])
@@ -299,15 +298,14 @@ def test_page_migration_data(input_data):
assert int(alloc_size) == 16 * 4096 # We allocated 16 pages in the test
# PID must be same
assert len(set(r["pid"] for r in page_migtation_buffers)) == 1
assert len(set(r["pid"] for r in page_migration_buffers)) == 1
for r in page_migtation_buffers:
for r in page_migration_buffers:
op = r["operation"]
assert r["size"] == 136
assert r["kind"] == bf_op_id
assert op != 0 and bf_op_names[op] != "NONE"
assert bf_op_names[op].lower() in r
assert op != 0 and bf_op_names[op] != "PAGE_MIGRATION_NONE"
assert bf_op_names[op].lower().replace("page_migration_", "") in r.keys()
if "page_migrate" in r:
assert r["page_migrate"]["from_node"] in node_ids
@@ -328,7 +326,7 @@ def test_page_migration_data(input_data):
assert 0 < r["start_timestamp"] < r["end_timestamp"]
# Check for events with our page
for r in page_migtation_buffers:
for r in page_migration_buffers:
if "page_migrate" in r and r["page_migrate"]["start_addr"] == start_addr:
assert end_addr == r["page_migrate"]["end_addr"]
+5 -3
Просмотреть файл
@@ -35,6 +35,8 @@ class dotdict(dict):
for k, v in self.items():
if isinstance(v, dict):
self.__setitem__(k, dotdict(v))
# print(k)
elif isinstance(v, list):
self.__setitem__(k, [dotdict(i) for i in v])
elif isinstance(v, (list, tuple)):
self.__setitem__(
k,
[dotdict(i) if isinstance(i, (list, tuple, dict)) else i for i in v],
)
+14 -19
Просмотреть файл
@@ -152,16 +152,15 @@ def test_external_correlation_ids(input_data):
assert itr["correlation_id"]["external"] in extern_corr_ids, f"[{titr}] {itr}"
def op_name(op_name, record):
op_key = None
for kind_node in record["names"]["kind_names"]:
if kind_node["value"] == op_name:
op_key = kind_node["key"]
for op_node in record["names"]["operation_names"]:
if op_node["key"] == op_key:
return op_node
def get_operation(record, kind_name, op_name=None):
for idx, itr in enumerate(record["names"]):
if kind_name == itr["kind"]:
if op_name is None:
return idx, itr["operations"]
else:
for oidx, oname in enumerate(itr["operations"]):
if op_name == oname:
return oidx
return None
@@ -179,16 +178,14 @@ def test_scratch_memory_tracking(input_data):
assert len(scratch_callback_data) == 2 * len(scratch_buffer_data)
cb_op_names = op_name("SCRATCH_MEMORY", callback_records)["value"]
bf_op_names = op_name("SCRATCH_MEMORY", buffer_records)["value"]
_, cb_op_names = get_operation(callback_records, "SCRATCH_MEMORY")
_, bf_op_names = get_operation(buffer_records, "SCRATCH_MEMORY")
assert len(cb_op_names) == 4
assert len(bf_op_names) == 4
# op name -> enum value
scratch_cb_op_map = {node["value"]: node["key"] for node in cb_op_names}
scratch_bf_op_map = {node["value"]: node["key"] for node in bf_op_names}
assert scratch_cb_op_map == scratch_bf_op_map
assert cb_op_names == bf_op_names
scratch_reported_agent_ids = set()
detected_agents_ids = set(
@@ -253,10 +250,8 @@ def test_scratch_memory_tracking(input_data):
), f"this:\n{this_node}\n\nnext:\n{next_node}"
# alloc has more data vs free and async reclaim
scratch_alloc_node = (
this_node["operation"] == scratch_cb_op_map["SCRATCH_MEMORY_ALLOC"]
)
if scratch_alloc_node:
scratch_alloc_node = cb_op_names[this_node["operation"]]
if scratch_alloc_node == "SCRATCH_MEMORY_ALLOC":
assert (
pl(this_node)["queue_id"]["handle"]
== pl(next_node)["queue_id"]["handle"]
+2 -4
Просмотреть файл
@@ -40,9 +40,6 @@ rocprofiler_configure(uint32_t version,
uint32_t priority,
rocprofiler_client_id_t* id)
{
// only activate if main tool
if(priority > 0) return NULL;
// set the client name
id->name = "Test C tool";
@@ -52,8 +49,9 @@ rocprofiler_configure(uint32_t version,
uint32_t patch = version % 100;
// generate info string
printf("%s is using rocprofiler-sdk v%i.%i.%i (%s)\n",
printf("%s (priority=%u) is using rocprofiler-sdk v%i.%i.%i (%s)\n",
id->name,
priority,
major,
minor,
patch,
+23 -155
Просмотреть файл
@@ -34,6 +34,7 @@
#include "common/defines.hpp"
#include "common/filesystem.hpp"
#include "common/hash.hpp"
#include "common/name_info.hpp"
#include "common/perfetto.hpp"
#include "common/serialization.hpp"
@@ -197,146 +198,14 @@ make_array(Tp&& arg, Args&&... args)
return std::array<Tp, N>{std::forward<Tp>(arg), std::forward<Args>(args)...};
}
using call_stack_t = std::vector<source_location>;
using buffer_kind_names_t = std::map<rocprofiler_buffer_tracing_kind_t, std::string>;
using buffer_kind_operation_names_t =
std::map<rocprofiler_buffer_tracing_kind_t, std::map<uint32_t, std::string>>;
using callback_kind_names_t = std::map<rocprofiler_callback_tracing_kind_t, std::string>;
using callback_kind_operation_names_t =
std::map<rocprofiler_callback_tracing_kind_t, std::map<uint32_t, std::string>>;
using call_stack_t = std::vector<source_location>;
using kernel_symbol_data_t = rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t;
using kernel_symbol_map_t = std::unordered_map<rocprofiler_kernel_id_t, kernel_symbol_data_t>;
struct callback_name_info
{
callback_kind_names_t kind_names = {};
callback_kind_operation_names_t operation_names = {};
template <typename ArchiveT>
void save(ArchiveT& ar) const
{
ar(cereal::make_nvp("kind_names", kind_names));
ar(cereal::make_nvp("operation_names", operation_names));
}
};
struct buffer_name_info
{
buffer_kind_names_t kind_names = {};
buffer_kind_operation_names_t operation_names = {};
template <typename ArchiveT>
void save(ArchiveT& ar) const
{
ar(cereal::make_nvp("kind_names", kind_names));
ar(cereal::make_nvp("operation_names", operation_names));
}
};
rocprofiler_client_id_t* client_id = nullptr;
rocprofiler_client_finalize_t client_fini_func = nullptr;
callback_name_info
get_callback_tracing_names()
{
auto cb_name_info = callback_name_info{};
//
// callback for each kind operation
//
static auto tracing_kind_operation_cb =
[](rocprofiler_callback_tracing_kind_t kindv, uint32_t operation, void* data_v) {
auto* name_info_v = static_cast<callback_name_info*>(data_v);
const char* name = nullptr;
ROCPROFILER_CALL(rocprofiler_query_callback_tracing_kind_operation_name(
kindv, operation, &name, nullptr),
"query buffer tracing kind operation name");
if(name) name_info_v->operation_names[kindv][operation] = name;
return 0;
};
//
// callback for each buffer kind (i.e. domain)
//
static auto tracing_kind_cb = [](rocprofiler_callback_tracing_kind_t kind, void* data) {
// store the buffer kind name
auto* name_info_v = static_cast<callback_name_info*>(data);
const char* name = nullptr;
ROCPROFILER_CALL(rocprofiler_query_callback_tracing_kind_name(kind, &name, nullptr),
"query buffer tracing kind operation name");
if(name) name_info_v->kind_names[kind] = name;
rocprofiler_iterate_callback_tracing_kind_operations(
kind, tracing_kind_operation_cb, static_cast<void*>(data));
return 0;
};
ROCPROFILER_CALL(rocprofiler_iterate_callback_tracing_kinds(tracing_kind_cb,
static_cast<void*>(&cb_name_info)),
"iterating buffer tracing kinds");
return cb_name_info;
}
buffer_name_info
get_buffer_tracing_names()
{
static const auto supported = std::unordered_set<rocprofiler_buffer_tracing_kind_t>{
ROCPROFILER_BUFFER_TRACING_HSA_CORE_API,
ROCPROFILER_BUFFER_TRACING_HSA_AMD_EXT_API,
ROCPROFILER_BUFFER_TRACING_HSA_IMAGE_EXT_API,
ROCPROFILER_BUFFER_TRACING_HSA_FINALIZE_EXT_API,
ROCPROFILER_BUFFER_TRACING_HIP_RUNTIME_API,
ROCPROFILER_BUFFER_TRACING_HIP_COMPILER_API,
ROCPROFILER_BUFFER_TRACING_MARKER_CORE_API,
ROCPROFILER_BUFFER_TRACING_MARKER_CONTROL_API,
ROCPROFILER_BUFFER_TRACING_MARKER_NAME_API,
ROCPROFILER_BUFFER_TRACING_MEMORY_COPY,
ROCPROFILER_BUFFER_TRACING_SCRATCH_MEMORY,
ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION,
};
auto cb_name_info = buffer_name_info{};
//
// callback for each kind operation
//
static auto tracing_kind_operation_cb =
[](rocprofiler_buffer_tracing_kind_t kindv, uint32_t operation, void* data_v) {
auto* name_info_v = static_cast<buffer_name_info*>(data_v);
const char* name = nullptr;
ROCPROFILER_CALL(rocprofiler_query_buffer_tracing_kind_operation_name(
kindv, operation, &name, nullptr),
"query buffer tracing kind operation name");
if(name) name_info_v->operation_names[kindv][operation] = name;
return 0;
};
//
// callback for each buffer kind (i.e. domain)
//
static auto tracing_kind_cb = [](rocprofiler_buffer_tracing_kind_t kind, void* data) {
// store the buffer kind name
auto* name_info_v = static_cast<buffer_name_info*>(data);
const char* name = nullptr;
ROCPROFILER_CALL(rocprofiler_query_buffer_tracing_kind_name(kind, &name, nullptr),
"query buffer tracing kind operation name");
if(name) name_info_v->kind_names[kind] = name;
rocprofiler_iterate_buffer_tracing_kind_operations(
kind, tracing_kind_operation_cb, static_cast<void*>(data));
return 0;
};
ROCPROFILER_CALL(rocprofiler_iterate_buffer_tracing_kinds(tracing_kind_cb,
static_cast<void*>(&cb_name_info)),
"iterating buffer tracing kinds");
return cb_name_info;
}
using callback_payload_t =
std::variant<rocprofiler_callback_tracing_code_object_load_data_t,
rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t,
@@ -1569,12 +1438,12 @@ write_json(call_stack_t* _call_stack)
{
using JSONOutputArchive = cereal::MinimalJSONOutputArchive;
constexpr auto json_prec = 32;
constexpr auto json_indent = JSONOutputArchive::Options::IndentChar::space;
auto json_opts = JSONOutputArchive::Options{json_prec, json_indent, 1};
auto json_ar = JSONOutputArchive{*ofs, json_opts};
auto buffer_name_info = get_buffer_tracing_names();
auto callback_name_info = get_callback_tracing_names();
constexpr auto json_prec = 32;
constexpr auto json_indent = JSONOutputArchive::Options::IndentChar::space;
auto json_opts = JSONOutputArchive::Options{json_prec, json_indent, 1};
auto json_ar = JSONOutputArchive{*ofs, json_opts};
auto buffer_names = rocprofiler::sdk::get_buffer_tracing_names();
auto callbk_names = rocprofiler::sdk::get_callback_tracing_names();
auto validate_page_migration =
(page_migration_status != ROCPROFILER_STATUS_ERROR_INCOMPATIBLE_KERNEL);
@@ -1598,7 +1467,7 @@ write_json(call_stack_t* _call_stack)
json_ar.startNode();
try
{
json_ar(cereal::make_nvp("names", callback_name_info));
json_ar(cereal::make_nvp("names", callbk_names));
json_ar(cereal::make_nvp("code_objects", code_object_records));
json_ar(cereal::make_nvp("kernel_symbols", kernel_symbol_records));
json_ar(cereal::make_nvp("hsa_api_traces", hsa_api_cb_records));
@@ -1619,7 +1488,7 @@ write_json(call_stack_t* _call_stack)
json_ar.startNode();
try
{
json_ar(cereal::make_nvp("names", buffer_name_info));
json_ar(cereal::make_nvp("names", buffer_names));
json_ar(cereal::make_nvp("kernel_dispatches", kernel_dispatch_bf_records));
json_ar(cereal::make_nvp("memory_copies", memory_copy_bf_records));
json_ar(cereal::make_nvp("scratch_memory_traces", scratch_memory_records));
@@ -1786,12 +1655,12 @@ write_perfetto()
}
{
auto buffer_name_info = get_buffer_tracing_names();
auto callbk_name_info = get_callback_tracing_names();
auto buffer_names = rocprofiler::sdk::get_buffer_tracing_names();
auto callbk_name_info = rocprofiler::sdk::get_callback_tracing_names();
for(auto itr : hsa_api_bf_records)
{
auto& name = buffer_name_info.operation_names.at(itr.kind).at(itr.operation);
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = thread_tracks.at(itr.thread_id);
auto _args = callback_arg_array_t{};
@@ -1803,7 +1672,7 @@ write_perfetto()
if(ritr != hsa_api_cb_records.end()) _args = ritr->args;
TRACE_EVENT_BEGIN(rocprofiler::trait::name<rocprofiler::category::hsa_api>::value,
::perfetto::StaticString(name.c_str()),
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
@@ -1830,7 +1699,7 @@ write_perfetto()
for(auto itr : hip_api_bf_records)
{
auto& name = buffer_name_info.operation_names.at(itr.kind).at(itr.operation);
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = thread_tracks.at(itr.thread_id);
auto _args = callback_arg_array_t{};
@@ -1842,7 +1711,7 @@ write_perfetto()
if(ritr != hip_api_cb_records.end()) _args = ritr->args;
TRACE_EVENT_BEGIN(rocprofiler::trait::name<rocprofiler::category::hip_api>::value,
::perfetto::StaticString(name.c_str()),
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
@@ -1869,11 +1738,11 @@ write_perfetto()
for(auto itr : memory_copy_bf_records)
{
auto& name = buffer_name_info.operation_names.at(itr.kind).at(itr.operation);
auto name = buffer_names.at(itr.kind, itr.operation);
auto& track = agent_tracks.at(itr.dst_agent_id.handle);
TRACE_EVENT_BEGIN(rocprofiler::trait::name<rocprofiler::category::memory_copy>::value,
::perfetto::StaticString(name.c_str()),
::perfetto::StaticString(name.data()),
track,
itr.start_timestamp,
::perfetto::Flow::ProcessScoped(itr.correlation_id.internal),
@@ -1886,7 +1755,9 @@ write_perfetto()
"src_agent",
agents_map.at(itr.src_agent_id).logical_node_id,
"dst_agent",
agents_map.at(itr.dst_agent_id).logical_node_id);
agents_map.at(itr.dst_agent_id).logical_node_id,
"copy_bytes",
itr.bytes);
TRACE_EVENT_END(rocprofiler::trait::name<rocprofiler::category::memory_copy>::value,
track,
itr.end_timestamp,
@@ -2070,9 +1941,6 @@ rocprofiler_configure(uint32_t 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 = "rocprofiler-sdk-json-tool";
@@ -2086,8 +1954,8 @@ rocprofiler_configure(uint32_t version,
// generate info string
auto info = std::stringstream{};
info << id->name << " is using rocprofiler-sdk v" << major << "." << minor << "." << patch
<< " (" << runtime_version << ")";
info << id->name << " (priority=" << priority << ") is using rocprofiler-sdk v" << major << "."
<< minor << "." << patch << " (" << runtime_version << ")";
std::clog << info.str() << std::endl;