33#include "timemory/components/gotcha/backends.hpp"
175template <
size_t Nt,
typename BundleT,
typename DiffT>
177:
public base<gotcha<Nt, BundleT, DiffT>, void>
181 "Error! {auto,component}_{list,tuple,bundle} in a GOTCHA specification "
182 "cannot include another gotcha_component");
197 template <
typename Tp>
222 typename std::conditional<differ_is_component, DiffT, void>::type;
227 return "Generates GOTCHA wrappers which can be used to wrap or replace "
228 "dynamically linked function calls";
236 return get_persistent_data().m_initializer;
243 return get_persistent_data().m_permit_list;
250 return get_persistent_data().m_reject_list;
257 static bool _instance =
false;
265 get_suppresses().insert(func);
272 std::array<std::pair<bool, bool>, Nt> _ready;
273 for(
size_t i = 0; i < Nt; ++i)
274 _ready.at(i) = { get_data().at(i).filled, get_data().at(i).ready };
282 for(
size_t i = 0; i < Nt; ++i)
284 if(get_data().
at(i).filled)
285 get_data().at(i).ready = val;
292 static auto set_ready(
const std::array<bool, Nt>& values)
294 for(
size_t i = 0; i < Nt; ++i)
296 if(get_data().
at(i).filled)
297 get_data().at(i).ready = values.at(i);
304 template <
size_t N,
typename Ret,
typename... Args>
313 init_storage<bundle_type>(0);
315 static_assert(N < Nt,
"Error! N must be less than Nt!");
316 auto& _data = get_data()[N];
318 if(!is_permitted<N, Ret, Args...>(_func))
321 if(_data.debug ==
nullptr)
329 storage_type::instance()->add_hash_id(_func);
330 storage_type::instance()->add_hash_id(_label);
332 if(!_tool.empty() && _label.find(_tool +
"/") != 0)
334 _label = _tool +
"/" + _label;
335 while(_label.find(
"//") != std::string::npos)
336 _label.erase(_label.find(
"//"), 1);
340 storage_type::instance()->add_hash_id(_label);
343 _data.priority = _priority;
344 _data.tool_id = _label;
345 _data.wrap_id = _func;
348 if(get_suppresses().find(_func) != get_suppresses().
end())
354 _data.constructor = [_func, _priority, _tool]() {
357 _data.destructor = []() { this_type::revert<N>(); };
358 _data.binding = std::move(construct_binder<N, Ret, Args...>(_data.wrap_id));
359 error_t ret_wrap = backend::gotcha::wrap(_data.binding, _data.tool_id);
360 check_error<N>(ret_wrap,
"binding");
365 _data.is_active =
true;
367 backend::gotcha::set_priority(_data.tool_id, _data.priority);
368 check_error<N>(ret_prio,
"set priority");
379 template <
size_t N,
typename Ret,
typename... Args>
383 return construct<N, Ret, Args...>(_func, _priority, _tool);
388 template <
size_t N,
typename Ret,
typename... Args>
389 static auto configure(
const std::vector<std::string>& _funcs,
int _priority = 0,
392 auto itr = _funcs.begin();
394 while(!ret && itr != _funcs.end())
396 ret =
construct<N, Ret, Args...>(*itr, _priority, _tool);
408 static_assert(N < Nt,
"Error! N must be less than Nt!");
409 auto& _data = get_data()[N];
411 if(_data.filled && _data.is_active)
413 _data.is_active =
false;
415 error_t ret_prio = backend::gotcha::set_priority(_data.tool_id, -1);
416 check_error<N>(ret_prio,
"get priority");
418 if(get_suppresses().find(_data.tool_id) != get_suppresses().
end())
433 static bool&
is_configured() {
return get_persistent_data().m_is_configured; }
437 static std::mutex&
get_mutex() {
return get_persistent_data().m_mutex; }
443 std::array<size_t, 5>
_info{};
445 for(
auto& itr : get_data())
447 _info.at(0) += (itr.ready) ? 1 : 0;
448 _info.at(1) += (itr.filled) ? 1 : 0;
449 _info.at(2) += (itr.is_active) ? 1 : 0;
450 _info.at(3) += (itr.is_finalized) ? 1 : 0;
451 _info.at(4) += (itr.suppression && !(*itr.suppression)) ? 1 : 0;
460 std::unique_lock<std::mutex> lk(
get_mutex(), std::defer_lock);
481 std::unique_lock<std::mutex> lk(
get_mutex(), std::defer_lock);
489 for(
auto& itr : get_data())
491 if(!itr.is_finalized)
493 itr.is_finalized =
true;
504 while(get_started() > 0)
506 while(get_thread_started() > 0)
507 --get_thread_started();
513 auto& _data = get_data();
514 for(
size_t i = 0; i < Nt; ++i)
523 if(storage_type::is_finalizing())
526 auto _n = get_started()++;
527 auto _t = get_thread_started()++;
532 static std::atomic<int64_t> _tcount(0);
533 static thread_local int64_t _tid = _tcount++;
534 std::stringstream ss;
535 ss <<
"[T" << _tid <<
"]> n = " << _n <<
", t = " << _t <<
"...\n";
536 std::cout << ss.str() << std::flush;
548 for(
auto& itr : get_data())
550 if(!itr.is_finalized)
557 auto& _data = get_data();
558 for(
size_t i = 0; i < Nt; ++i)
559 _data[i].ready = _data[i].filled;
565 auto _n = --get_started();
566 auto _t = --get_thread_started();
571 static std::atomic<int64_t> _tcount(0);
572 static thread_local int64_t _tid = _tcount++;
573 std::stringstream ss;
574 ss <<
"[T" << _tid <<
"]> n = " << _n <<
", t = " << _t <<
"...\n";
575 std::cout << ss.str() << std::flush;
581 auto& _data = get_data();
582 for(
size_t i = 0; i < Nt; ++i)
583 _data[i].ready =
false;
588 for(
auto& itr : get_data())
590 if(!itr.is_finalized)
600 template <
size_t N,
typename Ret,
typename... Args>
612#if !defined(TIMEMORY_NVCC_COMPILER)
613 template <
size_t N,
typename Ret,
typename... Args>
614 struct instrument<N, Ret,
std::tuple<Args...>> : instrument<N, Ret, Args...>
620 template <
size_t N,
typename Ret,
typename... Args>
632 struct persistent_data
636 for(
auto& itr : m_data)
640 ~persistent_data() =
default;
643 bool m_is_configured =
false;
644 std::atomic<int64_t> m_started{ 0 };
645 array_t<gotcha_data> m_data;
647 std::set<std::string> m_suppress = {
"malloc",
"calloc",
"free" };
649 for(
const auto& itr : get_data())
658 static persistent_data& get_persistent_data()
660 static persistent_data _instance;
667 static array_t<gotcha_data>& get_data() {
return get_persistent_data().m_data; }
672 static std::atomic<int64_t>& get_started() {
return get_persistent_data().m_started; }
677 static int64_t& get_thread_started()
679 static thread_local int64_t _instance = 0;
686 static std::set<std::string>& get_suppresses()
688 return get_persistent_data().m_suppress;
695 template <
size_t N,
typename Ret,
typename... Args>
702 if(std::is_same<operator_type, void>::value &&
703 (_func.find(
"MPI_") != std::string::npos ||
704 _func.find(
"mpi_") != std::string::npos))
706 static auto mpi_reject_list = {
708 "MPI_Initialized",
"MPI_Comm_rank",
709 "MPI_Comm_size",
"MPI_T_init_thread",
710 "MPI_Comm_split",
"MPI_Abort",
711 "MPI_Comm_split_type"
715 for(
auto& itr : _fort)
717 if(_fort[_fort.length() - 1] !=
'_')
723 for(
const auto& itr : mpi_reject_list)
725 if(_func == itr || _func == tofortran(itr))
729 printf(
"[gotcha]> Skipping gotcha binding for %s...\n",
741 if(_reject_list.count(_func) > 0)
745 printf(
"[gotcha]> GOTCHA binding for function '%s' is in reject "
754 if(!_permit_list.empty())
756 if(_permit_list.count(_func) == 0)
760 printf(
"[gotcha]> GOTCHA binding for function '%s' is not in permit "
778 auto& _data = get_data()[N];
779 std::stringstream msg;
780 msg <<
_prefix <<
" at index '" << N <<
"' for function '" << _data.wrap_id
781 <<
"' returned error code " <<
static_cast<int>(
_ret) <<
": "
782 << backend::gotcha::get_error(
_ret) <<
"\n";
783 std::cerr << msg.str();
787#if defined(TIMEMORY_USE_GOTCHA)
788 auto& _data = get_data()[N];
789 std::stringstream msg;
790 msg <<
"[gotcha::" << __FUNCTION__ <<
"]> " <<
_prefix <<
" :: "
791 <<
"wrapped: " << _data.wrap_id <<
", label: " << _data.tool_id;
800 std::cout << msg.str() << std::endl;
807 template <
size_t N,
typename Ret,
typename... Args,
typename This =
this_type,
808 typename std::enable_if<(This::components_size != 0),
int>::type = 0,
809 typename std::enable_if<!std::is_same<Ret, void>::value,
int>::type = 0>
812 auto& _data = get_data()[N];
813 _data.wrapper = (
void*) this_type::wrap<N, Ret, Args...>;
814 return binding_t{ _func.c_str(), _data.wrapper, &_data.wrappee };
819 template <
size_t N,
typename Ret,
typename... Args,
typename This =
this_type,
820 typename std::enable_if<(This::components_size != 0),
int>::type = 0,
821 typename std::enable_if<std::is_same<Ret, void>::value,
int>::type = 0>
824 auto& _data = get_data()[N];
825 _data.wrapper = (
void*) this_type::wrap_void<N, Args...>;
826 return binding_t{ _func.c_str(), _data.wrapper, &_data.wrappee };
831 template <
size_t N,
typename Ret,
typename... Args,
typename This =
this_type,
832 typename std::enable_if<This::components_size == 0, int>::type = 0,
833 typename std::enable_if<!std::is_same<Ret, void>::value,
int>::type = 0>
836 auto& _data = get_data()[N];
837 _data.wrapper = (
void*) this_type::replace_func<N, Ret, Args...>;
838 return binding_t{ _func.c_str(), _data.wrapper, &_data.wrappee };
843 template <
size_t N,
typename Ret,
typename... Args,
typename This =
this_type,
844 typename std::enable_if<This::components_size == 0, int>::type = 0,
845 typename std::enable_if<std::is_same<Ret, void>::value,
int>::type = 0>
848 auto& _data = get_data()[N];
849 _data.wrapper = (
void*) this_type::replace_void_func<N, Args...>;
850 return binding_t{ _func.c_str(), _data.wrapper, &_data.wrappee };
855 template <
typename Comp,
typename Ret,
typename... Args,
typename This =
this_type,
856 enable_if_t<This::differ_is_component, int> = 0,
857 enable_if_t<!std::is_same<Ret, void>::value,
int> = 0>
858 static Ret invoke(Comp& _comp, Ret (*_func)(Args...), Args&&...
_args)
861 using Invoker = gotcha_invoker<Type, Ret>;
862 Type& _obj = *_comp.template get<Type>();
868 template <
typename Comp,
typename Ret,
typename... Args,
typename This =
this_type,
869 enable_if_t<!This::differ_is_component, int> = 0,
870 enable_if_t<!std::is_same<Ret, void>::value,
int> = 0>
871 static Ret invoke(Comp&, Ret (*_func)(Args...), Args&&...
_args)
873 return _func(std::forward<Args>(
_args)...);
878 template <
typename Comp,
typename Ret,
typename... Args,
typename This =
this_type,
879 enable_if_t<This::differ_is_component, int> = 0,
880 enable_if_t<std::is_same<Ret, void>::value,
int> = 0>
881 static void invoke(Comp& _comp, Ret (*_func)(Args...), Args&&...
_args)
884 using Invoker = gotcha_invoker<Type, Ret>;
885 Type& _obj = *_comp.template get<Type>();
891 template <
typename Comp,
typename Ret,
typename... Args,
typename This =
this_type,
892 enable_if_t<!This::differ_is_component, int> = 0,
893 enable_if_t<std::is_same<Ret, void>::value,
int> = 0>
894 static void invoke(Comp&, Ret (*_func)(Args...), Args&&...
_args)
896 _func(std::forward<Args>(
_args)...);
901 static inline void toggle_suppress_on(
bool* _bsuppress,
bool& _did)
903 if(_bsuppress && *_bsuppress ==
false)
905 *(_bsuppress) =
true;
910 static inline void toggle_suppress_off(
bool* _bsuppress,
bool& _did)
912 if(_bsuppress && _did ==
true && *_bsuppress ==
true)
914 *(_bsuppress) =
false;
921 template <
size_t N,
typename Ret,
typename... Args>
922 static TIMEMORY_NOINLINE Ret wrap(Args...
_args)
924 static_assert(N < Nt,
"Error! N must be less than Nt!");
925#if defined(TIMEMORY_USE_GOTCHA)
926 auto& _data = get_data()[N];
928 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
929 static_assert(void_operator,
"operator_type should be void!");
931 static bool _protect_tls_alloc =
false;
933 using func_t = Ret (*)(Args...);
934 func_t _orig = (func_t)(gotcha_get_wrappee(_data.wrappee));
938 PRINT_HERE(
"nullptr to original function! wrappee: %s",
939 _data.tool_id.c_str());
943 if(_data.is_finalized || _protect_tls_alloc)
944 return (*_orig)(
_args...);
946 _protect_tls_alloc =
true;
948 gotcha_suppression::get() || (_data.suppression && *_data.suppression);
949 _protect_tls_alloc =
false;
951 if(!_data.ready || _suppress)
953 _protect_tls_alloc =
true;
954 static thread_local bool _recursive =
false;
955 _protect_tls_alloc =
false;
956 if(!_recursive && _data.debug && *_data.debug)
959 auto _tid = threading::get_id();
961 "[T%i][%s]> %s is either not ready (ready=%s) or is globally "
962 "suppressed (suppressed=%s)\n",
963 (
int) _tid, __FUNCTION__, _data.tool_id.c_str(),
964 (_data.ready) ?
"true" :
"false", (_suppress) ?
"true" :
"false");
968 return (*_orig)(
_args...);
971 bool did_data_toggle =
false;
972 bool did_glob_toggle =
false;
977 toggle_suppress_on(_data.suppression, did_data_toggle);
980 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
983 _obj.construct(
_args...);
985 _obj.audit(_data, audit::incoming{},
_args...);
986 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
989 Ret
_ret = invoke<bundle_type>(_obj, _orig, std::forward<Args>(
_args)...);
992 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
993 _obj.audit(_data, audit::outgoing{},
_ret);
995 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
998 toggle_suppress_off(_data.suppression, did_data_toggle);
1011 template <
size_t N,
typename... Args>
1012 static TIMEMORY_NOINLINE
void wrap_void(Args...
_args)
1014 static_assert(N < Nt,
"Error! N must be less than Nt!");
1015#if defined(TIMEMORY_USE_GOTCHA)
1016 auto& _data = get_data()[N];
1018 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
1019 static_assert(void_operator,
"operator_type should be void!");
1021 static bool _protect_tls_alloc =
false;
1023 using func_t = void (*)(Args...);
1024 auto _orig = (func_t)(gotcha_get_wrappee(_data.wrappee));
1028 PRINT_HERE(
"nullptr to original function! wrappee: %s",
1029 _data.tool_id.c_str());
1033 if(_data.is_finalized || _protect_tls_alloc)
1039 _protect_tls_alloc =
true;
1041 gotcha_suppression::get() || (_data.suppression && *_data.suppression);
1042 _protect_tls_alloc =
false;
1044 if(!_data.ready || _suppress)
1046 _protect_tls_alloc =
true;
1047 static thread_local bool _recursive =
false;
1048 _protect_tls_alloc =
false;
1049 if(!_recursive && _data.debug && *_data.debug)
1052 auto _tid = threading::get_id();
1054 "[T%i][%s]> %s is either not ready (ready=%s) or is globally "
1055 "suppressed (suppressed=%s)\n",
1056 (
int) _tid, __FUNCTION__, _data.tool_id.c_str(),
1057 (_data.ready) ?
"true" :
"false", (_suppress) ?
"true" :
"false");
1065 bool did_data_toggle =
false;
1066 bool did_glob_toggle =
false;
1070 _data.ready =
false;
1071 toggle_suppress_on(_data.suppression, did_data_toggle);
1072 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
1076 _obj.construct(
_args...);
1078 _obj.audit(_data, audit::incoming{},
_args...);
1079 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
1082 invoke<bundle_type>(_obj, _orig, std::forward<Args>(
_args)...);
1083 _data.ready =
false;
1085 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
1086 _obj.audit(_data, audit::outgoing{});
1090 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
1091 toggle_suppress_off(_data.suppression, did_data_toggle);
1101 template <
size_t N,
typename Ret,
typename... Args>
1102 static TIMEMORY_NOINLINE Ret replace_func(Args...
_args)
1104 static_assert(N < Nt,
"Error! N must be less than Nt!");
1105 static_assert(
components_size == 0,
"Error! Number of components must be zero!");
1107#if defined(TIMEMORY_USE_GOTCHA)
1108 static auto& _data = get_data()[N];
1112 typedef Ret (*func_t)(Args...);
1115 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
1116 static_assert(!void_operator,
"operator_type cannot be void!");
1118 auto _orig = (func_t) gotcha_get_wrappee(_data.wrappee);
1120 return (*_orig)(
_args...);
1122 _data.ready =
false;
1123 static wrap_type _obj{ _data.tool_id };
1124 Ret
_ret = invoke(_obj, _orig, std::forward<Args>(
_args)...);
1136 template <
size_t N,
typename... Args>
1137 static TIMEMORY_NOINLINE
void replace_void_func(Args...
_args)
1139 static_assert(N < Nt,
"Error! N must be less than Nt!");
1140#if defined(TIMEMORY_USE_GOTCHA)
1141 static auto& _data = get_data()[N];
1145 typedef void (*func_t)(Args...);
1148 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
1149 static_assert(!void_operator,
"operator_type cannot be void!");
1151 auto _orig = (func_t) gotcha_get_wrappee(_data.wrappee);
1156 _data.ready =
false;
1157 static wrap_type _obj{ _data.tool_id };
1158 invoke(_obj, _orig, std::forward<Args>(
_args)...);
1168 template <
typename Tp>
1169 static auto init_storage(
int) ->
decltype(Tp::init_storage())
1171 return Tp::init_storage();
1174 template <
typename Tp>
1175 static auto init_storage(
long)
This is a variadic component wrapper where all components are allocated on the stack and cannot be di...
typename tuple_type< T >::type tuple_type_t
typename component_type< T >::type component_type_t
const hash_alias_ptr_t hash_value_t std::string *& _ret
Ret invoke(string_view_t &&label, Func &&func, Args &&... args)
std::array< char *, 4 > _args
char const std::string & _prefix
std::string demangle(const char *_mangled_name, int *_status=nullptr)
tim::mpl::apply< std::string > string
typename impl::is_one_of< Tp, Types > is_one_of
check if type is in expansion
auto get(const auto_bundle< Tag, Types... > &_obj)
void consume_parameters(ArgsT &&...)
storage< Tp, Value > storage_type
gotcha< Nt, BundleT, DiffT > Type
A very lightweight storage class which provides nothing.
static void generate(const std::string &_func, const std::string &_tool="", int _priority=0)
The gotcha component rewrites the global offset table such that calling the wrapped function actually...
typename std::conditional< differ_is_component, DiffT, void >::type operator_type
gotcha< Nt, BundleT, DiffT > this_type
backend::gotcha::binding_t binding_t
std::array< Tp, Nt > array_t
static constexpr bool differentiator_is_component
static get_initializer_t & get_initializer()
static auto configure(const std::string &_func, int _priority=0, const std::string &_tool="")
static auto set_ready(bool val)
set filled wrappers to array of ready values
static get_select_list_t & get_reject_list()
reject listed functions are never wrapped by GOTCHA
std::set< std::string > select_list_t
static bool construct(const std::string &_func, int _priority=0, const std::string &_tool="")
backend::gotcha::error_t error_t
static constexpr bool differ_is_component
static void gotcha_factory(const std::string &_func, const std::string &_tool="", int _priority=0)
static void add_global_suppression(const std::string &func)
add function names at runtime to suppress wrappers
static constexpr size_t components_size
static std::mutex & get_mutex()
std::atomic< bool > atomic_bool_t
backend::gotcha::string_t wrappid_t
concepts::tuple_type_t< BundleT > tuple_type
static auto get_ready()
get an array of whether the wrappers are filled and ready
static auto set_ready(const std::array< bool, Nt > &values)
set filled wrappers to array of ready values
static bool & get_default_ready()
concepts::component_type_t< BundleT > bundle_type
static void global_finalize()
std::function< config_t()> get_initializer_t
std::function< select_list_t()> get_select_list_t
static get_select_list_t & get_permit_list()
when a permit list is provided, only these functions are wrapped by GOTCHA
static std::string label()
static value_type record()
std::function< void()> destructor_t
backend::gotcha::wrappee_t wrappee_t
static std::string description()
std::function< void()> constructor_t
static void thread_init()
static auto configure(const std::vector< std::string > &_funcs, int _priority=0, const std::string &_tool="")
static bool & is_configured()
determines if a variadic wrapper contains a gotcha component
concept that specifies that a type is a component. Components are used to perform some measurement,...
This operation attempts to call a member function which the component provides to internally store wh...
This operation attempts to call a member function which the component provides to internally store wh...
#define TIMEMORY_DELETE_COPY_MOVE_OBJECT(NAME)