2
0

rocprofiler library unit tests (#81)

* Update CI and linting workflows

- delete linting workflow
- compile default CI job with clang-tidy
- split out code coverage matrix entry to separate job
- code coverage job runs code coverage 3x
  - once for total code coverage
  - once for unittests code coverage
  - once for samples code coverage

* Update PTL submodule

- improves handling of when thread pool is destroyed in atexit handler

* Update lib/rocprofiler/buffer

- buffer::instance::get_internal_buffer()
- allocate_buffer invokes internal_threading::initialize() on first entry
- update flush routine
 - if wait is false, does not wait for task group to finish syncing
 - checks for callback pointer

* Update lib/rocprofiler/internal_threading

- modifications to handle destruction of statics before atexit handler is invoked

* Update lib/rocprofiler/registration.cpp

- reorder atexit call in initialize()
- protect finalize from executing more than once

* Add unittests for rocprofiler buffer

* Update CI workflow

- disable fail-fast for sanitizers
- move AddressSanitizer job to top of the list

* Update lib/rocprofiler/tests/buffer/CMakeLists.txt

- do not set memcheck LD_PRELOAD for rocprofiler-lib-buffer-tests

* Update lib/rocprofiler/registration.{hpp,cpp}

- only invoke client finalizers if initialized
- remove invoke_client_initializer
- move invoke_client functions to anonymous namespace (no declaration in header)
- set fini status in finalize

* Update scripts/thread-sanitizer-suppr.txt

- suppress false positive for double mutex lock in external/ptl/source/PTL/TaskGroup.hh

* Restructure lib/rocprofiler/tests

* Update lib/common

- add utility.cpp
- move read_command_line to utility.{hpp,cpp}
  - was formerly in config.cpp

* Update lib/rocprofiler

- checks for init status return configuration locked if status is not greater than -1
  - in other words, this prevents calling these functions directly (which was possible when check was for greater than 0

* Update lib/rocprofiler/context/context.{hpp,cpp}

- provide deactivate_client_contexts and deregister_client_contexts
  - these functions are used when the tool fails to configure

* Update lib/rocprofiler/registration.{hpp,cpp}

- internal "public" get_client_offet()
- client ids are offset by a random value to avoid default values behaving correctly

* Update lib/rocprofiler/tests

- fix rocprofiler_lib.registration_lambda_no_result

* Update lib/rocprofiler/tests

- fix rocprofiler_lib.registration_lambda_with_result

* Update lib/rocprofiler/tests

- remove deep bind from rocprofiler_lib.registration_lambda_with_result

* Update lib/rocprofiler/tests

- use RTLD_NOW when dlopen'ing in rocprofiler_lib.registration_lambda_with_result

* Update rocprofiler registration tests

- split registration tests into separate exe that links to shared library

* Formatting

* Update CI workflow

- always checkout submodules via actions/checkout

* Update lib/rocprofiler/buffer.{hpp,cpp}

- fix issue with buffer flushing not working when only called once

* Update rocprofiler lib registration test

- test for buffered callback

* Update include/rocprofiler/rocprofiler.h

- include internal_threading.h header

* Update rocprofiler lib registration test

- add in internal threading for buffered test
Este cometimento está contido em:
Jonathan R. Madsen
2023-09-26 19:21:31 -05:00
cometido por GitHub
ascendente 59cc2a382c
cometimento a646c1546c
25 ficheiros modificados com 1157 adições e 313 eliminações
+118 -83
Ver ficheiro
@@ -40,8 +40,10 @@
#include <cstdint>
#include <fstream>
#include <iostream>
#include <limits>
#include <memory>
#include <mutex>
#include <random>
#include <stdexcept>
#include <string>
#include <string_view>
@@ -62,6 +64,21 @@ namespace registration
{
namespace
{
// invoke all rocprofiler_configure symbols
bool
invoke_client_configures();
// invoke initialize functions returned from rocprofiler_configure
bool
invoke_client_initializers();
// invoke finalize functions returned from rocprofiler_configure
bool
invoke_client_finalizers();
// explicitly invoke the finalize function of a specific client
void invoke_client_finalizer(rocprofiler_client_id_t);
auto&
get_status()
{
@@ -143,11 +160,19 @@ struct client_library
std::vector<client_library>
find_clients()
{
auto data = std::vector<client_library>{};
auto data = std::vector<client_library>{};
auto priority_offset = get_client_offset();
if(get_forced_configure())
{
data.emplace_back(client_library{"(forced)", nullptr, get_forced_configure()});
LOG(ERROR) << "adding forced configure";
uint32_t _prio = priority_offset + data.size();
data.emplace_back(client_library{"(forced)",
nullptr,
get_forced_configure(),
nullptr,
rocprofiler_client_id_t{nullptr, _prio},
rocprofiler_client_id_t{nullptr, _prio}});
}
if(!rocprofiler_configure && !get_forced_configure())
@@ -159,8 +184,16 @@ find_clients()
if(rocprofiler_configure != &rocprofiler_configure)
throw std::runtime_error("rocprofiler_configure != &rocprofiler_configure");
if(&rocprofiler_configure != get_forced_configure())
data.emplace_back(client_library{"unknown", nullptr, &rocprofiler_configure});
if(rocprofiler_configure && rocprofiler_configure != get_forced_configure())
{
uint32_t _prio = priority_offset + data.size();
data.emplace_back(client_library{"unknown",
nullptr,
rocprofiler_configure,
nullptr,
rocprofiler_client_id_t{nullptr, _prio},
rocprofiler_client_id_t{nullptr, _prio}});
}
for(const auto& itr : get_link_map())
{
@@ -195,7 +228,7 @@ find_clients()
}
else
{
uint32_t _prio = data.size();
uint32_t _prio = priority_offset + data.size();
auto& entry =
data.emplace_back(client_library{itr,
handle,
@@ -228,31 +261,6 @@ get_registration_mutex()
static auto _v = mutex_t{};
return _v;
}
} // namespace
int
get_init_status()
{
return get_status().first.load(std::memory_order_acquire);
}
int
get_fini_status()
{
return get_status().second.load(std::memory_order_acquire);
}
void
set_init_status(int v)
{
get_status().first.store(v, std::memory_order_release);
}
void
set_fini_status(int v)
{
get_status().second.store(v, std::memory_order_release);
}
bool
invoke_client_configures()
@@ -265,9 +273,16 @@ invoke_client_configures()
LOG(ERROR) << __FUNCTION__;
size_t prio = 0;
for(auto& itr : get_clients())
{
if(!itr.configure_func)
{
LOG(ERROR) << "rocprofiler::registration::invoke_client_configures() attempted to "
"invoke configure function from "
<< itr.name << " that had no configuration function";
continue;
}
if(get_invoked_configures().find(itr.configure_func) != get_invoked_configures().end())
{
LOG(ERROR) << "rocprofiler::registration::invoke_client_configures() attempted to "
@@ -286,10 +301,20 @@ invoke_client_configures()
<< ")";
}
auto* _result = itr.configure_func(
ROCPROFILER_VERSION, ROCPROFILER_VERSION_STRING, prio++, &itr.mutable_client_id);
auto* _result = itr.configure_func(ROCPROFILER_VERSION,
ROCPROFILER_VERSION_STRING,
itr.internal_client_id.handle - get_client_offset(),
&itr.mutable_client_id);
if(_result)
{
itr.configure_result = std::make_unique<rocprofiler_tool_configure_result_t>(*_result);
}
else
{
context::deactivate_client_contexts(itr.internal_client_id);
context::deregister_client_contexts(itr.internal_client_id);
}
get_invoked_configures().emplace(itr.configure_func);
}
@@ -308,7 +333,6 @@ invoke_client_initializers()
LOG(ERROR) << __FUNCTION__;
set_init_status(-1);
for(auto& itr : get_clients())
{
if(itr.configure_result && itr.configure_result->initialize)
@@ -322,22 +346,21 @@ invoke_client_initializers()
}
}
// initialization is no longer available
set_init_status(1);
return true;
}
bool
invoke_client_finalizers()
{
if(get_fini_status() > 0) return false;
// NOTE: this function is expected to only be invoked from the finalize function (which sets the
// fini status)
if(get_init_status() < 1 || get_fini_status() > 0) return false;
auto _lk = scoped_lock_t{get_registration_mutex(), std::defer_lock};
if(_lk.owns_lock()) return false;
_lk.lock();
set_fini_status(-1);
for(auto& itr : get_clients())
{
if(itr.configure_result && itr.configure_result->finalize)
@@ -348,44 +371,6 @@ invoke_client_finalizers()
}
}
set_fini_status(1);
return true;
}
bool
invoke_client_initializer(rocprofiler_client_id_t client_id)
{
if(get_init_status() > 0) return false;
auto _lk = scoped_lock_t{get_registration_mutex(), std::defer_lock};
if(_lk.owns_lock()) return false;
_lk.lock();
// save the original status
auto _restore_status = get_init_status();
set_init_status(-1);
for(auto& itr : get_clients())
{
if(itr.internal_client_id.handle == client_id.handle &&
itr.mutable_client_id.handle == client_id.handle)
{
if(itr.configure_result && itr.configure_result->initialize)
{
context::push_client(itr.internal_client_id.handle);
itr.configure_result->initialize(&invoke_client_finalizer,
itr.configure_result->tool_data);
context::pop_client(itr.internal_client_id.handle);
// set to nullptr so initialize only gets called once
itr.configure_result->initialize = nullptr;
}
}
}
// we don't want the explicit client initialization to set the init status to 1
// we just want to restore what it previously was
set_init_status(_restore_status);
return true;
}
@@ -410,6 +395,44 @@ invoke_client_finalizer(rocprofiler_client_id_t client_id)
}
}
}
} // namespace
uint32_t
get_client_offset()
{
static uint32_t _v = []() {
auto gen = std::mt19937{std::random_device{}()};
auto rng = std::uniform_int_distribution<uint32_t>{
std::numeric_limits<uint8_t>::max(),
std::numeric_limits<uint32_t>::max() - std::numeric_limits<uint8_t>::max()};
return rng(gen);
}();
return _v;
}
int
get_init_status()
{
return get_status().first.load(std::memory_order_acquire);
}
int
get_fini_status()
{
return get_status().second.load(std::memory_order_acquire);
}
void
set_init_status(int v)
{
get_status().first.store(v, std::memory_order_release);
}
void
set_fini_status(int v)
{
get_status().second.store(v, std::memory_order_release);
}
void
initialize()
@@ -418,11 +441,15 @@ initialize()
static auto _ready = std::atomic<bool>{false};
std::call_once(_once, []() {
// initialization is in process
set_init_status(-1);
std::atexit(&finalize);
init_logging();
invoke_client_configures();
invoke_client_initializers();
internal_threading::initialize();
std::atexit(&finalize);
// initialization is no longer available
set_init_status(1);
_ready.store(true, std::memory_order_release);
});
@@ -436,11 +463,19 @@ initialize()
void
finalize()
{
hsa_shut_down();
invoke_client_finalizers();
for(auto& itr : rocprofiler::context::get_active_contexts())
itr.store(nullptr, std::memory_order_seq_cst);
internal_threading::finalize();
static auto _once = std::once_flag{};
std::call_once(_once, []() {
set_fini_status(-1);
hsa_shut_down();
if(get_init_status() > 0)
{
invoke_client_finalizers();
for(auto& itr : rocprofiler::context::get_active_contexts())
itr.store(nullptr, std::memory_order_seq_cst);
}
internal_threading::finalize();
set_fini_status(1);
});
}
} // namespace registration
} // namespace rocprofiler