Files
rocm-systems/source/lib/rocprofiler/external_correlation.cpp
T
Jonathan R. Madsen 3082288a25 Code object, kernel dispatch, and memory copy tracing (#177)
* Update samples/api_buffered_tracing

- external correlation id
- support ROCPROFILER_BUFFER_TRACING_KERNEL_DISPATCH

* Update lib/rocprofiler/context.cpp

- update alternative get_active_contexts paradigm

* Update lib/rocprofiler/external_correlation.cpp

- inherit correlation id from main thread

* Update lib/rocprofiler/hsa/queue.*

- typedef changes
- rocprofiler_packet union
- modify Queue::queue_info_session_t
  - use rocprofiler_packet
  - add thread id
  - add kernel id
  - add correlation id
- out of line definitions
- AsyncSignalHandler function update
  - handle kernel dispatch tracing
- Move CreateBarrierPacket and AddVendorSpecificPacket to lambdas
- handle contexts

* Update lib/rocprofiler/hsa/hsa.cpp

- remove unnecessary log function
- use new get_active_contexts paradigm
- use new correlation id updates

* Update AgentCache and kernel dispatch record

- include const rocprofiler_agent_t* in rocprofiler_buffer_tracing_kernel_dispatch_record_t
- AgentCache::get_rocp_agent returns const pointer

* Replace ROCPROFILER_SERVICE_ with ROCPROFILER_

* source formatting

* Code Object Tracing

- include/rocprofiler/callback_tracing.h
  - remove rocprofiler_callback_tracing_code_object_unload_data_t
  - remove rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t
- include/rocprofiler/fwd.h
  - remove ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT_UNLOAD
  - remove ROCPROFILER_CALLBACK_TRACING_CODE_OBJECT_DEVICE_KERNEL_SYMBOL_UNREGISTER
- lib/common/utility.hpp
  - assert_public_api_struct_properties()
  - init_public_api_struct(...)
- lib/rocprofiler/registration.cpp
  - invoke hsa::code_object_init
- lib/rocprofiler/hsa/CMakeLists.txt
  - compile code_object code
- lib/rocprofiler/hsa/code_object.{hpp,cpp}
  - tracing code object load/unload
- lib/rocprofiler/hsa/queue.cpp
  - get_kernel_id

* Update lib/rocprofiler/hsa/hsa.cpp

- fix should_wrap_functor logic (which was not handling callback_tracer + buffered_tracer properly)

* Update lib/rocprofiler/hsa/queue.cpp

- fix rocprofiler_buffer_tracing_kernel_dispatch_record_t construction

* Update samples/api_buffered_tracing/client.cpp

- print kernel names

* Move samples/apps to tests/apps

* Update lib/rocprofiler/hsa/code_object.cpp

- ensure unload callbacks when application is exiting
- support user data in between load/unload callbacks

* Update lib/rocprofiler/hsa/queue.{hpp,cpp}

- store contexts and external correlation ids in queue_info_session
- reduce signal_limiter to 96 to fix hangs
- fix support for kernel tracing and async memory copies

* Add lib/common/scope_destructor.hpp

- similar to static_cleanup_wrapper but different

* Update include/rocprofiler/buffer_tracing.h

- update rocprofiler_buffer_tracing_memory_copy_record_t
- remove operation: user can figure that out from correlation id
- add kernel id
- add rocprofiler agent id

* Update include/rocprofiler/callback_tracing.h

- fix data type of load_delta field in code object
- remove rocp_agent from kernel_symbol_register_data_t (known via code_object_id)

* Add samples/code_object_tracing

- sample demonstrating code object tracing

* Update samples

- minor tweak to print_call_stack

* Update lib/rocprofiler/hsa/code_object.cpp

- flip ordering of unload callbacks for code object unloading and kernel symbol deregistering

* clang-tidy fixes

* Update lib/rocprofiler/hsa/code_object.cpp

- fix heap-use-after-free issue with code object

* Update include/rocprofiler/external_correlation.h

- update documentation to include info about default value of external correlation value

* Use common::container::small_vector for contexts

- small_vector<const context*> is an ideal data structure for array of active contexts

* Update context handling for code object unload

- code object unload is only called for contexts which received the load callback

* Update samples

- improve ROCPROFILER_CALL macro to include status string
- api_buffered_tracing handles ROCPROFILER_STATUS_ERROR_BUFFER_BUSY

* Code object shutdown

- ensure code object callbacks are invoked prior to finalizing

* Update lib/common (memory allocators)

- added lib/common/memory folder with allocators

* Add lib/rocprofiler/allocator.*

- rocprofiler::allocator::static_data_allocator
  - special allocator for static data which finalizes before any data gets destroyed
- rocprofiler::allocator::unique_static_ptr_t
  - unique_ptr that uses static data deleter (ensure finalize is called)

* Update lib/rocprofiler/buffer.cpp

- flush checks fini status
- use unique_static_ptr_t

* Update lib/rocprofiler/internal_threading.*

- change meaning of thread_pool_t and task_group_t
- improve finalization to prevent data races and heap-use-after-free

* Update lib/rocprofiler/registration.cpp

- use static_data_allocator for client_library vector

* Update lib/rocprofiler/context/context.*

- use allocator::unique_static_ptr_t

* Update lib/rocprofiler/allocator.cpp

- avoid deadlock in deleter<static_data>::operator()

* Update lib/rocprofiler/registration.cpp

- avoid deadlock in rocprofiler::registration::finalize()

* Update lib/rocprofiler/hsa/code_object.cpp

- suppress duplicate reporting of code-object/kernel-symbol load/unload

* Update leak sanitizer suppressions

- __new_exitfn (via stdlib/cxa_atexit.c leaks
2023-11-13 22:30:15 -06:00

189 خطوط
6.7 KiB
C++

// 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 <rocprofiler/external_correlation.h>
#include <rocprofiler/fwd.h>
#include "lib/common/synchronized.hpp"
#include "lib/common/utility.hpp"
#include "lib/rocprofiler/context/context.hpp"
#include "lib/rocprofiler/external_correlation.hpp"
#include <unistd.h>
namespace rocprofiler
{
namespace external_correlation
{
namespace
{
auto
get_default_tid()
{
static auto _v = common::get_tid();
return _v;
}
constexpr auto empty_user_data = rocprofiler_user_data_t{.value = 0};
auto&
get_default_data_impl()
{
static auto _v = std::atomic<uint64_t>{0};
return _v;
}
auto
get_default_data()
{
return rocprofiler_user_data_t{.value =
get_default_data_impl().load(std::memory_order_relaxed)};
}
auto f_default_tid = get_default_tid(); // make sure it is initialized
} // namespace
rocprofiler_user_data_t
external_correlation::get(rocprofiler_thread_id_t tid) const
{
return data.rlock(
[](const external_correlation_map_t& _data, rocprofiler_thread_id_t tid_v) {
if(_data.count(tid_v) == 0) return get_default_data();
const auto& itr = _data.at(tid_v);
return itr.rlock([](const external_correlation_stack_t& data_stack) {
if(data_stack.empty()) return get_default_data();
return data_stack.back();
});
},
tid);
}
void
external_correlation::push(rocprofiler_thread_id_t tid, rocprofiler_user_data_t user_data)
{
static auto default_tid = get_default_tid();
// ensure that data contains key for provided thread id
while(!data.ulock(
[](const external_correlation_map_t& _data, rocprofiler_thread_id_t tid_v) {
return (_data.find(tid_v) != _data.end());
},
[](external_correlation_map_t& _data, rocprofiler_thread_id_t tid_v) {
_data.emplace(tid_v, external_correlation_stack_t{});
return true;
},
tid))
{}
// since we know from above that there will be a key for the tid, we start with a read
// lock and then once we have have the mapped data for the key, we leverage the enabling
// of the wlock const overload to remove the constness and use a write lock. If we were to use a
// write lock at the top lovel, then we would unnecessarily block other threads from writing to
// the stack of another thread
data.rlock(
[](const external_correlation_map_t& _data,
rocprofiler_thread_id_t tid_v,
rocprofiler_user_data_t user_data_v) {
const auto& itr = _data.at(tid_v);
itr.wlock([](external_correlation_stack_t& data_stack,
rocprofiler_user_data_t value) { data_stack.emplace_back(value); },
user_data_v);
// child threads inherit the current value on default thread
if(tid_v == default_tid)
get_default_data_impl().store(user_data_v.value, std::memory_order_relaxed);
},
tid,
user_data);
}
rocprofiler_user_data_t
external_correlation::pop(rocprofiler_thread_id_t tid)
{
static auto default_tid = get_default_tid();
return data.wlock(
[](external_correlation_map_t& _data, rocprofiler_thread_id_t tid_v) {
if(_data.count(tid_v) == 0) return empty_user_data;
auto& itr = _data.at(tid_v);
return itr.wlock([tid_v](external_correlation_stack_t& data_stack) {
if(data_stack.empty()) return empty_user_data;
auto ret = data_stack.back();
data_stack.pop_back();
// child threads inherit the current value on default thread
if(tid_v == default_tid)
{
uint64_t value = (!data_stack.empty()) ? data_stack.back().value : 0;
get_default_data_impl().store(value, std::memory_order_relaxed);
}
return ret;
});
},
tid);
}
} // namespace external_correlation
} // namespace rocprofiler
extern "C" {
rocprofiler_status_t
rocprofiler_push_external_correlation_id(rocprofiler_context_id_t context,
rocprofiler_thread_id_t tid,
rocprofiler_user_data_t external_correlation_id)
{
// assumption is that thread ids are monotonically increasing from the pid
static uint64_t pid_v = getpid();
if(tid < pid_v) return ROCPROFILER_STATUS_ERROR_INVALID_ARGUMENT;
for(auto& itr : rocprofiler::context::get_registered_contexts())
{
if(itr->context_idx == context.handle)
{
itr->correlation_tracer.external_correlator.push(tid, external_correlation_id);
return ROCPROFILER_STATUS_SUCCESS;
}
}
return ROCPROFILER_STATUS_ERROR_CONTEXT_NOT_FOUND;
}
rocprofiler_status_t
rocprofiler_pop_external_correlation_id(rocprofiler_context_id_t context,
rocprofiler_thread_id_t tid,
rocprofiler_user_data_t* external_correlation_id)
{
// assumption is that thread ids are monotonically increasing from the pid
static uint64_t pid_v = getpid();
if(tid < pid_v) return ROCPROFILER_STATUS_ERROR_INVALID_ARGUMENT;
for(auto& itr : rocprofiler::context::get_registered_contexts())
{
if(itr->context_idx == context.handle)
{
auto former = itr->correlation_tracer.external_correlator.pop(tid);
if(external_correlation_id) *external_correlation_id = former;
return ROCPROFILER_STATUS_SUCCESS;
}
}
return ROCPROFILER_STATUS_ERROR_CONTEXT_NOT_FOUND;
}
}