Misc updates for distribution (#233)
* Adding tools support * cmake formatting (cmake-format) (#227) Co-authored-by: SrirakshaNag <SrirakshaNag@users.noreply.github.com> * Checking to do rebase * Adding rocprofv2 script * cmake formatting (cmake-format) (#229) Co-authored-by: bgopesh <bgopesh@users.noreply.github.com> * Fixing build for the tool * Removing the requirement for rocm_version * Update rocprofiler_utilities.cmake * C++ filesystem fixes - added source/lib/common/filesystem.hpp - support older compilers which have <experimental/filesystem> and do not have <filesystem> - added samples/common/filesystem.hpp - samples now depend on "common" library which provides the correct filesystem header - renamed rocprofiler-stdcxxfs interface target to rocprofiler-cxx-filesystem - support old LLVM in addition to GNU - fix bin/rocprof/rocprof.cpp - was using VLA * Fix rocprofiler-drm include directories - OpenSUSE only has include/libdrm/drm.h (no include/drm/drm.h) * Tools fixes * Fix for the tools * Fix rocprofv2 script * Fixing Filesystem Issues * source formatting (clang-format v11) (#234) Co-authored-by: ammarwa <ammarwa@users.noreply.github.com> * Vlaindic/pc sampling api update (#235) * pcs: updating PC sampling API * source formatting (clang-format v11) (#232) Co-authored-by: vlaindic <vlaindic@users.noreply.github.com> --------- Co-authored-by: vlaindic <vladimir.indic@amd.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: vlaindic <vlaindic@users.noreply.github.com> * Vlaindic/pc sampling api update for ammar branch (#244) *Updating the documentation inside pc_sampling.h --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: vlaindic <vlaindic@users.noreply.github.com> * pcs: use @p in front of params * pcs: documenting struct fields updated * Fixing PC Sampling Documentation issues * Fixing PC Sampling Documentation * Relocated tools directory to source/lib/rocprofiler-tool * Fixes/updates to rocprofiler-tool - updated CMake - Fixed miscellaneous issues in the code (VLAs, etc.) - Updated rocprofv2 to reflect some minor env variables changes in rocprofiler-tool - Fixed clang-tidy warnings * Update lib/rocprofiler-tool/CMakeLists.txt - link to atomic library * Add $ORIGIN/.. RUNPATH to rocprofiler-tool * Adding readme file for tools * Renaming the tools readme file * Update ReadMe.md * Update ReadMe.md * Documentation updates - overview and explanation of design and concepts * Fix lib/rocprofiler-tool/README.md - delete ReadMe.md * Hacks for build * Update Filesystem * cmake formatting (cmake-format) (#248) Co-authored-by: ammarwa <ammarwa@users.noreply.github.com> * source formatting (clang-format v11) (#249) Co-authored-by: ammarwa <ammarwa@users.noreply.github.com> * source formatting (clang-format v11) (#250) Co-authored-by: ammarwa <ammarwa@users.noreply.github.com> * Addressing review comments on the tool readme file * Revert "Hacks for build" This reverts commit d6688cb3d1226c46fc97e37ced889a5b0d180940. * Fixes for GCC 7.5 compiler in OpenSUSE 15.4 * Update lib/rocprofiler-tool/CMakeLists.txt - link to AQL profile library * Fix lib/rocprofiler-tool/README.md - fix markdown * Fix lib/rocprofiler-tool - fix usage of hsa_ven_amd_loader_query_host_address * Fix unused variable warnings - byproduct of variables only used in assert statements * Update docs - update about.md - more "Important Changes" section here - update tool_library_overview.md - extend "Tool Library Design" section - write "Tool Initialization" section - write "Tool Finalization" section * Add ghc::filesystem submodule * Implement usage of ghc::filesystem * Add ROCPROFILER_BUILD_GHC_FS option - option to use external/filesystem (ghc) * Update samples/counter-collection - compile flags - common library - fixes for warnings * Update tests/kernel-tracing/CMakeLists.txt - change install location of kernel-tracing-test-tool and install rpath * Update samples/common/CMakeLists.txt - compile features requiring C++17 * Update lib/rocprofiler-tool/tool.cpp - remove include <filesystem> - comment out unused variable - remove unused functions - move some functions into anonymous namespace --------- Co-authored-by: Sriraksha Nagaraj <Sriraksha.Nagaraj@amd.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: SrirakshaNag <SrirakshaNag@users.noreply.github.com> Co-authored-by: gobhardw <gopesh.bhardwaj@amd.com> Co-authored-by: bgopesh <bgopesh@users.noreply.github.com> Co-authored-by: Jonathan R. Madsen <jonathanrmadsen@gmail.com> Co-authored-by: ammarwa <ammarwa@users.noreply.github.com> Co-authored-by: vlaindic <vladimir.indic@amd.com> Co-authored-by: vlaindic <vlaindic@users.noreply.github.com> Co-authored-by: Vladimir Indic <139573562+vlaindic@users.noreply.github.com> Co-authored-by: Benjamin Welton <bewelton@amd.com> Co-authored-by: Jonathan R. Madsen <jrmadsen@users.noreply.github.com>
This commit is contained in:
@@ -14,18 +14,12 @@ endif()
|
||||
enable_testing()
|
||||
include(CTest)
|
||||
|
||||
add_library(rocprofiler-samples-build-flags INTERFACE)
|
||||
add_library(rocprofiler::samples-build-flags ALIAS rocprofiler-samples-build-flags)
|
||||
target_compile_options(rocprofiler-samples-build-flags INTERFACE -W -Wall -Wextra
|
||||
-Wshadow)
|
||||
|
||||
if(ROCPROFILER_BUILD_CI OR ROCPROFILER_BUILD_WERROR)
|
||||
target_compile_options(rocprofiler-samples-build-flags INTERFACE -Werror)
|
||||
endif()
|
||||
# common utilities for samples
|
||||
add_subdirectory(common)
|
||||
|
||||
# actual samples
|
||||
add_subdirectory(api_callback_tracing)
|
||||
add_subdirectory(api_buffered_tracing)
|
||||
add_subdirectory(code_object_tracing)
|
||||
add_subdirectory(counter_collection)
|
||||
add_subdirectory(intercept_table)
|
||||
add_subdirectory(pc_sampling)
|
||||
|
||||
@@ -33,8 +33,8 @@ add_library(buffered-api-tracing-client SHARED)
|
||||
target_sources(buffered-api-tracing-client PRIVATE client.cpp client.hpp)
|
||||
target_link_libraries(
|
||||
buffered-api-tracing-client
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
PRIVATE rocprofiler::rocprofiler rocprofiler::samples-build-flags
|
||||
rocprofiler::samples-common-library)
|
||||
|
||||
set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP)
|
||||
find_package(Threads REQUIRED)
|
||||
@@ -42,9 +42,8 @@ find_package(Threads REQUIRED)
|
||||
add_executable(buffered-api-tracing)
|
||||
target_sources(buffered-api-tracing PRIVATE main.cpp)
|
||||
target_link_libraries(
|
||||
buffered-api-tracing
|
||||
PRIVATE buffered-api-tracing-client Threads::Threads
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
buffered-api-tracing PRIVATE buffered-api-tracing-client Threads::Threads
|
||||
rocprofiler::samples-build-flags)
|
||||
|
||||
add_test(NAME buffered-api-tracing COMMAND $<TARGET_FILE:buffered-api-tracing>)
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
#include <rocprofiler/registration.h>
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
|
||||
#include "common/defines.hpp"
|
||||
#include "common/filesystem.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
@@ -48,9 +51,9 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
@@ -59,22 +62,6 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#define ROCPROFILER_CALL(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \
|
||||
std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \
|
||||
<< " failed with error code " << CHECKSTATUS << ": " << status_msg \
|
||||
<< std::endl; \
|
||||
std::stringstream errmsg{}; \
|
||||
errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \
|
||||
<< status_msg << ")"; \
|
||||
throw std::runtime_error(errmsg.str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace client
|
||||
{
|
||||
namespace
|
||||
@@ -110,8 +97,6 @@ kernel_symbol_map_t client_kernels = {};
|
||||
void
|
||||
print_call_stack(const call_stack_t& _call_stack)
|
||||
{
|
||||
namespace fs = ::std::filesystem;
|
||||
|
||||
auto ofname = std::string{"api_buffered_trace.log"};
|
||||
if(auto* eofname = getenv("ROCPROFILER_SAMPLE_OUTPUT_FILE")) ofname = eofname;
|
||||
|
||||
@@ -141,8 +126,8 @@ print_call_stack(const call_stack_t& _call_stack)
|
||||
for(const auto& itr : _call_stack)
|
||||
{
|
||||
*ofs << std::left << std::setw(2) << ++n << "/" << std::setw(2) << _call_stack.size()
|
||||
<< " [" << fs::path{itr.file}.filename() << ":" << itr.line << "] " << std::setw(20)
|
||||
<< itr.function;
|
||||
<< " [" << common::fs::path{itr.file}.filename() << ":" << itr.line << "] "
|
||||
<< std::setw(20) << itr.function;
|
||||
if(!itr.context.empty()) *ofs << " :: " << itr.context;
|
||||
*ofs << "\n";
|
||||
}
|
||||
@@ -487,8 +472,9 @@ identify(uint64_t val)
|
||||
{
|
||||
auto _tid = rocprofiler_thread_id_t{};
|
||||
rocprofiler_get_thread_id(&_tid);
|
||||
rocprofiler_push_external_correlation_id(
|
||||
client_ctx, _tid, rocprofiler_user_data_t{.value = val});
|
||||
rocprofiler_user_data_t user_data = {};
|
||||
user_data.value = val;
|
||||
rocprofiler_push_external_correlation_id(client_ctx, _tid, user_data);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -33,8 +33,8 @@ add_library(callback-api-tracing-client SHARED)
|
||||
target_sources(callback-api-tracing-client PRIVATE client.cpp client.hpp)
|
||||
target_link_libraries(
|
||||
callback-api-tracing-client
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
PRIVATE rocprofiler::rocprofiler rocprofiler::samples-build-flags
|
||||
rocprofiler::samples-common-library)
|
||||
|
||||
set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP)
|
||||
find_package(Threads REQUIRED)
|
||||
@@ -42,9 +42,8 @@ find_package(Threads REQUIRED)
|
||||
add_executable(callback-api-tracing)
|
||||
target_sources(callback-api-tracing PRIVATE main.cpp)
|
||||
target_link_libraries(
|
||||
callback-api-tracing
|
||||
PRIVATE callback-api-tracing-client Threads::Threads
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
callback-api-tracing PRIVATE callback-api-tracing-client Threads::Threads
|
||||
rocprofiler::samples-build-flags)
|
||||
|
||||
add_test(NAME callback-api-tracing COMMAND $<TARGET_FILE:callback-api-tracing>)
|
||||
|
||||
|
||||
@@ -36,15 +36,18 @@
|
||||
#include <rocprofiler/registration.h>
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
|
||||
#include "common/defines.hpp"
|
||||
#include "common/filesystem.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
@@ -52,23 +55,6 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#define ROCPROFILER_CALL(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \
|
||||
std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \
|
||||
<< " failed with error code " << CHECKSTATUS << ": " << status_msg \
|
||||
<< std::endl; \
|
||||
std::stringstream errmsg{}; \
|
||||
errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \
|
||||
<< status_msg << ")"; \
|
||||
throw std::runtime_error(errmsg.str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace client
|
||||
{
|
||||
namespace
|
||||
@@ -99,8 +85,6 @@ rocprofiler_context_id_t client_ctx = {};
|
||||
void
|
||||
print_call_stack(const call_stack_t& _call_stack)
|
||||
{
|
||||
namespace fs = ::std::filesystem;
|
||||
|
||||
auto ofname = std::string{"api_callback_trace.log"};
|
||||
if(auto* eofname = getenv("ROCPROFILER_SAMPLE_OUTPUT_FILE")) ofname = eofname;
|
||||
|
||||
@@ -131,8 +115,8 @@ print_call_stack(const call_stack_t& _call_stack)
|
||||
for(const auto& itr : _call_stack)
|
||||
{
|
||||
*ofs << std::left << std::setw(2) << ++n << "/" << std::setw(2) << _call_stack.size()
|
||||
<< " [" << fs::path{itr.file}.filename() << ":" << itr.line << "] " << std::setw(20)
|
||||
<< itr.function;
|
||||
<< " [" << common::fs::path{itr.file}.filename() << ":" << itr.line << "] "
|
||||
<< std::setw(20) << itr.function;
|
||||
if(!itr.context.empty()) *ofs << " :: " << itr.context;
|
||||
*ofs << "\n";
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ add_library(code-object-tracing-client SHARED)
|
||||
target_sources(code-object-tracing-client PRIVATE client.cpp)
|
||||
target_link_libraries(
|
||||
code-object-tracing-client
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
PRIVATE rocprofiler::rocprofiler rocprofiler::samples-build-flags
|
||||
rocprofiler::samples-common-library)
|
||||
|
||||
set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP)
|
||||
find_package(Threads REQUIRED)
|
||||
@@ -43,7 +43,7 @@ add_executable(code-object-tracing)
|
||||
target_sources(code-object-tracing PRIVATE main.cpp)
|
||||
target_link_libraries(
|
||||
code-object-tracing PRIVATE code-object-tracing-client Threads::Threads
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
rocprofiler::samples-build-flags)
|
||||
|
||||
add_test(NAME code-object-tracing COMMAND $<TARGET_FILE:code-object-tracing>)
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#include <rocprofiler/registration.h>
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
|
||||
#include "common/defines.hpp"
|
||||
#include "common/filesystem.hpp"
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
@@ -45,9 +48,9 @@
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
@@ -57,22 +60,6 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#define ROCPROFILER_CALL(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \
|
||||
std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \
|
||||
<< " failed with error code " << CHECKSTATUS << ": " << status_msg \
|
||||
<< std::endl; \
|
||||
std::stringstream errmsg{}; \
|
||||
errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \
|
||||
<< status_msg << ")"; \
|
||||
throw std::runtime_error(errmsg.str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace client
|
||||
{
|
||||
namespace
|
||||
@@ -172,8 +159,6 @@ cxa_demangle(std::string_view _mangled_name, int* _status)
|
||||
void
|
||||
print_call_stack(const call_stack_t& _call_stack)
|
||||
{
|
||||
namespace fs = ::std::filesystem;
|
||||
|
||||
auto ofname = std::string{"code_object_trace.log"};
|
||||
if(auto* eofname = getenv("ROCPROFILER_SAMPLE_OUTPUT_FILE")) ofname = eofname;
|
||||
|
||||
@@ -203,8 +188,8 @@ print_call_stack(const call_stack_t& _call_stack)
|
||||
for(const auto& itr : _call_stack)
|
||||
{
|
||||
*ofs << std::left << std::setw(2) << ++n << "/" << std::setw(2) << _call_stack.size()
|
||||
<< " [" << fs::path{itr.file}.filename() << ":" << itr.line << "] " << std::setw(20)
|
||||
<< itr.function;
|
||||
<< " [" << common::fs::path{itr.file}.filename() << ":" << itr.line << "] "
|
||||
<< std::setw(20) << itr.function;
|
||||
if(!itr.context.empty()) *ofs << " :: " << itr.context;
|
||||
*ofs << "\n";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# common utilities for samples
|
||||
#
|
||||
|
||||
# build flags
|
||||
add_library(rocprofiler-samples-build-flags INTERFACE)
|
||||
add_library(rocprofiler::samples-build-flags ALIAS rocprofiler-samples-build-flags)
|
||||
target_compile_options(rocprofiler-samples-build-flags INTERFACE -W -Wall -Wextra
|
||||
-Wshadow)
|
||||
target_compile_features(rocprofiler-samples-build-flags INTERFACE cxx_std_17)
|
||||
|
||||
if(ROCPROFILER_BUILD_CI OR ROCPROFILER_BUILD_WERROR)
|
||||
target_compile_options(rocprofiler-samples-build-flags INTERFACE -Werror)
|
||||
endif()
|
||||
|
||||
# common utilities
|
||||
cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH COMMON_LIBRARY_INCLUDE_DIR)
|
||||
|
||||
add_library(rocprofiler-samples-common-library INTERFACE)
|
||||
add_library(rocprofiler::samples-common-library ALIAS rocprofiler-samples-common-library)
|
||||
target_link_libraries(rocprofiler-samples-common-library
|
||||
INTERFACE rocprofiler::samples-build-flags)
|
||||
target_compile_features(rocprofiler-samples-common-library INTERFACE cxx_std_17)
|
||||
target_include_directories(rocprofiler-samples-common-library
|
||||
INTERFACE ${COMMON_LIBRARY_INCLUDE_DIR})
|
||||
|
||||
set(EXTERNAL_SUBMODULE_DIR "${PROJECT_SOURCE_DIR}/../external")
|
||||
cmake_path(ABSOLUTE_PATH EXTERNAL_SUBMODULE_DIR NORMALIZE)
|
||||
|
||||
if(EXISTS ${EXTERNAL_SUBMODULE_DIR}/filesystem/include/ghc/filesystem.hpp)
|
||||
target_compile_definitions(
|
||||
rocprofiler-samples-common-library
|
||||
INTERFACE $<BUILD_INTERFACE:ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM=1>)
|
||||
target_include_directories(
|
||||
rocprofiler-samples-common-library SYSTEM
|
||||
INTERFACE $<BUILD_INTERFACE:${EXTERNAL_SUBMODULE_DIR}/filesystem/include>)
|
||||
endif()
|
||||
@@ -0,0 +1,39 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define ROCPROFILER_CALL(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \
|
||||
std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \
|
||||
<< " failed with error code " << CHECKSTATUS << ": " << status_msg \
|
||||
<< std::endl; \
|
||||
std::stringstream errmsg{}; \
|
||||
errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \
|
||||
<< status_msg << ")"; \
|
||||
throw std::runtime_error(errmsg.str()); \
|
||||
} \
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM)
|
||||
# if defined __has_include
|
||||
# if __has_include(<ghc/filesystem.hpp>)
|
||||
# define ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM 1
|
||||
# else
|
||||
# define ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM 0
|
||||
# endif
|
||||
# else
|
||||
# define ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM == 0
|
||||
# if defined __has_include
|
||||
# if __has_include(<version>)
|
||||
# include <version>
|
||||
# endif
|
||||
# endif
|
||||
|
||||
# if defined(__cpp_lib_filesystem)
|
||||
# define ROCPROFILER_SAMPLES_HAS_CPP_LIB_FILESYSTEM 1
|
||||
# else
|
||||
# if defined __has_include
|
||||
# if __has_include(<filesystem>)
|
||||
# define ROCPROFILER_SAMPLES_HAS_CPP_LIB_FILESYSTEM 1
|
||||
# endif
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// include the correct filesystem header
|
||||
#if defined(ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM) && \
|
||||
ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM > 0
|
||||
# include <ghc/filesystem.hpp>
|
||||
#elif defined(ROCPROFILER_SAMPLES_HAS_CPP_LIB_FILESYSTEM) && \
|
||||
ROCPROFILER_SAMPLES_HAS_CPP_LIB_FILESYSTEM > 0
|
||||
# include <filesystem>
|
||||
#else
|
||||
# include <experimental/filesystem>
|
||||
#endif
|
||||
|
||||
namespace common
|
||||
{
|
||||
#if defined(ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM) && \
|
||||
ROCPROFILER_SAMPLES_HAS_GHC_LIB_FILESYSTEM > 0
|
||||
namespace fs = ::ghc::filesystem; // NOLINT
|
||||
#elif defined(ROCPROFILER_SAMPLES_HAS_CPP_LIB_FILESYSTEM) && \
|
||||
ROCPROFILER_SAMPLES_HAS_CPP_LIB_FILESYSTEM > 0
|
||||
namespace fs = ::std::filesystem; // NOLINT
|
||||
#else
|
||||
namespace fs = ::std::experimental::filesystem; // NOLINT
|
||||
#endif
|
||||
} // namespace common
|
||||
@@ -31,7 +31,11 @@ endif()
|
||||
|
||||
add_library(counter-collection-buffer-client SHARED)
|
||||
target_sources(counter-collection-buffer-client PRIVATE client.cpp client.hpp)
|
||||
target_link_libraries(counter-collection-buffer-client PRIVATE rocprofiler::rocprofiler)
|
||||
target_link_libraries(
|
||||
counter-collection-buffer-client
|
||||
PUBLIC rocprofiler::samples-build-flags
|
||||
PRIVATE rocprofiler::rocprofiler rocprofiler::samples-common-library)
|
||||
|
||||
set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP)
|
||||
add_executable(counter-collection-buffer)
|
||||
target_sources(counter-collection-buffer PRIVATE main.cpp)
|
||||
@@ -55,8 +59,11 @@ set_tests_properties(
|
||||
add_library(counter-collection-functional-counter-client SHARED)
|
||||
target_sources(counter-collection-functional-counter-client
|
||||
PRIVATE print_functional_counters.cpp client.hpp)
|
||||
target_link_libraries(counter-collection-functional-counter-client
|
||||
PRIVATE rocprofiler::rocprofiler)
|
||||
target_link_libraries(
|
||||
counter-collection-functional-counter-client
|
||||
PUBLIC rocprofiler::samples-build-flags
|
||||
PRIVATE rocprofiler::rocprofiler rocprofiler::samples-common-library)
|
||||
|
||||
add_executable(counter-collection-print-functional-counters)
|
||||
target_sources(counter-collection-print-functional-counters PRIVATE main.cpp)
|
||||
target_link_libraries(
|
||||
|
||||
@@ -78,9 +78,9 @@ get_output_stream()
|
||||
static std::ostream* isTerm = []() -> std::ostream* {
|
||||
if(auto* outfile = getenv("ROCPROFILER_SAMPLE_OUTPUT_FILE"))
|
||||
{
|
||||
if(outfile == "stdout")
|
||||
if(std::string_view{outfile} == "stdout")
|
||||
return static_cast<std::ostream*>(&std::cout);
|
||||
else if(outfile == "stderr")
|
||||
else if(std::string_view{outfile} == "stderr")
|
||||
return &std::cerr;
|
||||
}
|
||||
return nullptr;
|
||||
@@ -125,12 +125,12 @@ buffered_callback(rocprofiler_context_id_t,
|
||||
}
|
||||
|
||||
void
|
||||
dispatch_callback(rocprofiler_queue_id_t queue_id,
|
||||
const rocprofiler_agent_t* agent,
|
||||
rocprofiler_correlation_id_t correlation_id,
|
||||
const hsa_kernel_dispatch_packet_t* dispatch_packet,
|
||||
void* callback_data_args,
|
||||
rocprofiler_profile_config_id_t* config)
|
||||
dispatch_callback(rocprofiler_queue_id_t /*queue_id*/,
|
||||
const rocprofiler_agent_t* agent,
|
||||
rocprofiler_correlation_id_t /*correlation_id*/,
|
||||
const hsa_kernel_dispatch_packet_t* /*dispatch_packet*/,
|
||||
void* /*callback_data_args*/,
|
||||
rocprofiler_profile_config_id_t* config)
|
||||
{
|
||||
/**
|
||||
* This simple example uses the same profile counter set for all agents.
|
||||
|
||||
@@ -108,12 +108,12 @@ buffered_callback(rocprofiler_context_id_t,
|
||||
}
|
||||
|
||||
void
|
||||
dispatch_callback(rocprofiler_queue_id_t queue_id,
|
||||
const rocprofiler_agent_t* agent,
|
||||
rocprofiler_correlation_id_t correlation_id,
|
||||
const hsa_kernel_dispatch_packet_t* dispatch_packet,
|
||||
void* callback_data_args,
|
||||
rocprofiler_profile_config_id_t* config)
|
||||
dispatch_callback(rocprofiler_queue_id_t /*queue_id*/,
|
||||
const rocprofiler_agent_t* agent,
|
||||
rocprofiler_correlation_id_t /*correlation_id*/,
|
||||
const hsa_kernel_dispatch_packet_t* /*dispatch_packet*/,
|
||||
void* /*callback_data_args*/,
|
||||
rocprofiler_profile_config_id_t* config)
|
||||
{
|
||||
auto& cap = *get_capture();
|
||||
auto wlock = std::unique_lock{cap.m_mutex};
|
||||
@@ -205,9 +205,8 @@ tool_fini(void*)
|
||||
|
||||
std::clog << "In tool fini\n";
|
||||
|
||||
auto cap_ptr = get_capture();
|
||||
auto& cap = *get_capture();
|
||||
auto wlock = std::unique_lock{cap.m_mutex};
|
||||
auto& cap = *get_capture();
|
||||
auto wlock = std::unique_lock{cap.m_mutex};
|
||||
|
||||
if(cap.captured.size() != cap.expected.size())
|
||||
{
|
||||
|
||||
@@ -33,17 +33,16 @@ add_library(intercept-table-client SHARED)
|
||||
target_sources(intercept-table-client PRIVATE client.cpp client.hpp)
|
||||
target_link_libraries(
|
||||
intercept-table-client
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
PRIVATE rocprofiler::rocprofiler rocprofiler::samples-build-flags
|
||||
rocprofiler::samples-common-library)
|
||||
|
||||
set_source_files_properties(main.cpp PROPERTIES LANGUAGE HIP)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_executable(intercept-table)
|
||||
target_sources(intercept-table PRIVATE main.cpp)
|
||||
target_link_libraries(
|
||||
intercept-table PRIVATE intercept-table-client Threads::Threads
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
target_link_libraries(intercept-table PRIVATE intercept-table-client Threads::Threads
|
||||
rocprofiler::samples-build-flags)
|
||||
|
||||
add_test(NAME intercept-table COMMAND $<TARGET_FILE:intercept-table>)
|
||||
|
||||
|
||||
@@ -36,15 +36,18 @@
|
||||
#include <rocprofiler/registration.h>
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
|
||||
#include "common/defines.hpp"
|
||||
#include "common/filesystem.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
@@ -52,22 +55,8 @@
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#define ROCPROFILER_CALL(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
std::string status_msg = rocprofiler_get_status_string(CHECKSTATUS); \
|
||||
std::cerr << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg \
|
||||
<< " failed with error code " << CHECKSTATUS << ": " << status_msg \
|
||||
<< std::endl; \
|
||||
std::stringstream errmsg{}; \
|
||||
errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \
|
||||
<< status_msg << ")"; \
|
||||
throw std::runtime_error(errmsg.str()); \
|
||||
} \
|
||||
}
|
||||
#include "common/defines.hpp"
|
||||
#include "common/filesystem.hpp"
|
||||
|
||||
namespace client
|
||||
{
|
||||
@@ -93,8 +82,6 @@ std::map<size_t, wrap_count_t> client_wrap_data = {};
|
||||
void
|
||||
print_call_stack(const call_stack_t& _call_stack)
|
||||
{
|
||||
namespace fs = ::std::filesystem;
|
||||
|
||||
auto ofname = std::string{"intercept_table.log"};
|
||||
if(auto* eofname = getenv("ROCPROFILER_SAMPLE_OUTPUT_FILE")) ofname = eofname;
|
||||
|
||||
@@ -125,8 +112,8 @@ print_call_stack(const call_stack_t& _call_stack)
|
||||
for(const auto& itr : _call_stack)
|
||||
{
|
||||
*ofs << std::left << std::setw(2) << ++n << "/" << std::setw(2) << _call_stack.size()
|
||||
<< " [" << fs::path{itr.file}.filename() << ":" << itr.line << "] " << std::setw(20)
|
||||
<< itr.function;
|
||||
<< " [" << common::fs::path{itr.file}.filename() << ":" << itr.line << "] "
|
||||
<< std::setw(20) << itr.function;
|
||||
if(!itr.context.empty()) *ofs << " :: " << itr.context;
|
||||
*ofs << "\n";
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
#
|
||||
#
|
||||
#
|
||||
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
|
||||
|
||||
project(rocprofiler-samples-pc-sampling LANGUAGES C CXX)
|
||||
|
||||
if(NOT TARGET rocprofiler::rocprofiler)
|
||||
find_package(rocprofiler REQUIRED)
|
||||
endif()
|
||||
|
||||
add_executable(pc_sampling_single-user-host-trap)
|
||||
target_sources(pc_sampling_single-user-host-trap PRIVATE common.h
|
||||
single-user-host-trap.cpp)
|
||||
target_link_libraries(
|
||||
pc_sampling_single-user-host-trap
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
|
||||
add_executable(pc_sampling_single-user-host-trap-retry)
|
||||
target_sources(pc_sampling_single-user-host-trap-retry
|
||||
PRIVATE common.h single-user-host-trap-retries-service-instantiation.cpp)
|
||||
target_link_libraries(
|
||||
pc_sampling_single-user-host-trap-retry
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
|
||||
add_executable(pc_sampling_single-user-multiple-agents)
|
||||
target_sources(pc_sampling_single-user-multiple-agents
|
||||
PRIVATE common.h single-user-multiple-agents.cpp)
|
||||
target_link_libraries(
|
||||
pc_sampling_single-user-multiple-agents
|
||||
PRIVATE rocprofiler::rocprofiler
|
||||
$<TARGET_NAME_IF_EXISTS:rocprofiler::samples-build-flags>)
|
||||
@@ -1,147 +0,0 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
constexpr size_t BUFFER_SIZE_BYTES = 4096;
|
||||
constexpr size_t WATERMARK = (BUFFER_SIZE_BYTES / 2);
|
||||
const std::string_view MI200_NAME = "gfx90a";
|
||||
|
||||
#define ROCPROFILER_CALL(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS != ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
puts(#result " failed"); \
|
||||
} \
|
||||
}
|
||||
|
||||
// We might want to test the calls that fails
|
||||
// e.g. calling `rocprofiler_configure_pc_sampling_service `
|
||||
// after previous initialization.
|
||||
#define ROCPROFILER_CALL_FAILS(result, msg) \
|
||||
{ \
|
||||
rocprofiler_status_t CHECKSTATUS = result; \
|
||||
if(CHECKSTATUS == ROCPROFILER_STATUS_SUCCESS) \
|
||||
{ \
|
||||
puts(#result " succeeded"); \
|
||||
} \
|
||||
}
|
||||
|
||||
static rocprofiler_status_t
|
||||
find_first_gpu_agent_impl(const rocprofiler_agent_t** agents, size_t num_agents, void* data)
|
||||
{
|
||||
// data is required
|
||||
if(!data) return ROCPROFILER_STATUS_ERROR;
|
||||
|
||||
auto* _out_agent = static_cast<rocprofiler_agent_t*>(data);
|
||||
// find the first GPU agent
|
||||
for(size_t i = 0; i < num_agents; i++)
|
||||
{
|
||||
if(agents[i]->type == ROCPROFILER_AGENT_TYPE_GPU)
|
||||
{
|
||||
*_out_agent = *agents[i];
|
||||
printf("[%s] %s :: id=%u, type=%i, num pc sample configs=%zu\n",
|
||||
__FUNCTION__,
|
||||
_out_agent->name,
|
||||
_out_agent->node_id,
|
||||
_out_agent->type,
|
||||
_out_agent->num_pc_sampling_configs);
|
||||
return ROCPROFILER_STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[%s] %s :: id=%u, type=%i, num pc sample configs=%zu\n",
|
||||
__FUNCTION__,
|
||||
agents[i]->name,
|
||||
agents[i]->node_id,
|
||||
agents[i]->type,
|
||||
agents[i]->num_pc_sampling_configs);
|
||||
}
|
||||
}
|
||||
return ROCPROFILER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
static std::optional<rocprofiler_agent_t>
|
||||
find_first_gpu_agent()
|
||||
{
|
||||
// This function returns the first gpu agent it encounters.
|
||||
// TODO: write the better function querying information about the agent,
|
||||
// and return if the agent is MI200.
|
||||
rocprofiler_agent_t gpu_agent;
|
||||
|
||||
auto status = rocprofiler_query_available_agents(
|
||||
&find_first_gpu_agent_impl, sizeof(rocprofiler_agent_t), static_cast<void*>(&gpu_agent));
|
||||
|
||||
if(status != ROCPROFILER_STATUS_SUCCESS) return std::nullopt;
|
||||
|
||||
return gpu_agent;
|
||||
}
|
||||
|
||||
static 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)
|
||||
{
|
||||
// Vladimir: I am not sure if this is the right way of iterating over PC sampling records.
|
||||
printf(
|
||||
"The number of delivered samples is: %zu, while the number of dropped samples is: %lu.\n",
|
||||
num_headers,
|
||||
drop_count);
|
||||
|
||||
for(size_t i = 0; i < num_headers; i++)
|
||||
{
|
||||
auto* cur_header = headers[i];
|
||||
if(cur_header->category == ROCPROFILER_BUFFER_CATEGORY_PC_SAMPLING)
|
||||
{
|
||||
auto* pc_sample = static_cast<rocprofiler_pc_sampling_record_t*>(cur_header->payload);
|
||||
printf("--- pc: %lx, dispatch_id: %lx, timestamp: %lu, hardware_id: %lu\n",
|
||||
pc_sample->pc,
|
||||
pc_sample->dispatch_id,
|
||||
pc_sample->timestamp,
|
||||
pc_sample->hardware_id);
|
||||
// Vladimir: How to parse the remaining part of the `rocprofiler_pc_sampling_record_t`
|
||||
// struct?
|
||||
}
|
||||
}
|
||||
// Vladimr: We might want to add somewhere in the documentation that headars actually contain PC
|
||||
// samples.
|
||||
}
|
||||
|
||||
static void
|
||||
run_HIP_app()
|
||||
{
|
||||
// TODO: provide the simple HIP app
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Vladimir: The example of using Host-trap PC sampling on a system with single MI200/300 by two
|
||||
// users. The first user initiates Host-Trap sampling with the configuration A. The second user
|
||||
// tries initiaiting stochastic sampling with configuration B and fails. Then it queries available
|
||||
// configurations and observes only the configuration A. It accepts it and starts PC sampling.
|
||||
// Vladimir: Currently, this example is written as a single-threaded program.
|
||||
// Decide whether to move the second user to a separate thread or process
|
||||
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#define HOST_TRAP_INTERVAL 1000
|
||||
|
||||
rocprofiler_pc_sampling_method_t host_trap_sampling_method;
|
||||
rocprofiler_pc_sampling_unit_t host_trap_sampling_unit_time;
|
||||
uint64_t host_trap_interval;
|
||||
|
||||
void
|
||||
second_user()
|
||||
{
|
||||
// creating a context
|
||||
rocprofiler_context_id_t context_id2;
|
||||
ROCPROFILER_CALL(rocprofiler_create_context(&context_id2),
|
||||
"Cannot create context for the second user\n");
|
||||
|
||||
auto gpu_agent = find_first_gpu_agent();
|
||||
if(!gpu_agent) throw std::runtime_error{"no gpu agents were found"};
|
||||
|
||||
// creating a buffer that will hold pc sampling information
|
||||
rocprofiler_buffer_policy_t lossless_buffer_action = ROCPROFILER_BUFFER_POLICY_LOSSLESS;
|
||||
rocprofiler_buffer_id_t buffer_id2;
|
||||
ROCPROFILER_CALL(rocprofiler_create_buffer(context_id2,
|
||||
BUFFER_SIZE_BYTES,
|
||||
WATERMARK,
|
||||
lossless_buffer_action,
|
||||
rocprofiler_pc_sampling_callback,
|
||||
nullptr,
|
||||
&buffer_id2),
|
||||
"Cannot create pc sampling buffer for the second user");
|
||||
|
||||
// The second user tries to create another pc sampling service with different configuration,
|
||||
// but the rocprofiler rejects it.
|
||||
rocprofiler_pc_sampling_method_t sampling_method2 = ROCPROFILER_PC_SAMPLING_METHOD_STOCHASTIC;
|
||||
rocprofiler_pc_sampling_unit_t sampling_unit2 = ROCPROFILER_PC_SAMPLING_UNIT_CYCLES;
|
||||
uint64_t interval2 = 2048; // I assumed micro secs, so this should be 1ms
|
||||
// The following function returns an error code indicating the PC sampling has already been
|
||||
// configured.
|
||||
ROCPROFILER_CALL_FAILS(
|
||||
rocprofiler_configure_pc_sampling_service(
|
||||
context_id2, *gpu_agent, sampling_method2, sampling_unit2, interval2, buffer_id2),
|
||||
"Instantiation of the PC sampling service should fail");
|
||||
|
||||
// After failure, the second user queries available configuration and observes the one chosen by
|
||||
// the first user.
|
||||
auto config_count = gpu_agent->num_pc_sampling_configs;
|
||||
auto configs = std::vector<rocprofiler_pc_sampling_configuration_t>{};
|
||||
configs.reserve(config_count);
|
||||
for(size_t i = 0; i < config_count; ++i)
|
||||
configs.emplace_back(gpu_agent->pc_sampling_configs[i]);
|
||||
|
||||
// Only one configuration should be listed, and its parameters should match the parameters set
|
||||
// by the first user. Vladimir: Is it ok to use assertions? In the release mode, they might be
|
||||
// ignored.
|
||||
assert(config_count == 1);
|
||||
rocprofiler_pc_sampling_configuration_t first_user_config = configs[0];
|
||||
assert(first_user_config.method == host_trap_sampling_method);
|
||||
assert(first_user_config.unit == host_trap_sampling_unit_time);
|
||||
// Vladimir: Should the min_interval and max_interval have the same value at this point (the PC
|
||||
// sampling is alredy configured)??
|
||||
assert(first_user_config.min_interval == host_trap_interval &&
|
||||
first_user_config.min_interval == first_user_config.max_interval);
|
||||
|
||||
// Reuse the same configuration set by the first user.
|
||||
// The second user is satisfied with the configuration chosen by the first user, so it
|
||||
// starts PC sampling.
|
||||
ROCPROFILER_CALL(rocprofiler_configure_pc_sampling_service(context_id2,
|
||||
*gpu_agent,
|
||||
first_user_config.method,
|
||||
first_user_config.unit,
|
||||
first_user_config.min_interval,
|
||||
buffer_id2),
|
||||
"The second user cannot share already created PC sampling configuration");
|
||||
|
||||
// Starting the context that should trigger PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_start_context(context_id2),
|
||||
"Cannot start PC sampling context for the second user");
|
||||
|
||||
// Running the applicaiton
|
||||
run_HIP_app();
|
||||
|
||||
// Stop the context that should stop PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_stop_context(context_id2),
|
||||
"Cannot start PC sampling context for the second user");
|
||||
|
||||
// Explicit buffer flush, before destroying it
|
||||
ROCPROFILER_CALL(rocprofiler_flush_buffer(buffer_id2),
|
||||
"Cannot destroy the second user's buffer");
|
||||
// Destroying the buffer
|
||||
ROCPROFILER_CALL(rocprofiler_destroy_buffer(buffer_id2), "Cannot destroy the second user's");
|
||||
}
|
||||
|
||||
int
|
||||
main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
// creating a context
|
||||
rocprofiler_context_id_t context_id;
|
||||
ROCPROFILER_CALL(rocprofiler_create_context(&context_id), "Cannot create context\n");
|
||||
|
||||
auto gpu_agent = find_first_gpu_agent();
|
||||
if(!gpu_agent)
|
||||
{
|
||||
fprintf(stderr, "no gpu agents were found\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// creating a buffer that will hold pc sampling information
|
||||
rocprofiler_buffer_policy_t drop_buffer_action = ROCPROFILER_BUFFER_POLICY_DISCARD;
|
||||
rocprofiler_buffer_id_t buffer_id;
|
||||
ROCPROFILER_CALL(rocprofiler_create_buffer(context_id,
|
||||
BUFFER_SIZE_BYTES,
|
||||
WATERMARK,
|
||||
drop_buffer_action,
|
||||
rocprofiler_pc_sampling_callback,
|
||||
nullptr,
|
||||
&buffer_id),
|
||||
"Cannot create pc sampling buffer");
|
||||
|
||||
// PC sampling service configuration
|
||||
host_trap_sampling_method = ROCPROFILER_PC_SAMPLING_METHOD_HOST_TRAP;
|
||||
host_trap_sampling_unit_time = ROCPROFILER_PC_SAMPLING_UNIT_TIME;
|
||||
// Vladimir: What units are we using for time? ms, micro secs, ns?
|
||||
host_trap_interval = HOST_TRAP_INTERVAL;
|
||||
// Instantiating the first PC sampling service succeeds.
|
||||
ROCPROFILER_CALL(rocprofiler_configure_pc_sampling_service(context_id,
|
||||
*gpu_agent,
|
||||
host_trap_sampling_method,
|
||||
host_trap_sampling_unit_time,
|
||||
host_trap_interval,
|
||||
buffer_id),
|
||||
"Cannot create PC sampling service");
|
||||
|
||||
// Trigger the second user code.
|
||||
// Vladimir: Discuss whether this should be put in a separate thread/process.
|
||||
second_user();
|
||||
|
||||
// Starting the context that should trigger PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_start_context(context_id), "Cannot start PC sampling context");
|
||||
|
||||
// Running the applicaiton
|
||||
run_HIP_app();
|
||||
|
||||
// Stop the context that should stop PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_stop_context(context_id), "Cannot start PC sampling context");
|
||||
|
||||
// Explicit buffer flush, before destroying it
|
||||
ROCPROFILER_CALL(rocprofiler_flush_buffer(buffer_id), "Cannot destroy buffer");
|
||||
// Destroying the buffer
|
||||
ROCPROFILER_CALL(rocprofiler_destroy_buffer(buffer_id), "Cannot destroy buffer");
|
||||
|
||||
// Vladimir: Do we need to destroy context or a service?
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Vladimir: The example of using Host-trap PC sampling exclusively on the system with single MI200.
|
||||
// If any of the rocprofiler calls returns status fail, we simply stop the application.
|
||||
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
#include <cstdlib>
|
||||
#include "common.h"
|
||||
|
||||
int
|
||||
main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
// creating a context
|
||||
rocprofiler_context_id_t context_id;
|
||||
ROCPROFILER_CALL(rocprofiler_create_context(&context_id), "Cannot create context\n");
|
||||
|
||||
auto gpu_agent = find_first_gpu_agent();
|
||||
if(!gpu_agent)
|
||||
{
|
||||
fprintf(stderr, "no gpu agents were found\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// creating a buffer that will hold pc sampling information
|
||||
rocprofiler_buffer_policy_t drop_buffer_action = ROCPROFILER_BUFFER_POLICY_DISCARD;
|
||||
rocprofiler_buffer_id_t buffer_id;
|
||||
ROCPROFILER_CALL(rocprofiler_create_buffer(context_id,
|
||||
BUFFER_SIZE_BYTES,
|
||||
WATERMARK,
|
||||
drop_buffer_action,
|
||||
rocprofiler_pc_sampling_callback,
|
||||
nullptr,
|
||||
&buffer_id),
|
||||
"Cannot create pc sampling buffer");
|
||||
|
||||
// PC sampling service configuration
|
||||
rocprofiler_pc_sampling_method_t sampling_method = ROCPROFILER_PC_SAMPLING_METHOD_HOST_TRAP;
|
||||
rocprofiler_pc_sampling_unit_t sampling_unit = ROCPROFILER_PC_SAMPLING_UNIT_TIME;
|
||||
// What units are we using for time? ms, micro secs, ns?
|
||||
uint64_t interval = 1000; // I assumed micro secs, so this should be 1ms
|
||||
// Instantiating the PC sampling service
|
||||
ROCPROFILER_CALL(
|
||||
rocprofiler_configure_pc_sampling_service(
|
||||
context_id, *gpu_agent, sampling_method, sampling_unit, interval, buffer_id),
|
||||
"Cannot create PC sampling service");
|
||||
|
||||
// Vladimir: Is this the place of retrying if someone already created the
|
||||
// configuration and the previous call fails?
|
||||
|
||||
// Starting the context that should trigger PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_start_context(context_id), "Cannot start PC sampling context");
|
||||
|
||||
// Running the applicaiton
|
||||
run_HIP_app();
|
||||
|
||||
// Stop the context that should stop PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_stop_context(context_id), "Cannot start PC sampling context");
|
||||
|
||||
// Explicit buffer flush, before destroying it
|
||||
ROCPROFILER_CALL(rocprofiler_flush_buffer(buffer_id), "Cannot destroy buffer");
|
||||
// Destroying the buffer
|
||||
ROCPROFILER_CALL(rocprofiler_destroy_buffer(buffer_id), "Cannot destroy buffer");
|
||||
|
||||
// Vladimir: Do we need to destroy context or a service?
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,225 +0,0 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2023 Advanced Micro Devices, Inc. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Vladimir: The example that shows how a single user can use PC sampling
|
||||
// on multiple GPU agents.
|
||||
|
||||
#include <rocprofiler/rocprofiler.h>
|
||||
#include <string.h>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include "common.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// GPU agents supporting some kind of PC sampling
|
||||
std::vector<rocprofiler_agent_t> gpu_agents;
|
||||
std::vector<rocprofiler_context_id_t> contexts;
|
||||
std::vector<rocprofiler_buffer_id_t> buffer_ids;
|
||||
|
||||
rocprofiler_status_t
|
||||
find_all_gpu_agents_supporting_pc_sampling_impl(const rocprofiler_agent_t** agents,
|
||||
size_t num_agents,
|
||||
void* data)
|
||||
{
|
||||
// data is required
|
||||
if(!data) return ROCPROFILER_STATUS_ERROR;
|
||||
|
||||
auto* _out_agents = static_cast<std::vector<rocprofiler_agent_t>*>(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=%u, type=%i, num pc sample configs=%zu\n",
|
||||
__FUNCTION__,
|
||||
agents[i]->name,
|
||||
agents[i]->node_id,
|
||||
agents[i]->type,
|
||||
agents[i]->num_pc_sampling_configs);
|
||||
return ROCPROFILER_STATUS_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("[%s] %s :: id=%u, type=%i, num pc sample configs=%zu\n",
|
||||
__FUNCTION__,
|
||||
agents[i]->name,
|
||||
agents[i]->node_id,
|
||||
agents[i]->type,
|
||||
agents[i]->num_pc_sampling_configs);
|
||||
}
|
||||
}
|
||||
|
||||
return !_out_agents->empty() ? ROCPROFILER_STATUS_SUCCESS : ROCPROFILER_STATUS_ERROR;
|
||||
}
|
||||
|
||||
void
|
||||
find_all_gpu_agents_supporting_pc_sampling()
|
||||
{
|
||||
// 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<void*>(&gpu_agents)),
|
||||
"Failed to find GPU agents");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void
|
||||
configure_host_trap_sampling(rocprofiler_context_id_t context_id,
|
||||
rocprofiler_buffer_id_t buffer_id,
|
||||
rocprofiler_agent_t gpu_agent)
|
||||
{
|
||||
// Vladimir: Does MI200 have only one configuration?
|
||||
assert(gpu_agent.num_pc_sampling_configs == 1);
|
||||
|
||||
// Extract the configuration
|
||||
auto host_trap_config = gpu_agent.pc_sampling_configs[0];
|
||||
|
||||
// The mean of min_interval and max_interval
|
||||
auto interval = (host_trap_config.min_interval + host_trap_config.max_interval) / 2;
|
||||
|
||||
ROCPROFILER_CALL(rocprofiler_configure_pc_sampling_service(context_id,
|
||||
gpu_agent,
|
||||
host_trap_config.method,
|
||||
host_trap_config.unit,
|
||||
interval,
|
||||
buffer_id),
|
||||
"Cannot create host-trap PC sampling service");
|
||||
}
|
||||
|
||||
rocprofiler_pc_sampling_configuration_t
|
||||
extract_stochastic_config(const rocprofiler_pc_sampling_configuration_t* configs,
|
||||
size_t num_configs)
|
||||
{
|
||||
// Iterate over an array of configurations and return the first one
|
||||
// with stochasting method.
|
||||
for(size_t i = 0; i < num_configs; i++)
|
||||
{
|
||||
if(configs[i].method == ROCPROFILER_PC_SAMPLING_METHOD_STOCHASTIC)
|
||||
{
|
||||
return configs[i];
|
||||
}
|
||||
}
|
||||
printf("Improper use of the `extract_stochastic_config` function.");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void
|
||||
configure_stochastic_sampling(rocprofiler_context_id_t context_id,
|
||||
rocprofiler_buffer_id_t buffer_id,
|
||||
rocprofiler_agent_t gpu_agent)
|
||||
{
|
||||
// Find the configuration matching stochastic sampling in cycles
|
||||
rocprofiler_pc_sampling_configuration_t stochastic_config =
|
||||
extract_stochastic_config(gpu_agent.pc_sampling_configs, gpu_agent.num_pc_sampling_configs);
|
||||
|
||||
// The mean of min_interval and max_interval
|
||||
auto interval = (stochastic_config.min_interval + stochastic_config.max_interval) / 2;
|
||||
|
||||
ROCPROFILER_CALL(rocprofiler_configure_pc_sampling_service(context_id,
|
||||
gpu_agent,
|
||||
stochastic_config.method,
|
||||
stochastic_config.unit,
|
||||
interval,
|
||||
buffer_id),
|
||||
"Cannot create stochastic PC sampling service");
|
||||
}
|
||||
|
||||
int
|
||||
main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
if(!find_first_gpu_agent())
|
||||
{
|
||||
fprintf(stderr, "no gpu agents were found\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
find_all_gpu_agents_supporting_pc_sampling();
|
||||
|
||||
if(gpu_agents.empty())
|
||||
{
|
||||
printf("No availabe gpu agents\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Vladimir: The relations I assumed:
|
||||
// - a context per gpu agent
|
||||
// - a buffer per context
|
||||
// - a pc sampling service per buffer
|
||||
// How about the following: Single context with mulitple buffers and PC sampling services?
|
||||
// When starting the context, does it start all PC sampling services at once?
|
||||
|
||||
for(auto gpu_agent : gpu_agents)
|
||||
{
|
||||
// creating a context
|
||||
rocprofiler_context_id_t context_id;
|
||||
ROCPROFILER_CALL(rocprofiler_create_context(&context_id), "Cannot create context\n");
|
||||
contexts.push_back(context_id);
|
||||
|
||||
// creating a buffer that will hold pc sampling information
|
||||
rocprofiler_buffer_policy_t drop_buffer_action = ROCPROFILER_BUFFER_POLICY_DISCARD;
|
||||
rocprofiler_buffer_id_t buffer_id;
|
||||
ROCPROFILER_CALL(rocprofiler_create_buffer(context_id,
|
||||
BUFFER_SIZE_BYTES,
|
||||
WATERMARK,
|
||||
drop_buffer_action,
|
||||
rocprofiler_pc_sampling_callback,
|
||||
nullptr,
|
||||
&buffer_id),
|
||||
"Cannot create pc sampling buffer");
|
||||
buffer_ids.push_back(buffer_id);
|
||||
|
||||
if(gpu_agent.name == MI200_NAME)
|
||||
configure_host_trap_sampling(context_id, buffer_id, gpu_agent);
|
||||
else
|
||||
configure_stochastic_sampling(context_id, buffer_id, gpu_agent);
|
||||
|
||||
// Starting the context that should trigger PC sampling
|
||||
ROCPROFILER_CALL(rocprofiler_start_context(context_id), "Cannot start PC sampling context");
|
||||
}
|
||||
|
||||
// Running the applicaiton
|
||||
run_HIP_app();
|
||||
|
||||
for(size_t i = 0; i < gpu_agents.size(); i++)
|
||||
{
|
||||
// Stop the context that should stop PC sampling?
|
||||
ROCPROFILER_CALL(rocprofiler_stop_context(contexts[i]), "Cannot start PC sampling context");
|
||||
// Explicit buffer flush, before destroying it
|
||||
ROCPROFILER_CALL(rocprofiler_flush_buffer(buffer_ids[i]), "Cannot destroy buffer");
|
||||
// Destroying the buffer
|
||||
ROCPROFILER_CALL(rocprofiler_destroy_buffer(buffer_ids[i]), "Cannot destroy buffer");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user