[SDK] KFD new events API (#321)

* Remove page-migration

* Add KFD events API

* Address review comments

* Move assert checks

* Update enum-string utils

* Update codeowners

* Update KFD header

* Add perfetto category

[ROCm/rocprofiler-sdk commit: 8a461afe20]
This commit is contained in:
Kuricheti, Mythreya
2025-06-26 11:28:45 -07:00
committed by GitHub
parent 58532974bc
commit bde07e7baa
41 changed files with 3132 additions and 2760 deletions
@@ -49,7 +49,7 @@ add_subdirectory(marker)
add_subdirectory(thread_trace)
add_subdirectory(tracing)
add_subdirectory(kernel_dispatch)
add_subdirectory(page_migration)
add_subdirectory(kfd)
add_subdirectory(rccl)
add_subdirectory(rocdecode)
add_subdirectory(rocjpeg)
@@ -30,9 +30,9 @@
#include "lib/rocprofiler-sdk/hsa/memory_allocation.hpp"
#include "lib/rocprofiler-sdk/hsa/scratch_memory.hpp"
#include "lib/rocprofiler-sdk/kernel_dispatch/kernel_dispatch.hpp"
#include "lib/rocprofiler-sdk/kfd/kfd.hpp"
#include "lib/rocprofiler-sdk/marker/marker.hpp"
#include "lib/rocprofiler-sdk/ompt/ompt.hpp"
#include "lib/rocprofiler-sdk/page_migration/page_migration.hpp"
#include "lib/rocprofiler-sdk/rccl/rccl.hpp"
#include "lib/rocprofiler-sdk/registration.hpp"
#include "lib/rocprofiler-sdk/rocdecode/rocdecode.hpp"
@@ -52,6 +52,7 @@
#include <limits>
#include <stdexcept>
#include <string_view>
#include <unordered_set>
#include <vector>
#define RETURN_STATUS_ON_FAIL(...) \
@@ -90,7 +91,6 @@ ROCPROFILER_BUFFER_TRACING_KIND_STRING(MARKER_NAME_API)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(MEMORY_COPY)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(MEMORY_ALLOCATION)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KERNEL_DISPATCH)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(PAGE_MIGRATION)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(SCRATCH_MEMORY)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(CORRELATION_ID_RETIREMENT)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(RCCL_API)
@@ -102,6 +102,14 @@ ROCPROFILER_BUFFER_TRACING_KIND_STRING(HIP_STREAM)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(HIP_RUNTIME_API_EXT)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(HIP_COMPILER_API_EXT)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(ROCDECODE_API_EXT)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_EVENT_PAGE_MIGRATE)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_EVENT_PAGE_FAULT)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_EVENT_QUEUE)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_EVENT_UNMAP_FROM_GPU)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_EVENT_DROPPED_EVENTS)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_PAGE_MIGRATE)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_PAGE_FAULT)
ROCPROFILER_BUFFER_TRACING_KIND_STRING(KFD_QUEUE)
template <size_t Idx, size_t... Tail>
std::pair<const char*, size_t>
@@ -172,8 +180,21 @@ rocprofiler_configure_buffer_tracing_service(rocprofiler_context_id_t
ctx->buffered_tracer->domains, kind, operations[i]));
}
if(kind == ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION)
RETURN_STATUS_ON_FAIL(rocprofiler::page_migration::init());
{
static constexpr auto kfd_events =
std::array{ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE,
ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT,
ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE,
ROCPROFILER_BUFFER_TRACING_KFD_EVENT_UNMAP_FROM_GPU,
ROCPROFILER_BUFFER_TRACING_KFD_PAGE_MIGRATE,
ROCPROFILER_BUFFER_TRACING_KFD_PAGE_FAULT,
ROCPROFILER_BUFFER_TRACING_KFD_QUEUE};
if(std::find(kfd_events.begin(), kfd_events.end(), kind) != kfd_events.end())
{
RETURN_STATUS_ON_FAIL(rocprofiler::kfd::init());
}
}
return ROCPROFILER_STATUS_SUCCESS;
}
@@ -282,11 +303,6 @@ rocprofiler_query_buffer_tracing_kind_operation_name(rocprofiler_buffer_tracing_
val = rocprofiler::kernel_dispatch::name_by_id(operation);
break;
}
case ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION:
{
val = rocprofiler::page_migration::name_by_id(operation);
break;
}
case ROCPROFILER_BUFFER_TRACING_OMPT:
{
val = rocprofiler::ompt::name_by_id(operation);
@@ -318,6 +334,18 @@ rocprofiler_query_buffer_tracing_kind_operation_name(rocprofiler_buffer_tracing_
val = rocprofiler::hip::stream::name_by_id(operation);
break;
}
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_UNMAP_FROM_GPU:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_DROPPED_EVENTS:
case ROCPROFILER_BUFFER_TRACING_KFD_PAGE_MIGRATE:
case ROCPROFILER_BUFFER_TRACING_KFD_PAGE_FAULT:
case ROCPROFILER_BUFFER_TRACING_KFD_QUEUE:
{
val = rocprofiler::kfd::name_by_id(kind, operation);
break;
}
};
if(!val)
@@ -432,11 +460,6 @@ rocprofiler_iterate_buffer_tracing_kind_operations(
ops = rocprofiler::kernel_dispatch::get_ids();
break;
}
case ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION:
{
ops = rocprofiler::page_migration::get_ids();
break;
}
case ROCPROFILER_BUFFER_TRACING_OMPT:
{
ops = rocprofiler::ompt::get_ids();
@@ -467,6 +490,18 @@ rocprofiler_iterate_buffer_tracing_kind_operations(
ops = rocprofiler::hip::stream::get_ids();
break;
}
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_UNMAP_FROM_GPU:
case ROCPROFILER_BUFFER_TRACING_KFD_EVENT_DROPPED_EVENTS:
case ROCPROFILER_BUFFER_TRACING_KFD_PAGE_MIGRATE:
case ROCPROFILER_BUFFER_TRACING_KFD_PAGE_FAULT:
case ROCPROFILER_BUFFER_TRACING_KFD_QUEUE:
{
ops = rocprofiler::kfd::get_ids(kind);
break;
}
}
for(const auto& itr : ops)
@@ -59,7 +59,7 @@ template <typename DomainT>
rocprofiler_status_t
add_domain(domain_context<DomainT>& _cfg, DomainT _domain)
{
static_assert((1 << domain_info<DomainT>::last) < std::numeric_limits<uint64_t>::max(),
static_assert((1UL << domain_info<DomainT>::last) < std::numeric_limits<uint64_t>::max(),
"uint64_t cannot handle all the domains");
if(_domain <= domain_info<DomainT>::none) return ROCPROFILER_STATUS_ERROR_KIND_NOT_FOUND;
@@ -42,9 +42,11 @@
* - 1.14 - Update kfd_event_data
* - 1.15 - Enable managing mappings in compute VMs with GEM_VA ioctl
* - 1.16 - Add contiguous VRAM allocation flag
* - 1.17 - Add SDMA queue creation with target SDMA engine ID
* - 1.18 - Rename pad in set_memory_policy_args to misc_process_flag
*/
#define KFD_IOCTL_MAJOR_VERSION 1
#define KFD_IOCTL_MINOR_VERSION 16
#define KFD_IOCTL_MINOR_VERSION 18
struct kfd_ioctl_get_version_args
{
@@ -53,14 +55,17 @@ struct kfd_ioctl_get_version_args
};
/* For kfd_ioctl_create_queue_args.queue_type. */
#define KFD_IOC_QUEUE_TYPE_COMPUTE 0x0
#define KFD_IOC_QUEUE_TYPE_SDMA 0x1
#define KFD_IOC_QUEUE_TYPE_COMPUTE_AQL 0x2
#define KFD_IOC_QUEUE_TYPE_SDMA_XGMI 0x3
#define KFD_IOC_QUEUE_TYPE_COMPUTE 0x0
#define KFD_IOC_QUEUE_TYPE_SDMA 0x1
#define KFD_IOC_QUEUE_TYPE_COMPUTE_AQL 0x2
#define KFD_IOC_QUEUE_TYPE_SDMA_XGMI 0x3
#define KFD_IOC_QUEUE_TYPE_SDMA_BY_ENG_ID 0x4
#define KFD_MAX_QUEUE_PERCENTAGE 100
#define KFD_MAX_QUEUE_PRIORITY 15
#define KFD_MIN_QUEUE_RING_SIZE 1024
struct kfd_ioctl_create_queue_args
{
__u64 ring_base_address; /* to KFD */
@@ -80,6 +85,8 @@ struct kfd_ioctl_create_queue_args
__u64 ctx_save_restore_address; /* to KFD */
__u32 ctx_save_restore_size; /* to KFD */
__u32 ctl_stack_size; /* to KFD */
__u32 sdma_engine_id; /* to KFD */
__u32 pad;
};
struct kfd_ioctl_destroy_queue_args
@@ -152,15 +159,18 @@ struct kfd_dbg_device_info_entry
#define KFD_IOC_CACHE_POLICY_COHERENT 0
#define KFD_IOC_CACHE_POLICY_NONCOHERENT 1
/* Misc. per process flags */
#define KFD_PROC_FLAG_MFMA_HIGH_PRECISION (1 << 0)
struct kfd_ioctl_set_memory_policy_args
{
__u64 alternate_aperture_base; /* to KFD */
__u64 alternate_aperture_size; /* to KFD */
__u32 gpu_id; /* to KFD */
__u32 default_policy; /* to KFD */
__u32 alternate_policy; /* to KFD */
__u32 pad;
__u32 gpu_id; /* to KFD */
__u32 default_policy; /* to KFD */
__u32 alternate_policy; /* to KFD */
__u32 misc_process_flag; /* to KFD */
};
/*
@@ -578,7 +588,9 @@ enum kfd_smi_event
KFD_SMI_EVENT_QUEUE_EVICTION = 9,
KFD_SMI_EVENT_QUEUE_RESTORE = 10,
KFD_SMI_EVENT_UNMAP_FROM_GPU = 11,
KFD_SMI_EVENT_DROPPED_EVENT = 12,
KFD_SMI_EVENT_PROCESS_START = 12,
KFD_SMI_EVENT_PROCESS_END = 13,
KFD_SMI_EVENT_DROPPED_EVENT = 14,
/*
* max event number, as a flag bit to get events from all processes,
@@ -589,14 +601,16 @@ enum kfd_smi_event
KFD_SMI_EVENT_ALL_PROCESS = 64
};
/* The reason of the page migration event */
enum KFD_MIGRATE_TRIGGERS
{
KFD_MIGRATE_TRIGGER_PREFETCH, /* Prefetch to GPU */
KFD_MIGRATE_TRIGGER_PREFETCH, /* Prefetch to GPU VRAM or system memory */
KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU, /* GPU page fault recover */
KFD_MIGRATE_TRIGGER_PAGEFAULT_CPU, /* CPU page fault recover */
KFD_MIGRATE_TRIGGER_TTM_EVICTION /* TTM eviction */
};
/* The reason of user queue evition event */
enum KFD_QUEUE_EVICTION_TRIGGERS
{
KFD_QUEUE_EVICTION_TRIGGER_SVM, /* SVM buffer migration */
@@ -607,6 +621,7 @@ enum KFD_QUEUE_EVICTION_TRIGGERS
KFD_QUEUE_EVICTION_CRIU_RESTORE /* CRIU restore */
};
/* The reason of unmap buffer from GPU event */
enum KFD_SVM_UNMAP_TRIGGERS
{
KFD_SVM_UNMAP_TRIGGER_MMU_NOTIFY, /* MMU notifier CPU buffer movement */
@@ -647,9 +662,8 @@ enum kfd_ioctl_spm_op
* @buf_size[in]: size of the destination buffer
* @timeout[in/out]: [in]: timeout in milliseconds, [out]: amount of time left
* `in the timeout window
* @bytes_copied[out]: amount of data that was copied to the previous dest_buf
* @has_data_loss: boolean indicating whether data was lost
* (e.g. due to a ring-buffer overflow)
* @bytes_copied[out]: total amount of data that was copied to the previous dest_buf
* @has_data_loss: total count for sub-block which has data loss
*
* This ioctl performs different functions depending on the @op parameter.
*
@@ -700,6 +714,22 @@ struct kfd_ioctl_spm_args
__u32 has_data_loss;
};
/**
* kfd_ioctl_spm_buffer_header - SPM Buffer header for kfd_ioctl_spm_args->dest_buf
*
* @version [out]: spm versiom
* @bytes_copied [out]: amount of data for each sub-block
* @has_data_loss: [out]: boolean indicating whether data was lost for each sub-block
* (e.g. due to a ring-buffer overflow)
*/
struct kfd_ioctl_spm_buffer_header
{
__u32 version; /* 0-23: minor 24-31: major */
__u32 bytes_copied;
__u32 has_data_loss;
__u32 reserved[5];
};
/*
* SVM event tracing via SMI system management interface
*
@@ -760,6 +790,7 @@ struct kfd_ioctl_spm_args
"%lld -%d %x %c\n", (ns), (pid), (node), (rescheduled)
#define KFD_EVENT_FMT_UNMAP_FROM_GPU(ns, pid, addr, size, node, unmap_trigger) \
"%lld -%d @%lx(%lx) %x %d\n", (ns), (pid), (addr), (size), (node), (unmap_trigger)
#define KFD_EVENT_FMT_PROCESS(pid, task_name) "%x %s\n", (pid), (task_name)
#define KFD_EVENT_FMT_DROPPED_EVENT(ns, pid, drop_count) "%lld -%d %d\n", (ns), (pid), (drop_count)
/**************************************************************************************************
@@ -0,0 +1,12 @@
#
#
set(ROCPROFILER_LIB_KFD_EVENT_SOURCES abi.cpp kfd.cpp)
set(ROCPROFILER_LIB_KFD_EVENT_HEADERS defines.hpp kfd.hpp utils.hpp)
target_sources(
rocprofiler-sdk-object-library PRIVATE ${ROCPROFILER_LIB_KFD_EVENT_SOURCES}
${ROCPROFILER_LIB_KFD_EVENT_HEADERS})
if(ROCPROFILER_BUILD_TESTS)
add_subdirectory(tests)
endif()
@@ -0,0 +1,88 @@
// 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.
#include "lib/common/container/small_vector.hpp"
#include "lib/common/defines.hpp"
#include "lib/common/mpl.hpp"
#include "lib/rocprofiler-sdk/details/kfd_ioctl.h"
#include "lib/rocprofiler-sdk/kfd/defines.hpp"
#include "lib/rocprofiler-sdk/kfd/utils.hpp"
#include <fmt/core.h>
#define ASSERT_SAME(A, B) static_assert(static_cast<size_t>(A) == static_cast<size_t>(B))
namespace rocprofiler
{
namespace kfd
{
static_assert(KFD_SMI_EVENT_NONE == 0);
static_assert(KFD_SMI_EVENT_MIGRATE_START == 5);
static_assert(KFD_SMI_EVENT_MIGRATE_END == 6);
static_assert(KFD_SMI_EVENT_PAGE_FAULT_START == 7);
static_assert(KFD_SMI_EVENT_PAGE_FAULT_END == 8);
static_assert(KFD_SMI_EVENT_QUEUE_EVICTION == 9);
static_assert(KFD_SMI_EVENT_QUEUE_RESTORE == 10);
static_assert(KFD_SMI_EVENT_UNMAP_FROM_GPU == 11);
static_assert(KFD_SMI_EVENT_DROPPED_EVENT == 14);
static_assert(KFD_SMI_EVENT_ALL_PROCESS == 64);
// If any of these changes, we can no longer static_cast the new triggers
// from the KFD trigger to the record operation type
static_assert(ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_LAST == 5);
static_assert(ROCPROFILER_KFD_PAGE_MIGRATE_LAST == 4);
static_assert(ROCPROFILER_KFD_EVENT_PAGE_FAULT_LAST == 5);
static_assert(ROCPROFILER_KFD_PAGE_FAULT_LAST == 4);
static_assert(ROCPROFILER_KFD_EVENT_QUEUE_LAST == 8);
static_assert(ROCPROFILER_KFD_QUEUE_LAST == 6);
static_assert(ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_LAST == 3);
static_assert(ROCPROFILER_KFD_EVENT_QUEUE_RESTORE_RESCHEDULED == 6);
static_assert(ROCPROFILER_KFD_EVENT_QUEUE_RESTORE == 7);
// clang-format off
ASSERT_SAME(ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PREFETCH, KFD_MIGRATE_TRIGGER_PREFETCH );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_GPU, KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_CPU, KFD_MIGRATE_TRIGGER_PAGEFAULT_CPU );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_TTM_EVICTION, KFD_MIGRATE_TRIGGER_TTM_EVICTION );
ASSERT_SAME(ROCPROFILER_KFD_PAGE_MIGRATE_PREFETCH, KFD_MIGRATE_TRIGGER_PREFETCH );
ASSERT_SAME(ROCPROFILER_KFD_PAGE_MIGRATE_PAGEFAULT_GPU, KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU );
ASSERT_SAME(ROCPROFILER_KFD_PAGE_MIGRATE_PAGEFAULT_CPU, KFD_MIGRATE_TRIGGER_PAGEFAULT_CPU );
ASSERT_SAME(ROCPROFILER_KFD_PAGE_MIGRATE_TTM_EVICTION, KFD_MIGRATE_TRIGGER_TTM_EVICTION );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SVM, KFD_QUEUE_EVICTION_TRIGGER_SVM );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_USERPTR, KFD_QUEUE_EVICTION_TRIGGER_USERPTR );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_TTM, KFD_QUEUE_EVICTION_TRIGGER_TTM );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SUSPEND, KFD_QUEUE_EVICTION_TRIGGER_SUSPEND );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_CRIU_CHECKPOINT, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_CRIU_RESTORE, KFD_QUEUE_EVICTION_CRIU_RESTORE );
ASSERT_SAME(ROCPROFILER_KFD_QUEUE_EVICT_SVM, KFD_QUEUE_EVICTION_TRIGGER_SVM );
ASSERT_SAME(ROCPROFILER_KFD_QUEUE_EVICT_USERPTR, KFD_QUEUE_EVICTION_TRIGGER_USERPTR );
ASSERT_SAME(ROCPROFILER_KFD_QUEUE_EVICT_TTM, KFD_QUEUE_EVICTION_TRIGGER_TTM );
ASSERT_SAME(ROCPROFILER_KFD_QUEUE_EVICT_SUSPEND, KFD_QUEUE_EVICTION_TRIGGER_SUSPEND );
ASSERT_SAME(ROCPROFILER_KFD_QUEUE_EVICT_CRIU_CHECKPOINT, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT );
ASSERT_SAME(ROCPROFILER_KFD_QUEUE_EVICT_CRIU_RESTORE, KFD_QUEUE_EVICTION_CRIU_RESTORE );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_MMU_NOTIFY, KFD_SVM_UNMAP_TRIGGER_MMU_NOTIFY );
ASSERT_SAME(ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_MMU_NOTIFY_MIGRATE, KFD_SVM_UNMAP_TRIGGER_MMU_NOTIFY_MIGRATE);
ASSERT_SAME(ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_UNMAP_FROM_CPU, KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU );
// clang-format on
} // namespace kfd
} // namespace rocprofiler
@@ -1,6 +1,6 @@
// MIT License
//
// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved.
// 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
@@ -44,20 +44,47 @@
} \
} while(0)
#define SPECIALIZE_PAGE_MIGRATION_INFO(ROCPROF_NAME, KFD_NAME, FORMAT_STRING) \
#define SPECIALIZE_KFD_EVENT_INFO(EVENT_NAME, KFD_NAME, BUFFER_KIND, FORMAT_STRING) \
template <> \
struct page_migration_info<ROCPROFILER_PAGE_MIGRATION_##ROCPROF_NAME> \
struct kfd_event_info<KFD_EVENT_##EVENT_NAME> \
{ \
static constexpr auto name = "PAGE_MIGRATION_" #ROCPROF_NAME; \
static constexpr size_t operation = ROCPROFILER_PAGE_MIGRATION_##ROCPROF_NAME; \
static constexpr size_t kfd_operation = KFD_SMI_EVENT_##KFD_NAME; \
static constexpr size_t kfd_bitmask = bitmask(KFD_SMI_EVENT_##KFD_NAME); \
static constexpr std::string_view format_str = FORMAT_STRING; \
static constexpr size_t kind = KFD_EVENT_##EVENT_NAME; \
static constexpr size_t buffer_kind = ROCPROFILER_BUFFER_TRACING_KFD_##BUFFER_KIND; \
static constexpr size_t kfd_id = KFD_SMI_EVENT_##KFD_NAME; \
static constexpr size_t kfd_bitmask = bitmask(KFD_SMI_EVENT_##KFD_NAME); \
static constexpr std::string_view format_str = FORMAT_STRING; \
};
#define SPECIALIZE_KFD_KIND_INFO(KIND_NAME, LAST_OP) \
template <> \
struct kfd_kind_info<ROCPROFILER_BUFFER_TRACING_KFD_##KIND_NAME> \
{ \
static constexpr auto kind = ROCPROFILER_BUFFER_TRACING_KFD_##KIND_NAME; \
static constexpr auto last = ::LAST_OP; \
}
#define SPECIALIZE_KFD_KIND_NAME(KIND_NAME_SUFFIX, OPERATION) \
template <> \
struct kfd_operation_info<ROCPROFILER_BUFFER_TRACING_KFD_##KIND_NAME_SUFFIX, OPERATION> \
{ \
static constexpr auto kind = ROCPROFILER_BUFFER_TRACING_KFD_##KIND_NAME_SUFFIX; \
static constexpr auto operation = ::OPERATION; \
static constexpr auto name = #OPERATION; \
}
#define SPECIALIZE_KFD_IOC_IOCTL(STRUCT, ARG_IOC) \
template <> \
struct IOC_event<STRUCT> \
{ \
static constexpr auto value = ARG_IOC; \
}
#define ASSERT_SAME_AND_COPY(member) \
if(end.member == start.member) \
{ \
ret.member = end.member; \
} \
else \
ROCP_ERROR << fmt::format("Expected member " #member " to be same in events. {} vs {}", \
end.member, \
start.member);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,87 @@
// 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.
#if defined(ROCPROFILER_LIB_ROCPROFILER_SDK_KFD_CPP_IMPL) && \
ROCPROFILER_LIB_ROCPROFILER_SDK_KFD_CPP_IMPL == 1
// clang-format off
// Mapping for kfd_event_id (utils.h) | ID from kfd_ioctl.h | buffer_tracing_kind_t | format string
SPECIALIZE_KFD_EVENT_INFO(PAGE_MIGRATE_START, MIGRATE_START, EVENT_PAGE_MIGRATE, "%x %ld -%d @%lx(%lx) %x->%x %x:%x %d\n");
SPECIALIZE_KFD_EVENT_INFO(PAGE_MIGRATE_END, MIGRATE_END, EVENT_PAGE_MIGRATE, "%x %ld -%d @%lx(%lx) %x->%x %d %d\n" );
SPECIALIZE_KFD_EVENT_INFO(PAGE_FAULT_START, PAGE_FAULT_START, EVENT_PAGE_FAULT, "%x %ld -%d @%lx(%x) %c\n" );
SPECIALIZE_KFD_EVENT_INFO(PAGE_FAULT_END, PAGE_FAULT_END, EVENT_PAGE_FAULT, "%x %ld -%d @%lx(%x) %c\n" );
SPECIALIZE_KFD_EVENT_INFO(QUEUE_EVICTION, QUEUE_EVICTION, EVENT_QUEUE, "%x %ld -%d %x %d\n" );
SPECIALIZE_KFD_EVENT_INFO(QUEUE_RESTORE, QUEUE_RESTORE, EVENT_QUEUE, "%x %ld -%d %x %c\n" );
SPECIALIZE_KFD_EVENT_INFO(UNMAP_FROM_GPU, UNMAP_FROM_GPU, EVENT_UNMAP_FROM_GPU, "%x %ld -%d @%lx(%lx) %x %d\n" );
SPECIALIZE_KFD_EVENT_INFO(DROPPED_EVENT, DROPPED_EVENT, EVENT_DROPPED_EVENTS, "%x %ld -%d %d\n" );
SPECIALIZE_KFD_KIND_INFO(EVENT_PAGE_MIGRATE, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_LAST);
SPECIALIZE_KFD_KIND_INFO(EVENT_PAGE_FAULT, ROCPROFILER_KFD_EVENT_PAGE_FAULT_LAST);
SPECIALIZE_KFD_KIND_INFO(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_LAST);
SPECIALIZE_KFD_KIND_INFO(EVENT_UNMAP_FROM_GPU, ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_LAST);
SPECIALIZE_KFD_KIND_INFO(EVENT_DROPPED_EVENTS, ROCPROFILER_KFD_EVENT_DROPPED_EVENTS_LAST);
SPECIALIZE_KFD_KIND_INFO(PAGE_MIGRATE, ROCPROFILER_KFD_PAGE_MIGRATE_LAST);
SPECIALIZE_KFD_KIND_INFO(PAGE_FAULT, ROCPROFILER_KFD_PAGE_FAULT_LAST);
SPECIALIZE_KFD_KIND_INFO(QUEUE, ROCPROFILER_KFD_QUEUE_LAST);
// Events
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_MIGRATE, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PREFETCH);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_MIGRATE, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_GPU);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_MIGRATE, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_CPU);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_MIGRATE, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_TTM_EVICTION);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_MIGRATE, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_END);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_FAULT, ROCPROFILER_KFD_EVENT_PAGE_FAULT_START);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_FAULT, ROCPROFILER_KFD_EVENT_PAGE_FAULT_START_READ_FAULT);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_FAULT, ROCPROFILER_KFD_EVENT_PAGE_FAULT_START_WRITE_FAULT);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_FAULT, ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_MIGRATED);
SPECIALIZE_KFD_KIND_NAME(EVENT_PAGE_FAULT, ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_UPDATED);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SVM);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_USERPTR);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_TTM);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SUSPEND);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_CRIU_CHECKPOINT);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_CRIU_RESTORE);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_RESTORE_RESCHEDULED);
SPECIALIZE_KFD_KIND_NAME(EVENT_QUEUE, ROCPROFILER_KFD_EVENT_QUEUE_RESTORE);
SPECIALIZE_KFD_KIND_NAME(EVENT_UNMAP_FROM_GPU, ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_MMU_NOTIFY);
SPECIALIZE_KFD_KIND_NAME(EVENT_UNMAP_FROM_GPU, ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_MMU_NOTIFY_MIGRATE);
SPECIALIZE_KFD_KIND_NAME(EVENT_UNMAP_FROM_GPU, ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_UNMAP_FROM_CPU);
SPECIALIZE_KFD_KIND_NAME(EVENT_DROPPED_EVENTS, ROCPROFILER_KFD_EVENT_DROPPED_EVENTS);
// Paired records
SPECIALIZE_KFD_KIND_NAME(PAGE_MIGRATE, ROCPROFILER_KFD_PAGE_MIGRATE_PREFETCH);
SPECIALIZE_KFD_KIND_NAME(PAGE_MIGRATE, ROCPROFILER_KFD_PAGE_MIGRATE_PAGEFAULT_GPU);
SPECIALIZE_KFD_KIND_NAME(PAGE_MIGRATE, ROCPROFILER_KFD_PAGE_MIGRATE_PAGEFAULT_CPU);
SPECIALIZE_KFD_KIND_NAME(PAGE_MIGRATE, ROCPROFILER_KFD_PAGE_MIGRATE_TTM_EVICTION);
SPECIALIZE_KFD_KIND_NAME(PAGE_FAULT, ROCPROFILER_KFD_PAGE_FAULT_READ_FAULT_MIGRATED);
SPECIALIZE_KFD_KIND_NAME(PAGE_FAULT, ROCPROFILER_KFD_PAGE_FAULT_READ_FAULT_UPDATED);
SPECIALIZE_KFD_KIND_NAME(PAGE_FAULT, ROCPROFILER_KFD_PAGE_FAULT_WRITE_FAULT_MIGRATED);
SPECIALIZE_KFD_KIND_NAME(PAGE_FAULT, ROCPROFILER_KFD_PAGE_FAULT_WRITE_FAULT_UPDATED);
SPECIALIZE_KFD_KIND_NAME(QUEUE, ROCPROFILER_KFD_QUEUE_EVICT_SVM);
SPECIALIZE_KFD_KIND_NAME(QUEUE, ROCPROFILER_KFD_QUEUE_EVICT_USERPTR);
SPECIALIZE_KFD_KIND_NAME(QUEUE, ROCPROFILER_KFD_QUEUE_EVICT_TTM);
SPECIALIZE_KFD_KIND_NAME(QUEUE, ROCPROFILER_KFD_QUEUE_EVICT_SUSPEND);
SPECIALIZE_KFD_KIND_NAME(QUEUE, ROCPROFILER_KFD_QUEUE_EVICT_CRIU_CHECKPOINT);
SPECIALIZE_KFD_KIND_NAME(QUEUE, ROCPROFILER_KFD_QUEUE_EVICT_CRIU_RESTORE);
// clang-format on
#endif
@@ -1,6 +1,6 @@
// MIT License
//
// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved.
// 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
@@ -24,22 +24,22 @@
#include "lib/common/container/small_vector.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/rocprofiler.h>
namespace rocprofiler
{
namespace page_migration
namespace kfd
{
const char*
name_by_id(uint32_t id);
name_by_id(uint32_t kind, uint32_t id);
std::vector<uint32_t>
get_ids();
get_ids(uint32_t kind);
rocprofiler_status_t
init();
void
finalize();
} // namespace page_migration
} // namespace kfd
} // namespace rocprofiler
@@ -0,0 +1,48 @@
# 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.
rocprofiler_deactivate_clang_tidy()
include(GoogleTest)
set(ROCPROFILER_LIB_KFD_EVENTS_TEST_SOURCES parser.cpp)
add_executable(kfd-events-parser)
target_sources(kfd-events-parser PRIVATE ${ROCPROFILER_LIB_KFD_EVENTS_TEST_SOURCES})
target_link_libraries(
kfd-events-parser
PRIVATE rocprofiler-sdk::rocprofiler-sdk-common-library
rocprofiler-sdk::rocprofiler-sdk-static-library GTest::gtest
GTest::gtest_main)
gtest_add_tests(
TARGET kfd-events-parser
SOURCES ${ROCPROFILER_LIB_COUNTER_TEST_SOURCES}
TEST_LIST kfd-events-parsers_TESTS
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set_tests_properties(
${kfd-events-parsers_TESTS}
PROPERTIES TIMEOUT 45 LABELS "unittests;kfd-events" FAIL_REGULAR_EXPRESSION
"${ROCPROFILER_DEFAULT_FAIL_REGEX}")
@@ -0,0 +1,274 @@
// 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.
#include <chrono>
#include <cstddef>
#include <limits>
#include <random>
#include <string>
#include <string_view>
#include "lib/rocprofiler-sdk/kfd/defines.hpp"
#include "lib/rocprofiler-sdk/kfd/utils.hpp"
#include "rocprofiler-sdk/kfd/kfd_id.h"
#include <fmt/core.h>
#include <gtest/gtest.h>
#include <unistd.h>
namespace rocprofiler
{
namespace kfd
{
#define ROCPROFILER_LIB_ROCPROFILER_SDK_KFD_CPP_IMPL 1
#include "lib/rocprofiler-sdk/kfd/kfd.def.cpp"
#undef ROCPROFILER_LIB_ROCPROFILER_SDK_KFD_CPP_IMPL
} // namespace kfd
} // namespace rocprofiler
namespace
{
using namespace rocprofiler::kfd;
// Internally, get_node_map is called for a given gpu ID
// We are testing the parser, so use an identity mapping
const agent_id_map_t agent_map = {
{0, {0}},
{0x1ea0, {0x1ea0}},
{0xfc7d, {0xfc7d}},
{0xad5c, {0xad5c}},
};
kfd_event_record
parse(std::string_view arg)
{
size_t kfd_id = std::numeric_limits<size_t>::max();
const auto scan_count = std::sscanf(arg.data(), "%lx ", &kfd_id);
EXPECT_EQ(scan_count, 1);
EXPECT_GE(kfd_id, KFD_SMI_EVENT_MIGRATE_START);
EXPECT_LE(kfd_id, KFD_SMI_EVENT_DROPPED_EVENT);
auto event_id = to_rocprofiler_kfd_event_id_func(static_cast<rocprofiler_kfd_event_id>(kfd_id),
std::make_index_sequence<KFD_EVENT_LAST>{});
EXPECT_GE(event_id, KFD_EVENT_PAGE_MIGRATE_START);
EXPECT_LE(event_id, KFD_EVENT_DROPPED_EVENT);
return parse_event(event_id, agent_map, arg);
}
} // namespace
/* The following tests ensure that we can correctly parse and convert an event to the structured
equivalent
*/
TEST(rocprofiler_lib, parse_kfd_event_page_migrate_start)
{
using namespace rocprofiler::kfd;
const auto event = parse("5 14601738586508 -152990 @7f0f15200(2c02) 0->1ea0 1ea0:0 1");
const auto migrate_start = event.data.page_migrate_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_GPU);
EXPECT_EQ(migrate_start.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE);
EXPECT_EQ(migrate_start.operation, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_GPU);
EXPECT_EQ(migrate_start.timestamp, 14601738586508);
EXPECT_EQ(migrate_start.pid, 152990);
EXPECT_EQ(migrate_start.start_address.handle, 0x7f0f15200 << 12);
EXPECT_EQ(migrate_start.end_address.handle, (0x7f0f15200 + 0x2c02) << 12);
EXPECT_EQ(migrate_start.src_agent.handle, 0);
EXPECT_EQ(migrate_start.dst_agent.handle, 0x1ea0);
EXPECT_EQ(migrate_start.prefetch_agent.handle, 0x1ea0);
EXPECT_EQ(migrate_start.preferred_agent.handle, 0);
EXPECT_EQ(migrate_start.error_code, 0);
}
TEST(rocprofiler_lib, parse_kfd_event_page_migrate_end)
{
using namespace rocprofiler::kfd;
const auto event = parse("6 15202910515905 -152990 @7f0f15200(2c02) 0->1ea0 1 75");
const auto migrate_end = event.data.page_migrate_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_END);
EXPECT_EQ(migrate_end.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_MIGRATE);
EXPECT_EQ(migrate_end.operation, ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_END);
EXPECT_EQ(migrate_end.timestamp, 15202910515905);
EXPECT_EQ(migrate_end.pid, 152990);
EXPECT_EQ(migrate_end.start_address.handle, 0x7f0f15200 << 12);
EXPECT_EQ(migrate_end.end_address.handle, (0x7f0f15200 + 0x2c02) << 12);
EXPECT_EQ(migrate_end.src_agent.handle, 0);
EXPECT_EQ(migrate_end.dst_agent.handle, 0x1ea0);
EXPECT_EQ(migrate_end.prefetch_agent.handle, 0); // Not generated for end event
EXPECT_EQ(migrate_end.preferred_agent.handle, 0); // Not generated for end event
EXPECT_EQ(migrate_end.error_code, 75);
}
TEST(rocprofiler_lib, parse_kfd_event_page_fault_start)
{
using namespace rocprofiler::kfd;
const auto event = parse("7 40507688414784 -156893 @7fa608127(ad5c) R");
const auto page_fault = event.data.page_fault_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_PAGE_FAULT_START_READ_FAULT);
EXPECT_EQ(page_fault.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT);
EXPECT_EQ(page_fault.operation, ROCPROFILER_KFD_EVENT_PAGE_FAULT_START_READ_FAULT);
EXPECT_EQ(page_fault.timestamp, 40507688414784);
EXPECT_EQ(page_fault.pid, 156893);
EXPECT_EQ(page_fault.agent_id.handle, 0xad5c);
EXPECT_EQ(page_fault.address.handle, 0x7fa608127 << 12);
}
TEST(rocprofiler_lib, parse_kfd_event_page_fault_end)
{
using namespace rocprofiler::kfd;
{
const auto event = parse("8 40507688514376 -156893 @7fa608127(ad5c) U");
const auto page_fault = event.data.page_fault_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_UPDATED);
EXPECT_EQ(page_fault.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT);
EXPECT_EQ(page_fault.operation, ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_UPDATED);
EXPECT_EQ(page_fault.timestamp, 40507688514376);
EXPECT_EQ(page_fault.pid, 156893);
EXPECT_EQ(page_fault.agent_id.handle, 0xad5c);
EXPECT_EQ(page_fault.address.handle, 0x7fa608127 << 12);
}
{
const auto event = parse("8 40507688516386 -156898 @7fa698127(ad5c) M");
const auto page_fault = event.data.page_fault_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_MIGRATED);
EXPECT_EQ(page_fault.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_PAGE_FAULT);
EXPECT_EQ(page_fault.operation, ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_MIGRATED);
EXPECT_EQ(page_fault.timestamp, 40507688516386);
EXPECT_EQ(page_fault.pid, 156898);
EXPECT_EQ(page_fault.agent_id.handle, 0xad5c);
EXPECT_EQ(page_fault.address.handle, 0x7fa698127 << 12);
}
}
TEST(rocprofiler_lib, parse_kfd_event_queue_eviction)
{
using namespace rocprofiler::kfd;
{
const auto event = parse("9 38086928279363 -125752 1ea0 1");
const auto queue_event = event.data.queue_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_USERPTR);
EXPECT_EQ(queue_event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(queue_event.operation, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_USERPTR);
EXPECT_EQ(queue_event.timestamp, 38086928279363);
EXPECT_EQ(queue_event.pid, 125752);
EXPECT_EQ(queue_event.agent_id.handle, 0x1ea0);
}
{
const auto event = parse("9 38086928279363 -125752 1ea0 3");
const auto queue_event = event.data.queue_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SUSPEND);
EXPECT_EQ(queue_event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(queue_event.operation, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SUSPEND);
EXPECT_EQ(queue_event.timestamp, 38086928279363);
EXPECT_EQ(queue_event.pid, 125752);
EXPECT_EQ(queue_event.agent_id.handle, 0x1ea0);
}
}
TEST(rocprofiler_lib, parse_kfd_event_queue_restore)
{
using namespace rocprofiler::kfd;
{
const auto event = parse("a 38652512365099 -131057 fc7d");
const auto queue_event = event.data.queue_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_QUEUE_RESTORE);
EXPECT_EQ(queue_event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(queue_event.operation, ROCPROFILER_KFD_EVENT_QUEUE_RESTORE);
EXPECT_EQ(queue_event.timestamp, 38652512365099);
EXPECT_EQ(queue_event.pid, 131057);
EXPECT_EQ(queue_event.agent_id.handle, 0xfc7d);
}
{
const auto event = parse("a 40082605896929 -148516 fc7d R");
const auto queue_event = event.data.queue_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_QUEUE_RESTORE_RESCHEDULED);
EXPECT_EQ(queue_event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_QUEUE);
EXPECT_EQ(queue_event.operation, ROCPROFILER_KFD_EVENT_QUEUE_RESTORE_RESCHEDULED);
EXPECT_EQ(queue_event.timestamp, 40082605896929);
EXPECT_EQ(queue_event.pid, 148516);
EXPECT_EQ(queue_event.agent_id.handle, 0xfc7d);
}
}
TEST(rocprofiler_lib, parse_kfd_event_unmap_from_gpu)
{
using namespace rocprofiler::kfd;
const auto event = parse("b 15203214457186 -152990 @7f93ac200(200) 1ea0 2");
const auto unmap = event.data.unmap_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_UNMAP_FROM_GPU);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_TTM);
EXPECT_EQ(unmap.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_UNMAP_FROM_GPU);
EXPECT_EQ(unmap.operation, ROCPROFILER_KFD_EVENT_QUEUE_EVICT_TTM);
EXPECT_EQ(unmap.timestamp, 15203214457186);
EXPECT_EQ(unmap.pid, 152990);
EXPECT_EQ(unmap.start_address.handle, 0x7f93ac200 << 12);
EXPECT_EQ(unmap.end_address.handle, (0x7f93ac200 + 0x200) << 12);
EXPECT_EQ(unmap.agent_id.handle, 0x1ea0);
}
TEST(rocprofiler_lib, parse_kfd_event_dropped_event)
{
using namespace rocprofiler::kfd;
const auto event = parse("e 18203445217186 -323990 5");
const auto dropped = event.data.dropped_event;
EXPECT_EQ(event.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_DROPPED_EVENTS);
EXPECT_EQ(event.operation, ROCPROFILER_KFD_EVENT_DROPPED_EVENTS);
EXPECT_EQ(dropped.kind, ROCPROFILER_BUFFER_TRACING_KFD_EVENT_DROPPED_EVENTS);
EXPECT_EQ(dropped.operation, ROCPROFILER_KFD_EVENT_DROPPED_EVENTS);
EXPECT_EQ(dropped.timestamp, 18203445217186);
EXPECT_EQ(dropped.pid, 323990);
EXPECT_EQ(dropped.count, 5);
}
@@ -1,6 +1,6 @@
// MIT License
//
// Copyright (c) 2023-2025 Advanced Micro Devices, Inc. All rights reserved.
// 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
@@ -30,94 +30,57 @@
#include <algorithm>
#include <cstdint>
#include <limits>
#include <string_view>
#include <unordered_map>
#include <utility>
namespace rocprofiler
{
namespace page_migration
namespace kfd
{
/* serves as an overview of what events we capture and report
struct event_page_fault_start_t
enum rocprofiler_kfd_event_id
{
int kind;
uint64_t timestamp;
int pid;
int node_id;
uint64_t address;
fault_t fault;
KFD_EVENT_NONE = -1,
KFD_EVENT_PAGE_MIGRATE_START,
KFD_EVENT_PAGE_MIGRATE_END,
KFD_EVENT_PAGE_FAULT_START,
KFD_EVENT_PAGE_FAULT_END,
KFD_EVENT_QUEUE_EVICTION,
KFD_EVENT_QUEUE_RESTORE,
KFD_EVENT_UNMAP_FROM_GPU,
KFD_EVENT_DROPPED_EVENT,
KFD_EVENT_LAST,
};
struct event_page_fault_end_t
struct kfd_event_record
{
int kind;
uint64_t timestamp;
uint32_t pid;
int node_id;
uint64_t address;
bool migrated;
rocprofiler_buffer_tracing_kind_t kind{ROCPROFILER_BUFFER_TRACING_NONE};
int operation{-1};
union
{
rocprofiler_buffer_tracing_kfd_event_page_migrate_record_t page_migrate_event;
rocprofiler_buffer_tracing_kfd_event_page_fault_record_t page_fault_event;
rocprofiler_buffer_tracing_kfd_event_queue_record_t queue_event;
rocprofiler_buffer_tracing_kfd_event_unmap_from_gpu_record_t unmap_event;
rocprofiler_buffer_tracing_kfd_event_dropped_events_record_t dropped_event;
rocprofiler_buffer_tracing_kfd_page_migrate_record_t page_migrate_record;
rocprofiler_buffer_tracing_kfd_page_fault_record_t page_fault_record;
rocprofiler_buffer_tracing_kfd_queue_record_t queue_record;
} data;
};
struct event_migrate_start_t
{
int kind;
uint64_t timestamp;
uint32_t pid;
uint64_t start;
uint64_t end_offset;
uint32_t from;
uint32_t to;
uint32_t prefetch_node; // last prefetch location, 0 for CPU, or GPU id
uint32_t preferred_node; // perferred location, 0 for CPU, or GPU id
uint32_t trigger;
};
using agent_id_map_t = std::unordered_map<uint64_t, rocprofiler_agent_id_t>;
struct event_migrate_end_t
{
int kind;
uint64_t timestamp;
uint32_t pid;
uint64_t start;
uint64_t end_offset;
uint32_t from;
uint32_t to;
uint32_t trigger;
};
template <uint32_t>
struct kfd_event_info;
struct event_queue_eviction_t
{
int kind;
uint64_t timestamp;
uint32_t pid;
int node_id;
uint32_t trigger;
};
template <uint32_t>
struct kfd_kind_info;
struct event_queue_restore_t
{
int kind;
uint64_t timestamp;
uint32_t pid;
int node_id;
bool rescheduled;
};
struct event_unmap_from_gpu_t
{
int kind;
uint64_t timestamp;
uint32_t pid;
uint64_t address;
uint64_t size;
int node_id;
uint32_t trigger;
};
*/
template <size_t>
struct page_migration_info;
template <uint32_t, uint32_t>
struct kfd_operation_info;
using namespace rocprofiler::common;
@@ -145,14 +108,14 @@ constexpr size_t bitmask(std::index_sequence<Args...>)
template <size_t... Ints>
constexpr size_t kfd_bitmask(std::index_sequence<Ints...>)
{
return (page_migration_info<Ints>::kfd_bitmask | ...);
return (kfd_event_info<Ints>::kfd_bitmask | ...);
}
template <size_t OpInx, size_t... OpInxs>
constexpr size_t
kfd_bitmask_impl(size_t rocprof_op, std::index_sequence<OpInx, OpInxs...>)
{
if(rocprof_op == OpInx) return page_migration_info<OpInx>::kfd_bitmask;
if(rocprof_op == OpInx) return kfd_event_info<OpInx>::kfd_bitmask;
if constexpr(sizeof...(OpInxs) > 0)
return kfd_bitmask_impl(rocprof_op, std::index_sequence<OpInxs...>{});
else
@@ -174,24 +137,25 @@ kfd_bitmask(const container::small_vector<size_t>& rocprof_event_ids,
template <size_t OpInx, size_t... OpInxs>
constexpr size_t
kfd_to_rocprof_op(size_t kfd_id, std::index_sequence<OpInx, OpInxs...>)
to_rocprofiler_kfd_event_id_func(size_t kfd_id, std::index_sequence<OpInx, OpInxs...>)
{
if(kfd_id == page_migration_info<OpInx>::kfd_operation) return OpInx;
if(kfd_id == kfd_event_info<OpInx>::kfd_id) return OpInx;
if constexpr(sizeof...(OpInxs) > 0)
return kfd_to_rocprof_op(kfd_id, std::index_sequence<OpInxs...>{});
return to_rocprofiler_kfd_event_id_func(kfd_id, std::index_sequence<OpInxs...>{});
else
return 0;
return std::numeric_limits<size_t>::max();
}
kfd_event_record
parse_event(size_t event_id, const agent_id_map_t& agents, std::string_view strn);
size_t
get_rocprof_op(const std::string_view event_data);
void
kfd_readlines(const std::string_view str, void(handler)(std::string_view));
using rocprof_buffer_op_t = rocprofiler_page_migration_operation_t;
using node_fd_t = int;
} // namespace page_migration
} // namespace kfd
} // namespace rocprofiler
@@ -1,7 +0,0 @@
#
#
set(ROCPROFILER_LIB_UVM_SOURCES abi.cpp page_migration.cpp)
set(ROCPROFILER_LIB_UVM_HEADERS defines.hpp page_migration.hpp utils.hpp)
target_sources(rocprofiler-sdk-object-library PRIVATE ${ROCPROFILER_LIB_UVM_SOURCES}
${ROCPROFILER_LIB_UVM_HEADERS})
@@ -1,107 +0,0 @@
// 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 <fmt/core.h>
#include "lib/common/container/small_vector.hpp"
#include "lib/common/defines.hpp"
#include "lib/common/mpl.hpp"
#include "lib/rocprofiler-sdk/details/kfd_ioctl.h"
#include "lib/rocprofiler-sdk/page_migration/utils.hpp"
#define ASSERT_SAME(A, B) static_assert(static_cast<size_t>(A) == static_cast<size_t>(B))
#define ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL 1
#include "lib/rocprofiler-sdk/page_migration/page_migration.def.cpp"
#undef ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL
namespace rocprofiler
{
namespace page_migration
{
using namespace rocprofiler::page_migration;
using namespace rocprofiler::common::container;
using rocprofiler_page_migration_seq_t = std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>;
static_assert(KFD_SMI_EVENT_NONE == 0);
static_assert(KFD_SMI_EVENT_MIGRATE_START == 5);
static_assert(KFD_SMI_EVENT_MIGRATE_END == 6);
static_assert(KFD_SMI_EVENT_PAGE_FAULT_START == 7);
static_assert(KFD_SMI_EVENT_PAGE_FAULT_END == 8);
static_assert(KFD_SMI_EVENT_QUEUE_EVICTION == 9);
static_assert(KFD_SMI_EVENT_QUEUE_RESTORE == 10);
static_assert(KFD_SMI_EVENT_UNMAP_FROM_GPU == 11);
static_assert(KFD_SMI_EVENT_DROPPED_EVENT == 12);
static_assert(KFD_SMI_EVENT_ALL_PROCESS == 64);
// Update page_migration.def.cpp with event mappings
// Update page_migration.cpp to parse and report new event
static_assert(ROCPROFILER_PAGE_MIGRATION_LAST == 9,
"New event added, update KFD to ROCPROFILER mappings");
// clang-format off
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_TRIGGER_PAGEFAULT_GPU, KFD_MIGRATE_TRIGGER_PAGEFAULT_GPU );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_TRIGGER_PAGEFAULT_CPU, KFD_MIGRATE_TRIGGER_PAGEFAULT_CPU );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_TRIGGER_TTM_EVICTION, KFD_MIGRATE_TRIGGER_TTM_EVICTION );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_SVM, KFD_QUEUE_EVICTION_TRIGGER_SVM );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_USERPTR, KFD_QUEUE_EVICTION_TRIGGER_USERPTR );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_TTM, KFD_QUEUE_EVICTION_TRIGGER_TTM );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_SUSPEND, KFD_QUEUE_EVICTION_TRIGGER_SUSPEND );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_CRIU_CHECKPOINT, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_CRIU_RESTORE, KFD_QUEUE_EVICTION_CRIU_RESTORE );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU_TRIGGER_MMU_NOTIFY, KFD_SVM_UNMAP_TRIGGER_MMU_NOTIFY );
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU_TRIGGER_MMU_NOTIFY_MIGRATE, KFD_SVM_UNMAP_TRIGGER_MMU_NOTIFY_MIGRATE);
ASSERT_SAME(ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU_TRIGGER_UNMAP_FROM_CPU, KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU );
// clang-format on
static_assert(kfd_bitmask(std::index_sequence<ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_START,
ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END,
ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU>()) ==
(KFD_SMI_EVENT_MASK_FROM_INDEX(KFD_SMI_EVENT_PAGE_FAULT_START) |
KFD_SMI_EVENT_MASK_FROM_INDEX(KFD_SMI_EVENT_MIGRATE_END) |
KFD_SMI_EVENT_MASK_FROM_INDEX(KFD_SMI_EVENT_UNMAP_FROM_GPU)));
static_assert((page_migration_info<ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END>::kfd_bitmask |
page_migration_info<ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION>::kfd_bitmask |
page_migration_info<ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU>::kfd_bitmask) ==
(KFD_SMI_EVENT_MASK_FROM_INDEX(KFD_SMI_EVENT_MIGRATE_END) |
KFD_SMI_EVENT_MASK_FROM_INDEX(KFD_SMI_EVENT_QUEUE_EVICTION) |
KFD_SMI_EVENT_MASK_FROM_INDEX(KFD_SMI_EVENT_UNMAP_FROM_GPU)));
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_MIGRATE_START, rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_START);
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_MIGRATE_END, rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END);
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_PAGE_FAULT_START,
rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_START);
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_PAGE_FAULT_END, rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_END);
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_QUEUE_EVICTION, rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION);
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_QUEUE_RESTORE, rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_QUEUE_RESTORE);
static_assert(kfd_to_rocprof_op(KFD_SMI_EVENT_UNMAP_FROM_GPU, rocprofiler_page_migration_seq_t{}) ==
ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU);
} // namespace page_migration
} // namespace rocprofiler
@@ -1,937 +0,0 @@
// 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 "lib/rocprofiler-sdk/page_migration/page_migration.hpp"
#include "lib/common/logging.hpp"
#include "lib/common/mpl.hpp"
#include "lib/common/static_object.hpp"
#include "lib/common/utility.hpp"
#include "lib/rocprofiler-sdk/agent.hpp"
#include "lib/rocprofiler-sdk/buffer.hpp"
#include "lib/rocprofiler-sdk/context/context.hpp"
#include "lib/rocprofiler-sdk/details/kfd_ioctl.h"
#include "lib/rocprofiler-sdk/internal_threading.hpp"
#include "lib/rocprofiler-sdk/page_migration/utils.hpp"
#include <rocprofiler-sdk/agent.h>
#include <rocprofiler-sdk/buffer_tracing.h>
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/hsa/api_id.h>
#include <rocprofiler-sdk/hsa/table_id.h>
#include <fmt/core.h>
#include <hsa/amd_hsa_signal.h>
#include <hsa/hsa.h>
#include <sys/poll.h>
#include <unistd.h>
#include <atomic>
#include <cerrno>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <memory>
#include <ratio>
#include <stdexcept>
#include <string>
#include <string_view>
#include <thread>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <fcntl.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#define ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL 1
#include "page_migration.def.cpp"
#undef ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL
namespace rocprofiler
{
namespace page_migration
{
template <typename T>
using small_vector = common::container::small_vector<T>;
using context_t = context::context;
using context_array_t = common::container::small_vector<const context_t*>;
template <size_t>
struct page_migration_info;
template <size_t>
struct kfd_event_info;
template <typename EnumT, int ValueE>
struct page_migration_enum_info;
template <typename EnumT>
struct page_migration_bounds;
// Parsing and utilities
namespace
{
constexpr auto
page_to_bytes(size_t val)
{
// each page is 4KB = 4096 bytes
return val << 12;
}
template <size_t>
page_migration_record_t parse_event(std::string_view)
{
ROCP_FATAL_IF(false) << page_migration_info<ROCPROFILER_PAGE_MIGRATION_NONE>::format_str;
return {};
}
auto
get_node_agent_id(uint32_t _node_id)
{
using agent_id_map_t = std::unordered_map<uint64_t, rocprofiler_agent_id_t>;
static auto*& _data = static_object<agent_id_map_t>::construct([]() {
auto _v = std::unordered_map<uint64_t, rocprofiler_agent_id_t>{};
for(const auto* agent : agent::get_agents())
_v.emplace(agent->gpu_id, agent->id);
return _v;
}());
CHECK(_data != nullptr);
ROCP_FATAL_IF(_data->count(_node_id) == 0) << "page_migration: unknown node id: " << _node_id;
return _data->at(_node_id);
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_START>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.page_fault_start;
uint32_t kind{};
uint32_t _node_id = 0;
char fault;
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_START>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&e.address,
&_node_id,
&fault);
e.read_fault = (fault == 'R');
e.address = page_to_bytes(e.address);
e.agent_id = get_node_agent_id(_node_id);
ROCP_TRACE << fmt::format("Page fault start [ ts: {} pid: {} addr: 0x{:X} node: {} ] \n",
rec.timestamp,
rec.pid,
e.address,
e.agent_id.handle);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_END>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.page_fault_end;
uint32_t kind{};
uint32_t _node_id = 0;
char migrated;
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_END>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&e.address,
&_node_id,
&migrated);
// M or U -> migrated / unmigrated?
if(migrated == 'M')
e.migrated = true;
else if(migrated == 'U')
e.migrated = false;
else
ROCP_WARNING << "Unknown PAGE_FAULT_END migrated/unmigrated state";
e.address = page_to_bytes(e.address);
e.agent_id = get_node_agent_id(_node_id);
ROCP_TRACE << fmt::format(
"Page fault end [ ts: {} pid: {} addr: 0x{:X} node: {} migrated: {} ] \n",
rec.timestamp,
rec.pid,
e.address,
e.agent_id.handle,
migrated);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_START>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.page_migrate_start;
uint32_t kind{};
uint32_t trigger{};
uint32_t _from_node = 0;
uint32_t _to_node = 0;
uint32_t _prefetch_node = 0;
uint32_t _preferred_node = 0;
std::sscanf(
str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_START>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&e.start_addr,
&e.end_addr,
&_from_node,
&_to_node,
&_prefetch_node,
&_preferred_node,
&trigger);
e.end_addr += e.start_addr;
e.trigger = static_cast<migrate_trigger_t>(trigger);
e.start_addr = page_to_bytes(e.start_addr);
e.end_addr = page_to_bytes(e.end_addr) - 1;
e.from_agent = get_node_agent_id(_from_node);
e.to_agent = get_node_agent_id(_to_node);
e.prefetch_agent = get_node_agent_id(_prefetch_node);
e.preferred_agent = get_node_agent_id(_preferred_node);
ROCP_TRACE << fmt::format(
"Page migrate start [ ts: {} pid: {} addr s: 0x{:X} addr "
"e: 0x{:X} size: {}B from node: {} to node: {} prefetch node: {} preferred node: {} "
"trigger: {} ] \n",
rec.timestamp,
rec.pid,
e.start_addr,
e.end_addr,
(e.end_addr - e.start_addr),
e.from_agent.handle,
e.to_agent.handle,
e.prefetch_agent.handle,
e.preferred_agent.handle,
trigger);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.page_migrate_end;
uint32_t kind{};
uint32_t trigger{};
uint32_t _from_node = 0;
uint32_t _to_node = 0;
int32_t _error_code = 0;
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&e.start_addr,
&e.end_addr,
&_from_node,
&_to_node,
&trigger,
&_error_code);
e.end_addr += e.start_addr;
e.trigger = static_cast<migrate_trigger_t>(trigger);
e.start_addr = page_to_bytes(e.start_addr);
e.end_addr = page_to_bytes(e.end_addr) - 1;
e.from_agent = get_node_agent_id(_from_node);
e.to_agent = get_node_agent_id(_to_node);
// For older kernel versions, no event is generated in the error case,
// so the default value of 0 is correct for any given end event.
e.error_code = _error_code;
ROCP_TRACE << fmt::format("Page migrate end [ ts: {} pid: {} addr s: 0x{:X} addr e: "
"0x{:X} from node: {} to node: {} trigger: {} error code: {}] \n",
rec.timestamp,
rec.pid,
e.start_addr,
e.end_addr,
e.from_agent.handle,
e.to_agent.handle,
trigger,
_error_code);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.queue_eviction;
uint32_t kind{};
uint32_t trigger{};
uint32_t _node_id = 0;
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&_node_id,
&trigger);
e.trigger = static_cast<queue_suspend_trigger_t>(trigger);
e.agent_id = get_node_agent_id(_node_id);
ROCP_TRACE << fmt::format("Queue evict [ ts: {} pid: {} node: {} trigger: {} ] \n",
rec.timestamp,
rec.pid,
e.agent_id.handle,
trigger);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_QUEUE_RESTORE>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.queue_restore;
uint32_t kind{};
uint32_t _node_id = 0;
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_QUEUE_RESTORE>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&_node_id);
// check if we have a valid char at the end. -1 has \0
if(str[str.size() - 2] == 'R')
e.rescheduled = true;
else
e.rescheduled = false;
e.agent_id = get_node_agent_id(_node_id);
ROCP_TRACE << fmt::format(
"Queue restore [ ts: {} pid: {} node: {} ] \n", rec.timestamp, rec.pid, e.agent_id.handle);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.unmap_from_gpu;
uint32_t kind{};
uint32_t trigger{};
uint32_t _node_id = 0;
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&e.start_addr,
&e.end_addr,
&_node_id,
&trigger);
e.end_addr += e.start_addr;
e.trigger = static_cast<unmap_from_gpu_trigger_t>(trigger);
e.start_addr = page_to_bytes(e.start_addr);
e.end_addr = page_to_bytes(e.end_addr);
e.agent_id = get_node_agent_id(_node_id);
ROCP_TRACE << fmt::format(
"Unmap from GPU [ ts: {} pid: {} start addr: 0x{:X} end addr: 0x{:X} "
"node: {} trigger {} ] \n",
rec.timestamp,
rec.pid,
e.start_addr,
e.end_addr,
e.agent_id.handle,
trigger);
return rec;
}
template <>
page_migration_record_t
parse_event<ROCPROFILER_PAGE_MIGRATION_DROPPED_EVENT>(std::string_view str)
{
auto rec = page_migration_record_t{};
auto& e = rec.args.dropped_event;
uint32_t kind{};
std::sscanf(str.data(),
page_migration_info<ROCPROFILER_PAGE_MIGRATION_DROPPED_EVENT>::format_str.data(),
&kind,
&rec.timestamp,
&rec.pid,
&e.dropped_events_count);
ROCP_TRACE << fmt::format("Dropped events [ ts: {} pid: {} dropped count: {} ] \n",
rec.timestamp,
rec.pid,
e.dropped_events_count);
return rec;
}
template <>
page_migration_record_t parse_event<ROCPROFILER_PAGE_MIGRATION_NONE>(std::string_view)
{
ROCP_CI_LOG(WARNING)
<< "ROCPROFILER_PAGE_MIGRATION_NONE for parsing page migration events should not happen";
return common::init_public_api_struct(page_migration_record_t{});
}
template <size_t OpInx, size_t... OpInxs>
page_migration_record_t
parse_event(size_t event_id, std::string_view strn, std::index_sequence<OpInx, OpInxs...>)
{
if(OpInx == static_cast<uint32_t>(event_id))
{
auto rec = parse_event<OpInx>(strn);
rec.size = sizeof(page_migration_record_t);
rec.kind = ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION;
rec.operation = static_cast<rocprofiler_page_migration_operation_t>(OpInx);
return rec;
}
if constexpr(sizeof...(OpInxs) > 0)
return parse_event(event_id, strn, std::index_sequence<OpInxs...>{});
return page_migration_record_t{};
}
/* -----------------------------------------------------------------------------------*/
} // namespace
size_t
get_rocprof_op(const std::string_view event_data)
{
uint32_t kfd_event_id{};
std::sscanf(event_data.data(), "%x ", &kfd_event_id);
auto rocprof_id =
kfd_to_rocprof_op(static_cast<kfd_event_id_t>(kfd_event_id),
std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
ROCP_CI_LOG_IF(WARNING, rocprof_id == 0)
<< fmt::format("Failed to parse KFD event ID {}. Parsed ID: {}, SDK ID: {}\n",
event_data[0],
kfd_event_id,
rocprof_id);
return rocprof_id;
}
void
kfd_readlines(const std::string_view str, void(handler)(std::string_view))
{
const auto find_newline = [&](auto b) { return std::find(b, str.cend(), '\n'); };
const auto* cursor = str.cbegin();
for(const auto* pos = find_newline(cursor); pos != str.cend(); pos = find_newline(cursor))
{
size_t char_count = pos - cursor;
assert(char_count > 0);
std::string_view event_str{cursor, char_count};
ROCP_INFO << fmt::format("KFD event: [{}]", event_str);
handler(event_str);
cursor = pos + 1;
}
}
// Event capture and reporting
namespace
{
constexpr auto kfd_ioctl_version = (1000 * KFD_IOCTL_MAJOR_VERSION) + KFD_IOCTL_MINOR_VERSION;
// Support has been added in kfdv >= 1.10+
static_assert(kfd_ioctl_version >= 1010, "KFD SMI support missing in kfd_ioctl.h");
auto
get_contexts(int operation)
{
auto active_contexts = context::get_active_contexts([](const auto* ctx) {
return (ctx->buffered_tracer &&
ctx->buffered_tracer->domains(ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION));
});
auto operation_ctxs = context::context_array_t{};
for(const auto* itr : active_contexts)
{
// if the given domain + op is not enabled, skip this context
if(itr->buffered_tracer->domains(ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION, operation))
{
operation_ctxs.emplace_back(itr);
}
}
return operation_ctxs;
}
void
handle_reporting(std::string_view event_data)
{
const auto op_inx = get_rocprof_op(event_data);
auto buffered_contexts = get_contexts(op_inx);
if(buffered_contexts.empty()) return;
// Parse and process the event
auto record = parse_event(
op_inx, event_data, std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
for(const auto& itr : buffered_contexts)
{
auto* buffer = buffer::get_buffer(
itr->buffered_tracer->buffer_data.at(ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION));
CHECK_NOTNULL(buffer)->emplace(
ROCPROFILER_BUFFER_CATEGORY_TRACING, ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION, record);
}
}
} // namespace
namespace
{
void poll_events(small_vector<pollfd>);
}
// KFD utils
namespace kfd
{
using fd_flags_t = decltype(EFD_NONBLOCK);
using fd_t = decltype(pollfd::fd);
constexpr auto KFD_DEVICE_PATH{"/dev/kfd"};
SPECIALIZE_KFD_IOC_IOCTL(kfd_ioctl_get_version_args, AMDKFD_IOC_GET_VERSION);
SPECIALIZE_KFD_IOC_IOCTL(kfd_ioctl_smi_events_args, AMDKFD_IOC_SMI_EVENTS);
namespace
{
template <typename T>
auto
ioctl(int kfd_fd, T& args)
{
// from hsaKmt library (hsakmt/src/libhsakmt.c)
int exit_code{};
do
{
exit_code = ::ioctl(kfd_fd, IOC_event<T>::value, static_cast<void*>(&args));
} while(exit_code == -1 && (errno == EINTR || errno == EAGAIN));
if(exit_code == -1 && errno == EBADF)
{
/* In case pthread_atfork didn't catch it, this will
* make any subsequent hsaKmt calls fail in CHECK_KFD_OPEN.
*/
CHECK(true && "KFD file descriptor not valid in this process\n");
}
return exit_code;
}
struct kfd_device_fd
{
fd_t fd{-1};
kfd_device_fd()
{
fd = ::open(KFD_DEVICE_PATH, O_RDWR | O_CLOEXEC);
ROCP_FATAL_IF(fd == -1) << "Error opening KFD handle @ " << KFD_DEVICE_PATH;
}
~kfd_device_fd()
{
if(fd >= 0) close(fd);
}
};
const kfd_ioctl_get_version_args
get_version()
{
static kfd_ioctl_get_version_args version = [&]() {
auto args = kfd_ioctl_get_version_args{0, 0};
kfd_device_fd kfd_fd{};
if(ioctl(kfd_fd.fd, args) != -1)
ROCP_INFO << fmt::format("KFD v{}.{}", args.major_version, args.minor_version);
else
ROCP_ERROR << fmt::format("Could not determine KFD version");
return args;
}();
return version;
}
struct poll_kfd_t
{
static constexpr auto DEFAULT_FLAGS{EFD_CLOEXEC};
struct gpu_fd_t
{
unsigned int node_id = 0;
fd_t fd = {};
const rocprofiler_agent_t* agent = nullptr;
};
kfd_device_fd kfd_fd = {};
pollfd thread_notify = {};
std::thread bg_thread = {};
bool active = {false};
poll_kfd_t() = default;
poll_kfd_t(const small_vector<size_t>& rprof_ev)
: kfd_fd{kfd_device_fd{}}
{
small_vector<pollfd> file_handles = {};
const auto kfd_flags =
kfd_bitmask(rprof_ev, std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
ROCP_TRACE << fmt::format("Setting KFD flags to [0b{:b}] \n", kfd_flags);
// Create fd for notifying thread when we want to wake it up, and an eventfd for any events
// to this thread
file_handles.emplace_back(
pollfd{.fd = eventfd(0, DEFAULT_FLAGS), .events = 0, .revents = 0});
fd_t thread_pipes[2]{};
[&]() {
const auto retcode = pipe2(&thread_pipes[0], DEFAULT_FLAGS);
const auto _err = errno;
ROCP_FATAL_IF(retcode != 0)
<< fmt::format("Pipe creation for page-migration thread notify returned {} :: {}\n",
retcode,
strerror(_err));
}();
thread_notify = pollfd{
.fd = thread_pipes[1],
.events = POLLIN,
.revents = 0,
};
// add pipe listening end to fds to watch
file_handles.emplace_back(pollfd{thread_pipes[0], POLLIN, 0});
// get FD, start thread, and then enable events
for(const auto& agent : agent::get_agents())
{
if(agent->type == ROCPROFILER_AGENT_TYPE_GPU)
{
auto gpu_event_fd = get_node_fd(agent->gpu_id);
file_handles.emplace_back(pollfd{gpu_event_fd, POLLIN, 0});
ROCP_TRACE << fmt::format(
"GPU node {} with fd {} added\n", agent->gpu_id, gpu_event_fd);
}
}
// Enable KFD masked events by writing flags to kfd fd
for(size_t i = 2; i < file_handles.size(); ++i)
{
auto& fd = file_handles[i];
auto write_size = write(fd.fd, &kfd_flags, sizeof(kfd_flags));
ROCP_TRACE << fmt::format(
"Writing {} to GPU fd {} ({} bytes)\n", kfd_flags, fd.fd, write_size);
CHECK(write_size == sizeof(kfd_flags));
}
// start bg thread
internal_threading::notify_pre_internal_thread_create(ROCPROFILER_LIBRARY);
bg_thread = std::thread{poll_events, file_handles};
internal_threading::notify_post_internal_thread_create(ROCPROFILER_LIBRARY);
active = true;
}
static auto get_event_id(const std::string_view& strn)
{
uint32_t event_id{std::numeric_limits<uint32_t>::max()};
std::sscanf(strn.data(), "%x ", &event_id);
CHECK(event_id <= KFD_SMI_EVENT_ALL_PROCESS);
}
poll_kfd_t(const poll_kfd_t&) = delete;
poll_kfd_t& operator=(const poll_kfd_t&) = delete;
poll_kfd_t(poll_kfd_t&&) noexcept = default;
poll_kfd_t& operator=(poll_kfd_t&&) noexcept = default;
~poll_kfd_t()
{
ROCP_TRACE << fmt::format("Terminating poll_kfd\n");
if(!active) return;
// wake thread up
auto bytes_written{-1};
do
{
bytes_written = write(thread_notify.fd, "E", 1);
} while(bytes_written == -1 && (errno == EINTR || errno == EAGAIN));
bg_thread.join();
close(thread_notify.fd);
ROCP_TRACE << fmt::format("Background thread signalled\n");
}
node_fd_t get_node_fd(int gpu_node_id) const
{
kfd_ioctl_smi_events_args args{};
args.gpuid = gpu_node_id;
if(auto ret = ioctl(kfd_fd.fd, args); ret == -1)
ROCP_ERROR << fmt::format(
"Could not get GPU node {} file descriptor (exit code: {})", gpu_node_id, ret);
return args.anon_fd;
}
};
} // namespace
} // namespace kfd
// for all contexts
struct config
{
private:
kfd::poll_kfd_t kfd_handle{};
static inline config* _config{nullptr};
config(const small_vector<size_t>& _event_ids)
: kfd_handle{_event_ids}
{}
public:
static void init(const small_vector<size_t>& event_ids) { _config = new config{event_ids}; }
static void reset()
{
config* ptr = nullptr;
std::swap(ptr, _config);
delete ptr;
}
static void reset_on_fork() { _config = nullptr; }
};
namespace
{
void
poll_events(small_vector<pollfd> file_handles)
{
// storage to write records to, 1MB
constexpr size_t PREALLOCATE_ELEMENT_COUNT{1024 * 128};
std::string scratch_buffer(PREALLOCATE_ELEMENT_COUNT, '\0');
auto& exitfd = file_handles[1];
// Wait or spin on events.
// 0 -> return immediately even if no events
// -1 -> wait indefinitely
pthread_setname_np(pthread_self(), "bg:pagemigr");
for(auto& fd : file_handles)
{
ROCP_TRACE << fmt::format(
"Handle = {}, events = {}, revents = {}\n", fd.fd, fd.events, fd.revents);
}
while(true)
{
auto poll_ret = poll(file_handles.data(), file_handles.size(), -1);
if(poll_ret == -1)
{
ROCP_CI_LOG(WARNING)
<< "Background thread file descriptors for page-migration are invalid";
return;
}
if((exitfd.revents & POLLIN) != 0)
{
for(const auto& f : file_handles)
{
close(f.fd);
}
ROCP_INFO << "Terminating background thread\n";
return;
}
using namespace std::chrono_literals;
// 0 and 1 are for generic and pipe-notify handles
for(size_t i = 2; i < file_handles.size(); ++i)
{
auto& fd = file_handles[i];
// We have data to read, perhaps multiple events
if((fd.revents & POLLIN) != 0)
{
size_t status_size = read(fd.fd, scratch_buffer.data(), scratch_buffer.size());
auto event_strings = std::string_view{scratch_buffer.data(), status_size};
kfd_readlines(event_strings, handle_reporting);
}
fd.revents = 0;
}
}
}
template <size_t Idx, size_t... IdxTail>
const char*
name_by_id(const uint32_t id, std::index_sequence<Idx, IdxTail...>)
{
if(Idx == id) return page_migration_info<Idx>::name;
if constexpr(sizeof...(IdxTail) > 0)
return name_by_id(id, std::index_sequence<IdxTail...>{});
else
return nullptr;
}
template <size_t... Idx>
void
get_ids(std::vector<uint32_t>& _id_list, std::index_sequence<Idx...>)
{
auto _emplace = [](auto& _vec, uint32_t _v) {
if(_v < static_cast<uint32_t>(ROCPROFILER_HSA_AMD_EXT_API_ID_LAST)) _vec.emplace_back(_v);
};
(_emplace(_id_list, page_migration_info<Idx>::operation), ...);
}
bool
context_filter(const context::context* ctx)
{
return (ctx->buffered_tracer &&
(ctx->buffered_tracer->domains(ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION)));
}
template <size_t... Idx>
void
to_bitmask(small_vector<size_t>& _id_list, std::index_sequence<Idx...>)
{
auto _emplace = [](auto& _vec, uint32_t _v) {
if(_v < static_cast<uint32_t>(ROCPROFILER_HSA_AMD_EXT_API_ID_LAST)) _vec.emplace_back(_v);
};
(_emplace(_id_list, page_migration_info<Idx>::operation), ...);
}
template <size_t... Inxs>
rocprofiler_status_t init(std::index_sequence<Inxs...>)
{
static const small_vector<size_t> event_ids{Inxs...};
// Check if version is more than 1.11
auto ver = kfd::get_version();
if(ver.major_version * 1000 + ver.minor_version > 1011)
{
if(!context::get_registered_contexts(context_filter).empty())
{
config::init(event_ids);
}
return ROCPROFILER_STATUS_SUCCESS;
}
else
{
// Add a buffer record with this info
ROCP_ERROR << fmt::format(
"KFD does not support SVM event reporting in v{}.{} (requires v1.11)",
ver.major_version,
ver.minor_version);
return ROCPROFILER_STATUS_ERROR_INCOMPATIBLE_KERNEL;
}
}
} // namespace
} // namespace page_migration
} // namespace rocprofiler
namespace rocprofiler::page_migration
{
rocprofiler_status_t
init()
{
pthread_atfork(nullptr, nullptr, []() {
// null out child's copy on fork and reinitialize
// otherwise all children wait on the same thread to join
config::reset_on_fork();
init(std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
});
return init(std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
}
void
finalize()
{
config::reset();
}
const char*
name_by_id(uint32_t id)
{
return name_by_id(id, std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
}
std::vector<uint32_t>
get_ids()
{
auto _data = std::vector<uint32_t>{};
_data.reserve(ROCPROFILER_PAGE_MIGRATION_LAST);
get_ids(_data, std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>{});
return _data;
}
} // namespace rocprofiler::page_migration
@@ -1,60 +0,0 @@
// 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 "lib/rocprofiler-sdk/page_migration/defines.hpp"
#include "lib/rocprofiler-sdk/page_migration/page_migration.hpp"
#if defined(ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL) && \
ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL == 1
namespace rocprofiler
{
namespace page_migration
{
using namespace rocprofiler::common;
using kfd_event_id_t = decltype(KFD_SMI_EVENT_NONE);
using migrate_trigger_t = rocprofiler_page_migration_trigger_t;
using page_migration_record_t = rocprofiler_buffer_tracing_page_migration_record_t;
using queue_suspend_trigger_t = rocprofiler_page_migration_queue_suspend_trigger_t;
using unmap_from_gpu_trigger_t = rocprofiler_page_migration_unmap_from_gpu_trigger_t;
using trigger_type_list_t = common::mpl::type_list<rocprofiler_page_migration_trigger_t,
queue_suspend_trigger_t,
unmap_from_gpu_trigger_t>;
// clang-format off
// Map ROCPROF UVM enums to KFD enums
SPECIALIZE_PAGE_MIGRATION_INFO(NONE, NONE, "Error: Invalid UVM event from KFD" );
SPECIALIZE_PAGE_MIGRATION_INFO(PAGE_MIGRATE_START, MIGRATE_START, "%x %ld -%d @%lx(%lx) %x->%x %x:%x %d\n");
SPECIALIZE_PAGE_MIGRATION_INFO(PAGE_MIGRATE_END, MIGRATE_END, "%x %ld -%d @%lx(%lx) %x->%x %d %d\n" );
SPECIALIZE_PAGE_MIGRATION_INFO(PAGE_FAULT_START, PAGE_FAULT_START, "%x %ld -%d @%lx(%x) %c\n" );
SPECIALIZE_PAGE_MIGRATION_INFO(PAGE_FAULT_END, PAGE_FAULT_END, "%x %ld -%d @%lx(%x) %c\n" );
SPECIALIZE_PAGE_MIGRATION_INFO(QUEUE_EVICTION, QUEUE_EVICTION, "%x %ld -%d %x %d\n" );
SPECIALIZE_PAGE_MIGRATION_INFO(QUEUE_RESTORE, QUEUE_RESTORE, "%x %ld -%d %x\n" );
SPECIALIZE_PAGE_MIGRATION_INFO(UNMAP_FROM_GPU, UNMAP_FROM_GPU, "%x %ld -%d @%lx(%lx) %x %d\n" );
SPECIALIZE_PAGE_MIGRATION_INFO(DROPPED_EVENT, DROPPED_EVENT, "%x %ld -%d %d\n" );
#undef SPECIALIZE_PAGE_MIGRATION_INFO
// clang-format on
} // namespace page_migration
} // namespace rocprofiler
#endif
@@ -43,9 +43,9 @@
#include "lib/rocprofiler-sdk/hsa/scratch_memory.hpp"
#include "lib/rocprofiler-sdk/intercept_table.hpp"
#include "lib/rocprofiler-sdk/internal_threading.hpp"
#include "lib/rocprofiler-sdk/kfd/kfd.hpp"
#include "lib/rocprofiler-sdk/marker/marker.hpp"
#include "lib/rocprofiler-sdk/ompt.hpp"
#include "lib/rocprofiler-sdk/page_migration/page_migration.hpp"
#include "lib/rocprofiler-sdk/pc_sampling/code_object.hpp"
#include "lib/rocprofiler-sdk/pc_sampling/service.hpp"
#include "lib/rocprofiler-sdk/rccl/rccl.hpp"
@@ -757,7 +757,7 @@ finalize()
hsa::queue_controller_fini();
thread_trace::finalize();
ompt::finalize_ompt();
page_migration::finalize();
kfd::finalize();
#if ROCPROFILER_SDK_HSA_PC_SAMPLING > 0
// WARNING: this must precede `code_object::finalize()`
pc_sampling::code_object::finalize();
@@ -20,8 +20,7 @@ set(rocprofiler_lib_sources
naming.cpp
timestamp.cpp
version.cpp
hsa_barrier.cpp
page_migration.cpp)
hsa_barrier.cpp)
add_executable(rocprofiler-sdk-lib-tests)
target_sources(rocprofiler-sdk-lib-tests PRIVATE ${rocprofiler_lib_sources}
@@ -91,9 +91,6 @@ TEST(enum_string, fwd_h)
TEST_STR(ROCPROFILER_STATUS_ERROR_BUFFER_BUSY);
TEST_STR(ROCPROFILER_STATUS_ERROR_CONFIGURATION_LOCKED);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_START);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION);
// rocprofiler_buffer_category_t
TEST_STR(ROCPROFILER_BUFFER_CATEGORY_TRACING);
TEST_STR(ROCPROFILER_BUFFER_CATEGORY_COUNTERS);
@@ -116,6 +113,7 @@ TEST(enum_string, fwd_h)
TEST_STR(ROCPROFILER_BUFFER_TRACING_MARKER_NAME_API);
TEST_STR(ROCPROFILER_BUFFER_TRACING_SCRATCH_MEMORY);
TEST_STR(ROCPROFILER_BUFFER_TRACING_ROCDECODE_API);
TEST_STR(ROCPROFILER_BUFFER_TRACING_KFD_QUEUE);
// rocprofiler_code_object_operation_t
TEST_STR(ROCPROFILER_CODE_OBJECT_NONE);
@@ -152,10 +150,6 @@ TEST(enum_string, fwd_h)
TEST_STR(ROCPROFILER_BUFFER_POLICY_DISCARD);
TEST_STR(ROCPROFILER_BUFFER_POLICY_LOSSLESS);
// rocprofiler_page_migration_operation_t
TEST_STR(ROCPROFILER_PAGE_MIGRATION_NONE);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_DROPPED_EVENT);
// rocprofiler_scratch_memory_operation_t
TEST_STR(ROCPROFILER_SCRATCH_MEMORY_NONE);
TEST_STR(ROCPROFILER_SCRATCH_MEMORY_ALLOC);
@@ -265,16 +259,18 @@ TEST(enum_string, roctx_api_id)
TEST_API_ID_STR(ROCPROFILER_MARKER_NAME_API_ID, roctxNameHipStream);
}
TEST(enum_string, page_migration_triggers)
TEST(enum_string, kfd_events)
{
TEST_STR(ROCPROFILER_PAGE_MIGRATION_TRIGGER_PREFETCH);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_TRIGGER_TTM_EVICTION);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_SVM);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_QUEUE_SUSPEND_TRIGGER_CRIU_RESTORE);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU_TRIGGER_MMU_NOTIFY);
TEST_STR(ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU_TRIGGER_UNMAP_FROM_CPU);
TEST_STR(ROCPROFILER_KFD_EVENT_PAGE_MIGRATE_PAGEFAULT_CPU);
TEST_STR(ROCPROFILER_KFD_EVENT_PAGE_FAULT_END_PAGE_MIGRATED);
TEST_STR(ROCPROFILER_KFD_EVENT_QUEUE_EVICT_SUSPEND);
TEST_STR(ROCPROFILER_KFD_EVENT_QUEUE_RESTORE);
TEST_STR(ROCPROFILER_KFD_EVENT_UNMAP_FROM_GPU_UNMAP_FROM_CPU);
TEST_STR(ROCPROFILER_KFD_EVENT_DROPPED_EVENTS);
TEST_STR(ROCPROFILER_KFD_PAGE_MIGRATE_PAGEFAULT_CPU);
TEST_STR(ROCPROFILER_KFD_PAGE_MIGRATE_TTM_EVICTION);
TEST_STR(ROCPROFILER_KFD_PAGE_FAULT_WRITE_FAULT_UPDATED);
TEST_STR(ROCPROFILER_KFD_QUEUE_EVICT_CRIU_RESTORE);
}
TEST(enum_string, rccl_api_id)
@@ -1,106 +0,0 @@
// 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 "lib/common/container/small_vector.hpp"
#include "lib/common/defines.hpp"
#include "lib/common/mpl.hpp"
#include "lib/rocprofiler-sdk/page_migration/utils.hpp"
#include <rocprofiler-sdk/buffer_tracing.h>
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/rocprofiler.h>
#include <fmt/core.h>
#include <gtest/gtest.h>
#include <sstream>
#include <string_view>
#include <utility>
#define ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL 1
#include "lib/rocprofiler-sdk/page_migration/page_migration.def.cpp"
#undef ROCPROFILER_LIB_ROCPROFILER_SDK_PAGE_MIGRATION_PAGE_MIGRATION_CPP_IMPL
namespace
{
constexpr std::string_view MULTILINE_STRING = "This is 0 Line 0\n"
"This is 10 Line 1\n"
"This is 20 Line 2\n"
"This is 30 Line 3\n"
"This is 40 Line 4\n";
}
void
return_line(const std::string_view line)
{
static int line_no = 0;
std::stringstream strs{};
strs << fmt::format("This is {} Line {}", line_no * 10, line_no);
EXPECT_EQ(strs.str(), line);
line_no++;
}
auto
parse_lines()
{
rocprofiler::page_migration::kfd_readlines(MULTILINE_STRING, return_line);
}
TEST(page_migration, readlines)
{
// Ensure all lines are read
parse_lines();
}
TEST(page_migration, rocprof_kfd_map)
{
using namespace rocprofiler::page_migration;
using namespace rocprofiler::common::container;
using rocprofiler_page_migration_seq_t =
std::make_index_sequence<ROCPROFILER_PAGE_MIGRATION_LAST>;
const small_vector<size_t> vec{ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END,
ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION,
ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU};
EXPECT_EQ((page_migration_info<ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END>::kfd_bitmask |
page_migration_info<ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION>::kfd_bitmask |
page_migration_info<ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU>::kfd_bitmask),
kfd_bitmask(vec, rocprofiler_page_migration_seq_t{}));
const auto to_kfd_str = [](kfd_smi_event e) {
std::string str = fmt::format("{:x} ", static_cast<size_t>(e));
return rocprofiler::page_migration::get_rocprof_op({str});
};
// clang-format off
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_MIGRATE_START), ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_START);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_MIGRATE_END), ROCPROFILER_PAGE_MIGRATION_PAGE_MIGRATE_END);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_PAGE_FAULT_START), ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_START);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_PAGE_FAULT_END), ROCPROFILER_PAGE_MIGRATION_PAGE_FAULT_END);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_QUEUE_EVICTION), ROCPROFILER_PAGE_MIGRATION_QUEUE_EVICTION);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_QUEUE_RESTORE), ROCPROFILER_PAGE_MIGRATION_QUEUE_RESTORE);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_UNMAP_FROM_GPU), ROCPROFILER_PAGE_MIGRATION_UNMAP_FROM_GPU);
EXPECT_EQ(to_kfd_str(KFD_SMI_EVENT_DROPPED_EVENT), ROCPROFILER_PAGE_MIGRATION_DROPPED_EVENT);
// clang-format on
}