HIP API Tracing (#357)

* Update include/rocprofiler-sdk/hip*

- updates for intercept table

* Update lib/common/units.hpp

- clang-tidy fixes

* Add lib/rocprofiler-sdk/hip

- tracing implementation for the HIP intercept table

* Update source/lib/rocprofiler-sdk/CMakeLists.txt

- add_subdirectory(hip)

* Update source/lib/rocprofiler-sdk/hsa

- offset function in hsa_api_info<Idx>
- remove report_activity, set_callback
- Tweak HSA_API_TABLE_LOOKUP_DEFINITION

* Update lib/rocprofiler-sdk/hip

- rocprofiler::hip::copy_table
- stringize_impl print dereferenced pointers when possible

* Update lib/rocprofiler-sdk/hsa/utils.hpp

- stringize_impl print dereferenced pointers when possible

* Update lib/rocprofiler-sdk/tests/intercept_table.cpp

- remove failures for intercepting HIP API tables

* Update include/rocprofiler-sdk/fwd.h

- add ROCPROFILER_HIP_RUNTIME_LIBRARY (== ROCPROFILER_HIP_LIBRARY)
- add ROCPROFILER_HIP_COMPILER_LIBRARY

* Update lib/rocprofiler-sdk/buffer_tracing.cpp

- Support ROCPROFILER_BUFFER_TRACING_HIP_API in rocprofiler_query_buffer_tracing_kind_operation_name
- Support ROCPROFILER_BUFFER_TRACING_HIP_API in rocprofiler_iterate_buffer_tracing_kind_operations

* Update lib/rocprofiler-sdk/callback_tracing.cpp

- Support ROCPROFILER_CALLBACK_TRACING_HIP_API in rocprofiler_query_callback_tracing_kind_operation_name
- Support ROCPROFILER_CALLBACK_TRACING_HIP_API in rocprofiler_iterate_callback_tracing_kind_operations
- Support ROCPROFILER_CALLBACK_TRACING_HIP_API in rocprofiler_iterate_callback_tracing_kind_operation_args

* Update lib/rocprofiler-sdk/intercept_table.cpp

- support HipDispatchTable and HipCompilerDispatchTable

* Update lib/rocprofiler-sdk/internal_threading.cpp

- Support ROCPROFILER_HIP_COMPILER_LIBRARY

* Update lib/rocprofiler-sdk/registration.cpp

- Support "hip" and "hip_compiler" in rocprofiler_set_api_table
- Added some extra logging

* Update samples/api_{buffered,callback}_tracing

- Modifications to demonstrate HIP API tracing

* Update tests/kernel-tracing

- Modifications to handle/test HIP API tracing

* Separate HIP tracing from HIP compiler tracing

* Fix installation of include/rocprofiler-sdk/hip/*

- add compiler and table headers to install

* Fixes to HIP interception

- hip_api_trace.hpp was updated a bit
  - removed hipGetDeviceProperties (generic)
  - added hipGetDevicePropertiesR0600
  - added hipGetDevicePropertiesR0000
  - removed hipRegisterTracerCallback
  - reordered hipCreateChannelDesc, hipExtModuleLaunchKernel, hipHccModuleLaunchKernel
  - added hipDrvGraphAddMemsetNode
- static asserts in hsa_api_info ensuring ordering of pointers

* Update lib/rocprofiler-sdk/hip/hip.*

- use size_t instead of rocprofiler_hip_table_api_id_t as non-type template parameter (smaller binary)
- separated out population of callback_context_data and buffered_context_data into non-template function (significantly smaller binary)

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

- separated out population of callback_context_data and buffered_context_data into non-template function (significantly smaller binary)

* Update test/kernel-tracing/validate.py

- does not expect any hip_api_traces until libamdhip.so actually starts using rocprofiler-register

* Update tests/tools/json-tool.cpp

- fix context associated with "HIP_API_CALLBACK"

* Update external/CMakeLists.txt

- move misc variables to top of CMakeLists.txt so they apply to all external subprojects
  - BUILD_TESTING (OFF)
  - BUILD_SHARED_LIBS (OFF)
  - BUILD_OBJECT_LIBS (OFF)
  - BUILD_STATIC_LIBS (ON)
  - CMAKE_POSITION_INDEPENDENT_CODE (ON)
  - CMAKE_VISIBILITY_INLINES_HIDDEN (ON)
  - CMAKE_CXX_VISIBILITY_PRESET (hidden)
- disable using libunwind in glog

* Update lib/rocprofiler-{sdk,sdk-tool}/CMakeLists.txt

- remove explicit setting of SKIP_BUILD_RPATH

* Update CMakeLists.txt

- set high-level CMAKE_BUILD_RPATH and CMAKE_INSTALL_RPATH_USE_LINK_PATH

* Update tests/CMakeLists.txt

- include(GNUInstallDirs)

* Update samples/CMakeLists.txt

- include(GNUInstallDirs)

* Update include/rocprofiler-sdk/hip/{compiler_api,api}_args.h

- remove extern "C" due to incompatibility b/t empty struct in C (size 0) vs. empty struct in C++ (size 1)

* Update lib/rocprofiler-sdk/hip/details/ostream.hpp

- clang-tidy fixes

* Update cmake/rocprofiler_linting.cmake

- add a feature for clang tidy exe

* Update lib/rocprofiler-sdk/hip/hip.cpp

- use recursion instead of fold expression due to clang-tidy errors (maximum nesting level exceeded)

* Update lib/rocprofiler-sdk/buffer_tracing.cpp

- fix merge

* Update lib/rocprofiler-sdk/callback_tracing.cpp

- fix merge

* Update bin/rocprofv3

- args for marker, HIP runtime, and HIP compiler tracing

* Update tests/apps/simple-transpose

- use roctx

* Update tests/rocprofv3/tracing

- validate marker API data

* Update lib/rocprofiler-sdk-tool

- support for HIP runtime, HIP compiler, marker API

* Update queue/queue_controller/registration/utility

- call hsa::queue_controller_fini() during finalization
- add a yield function to common/utility.hpp
  - implements a thread yield + sleep
- add a sync function to Queue class
- add a iterate_queues member function to QueueController
  - this is used to sync each queue during queue_controller_fini()

* Fix data races: queue/context/stable_vector

- stable_vector::emplace_back returns reference
- correlation id map uses stable_vector
- queue_info_session has explicit fields for queue id, hsa agent, rocp agent
- use hsa::get_table() in AsyncSignalHandler
- WriteInterceptor does not use TLS for context array

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

- static object for API subtables
- accessors for API subtables
- google tests for HSA API subtables

* Update lib/rocprofiler-sdk/hsa/{queue,async_copy}.cpp

- use HSA subtable accessors

* Update rocprofiler_memcheck and CI workflow

- use GCC 13 instead of GCC 11 due to suspected false positives in thread sanitizer
  - GCC 13 uses libtsan.so.2

* Update CI workflow

* Update lib/rocprofiler-sdk/counters/{metrics,counters}

- fix possibly dangling reference to a temporary from gcc-13

* Update thread-sanitizer-suppr.txt

- Ignore data races originating in hsa-runtime library

* Update cmake/rocprofiler_memcheck.cmake

- Deduce the sanitizer library to preload by compiling an application and extracting the linked sanitizer library

* Update tests/rocprofv3/tracing/CMakeLists.txt

- add csv files to REQUIRED_FILES and ATTACH_ON_FAIL in validate test

* Update lib/common/container/record_header_buffer.hpp

- fix data race identified by gcc v13 and libtsan.so.2

* Update hip API id, args, and def

- remove hipDrvGraphAddMemsetNode (not part of ROCm 6.0

* Update lib/common/container/record_header_buffer.hpp

- fix deadlock in save/read/reset

* Update source/docs/CMakeLists.txt

- remove COMMAND_ERROR_IS_FATAL ANY to allow for printing of stdout/stderr

* Update lib/rocprofiler-sdk/hip/details/ostream.hpp

- remove overloads for HIP_MEMSET_NODE_PARAMS

* Update docs/CMakeLists.txt

- use find_program for shell instead of hardcoded /bin/bash
This commit is contained in:
Jonathan R. Madsen
2024-01-24 16:32:54 -06:00
committed by GitHub
parent f84c2f4386
commit c641749fe6
74 changed files with 10091 additions and 729 deletions
@@ -127,7 +127,14 @@ struct record_header_buffer
auto is_full() const;
private:
std::atomic<int32_t> m_locked = {0};
/// this is an explicit write lock that does not guard against deadlocking like lock()
void write_lock();
/// this is an explicit write unlock that does not guard against deadlocking like unlock()
void write_unlock();
private:
std::atomic<int64_t> m_locked = {0};
std::atomic<size_t> m_index = {};
std::shared_mutex m_shared = {};
base_buffer_t m_buffer = {};
@@ -144,14 +151,14 @@ inline void
record_header_buffer::lock()
{
auto n = m_locked.fetch_add(1, std::memory_order_release);
if(n == 0) m_shared.lock();
if(n == 0) write_lock();
}
inline void
record_header_buffer::unlock()
{
auto n = m_locked.fetch_add(-1, std::memory_order_release);
if(n <= 1) m_shared.unlock();
auto n = m_locked.fetch_sub(1, std::memory_order_release);
if(n <= 1) write_unlock();
}
inline void
@@ -166,6 +173,18 @@ record_header_buffer::read_unlock()
m_shared.unlock_shared();
}
inline void
record_header_buffer::write_lock()
{
m_shared.lock();
}
inline void
record_header_buffer::write_unlock()
{
m_shared.unlock();
}
inline bool
record_header_buffer::is_allocated() const
{
@@ -212,21 +231,22 @@ template <typename Tp>
bool
record_header_buffer::emplace(uint64_t _hash, Tp& _v)
{
if(is_locked() || m_headers.empty()) return false;
if(m_headers.empty()) return false;
// request N bytes in the buffer (where N=sizeof(Tp)) and if
// available, copy _v into the buffer region
auto _create_record = [](auto& _buf, auto& _data) {
constexpr auto buffer_sz = sizeof(Tp);
void* _ptr = _buf.request(buffer_sz, false);
if(_ptr) new(_ptr) Tp{_data};
return _ptr;
};
constexpr auto request_size = sizeof(Tp);
// in theory, we shouldn't need to lock here but the thread sanitizer says there is a race.
// the lock will be short-lived so hopefully, it will scale fine
write_lock();
auto* _addr = m_buffer.request(request_size, false);
write_unlock();
read_lock();
auto _addr = _create_record(m_buffer, _v);
if(_addr)
{
// placement new
new(_addr) Tp{_v};
// if there is space in the buffer, atomically get an index
// for where the header record should be placed.
// NOTE: m_headers was resized to be large enough to accomodate
@@ -245,21 +265,22 @@ template <typename Tp>
bool
record_header_buffer::emplace(uint32_t _category, uint32_t _kind, Tp& _v)
{
if(is_locked() || m_headers.empty()) return false;
if(m_headers.empty()) return false;
// request N bytes in the buffer (where N=sizeof(Tp)) and if
// available, copy _v into the buffer region
auto _create_record = [](auto& _buf, auto& _data) {
constexpr auto buffer_sz = sizeof(Tp);
void* _ptr = _buf.request(buffer_sz, false);
if(_ptr) new(_ptr) Tp{_data};
return _ptr;
};
constexpr auto request_size = sizeof(Tp);
// in theory, we shouldn't need to lock here but the thread sanitizer says there is a race.
// the lock will be short-lived so hopefully, it will scale fine
write_lock();
auto* _addr = m_buffer.request(request_size, false);
write_unlock();
read_lock();
auto _addr = _create_record(m_buffer, _v);
if(_addr)
{
// placement new
new(_addr) Tp{_v};
// if there is space in the buffer, atomically get an index
// for where the header record should be placed.
// NOTE: m_headers was resized to be large enough to accomodate
@@ -220,7 +220,7 @@ public:
void push_back(Tp&& t);
template <typename... Args>
void emplace_back(Args&&... args);
reference emplace_back(Args&&... args);
reference operator[](size_type i);
@@ -351,10 +351,10 @@ stable_vector<Tp, ChunkSizeV>::push_back(Tp&& t)
template <typename Tp, size_t ChunkSizeV>
template <typename... Args>
void
typename stable_vector<Tp, ChunkSizeV>::reference
stable_vector<Tp, ChunkSizeV>::emplace_back(Args&&... args)
{
last_chunk().emplace_back(std::forward<Args>(args)...);
return last_chunk().emplace_back(std::forward<Args>(args)...);
}
template <typename Tp, size_t ChunkSizeV>
+4 -4
View File
@@ -187,7 +187,7 @@ get_memory_unit(std::string _unit)
using return_type = std::tuple<string_t, int64_t>;
using inner_t = std::tuple<string_t, string_t, int64_t>;
if(_unit.length() == 0) return return_type{"MB", units::megabyte};
if(_unit.empty()) return return_type{"MB", units::megabyte};
for(auto& itr : _unit)
itr = tolower(itr);
@@ -228,7 +228,7 @@ get_timing_unit(std::string _unit)
using return_type = std::tuple<string_t, int64_t>;
using inner_t = std::tuple<string_t, strset_t, int64_t>;
if(_unit.length() == 0) return return_type{"sec", units::sec};
if(_unit.empty()) return return_type{"sec", units::sec};
for(auto& itr : _unit)
itr = tolower(itr);
@@ -264,7 +264,7 @@ get_frequncy_unit(std::string _unit)
using return_type = std::tuple<string_t, int64_t>;
using inner_t = std::tuple<string_t, string_t, int64_t>;
if(_unit.length() == 0) return return_type{"MHz", units::megahertz};
if(_unit.empty()) return return_type{"MHz", units::megahertz};
for(auto& itr : _unit)
itr = tolower(itr);
@@ -295,7 +295,7 @@ get_power_unit(const std::string& _unit)
using return_type = std::tuple<string_t, int64_t>;
using inner_t = std::tuple<string_t, string_t, int64_t>;
if(_unit.length() == 0) return return_type{"watts", units::watt};
if(_unit.empty()) return return_type{"watts", units::watt};
auto _lunit = _unit;
for(auto& itr : _lunit)
+31
View File
@@ -40,6 +40,7 @@
#include <ratio>
#include <stdexcept>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
@@ -210,5 +211,35 @@ private:
data_type m_data = {};
functor_type m_destroy_func = {};
};
template <typename Tp = long, typename RatioT = std::ratio<1, 1000>>
void
yield(std::chrono::duration<Tp, RatioT> duration = std::chrono::milliseconds{10})
{
std::this_thread::yield();
std::this_thread::sleep_for(duration);
}
template <typename PredicateT, typename Tp = long, typename RatioT = std::ratio<1, 1000>>
bool
yield(PredicateT&& predicate,
std::chrono::duration<Tp, RatioT> max_yield_time,
std::chrono::duration<Tp, RatioT> query_interval = std::chrono::milliseconds{10})
{
auto now = []() { return std::chrono::steady_clock::now(); };
auto start = now();
auto result = false;
while(!(result = predicate()))
{
yield(query_interval);
if((now() - start) > max_yield_time)
{
break;
}
}
// return the result of the last predicate query
return result;
}
} // namespace common
} // namespace rocprofiler