diff --git a/samples/pc_sampling/client.cpp b/samples/pc_sampling/client.cpp index eb4a2f13fd..e6823d4816 100644 --- a/samples/pc_sampling/client.cpp +++ b/samples/pc_sampling/client.cpp @@ -77,6 +77,8 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* /*tool_data*/) if(client::pcs::gpu_agents.empty()) { *utils::get_output_stream() << "No availabe gpu agents supporting PC sampling" << std::endl; + // Emit the message to explicitly skip the sample. + std::cerr << "PC sampling unavailable" << std::endl; // Exit with no error if none of the GPUs support PC sampling. exit(0); } diff --git a/source/lib/rocprofiler-sdk/pc_sampling.cpp b/source/lib/rocprofiler-sdk/pc_sampling.cpp index d4cf03d35e..855e57aef1 100644 --- a/source/lib/rocprofiler-sdk/pc_sampling.cpp +++ b/source/lib/rocprofiler-sdk/pc_sampling.cpp @@ -50,7 +50,10 @@ is_pc_sampling_explicitly_enabled() auto pc_sampling_enabled = rocprofiler::common::get_env("ROCPROFILER_PC_SAMPLING_BETA_ENABLED", false); - if(!pc_sampling_enabled) LOG(ERROR) << "PC sampling unavailable\n"; + if(!pc_sampling_enabled) + ROCP_INFO << "PC sampling unavailable. The feature is implicitly disabled. " + << "To use it on a supported architecture, " + << "set ROCPROFILER_PC_SAMPLING_BETA_ENABLED=ON in the environment"; return pc_sampling_enabled; } @@ -93,7 +96,7 @@ rocprofiler_configure_pc_sampling_service(rocprofiler_context_id_t conte (void) interval; (void) buffer_id; - ROCP_ERROR << "PC sampling unavailable\n"; + ROCP_INFO << "PC sampling unavailable. The feature depends on the latest HSA runtime."; // ROCr runtime is missing PC sampling. return ROCPROFILER_STATUS_ERROR_NOT_AVAILABLE; @@ -121,7 +124,7 @@ rocprofiler_query_pc_sampling_agent_configurations( (void) cb; (void) user_data; - ROCP_ERROR << "PC sampling unavailable\n"; + ROCP_INFO << "PC sampling unavailable. The feature depends on the latest HSA runtime."; // ROCr runtime is missing PC sampling. return ROCPROFILER_STATUS_ERROR_NOT_AVAILABLE; diff --git a/source/lib/rocprofiler-sdk/pc_sampling/ioctl/ioctl_adapter.cpp b/source/lib/rocprofiler-sdk/pc_sampling/ioctl/ioctl_adapter.cpp index 101154588c..cdd108fe6e 100644 --- a/source/lib/rocprofiler-sdk/pc_sampling/ioctl/ioctl_adapter.cpp +++ b/source/lib/rocprofiler-sdk/pc_sampling/ioctl/ioctl_adapter.cpp @@ -210,7 +210,7 @@ is_pc_sampling_supported(const rocprofiler_agent_t* agent) { // The KFD IOCTL version is the same for all available devices. // Thus, emit the message and skip all tests and samples on the system in use. - ROCP_ERROR << "PC sampling unavailable\n"; + ROCP_INFO << "PC sampling unavailable. Please update amdgpu driver to at least 1.16."; return ROCPROFILER_STATUS_ERROR_INCOMPATIBLE_KERNEL; } diff --git a/source/lib/rocprofiler-sdk/pc_sampling/tests/query_configuration.cpp b/source/lib/rocprofiler-sdk/pc_sampling/tests/query_configuration.cpp index 4ac8058eee..c54c457764 100644 --- a/source/lib/rocprofiler-sdk/pc_sampling/tests/query_configuration.cpp +++ b/source/lib/rocprofiler-sdk/pc_sampling/tests/query_configuration.cpp @@ -219,9 +219,20 @@ TEST(pc_sampling, query_configs_agent_does_not_exists) { int cb_data = USER_DATA_VAL; // The agent does not exists - EXPECT_EQ(rocprofiler_query_pc_sampling_agent_configurations( - rocprofiler_agent_id_t{.handle = 0xDEADBEEF}, check_all_configs_cb, &cb_data), - ROCPROFILER_STATUS_ERROR_AGENT_NOT_FOUND); + auto status = rocprofiler_query_pc_sampling_agent_configurations( + rocprofiler_agent_id_t{.handle = 0xDEADBEEF}, check_all_configs_cb, &cb_data); + + std::set reasons_to_skip_test = { + ROCPROFILER_STATUS_ERROR_NOT_IMPLEMENTED, + ROCPROFILER_STATUS_ERROR_NOT_AVAILABLE, + ROCPROFILER_STATUS_ERROR_INCOMPATIBLE_KERNEL}; + + // emit the error to skip a test + if(reasons_to_skip_test.count(status) > 0) ROCP_ERROR << "PC sampling unavailable"; + + // If PC sampling API is enabled, and this system supports this feature, + // then assert the agent with id `0xDEADBEEF` does not exist. + EXPECT_EQ(status, ROCPROFILER_STATUS_ERROR_AGENT_NOT_FOUND); } TEST(pc_sampling, query_configs_after_service_setup) diff --git a/source/lib/rocprofiler-sdk/pc_sampling/tests/samples_processing.cpp b/source/lib/rocprofiler-sdk/pc_sampling/tests/samples_processing.cpp deleted file mode 100644 index ea1ccc634f..0000000000 --- a/source/lib/rocprofiler-sdk/pc_sampling/tests/samples_processing.cpp +++ /dev/null @@ -1,438 +0,0 @@ -// MIT License -// -// Copyright (c) 2023 ROCm Developer Tools -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include -#include -#include -#include - -#include "lib/common/utility.hpp" -#include "lib/rocprofiler-sdk/hsa/agent_cache.hpp" -#include "lib/rocprofiler-sdk/hsa/hsa.hpp" -#include "lib/rocprofiler-sdk/hsa/queue.hpp" -#include "lib/rocprofiler-sdk/hsa/queue_controller.hpp" -#include "lib/rocprofiler-sdk/pc_sampling/parser/rocr.h" -#include "lib/rocprofiler-sdk/pc_sampling/tests/pc_sampling_internals.hpp" -#include "pc_sampling_internals.hpp" - -#include -#include -#include - -constexpr size_t BUFFER_SIZE_BYTES = 8192; -constexpr size_t WATERMARK = (BUFFER_SIZE_BYTES / 4); - -#define ROCPROFILER_CALL(ARG, MSG) \ - { \ - auto _status = (ARG); \ - EXPECT_EQ(_status, ROCPROFILER_STATUS_SUCCESS) << MSG << " :: " << #ARG; \ - } - -namespace -{ -#define NUM_SAMPLES 5 -#define TRAP_ID 0 - -struct callback_data -{ - rocprofiler_client_id_t* client_id = nullptr; - rocprofiler_client_finalize_t client_fini_func = nullptr; - rocprofiler_context_id_t client_ctx = {0}; - rocprofiler_buffer_id_t client_buffer = {}; - rocprofiler_callback_thread_t client_thread = {}; - uint64_t client_workflow_count = {}; - uint64_t client_callback_count = {}; - int64_t current_depth = 0; - int64_t max_depth = 0; - std::map client_correlation = {}; - std::vector gpu_pcs_agents = {}; -}; - -struct agent_data -{ - uint64_t agent_count = 0; - std::vector agents = {}; -}; - -rocprofiler_status_t -find_all_gpu_agents_supporting_pc_sampling_impl(const rocprofiler_agent_t** agents, - size_t num_agents, - void* user_data) -{ - // user_data represent the pointer to the array where gpu_agent will be stored - if(!user_data) return ROCPROFILER_STATUS_ERROR; - - auto* _out_agents = static_cast*>(user_data); - // find the first GPU agent - for(size_t i = 0; i < num_agents; i++) - { - if(agents[i]->type == ROCPROFILER_AGENT_TYPE_GPU) - { - // Skip GPU agents not supporting PC sampling - // Vladimir: The assumption is that if a GPU agent does not support PC sampling, - // the size is 0. - if(agents[i]->num_pc_sampling_configs == 0) continue; - - _out_agents->push_back(agents[i]); - - printf("[%s] %s :: id=%zu, type=%i, num pc sample configs=%zu\n", - __FUNCTION__, - agents[i]->name, - agents[i]->id.handle, - agents[i]->type, - agents[i]->num_pc_sampling_configs); - } - else - { - printf("[%s] %s :: id=%zu, type=%i, num pc sample configs=%zu\n", - __FUNCTION__, - agents[i]->name, - agents[i]->id.handle, - agents[i]->type, - agents[i]->num_pc_sampling_configs); - } - } - - return !_out_agents->empty() ? ROCPROFILER_STATUS_SUCCESS : ROCPROFILER_STATUS_ERROR; -} - -const rocprofiler_pc_sampling_configuration_t -extract_pc_sampling_config_prefer_stochastic(rocprofiler_agent_id_t agent_id) -{ - auto cb = [](const rocprofiler_pc_sampling_configuration_t* configs, - size_t num_config, - void* user_data) { - auto* avail_configs = - static_cast*>(user_data); - // printf("The agent with the id: %lu supports the %lu configurations: \n", - // agent_id_.handle, num_config); - for(size_t i = 0; i < num_config; i++) - { - avail_configs->emplace_back(configs[i]); - } - return ROCPROFILER_STATUS_SUCCESS; - }; - std::vector configs; - ROCPROFILER_CALL(rocprofiler_query_pc_sampling_agent_configurations(agent_id, cb, &configs), - "Failed to query available configurations"); - - const rocprofiler_pc_sampling_configuration_t* first_host_trap_config = nullptr; - const rocprofiler_pc_sampling_configuration_t* first_stochastic_config = nullptr; - // Search until encountering on the stochastic configuration, if any. - // Otherwise, use the host trap config - for(auto const& cfg : configs) - { - if(cfg.method == ROCPROFILER_PC_SAMPLING_METHOD_STOCHASTIC) - { - first_stochastic_config = &cfg; - break; - } - else if(!first_host_trap_config && cfg.method == ROCPROFILER_PC_SAMPLING_METHOD_HOST_TRAP) - { - first_host_trap_config = &cfg; - } - } - - // Check if the stochastic config is found. Use host trap config otherwise. - const rocprofiler_pc_sampling_configuration_t* picked_cfg = - (first_stochastic_config != nullptr) ? first_stochastic_config : first_host_trap_config; - - return *picked_cfg; -} - -void -rocprofiler_pc_sampling_callback(rocprofiler_context_id_t /*context_id*/, - rocprofiler_buffer_id_t /*buffer_id*/, - rocprofiler_record_header_t** headers, - size_t num_headers, - void* /*data*/, - uint64_t drop_count) -{ - EXPECT_EQ(drop_count, 0); - - for(size_t i = 0; i < num_headers; i++) - { - auto* cur_header = headers[i]; - - if(cur_header == nullptr) - { - throw std::runtime_error{ - "rocprofiler provided a null pointer to header. this should never happen"}; - } - else if(cur_header->hash != - rocprofiler_record_header_compute_hash(cur_header->category, cur_header->kind)) - { - throw std::runtime_error{"rocprofiler_record_header_t (category | kind) != hash"}; - } - else if(cur_header->category == ROCPROFILER_BUFFER_CATEGORY_PC_SAMPLING) - { - auto* pc_sample = static_cast(cur_header->payload); - // FIXME: find the cause why this fails - // EXPECT_EQ(pc_sample->correlation_id.internal, 1); - EXPECT_EQ(pc_sample->pc, i + 1); - EXPECT_EQ(pc_sample->timestamp, i + 33); - EXPECT_EQ(pc_sample->hw_id, 0); - } - else - { - throw std::runtime_error{"unexpected rocprofiler_record_header_t category + kind"}; - } - } -} - -} // namespace - -TEST(pc_sampling, processing_pc_samples) -{ - using init_func_t = int (*)(rocprofiler_client_finalize_t, void*); - using fini_func_t = void (*)(void*); - - // using hsa_iterate_agents_cb_t = hsa_status_t (*)(hsa_agent_t, void*); - - auto cmd_line = rocprofiler::common::read_command_line(getpid()); - ASSERT_FALSE(cmd_line.empty()); - - static init_func_t tool_init = [](rocprofiler_client_finalize_t fini_func, - void* client_data) -> int { - auto* cb_data = static_cast(client_data); - - cb_data->client_workflow_count++; - cb_data->client_fini_func = fini_func; - - // This function returns the all gpu agents supporting some kind of PC sampling - ROCPROFILER_CALL( - rocprofiler_query_available_agents(&find_all_gpu_agents_supporting_pc_sampling_impl, - sizeof(rocprofiler_agent_t), - static_cast(&cb_data->gpu_pcs_agents)), - "Failed to find GPU agents"); - - ROCPROFILER_CALL(rocprofiler_create_context(&cb_data->client_ctx), - "failed to create context"); - - ROCPROFILER_CALL(rocprofiler_create_buffer(cb_data->client_ctx, - 4096, - 2048, - ROCPROFILER_BUFFER_POLICY_LOSSLESS, - rocprofiler_pc_sampling_callback, - client_data, - &cb_data->client_buffer), - "buffer creation failed"); - - const auto* agent = cb_data->gpu_pcs_agents.at(0); - const auto agent_id = agent->id; - const auto pcs_config = extract_pc_sampling_config_prefer_stochastic(agent_id); - - size_t interval = pcs_config.max_interval; - - // This calls succeeds - ROCPROFILER_CALL(rocprofiler_configure_pc_sampling_service(cb_data->client_ctx, - agent->id, - pcs_config.method, - pcs_config.unit, - interval, - cb_data->client_buffer, - 0), - "Failed to configure PC sampling service"); - - ROCPROFILER_CALL(rocprofiler_create_callback_thread(&cb_data->client_thread), - "failure creating callback thread"); - - ROCPROFILER_CALL( - rocprofiler_assign_callback_thread(cb_data->client_buffer, cb_data->client_thread), - "failed to assign thread for buffer"); - - int valid_ctx = 0; - ROCPROFILER_CALL(rocprofiler_context_is_valid(cb_data->client_ctx, &valid_ctx), - "failure checking context validity"); - - EXPECT_EQ(valid_ctx, 1); - - ROCPROFILER_CALL(rocprofiler_start_context(cb_data->client_ctx), - "rocprofiler context start failed"); - - // no errors - return 0; - }; - - static fini_func_t tool_fini = [](void* client_data) -> void { - auto* cb_data = static_cast(client_data); - // FIXME: for some reason, this returns context not found - // ROCPROFILER_CALL(rocprofiler_stop_context(cb_data->client_ctx), - // "rocprofiler context stop failed"); - - cb_data->client_workflow_count++; - }; - - static auto cb_data = callback_data{}; - - static auto cfg_result = - rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t), - tool_init, - tool_fini, - static_cast(&cb_data)}; - - static rocprofiler_configure_func_t rocp_init = - [](uint32_t version, - const char* runtime_version, - uint32_t prio, - rocprofiler_client_id_t* client_id) -> rocprofiler_tool_configure_result_t* { - auto expected_version = ROCPROFILER_VERSION; - EXPECT_EQ(expected_version, version); - EXPECT_EQ(std::string_view{runtime_version}, std::string_view{ROCPROFILER_VERSION_STRING}); - EXPECT_EQ(prio, 0); - EXPECT_EQ(client_id->name, nullptr); - cb_data.client_id = client_id; - cb_data.client_id->name = ::testing::UnitTest::GetInstance()->current_test_info()->name(); - - return &cfg_result; - }; - - EXPECT_EQ(rocprofiler_force_configure(rocp_init), ROCPROFILER_STATUS_SUCCESS); - - // Further tests assumes the existence of at least one GPU agent supporting - if(cb_data.gpu_pcs_agents.size() == 0) return; - - auto& hsa_table = rocprofiler::hsa::get_table(); - auto* pc_sampling_table_ = hsa_table.pc_sampling_ext_; - EXPECT_NE(pc_sampling_table_, nullptr); - - pc_sampling_table_->hsa_ven_amd_pcs_create_from_id_fn = - [](uint32_t /*ioctl_pcs_id*/, - hsa_agent_t /*agent*/, - hsa_ven_amd_pcs_method_kind_t /*method*/, - hsa_ven_amd_pcs_units_t /*units*/, - size_t /*interval*/, - size_t /*latency*/, - size_t /*buffer_size*/, - hsa_ven_amd_pcs_data_ready_callback_t /*data_ready_callback*/, - void* /*client_callback_data*/, - hsa_ven_amd_pcs_t* /*pc_sampling*/) { return HSA_STATUS_SUCCESS; }; - - pc_sampling_table_->hsa_ven_amd_pcs_flush_fn = [](hsa_ven_amd_pcs_t /*pc_sampling*/) { - return HSA_STATUS_SUCCESS; - }; - - auto* ext_table_ = hsa_table.amd_ext_; - EXPECT_NE(ext_table_, nullptr); - - ext_table_->hsa_amd_queue_get_info_fn = - [](hsa_queue_t* queue, hsa_queue_info_attribute_t attribute, void* value) { - (void) queue; - switch(attribute) - { - case HSA_AMD_QUEUE_INFO_AGENT: - *(reinterpret_cast(value)) = hsa_agent_t{.handle = 1}; - break; - case HSA_AMD_QUEUE_INFO_DOORBELL_ID: - *(reinterpret_cast(value)) = 0; - break; - default: return HSA_STATUS_ERROR_INVALID_ARGUMENT; - } - return HSA_STATUS_SUCCESS; - }; - -#if 1 - - // Set the HSA agent for the active PCSamplingConfiguration, - // The reason for setting HSA agent manually follows. - // The test links against rocporifler static library. - // Hence, the rocprofiler_set_api_table is not called. - auto* service = rocprofiler::pc_sampling::get_active_pc_sampling_service().load(); - EXPECT_NE(service, nullptr); - const auto* rocp_agent = cb_data.gpu_pcs_agents.at(0); - auto agent_session = service->agent_sessions.at(rocp_agent->id).get(); - hsa_agent_t pseudo_hsa_agent = {.handle = 1}; - agent_session->hsa_agent = std::make_unique(pseudo_hsa_agent); - - // TODO: We need to register the agent inside the parser - rocprofiler::pc_sampling::hsa::get_pc_sampling_parser().register_buffer_for_agent( - cb_data.client_buffer.handle, rocp_agent->id.handle); - - // The following test calls some segments of internal PC sampling API implementation - // by mimicking the HIP and ROCr - - // Generate dispatch and marker packet - rocprofiler::hsa::rocprofiler_packet dispatch_pkt; - auto marker_pkt = - rocprofiler::pc_sampling::hsa::generate_marker_packet_for_kernel(&dispatch_pkt); - - // create a pseudo hsa queue - hsa_queue_t queue; - queue.size = 1024; - // Mimic the ROCr and notify the pc sampling service that the marker packet has been encoutered. - rocprofiler::pc_sampling::hsa::amd_intercept_marker_handler_callback( - &marker_pkt.marker, &queue, 0); - - // We need to generate some samples and send them via data_ready_calllback. - size_t num_samples = NUM_SAMPLES; - auto samples_data_size = num_samples * sizeof(packet_union_t); - - static hsa_ven_amd_pcs_data_copy_callback_t hsa_mock_data_copy_callback = - [](void* hsa_callback_data, size_t data_size, void* destination) { - (void) hsa_callback_data; - (void) data_size; - using rocr_buffer_t = std::vector; - auto samples_buff = rocr_buffer_t{}; - for(size_t i = 0; i < NUM_SAMPLES; i++) - { - perf_sample_host_trap_v1 hs; - hs.pc = i + 1; - hs.exec_mask = 0xF; - hs.workgroup_id_x = 1; - hs.workgroup_id_y = 2; - hs.workgroup_id_z = 3; - hs.chiplet_and_wave_id = 0; - hs.hw_id = 0; - hs.timestamp = 33 + i; - hs.correlation_id = TRAP_ID; - samples_buff.push_back(packet_union_t{.host = hs}); - } - // copy the data - std::memcpy(destination, samples_buff.data(), NUM_SAMPLES * sizeof(packet_union_t)); - // clear the data - return HSA_STATUS_SUCCESS; - }; - - // calling data_ready_callback that will result in copying the data from above - // to the client buffer via PC sampling parser - rocprofiler::pc_sampling::PCSAgentSession pcs_agent_session; - pcs_agent_session.agent = rocp_agent; - pcs_agent_session.method = ROCPROFILER_PC_SAMPLING_METHOD_HOST_TRAP; - - size_t lost_samples = 0; - - rocprofiler::pc_sampling::hsa::data_ready_callback( - &pcs_agent_session, samples_data_size, lost_samples, hsa_mock_data_copy_callback, nullptr); - - rocprofiler::pc_sampling::hsa::kernel_completion_cb( - nullptr, rocp_agent, static_cast(1), dispatch_pkt, nullptr); - - // Flush the buffer explicitly - ROCPROFILER_CALL(rocprofiler_flush_buffer(cb_data.client_buffer), - "rocprofiler flush buffer failed"); - // Stop the context - ROCPROFILER_CALL(rocprofiler_stop_context(cb_data.client_ctx), - "rocprofiler context stop failed"); -#endif -} diff --git a/tests/pc_sampling/pcs.cpp b/tests/pc_sampling/pcs.cpp index 551852b83c..baee8b477e 100644 --- a/tests/pc_sampling/pcs.cpp +++ b/tests/pc_sampling/pcs.cpp @@ -421,7 +421,8 @@ configure_pc_sampling_on_all_agents(rocprofiler_context_id_t context) if(pc_sampler->gpu_agents.empty()) { *utils::get_output_stream() << "No availabe gpu agents supporting PC sampling" << std::endl; - *utils::get_output_stream() << "PC sampling unavailable" << std::endl; + // Emit the message to skip the test. + std::cerr << "PC sampling unavailable" << std::endl; // Exit with no error if none of the GPUs support PC sampling. exit(0); }