timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
components.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25/**
26 * \file timemory/components/gotcha/components.hpp
27 * \brief Implementation of the gotcha component(s)
28 */
29
30#pragma once
31
33#include "timemory/components/gotcha/backends.hpp"
35#include "timemory/macros.hpp"
41#include "timemory/units.hpp"
43
44//======================================================================================//
45//
46namespace tim
47{
48namespace component
49{
50//
51//======================================================================================//
52//
53// template params:
54// Nt == max number of GOTCHA wrappers
55// BundleT == {auto,component}_{tuple,list,bundle}
56// DiffT == extra param to differentiate when Nt and BundleT are same
57//
58// TODO: filter any gotcha components out of BundleT
59//
60/// \struct tim::component::gotcha
61/// \tparam Nt Max number of functions which will wrapped by this component
62/// \tparam BundleT Component bundle to wrap around the function(s)
63/// \tparam DiffT Differentiator type to distinguish different sets of wrappers with
64/// identical values of `Nt` and `BundleT` (or provide function call operator if replacing
65/// functions instead of wrapping functions)
66///
67/// \brief The gotcha component rewrites the global offset table such that calling the
68/// wrapped function actually invokes either a function which is wrapped by timemory
69/// instrumentation or is replaced by a timemory component with an function call operator
70/// (`operator()`) whose return value and arguments exactly match the original function.
71/// This component is only available on Linux and can only by applied to external,
72/// dynamically-linked functions (i.e. functions defined in a shared library).
73/// If the `BundleT` template parameter is a non-empty component bundle, this component
74/// will surround the original function call with:
75///
76/// \code{.cpp}
77/// bundle_type _obj{ "<NAME-OF-ORIGINAL-FUNCTION>" };
78/// _obj.construct(_args...);
79/// _obj.start();
80/// _obj.audit("<NAME-OF-ORIGINAL-FUNCTION>", _args...);
81///
82/// Ret _ret = <CALL-ORIGINAL-FUNCTION>
83///
84/// _obj.audit("<NAME-OF-ORIGINAL-FUNCTION>", _ret);
85/// _obj.stop();
86/// \endcode
87///
88/// If the `BundleT` template parameter is an empty variadic class, e.g. `std::tuple<>`,
89/// `tim::component_tuple<>`, etc., and the `DiffT` template parameter is a timemory
90/// component, the assumption is that the `DiffT` component has a function call operator
91/// which should replace the original function call, e.g. `void* malloc(size_t)` can be
92/// replaced with a component with `void* operator()(size_t)`, e.g.:
93///
94/// \code{.cpp}
95/// // replace 'double exp(double)'
96/// struct exp_replace : base<exp_replace, void>
97/// {
98/// double operator()(double value)
99/// {
100/// float result = expf(static_cast<float>(value));
101/// return static_cast<double>(result);
102/// }
103/// };
104/// \endcode
105///
106/// Example usage:
107///
108/// \code{.cpp}
109/// #include <timemory/timemory.hpp>
110///
111/// #include <cassert>
112/// #include <cmath>
113/// #include <tuple>
114///
115/// using empty_tuple_t = std::tuple<>;
116/// using base_bundle_t = tim::component_tuple<wall_clock, cpu_clock>;
117/// using gotcha_wrap_t = tim::component::gotcha<2, base_bundle_t, void>;
118/// using gotcha_repl_t = tim::component::gotcha<2, empty_tuple_t, exp_replace>;
119/// using impl_bundle_t = tim::mpl::append_type_t<base_bundle_t,
120/// tim::type_list<gotcha_wrap_t,
121/// gotcha_repl_t>>;
122///
123/// void init_wrappers()
124/// {
125/// // wraps the sin and cos math functions
126/// gotcha_wrap_t::get_initializer() = []()
127/// {
128/// TIMEMORY_C_GOTCHA(gotcha_wrap_t, 0, sin); // index 0 replaces sin
129/// TIMEMORY_C_GOTCHA(gotcha_wrap_t, 1, cos); // index 1 replace cos
130/// };
131///
132/// // replaces the 'exp' function which may be 'exp' in symbols table
133/// // or '__exp_finite' in symbols table (use `nm <bindary>` to determine)
134/// gotcha_repl_t::get_initializer() = []()
135/// {
136/// TIMEMORY_C_GOTCHA(gotcha_repl_t, 0, exp);
137/// TIMEMORY_DERIVED_GOTCHA(gotcha_repl_t, 1, exp, "__exp_finite");
138/// };
139/// }
140///
141/// // the following is useful to avoid having to call 'init_wrappers()' explicitly:
142/// // use comma operator to call 'init_wrappers' and return true
143/// static auto called_init_at_load = (init_wrappers(), true);
144///
145/// int main()
146/// {
147/// assert(called_init_at_load == true);
148///
149/// double angle = 45.0 * (M_PI / 180.0);
150///
151/// impl_bundle_t _obj{ "main" };
152///
153/// // gotcha wrappers not activated yet
154/// printf("cos(%f) = %f\n", angle, cos(angle));
155/// printf("sin(%f) = %f\n", angle, sin(angle));
156/// printf("exp(%f) = %f\n", angle, exp(angle));
157///
158/// // gotcha wrappers are reference counted according to start/stop
159/// _obj.start();
160///
161/// printf("cos(%f) = %f\n", angle, cos(angle));
162/// printf("sin(%f) = %f\n", angle, sin(angle));
163/// printf("exp(%f) = %f\n", angle, exp(angle));
164///
165/// _obj.stop();
166///
167/// // gotcha wrappers will be deactivated
168/// printf("cos(%f) = %f\n", angle, cos(angle));
169/// printf("sin(%f) = %f\n", angle, sin(angle));
170/// printf("exp(%f) = %f\n", angle, exp(angle));
171///
172/// return 0;
173/// }
174/// \endcode
175template <size_t Nt, typename BundleT, typename DiffT>
176struct gotcha
177: public base<gotcha<Nt, BundleT, DiffT>, void>
179{
181 "Error! {auto,component}_{list,tuple,bundle} in a GOTCHA specification "
182 "cannot include another gotcha_component");
183
184 using value_type = void;
190
191 friend struct operation::record<this_type>;
192 friend struct operation::start<this_type>;
193 friend struct operation::stop<this_type>;
194 friend struct operation::set_started<this_type>;
195 friend struct operation::set_stopped<this_type>;
196
197 template <typename Tp>
198 using array_t = std::array<Tp, Nt>;
199
200 using binding_t = backend::gotcha::binding_t;
201 using wrappee_t = backend::gotcha::wrappee_t;
203 using error_t = backend::gotcha::error_t;
204 using constructor_t = std::function<void()>;
205 using destructor_t = std::function<void()>;
206 using atomic_bool_t = std::atomic<bool>;
207
208 using select_list_t = std::set<std::string>;
209
210 using config_t = void;
211 using get_initializer_t = std::function<config_t()>;
212 using get_select_list_t = std::function<select_list_t()>;
213
215 static constexpr bool differ_is_component =
218 // backwards-compat
220
222 typename std::conditional<differ_is_component, DiffT, void>::type;
223
224 static std::string label() { return "gotcha"; }
226 {
227 return "Generates GOTCHA wrappers which can be used to wrap or replace "
228 "dynamically linked function calls";
229 }
230 static value_type record() {}
231
232 //----------------------------------------------------------------------------------//
233
235 {
236 return get_persistent_data().m_initializer;
237 }
238
239 //----------------------------------------------------------------------------------//
240 /// when a permit list is provided, only these functions are wrapped by GOTCHA
242 {
243 return get_persistent_data().m_permit_list;
244 }
245
246 //----------------------------------------------------------------------------------//
247 /// reject listed functions are never wrapped by GOTCHA
249 {
250 return get_persistent_data().m_reject_list;
251 }
252
253 //----------------------------------------------------------------------------------//
254
255 static bool& get_default_ready()
256 {
257 static bool _instance = false;
258 return _instance;
259 }
260
261 //----------------------------------------------------------------------------------//
262 /// add function names at runtime to suppress wrappers
263 static void add_global_suppression(const std::string& func)
264 {
265 get_suppresses().insert(func);
266 }
267
268 //----------------------------------------------------------------------------------//
269 /// get an array of whether the wrappers are filled and ready
270 static auto get_ready()
271 {
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 };
275 return _ready;
276 }
277
278 //----------------------------------------------------------------------------------//
279 /// set filled wrappers to array of ready values
280 static auto set_ready(bool val)
281 {
282 for(size_t i = 0; i < Nt; ++i)
283 {
284 if(get_data().at(i).filled)
285 get_data().at(i).ready = val;
286 }
287 return get_ready();
288 }
289
290 //----------------------------------------------------------------------------------//
291 /// set filled wrappers to array of ready values
292 static auto set_ready(const std::array<bool, Nt>& values)
293 {
294 for(size_t i = 0; i < Nt; ++i)
295 {
296 if(get_data().at(i).filled)
297 get_data().at(i).ready = values.at(i);
298 }
299 return get_ready();
300 }
301
302 //----------------------------------------------------------------------------------//
303
304 template <size_t N, typename Ret, typename... Args>
305 static bool construct(const std::string& _func, int _priority = 0,
306 const std::string& _tool = "")
307 {
308 if(_func.empty())
309 return false;
310
312
313 init_storage<bundle_type>(0);
314
315 static_assert(N < Nt, "Error! N must be less than Nt!");
316 auto& _data = get_data()[N];
317
318 if(!is_permitted<N, Ret, Args...>(_func))
319 return false;
320
321 if(_data.debug == nullptr)
322 _data.debug = &settings::debug();
323
324 if(!_data.filled)
325 {
326 auto _label = demangle(_func);
327
328 // ensure the hash to string pairing is stored
329 storage_type::instance()->add_hash_id(_func);
330 storage_type::instance()->add_hash_id(_label);
331
332 if(!_tool.empty() && _label.find(_tool + "/") != 0)
333 {
334 _label = _tool + "/" + _label;
335 while(_label.find("//") != std::string::npos)
336 _label.erase(_label.find("//"), 1);
337 }
338
339 // ensure the hash to string pairing is stored
340 storage_type::instance()->add_hash_id(_label);
341
342 _data.filled = true;
343 _data.priority = _priority;
344 _data.tool_id = _label;
345 _data.wrap_id = _func;
346 _data.ready = get_default_ready();
347
348 if(get_suppresses().find(_func) != get_suppresses().end())
349 {
350 _data.suppression = &gotcha_suppression::get();
351 _data.ready = false;
352 }
353
354 _data.constructor = [_func, _priority, _tool]() {
355 this_type::construct<N, Ret, Args...>(_func, _priority, _tool);
356 };
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");
361 }
362
363 if(!_data.is_active)
364 {
365 _data.is_active = true;
366 error_t ret_prio =
367 backend::gotcha::set_priority(_data.tool_id, _data.priority);
368 check_error<N>(ret_prio, "set priority");
369 }
370
371 if(!_data.ready)
372 revert<N>();
373
374 return _data.filled;
375 }
376
377 //----------------------------------------------------------------------------------//
378
379 template <size_t N, typename Ret, typename... Args>
380 static auto configure(const std::string& _func, int _priority = 0,
381 const std::string& _tool = "")
382 {
383 return construct<N, Ret, Args...>(_func, _priority, _tool);
384 }
385
386 //----------------------------------------------------------------------------------//
387
388 template <size_t N, typename Ret, typename... Args>
389 static auto configure(const std::vector<std::string>& _funcs, int _priority = 0,
390 const std::string& _tool = "")
391 {
392 auto itr = _funcs.begin();
393 auto ret = false;
394 while(!ret && itr != _funcs.end())
395 {
396 ret = construct<N, Ret, Args...>(*itr, _priority, _tool);
397 ++itr;
398 }
399 }
400
401 //----------------------------------------------------------------------------------//
402
403 template <size_t N>
404 static bool revert()
405 {
407
408 static_assert(N < Nt, "Error! N must be less than Nt!");
409 auto& _data = get_data()[N];
410
411 if(_data.filled && _data.is_active)
412 {
413 _data.is_active = false;
414
415 error_t ret_prio = backend::gotcha::set_priority(_data.tool_id, -1);
416 check_error<N>(ret_prio, "get priority");
417
418 if(get_suppresses().find(_data.tool_id) != get_suppresses().end())
419 {
420 _data.ready = false;
421 }
422 else
423 {
424 _data.ready = get_default_ready();
425 }
426 }
427
428 return _data.filled;
429 }
430
431 //----------------------------------------------------------------------------------//
432
433 static bool& is_configured() { return get_persistent_data().m_is_configured; }
434
435 //----------------------------------------------------------------------------------//
436
437 static std::mutex& get_mutex() { return get_persistent_data().m_mutex; }
438
439 //----------------------------------------------------------------------------------//
440
441 static auto get_info()
442 {
443 std::array<size_t, 5> _info{};
444 _info.fill(0);
445 for(auto& itr : get_data())
446 {
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;
452 }
453 return _info;
454 }
455
456 //----------------------------------------------------------------------------------//
457
458 static void configure()
459 {
460 std::unique_lock<std::mutex> lk(get_mutex(), std::defer_lock);
461 if(!lk.owns_lock())
462 lk.lock();
463
464 if(!is_configured())
465 {
466 is_configured() = true;
467 lk.unlock();
468 auto& _init = get_initializer();
469 _init();
470 }
471 }
472
473 //----------------------------------------------------------------------------------//
474
475 static void enable() { configure(); }
476
477 //----------------------------------------------------------------------------------//
478
479 static void disable()
480 {
481 std::unique_lock<std::mutex> lk(get_mutex(), std::defer_lock);
482 if(!lk.owns_lock())
483 lk.lock();
484
485 if(is_configured())
486 {
487 is_configured() = false;
488 lk.unlock();
489 for(auto& itr : get_data())
490 {
491 if(!itr.is_finalized)
492 {
493 itr.is_finalized = true;
494 itr.destructor();
495 }
496 }
497 }
498 }
499
500 //----------------------------------------------------------------------------------//
501
502 static void global_finalize()
503 {
504 while(get_started() > 0)
505 --get_started();
506 while(get_thread_started() > 0)
507 --get_thread_started();
508 disable();
509 }
510
511 static void thread_init()
512 {
513 auto& _data = get_data();
514 for(size_t i = 0; i < Nt; ++i)
515 _data[i].ready = (_data[i].filled && get_default_ready());
516 }
517
518public:
519 //----------------------------------------------------------------------------------//
520
521 void start()
522 {
523 if(storage_type::is_finalizing())
524 return;
525
526 auto _n = get_started()++;
527 auto _t = get_thread_started()++;
528
529#if defined(DEBUG)
530 if(settings::debug())
531 {
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;
537 }
538#endif
539
540 // this ensures that if started from multiple threads, all threads synchronize
541 // before
542 if(_t == 0 && !is_configured())
543 configure();
544
545 if(_n == 0)
546 {
547 configure();
548 for(auto& itr : get_data())
549 {
550 if(!itr.is_finalized)
551 itr.constructor();
552 }
553 }
554
555 if(_t == 0)
556 {
557 auto& _data = get_data();
558 for(size_t i = 0; i < Nt; ++i)
559 _data[i].ready = _data[i].filled;
560 }
561 }
562
563 void stop()
564 {
565 auto _n = --get_started();
566 auto _t = --get_thread_started();
567
568#if defined(DEBUG)
569 if(settings::debug())
570 {
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;
576 }
577#endif
578
579 if(_t == 0)
580 {
581 auto& _data = get_data();
582 for(size_t i = 0; i < Nt; ++i)
583 _data[i].ready = false;
584 }
585
586 if(_n == 0)
587 {
588 for(auto& itr : get_data())
589 {
590 if(!itr.is_finalized)
591 itr.destructor();
592 }
593 }
594 }
595
596public:
597 //----------------------------------------------------------------------------------//
598 // secondary method
599 //
600 template <size_t N, typename Ret, typename... Args>
602 {
603 static void generate(const std::string& _func, const std::string& _tool = "",
604 int _priority = 0)
605 {
606 this_type::configure<N, Ret, Args...>(_func, _priority, _tool);
607 }
608 };
609
610 //----------------------------------------------------------------------------------//
611
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...>
615 {};
616#endif
617
618 //----------------------------------------------------------------------------------//
619
620 template <size_t N, typename Ret, typename... Args>
621 static void gotcha_factory(const std::string& _func, const std::string& _tool = "",
622 int _priority = 0)
623 {
624 instrument<N, Ret, Args...>::generate(_func, _tool, _priority);
625 }
626
627 //----------------------------------------------------------------------------------//
628
629private:
630 //----------------------------------------------------------------------------------//
631 //
632 struct persistent_data
633 {
634 persistent_data()
635 {
636 for(auto& itr : m_data)
637 itr.ready = get_default_ready();
638 }
639
640 ~persistent_data() = default;
641 TIMEMORY_DELETE_COPY_MOVE_OBJECT(persistent_data)
642
643 bool m_is_configured = false;
644 std::atomic<int64_t> m_started{ 0 };
645 array_t<gotcha_data> m_data;
646 std::mutex m_mutex;
647 std::set<std::string> m_suppress = { "malloc", "calloc", "free" };
648 get_initializer_t m_initializer = []() {
649 for(const auto& itr : get_data())
650 itr.constructor();
651 };
652 get_select_list_t m_reject_list = []() { return select_list_t{}; };
653 get_select_list_t m_permit_list = []() { return select_list_t{}; };
654 };
655
656 //----------------------------------------------------------------------------------//
657 //
658 static persistent_data& get_persistent_data()
659 {
660 static persistent_data _instance;
661 return _instance;
662 }
663
664 //----------------------------------------------------------------------------------//
665 /// \fn array_t<gotcha_data>& get_data()
666 /// \brief Gotcha wrapper data
667 static array_t<gotcha_data>& get_data() { return get_persistent_data().m_data; }
668
669 //----------------------------------------------------------------------------------//
670 /// \fn std::atomic<int64_t>& get_started()
671 /// \brief Global counter of active gotchas started
672 static std::atomic<int64_t>& get_started() { return get_persistent_data().m_started; }
673
674 //----------------------------------------------------------------------------------//
675 /// \fn int64_t& get_thread_started()
676 /// \brief Thread-local counter of activate gotchas
677 static int64_t& get_thread_started()
678 {
679 static thread_local int64_t _instance = 0;
680 return _instance;
681 }
682
683 //----------------------------------------------------------------------------------//
684 /// \fn std::set<std::string>& get_suppresses()
685 /// \brief global suppression when being used
686 static std::set<std::string>& get_suppresses()
687 {
688 return get_persistent_data().m_suppress;
689 }
690
691 //----------------------------------------------------------------------------------//
692 /// \brief bool is_permitted()
693 /// Check the permit list and reject list for whether the component is permitted
694 /// to be wrapped.
695 template <size_t N, typename Ret, typename... Args>
696 static bool is_permitted(const std::string& _func)
697 {
698 // if instruments are being used, we need to restrict using GOTCHAs around
699 // certain MPI functions which can cause deadlocks. However, allow
700 // these GOTCHA components which serve as function replacements to
701 // wrap these functions
702 if(std::is_same<operator_type, void>::value &&
703 (_func.find("MPI_") != std::string::npos ||
704 _func.find("mpi_") != std::string::npos))
705 {
706 static auto mpi_reject_list = { // "MPI_Init", "MPI_Finalize",
707 "MPI_Pcontrol", // "MPI_Init_thread",
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"
712 };
713
714 auto tofortran = [](std::string _fort) {
715 for(auto& itr : _fort)
716 itr = tolower(itr);
717 if(_fort[_fort.length() - 1] != '_')
718 _fort += "_";
719 return _fort;
720 };
721
722 // if function matches a reject_listed entry, do not construct wrapper
723 for(const auto& itr : mpi_reject_list)
724 {
725 if(_func == itr || _func == tofortran(itr))
726 {
727 if(settings::debug())
728 {
729 printf("[gotcha]> Skipping gotcha binding for %s...\n",
730 _func.c_str());
731 }
732 return false;
733 }
734 }
735 }
736
737 const select_list_t& _permit_list = get_permit_list()();
738 const select_list_t& _reject_list = get_reject_list()();
739
740 // if function matches a reject_listed entry, do not construct wrapper
741 if(_reject_list.count(_func) > 0)
742 {
743 if(settings::debug())
744 {
745 printf("[gotcha]> GOTCHA binding for function '%s' is in reject "
746 "list...\n",
747 _func.c_str());
748 }
749 return false;
750 }
751
752 // if a permit_list was provided, then do not construct wrapper if not in permit
753 // list
754 if(!_permit_list.empty())
755 {
756 if(_permit_list.count(_func) == 0)
757 {
758 if(settings::debug())
759 {
760 printf("[gotcha]> GOTCHA binding for function '%s' is not in permit "
761 "list...\n",
762 _func.c_str());
763 }
764 return false;
765 }
766 }
767
768 return true;
769 }
770
771 //----------------------------------------------------------------------------------//
772
773 template <size_t N>
774 static void check_error(error_t _ret, const std::string& _prefix)
775 {
776 if(_ret != GOTCHA_SUCCESS && (settings::verbose() > -1 || settings::debug()))
777 {
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();
784 }
785 else if(settings::verbose() > 1 || settings::debug())
786 {
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;
792 /*
793 if((void*) _data.binding != nullptr)
794 {
795 msg << ", wrapped pointer: " << _data.binding.wrapper_pointer
796 << ", function_handle: " << _data.binding.function_handle
797 << ", name: " << _data.binding.name;
798 }
799 */
800 std::cout << msg.str() << std::endl;
801#endif
802 }
803 }
804
805 //----------------------------------------------------------------------------------//
806
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>
810 static binding_t construct_binder(const std::string& _func)
811 {
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 };
815 }
816
817 //----------------------------------------------------------------------------------//
818
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>
822 static binding_t construct_binder(const std::string& _func)
823 {
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 };
827 }
828
829 //----------------------------------------------------------------------------------//
830
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>
834 static binding_t construct_binder(const std::string& _func)
835 {
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 };
839 }
840
841 //----------------------------------------------------------------------------------//
842
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>
846 static binding_t construct_binder(const std::string& _func)
847 {
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 };
851 }
852
853 //----------------------------------------------------------------------------------//
854
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)
859 {
860 using Type = DiffT;
861 using Invoker = gotcha_invoker<Type, Ret>;
862 Type& _obj = *_comp.template get<Type>();
863 return Invoker::invoke(_obj, _func, std::forward<Args>(_args)...);
864 }
865
866 //----------------------------------------------------------------------------------//
867
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)
872 {
873 return _func(std::forward<Args>(_args)...);
874 }
875
876 //----------------------------------------------------------------------------------//
877
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)
882 {
883 using Type = DiffT;
884 using Invoker = gotcha_invoker<Type, Ret>;
885 Type& _obj = *_comp.template get<Type>();
886 Invoker::invoke(_obj, _func, std::forward<Args>(_args)...);
887 }
888
889 //----------------------------------------------------------------------------------//
890
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)
895 {
896 _func(std::forward<Args>(_args)...);
897 }
898
899 //----------------------------------------------------------------------------------//
900
901 static inline void toggle_suppress_on(bool* _bsuppress, bool& _did)
902 {
903 if(_bsuppress && *_bsuppress == false)
904 {
905 *(_bsuppress) = true;
906 _did = true;
907 }
908 }
909
910 static inline void toggle_suppress_off(bool* _bsuppress, bool& _did)
911 {
912 if(_bsuppress && _did == true && *_bsuppress == true)
913 {
914 *(_bsuppress) = false;
915 _did = false;
916 }
917 }
918
919 //----------------------------------------------------------------------------------//
920
921 template <size_t N, typename Ret, typename... Args>
922 static TIMEMORY_NOINLINE Ret wrap(Args... _args)
923 {
924 static_assert(N < Nt, "Error! N must be less than Nt!");
925#if defined(TIMEMORY_USE_GOTCHA)
926 auto& _data = get_data()[N];
927
928 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
929 static_assert(void_operator, "operator_type should be void!");
930 // protects against TLS calling malloc when malloc is wrapped
931 static bool _protect_tls_alloc = false;
932
933 using func_t = Ret (*)(Args...);
934 func_t _orig = (func_t)(gotcha_get_wrappee(_data.wrappee));
935
936 if(!_orig)
937 {
938 PRINT_HERE("nullptr to original function! wrappee: %s",
939 _data.tool_id.c_str());
940 return Ret{};
941 }
942
943 if(_data.is_finalized || _protect_tls_alloc)
944 return (*_orig)(_args...);
945
946 _protect_tls_alloc = true;
947 auto _suppress =
948 gotcha_suppression::get() || (_data.suppression && *_data.suppression);
949 _protect_tls_alloc = false;
950
951 if(!_data.ready || _suppress)
952 {
953 _protect_tls_alloc = true;
954 static thread_local bool _recursive = false;
955 _protect_tls_alloc = false;
956 if(!_recursive && _data.debug && *_data.debug)
957 {
958 _recursive = true;
959 auto _tid = threading::get_id();
960 fprintf(stderr,
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");
965 fflush(stderr);
966 _recursive = false;
967 }
968 return (*_orig)(_args...);
969 }
970
971 bool did_data_toggle = false;
972 bool did_glob_toggle = false;
973
974 // make sure the function is not recursively entered
975 // (important for allocation-based wrappers)
976 _data.ready = false;
977 toggle_suppress_on(_data.suppression, did_data_toggle);
978
979 // bundle_type is always: component_{tuple,list,bundle}
980 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
981 //
982 bundle_type _obj{ _data.tool_id };
983 _obj.construct(_args...);
984 _obj.start();
985 _obj.audit(_data, audit::incoming{}, _args...);
986 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
987
988 _data.ready = true;
989 Ret _ret = invoke<bundle_type>(_obj, _orig, std::forward<Args>(_args)...);
990 _data.ready = false;
991
992 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
993 _obj.audit(_data, audit::outgoing{}, _ret);
994 _obj.stop();
995 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
996
997 // allow re-entrance into wrapper
998 toggle_suppress_off(_data.suppression, did_data_toggle);
999 _data.ready = true;
1000
1001 return _ret;
1002#else
1004 PRINT_HERE("%s", "should not be here!");
1005#endif
1006 return Ret{};
1007 }
1008
1009 //----------------------------------------------------------------------------------//
1010
1011 template <size_t N, typename... Args>
1012 static TIMEMORY_NOINLINE void wrap_void(Args... _args)
1013 {
1014 static_assert(N < Nt, "Error! N must be less than Nt!");
1015#if defined(TIMEMORY_USE_GOTCHA)
1016 auto& _data = get_data()[N];
1017
1018 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
1019 static_assert(void_operator, "operator_type should be void!");
1020 // protects against TLS calling malloc when malloc is wrapped
1021 static bool _protect_tls_alloc = false;
1022
1023 using func_t = void (*)(Args...);
1024 auto _orig = (func_t)(gotcha_get_wrappee(_data.wrappee));
1025
1026 if(!_orig)
1027 {
1028 PRINT_HERE("nullptr to original function! wrappee: %s",
1029 _data.tool_id.c_str());
1030 return;
1031 }
1032
1033 if(_data.is_finalized || _protect_tls_alloc)
1034 {
1035 (*_orig)(_args...);
1036 return;
1037 }
1038
1039 _protect_tls_alloc = true;
1040 auto _suppress =
1041 gotcha_suppression::get() || (_data.suppression && *_data.suppression);
1042 _protect_tls_alloc = false;
1043
1044 if(!_data.ready || _suppress)
1045 {
1046 _protect_tls_alloc = true;
1047 static thread_local bool _recursive = false;
1048 _protect_tls_alloc = false;
1049 if(!_recursive && _data.debug && *_data.debug)
1050 {
1051 _recursive = true;
1052 auto _tid = threading::get_id();
1053 fprintf(stderr,
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");
1058 fflush(stderr);
1059 _recursive = false;
1060 }
1061 (*_orig)(_args...);
1062 return;
1063 }
1064
1065 bool did_data_toggle = false;
1066 bool did_glob_toggle = false;
1067
1068 // make sure the function is not recursively entered
1069 // (important for allocation-based wrappers)
1070 _data.ready = false;
1071 toggle_suppress_on(_data.suppression, did_data_toggle);
1072 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
1073
1074 //
1075 bundle_type _obj{ _data.tool_id };
1076 _obj.construct(_args...);
1077 _obj.start();
1078 _obj.audit(_data, audit::incoming{}, _args...);
1079 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
1080
1081 _data.ready = true;
1082 invoke<bundle_type>(_obj, _orig, std::forward<Args>(_args)...);
1083 _data.ready = false;
1084
1085 toggle_suppress_on(&gotcha_suppression::get(), did_glob_toggle);
1086 _obj.audit(_data, audit::outgoing{});
1087 _obj.stop();
1088
1089 // allow re-entrance into wrapper
1090 toggle_suppress_off(&gotcha_suppression::get(), did_glob_toggle);
1091 toggle_suppress_off(_data.suppression, did_data_toggle);
1092 _data.ready = true;
1093#else
1095 PRINT_HERE("%s", "should not be here!");
1096#endif
1097 }
1098
1099 //----------------------------------------------------------------------------------//
1100
1101 template <size_t N, typename Ret, typename... Args>
1102 static TIMEMORY_NOINLINE Ret replace_func(Args... _args)
1103 {
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!");
1106
1107#if defined(TIMEMORY_USE_GOTCHA)
1108 static auto& _data = get_data()[N];
1109
1110 // PRINT_HERE("%s", _data.tool_id.c_str());
1111
1112 typedef Ret (*func_t)(Args...);
1113 using wrap_type = tim::component_tuple<operator_type>;
1114
1115 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
1116 static_assert(!void_operator, "operator_type cannot be void!");
1117
1118 auto _orig = (func_t) gotcha_get_wrappee(_data.wrappee);
1119 if(!_data.ready)
1120 return (*_orig)(_args...);
1121
1122 _data.ready = false;
1123 static wrap_type _obj{ _data.tool_id };
1124 Ret _ret = invoke(_obj, _orig, std::forward<Args>(_args)...);
1125 _data.ready = true;
1126 return _ret;
1127#else
1129 PRINT_HERE("%s", "should not be here!");
1130 return Ret{};
1131#endif
1132 }
1133
1134 //----------------------------------------------------------------------------------//
1135
1136 template <size_t N, typename... Args>
1137 static TIMEMORY_NOINLINE void replace_void_func(Args... _args)
1138 {
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];
1142
1143 // PRINT_HERE("%s", _data.tool_id.c_str());
1144
1145 typedef void (*func_t)(Args...);
1146 using wrap_type = tim::component_tuple<operator_type>;
1147
1148 static constexpr bool void_operator = std::is_same<operator_type, void>::value;
1149 static_assert(!void_operator, "operator_type cannot be void!");
1150
1151 auto _orig = (func_t) gotcha_get_wrappee(_data.wrappee);
1152 if(!_data.ready)
1153 (*_orig)(_args...);
1154 else
1155 {
1156 _data.ready = false;
1157 static wrap_type _obj{ _data.tool_id };
1158 invoke(_obj, _orig, std::forward<Args>(_args)...);
1159 _data.ready = true;
1160 }
1161#else
1163 PRINT_HERE("%s", "should not be here!");
1164#endif
1165 }
1166
1167private:
1168 template <typename Tp>
1169 static auto init_storage(int) -> decltype(Tp::init_storage())
1170 {
1171 return Tp::init_storage();
1172 }
1173
1174 template <typename Tp>
1175 static auto init_storage(long)
1176 {}
1177};
1178//
1179} // namespace component
1180} // namespace tim
This is a variadic component wrapper where all components are allocated on the stack and cannot be di...
std::string string_t
Definition: library.cpp:57
STL namespace.
typename tuple_type< T >::type tuple_type_t
Definition: concepts.hpp:580
typename component_type< T >::type component_type_t
Definition: concepts.hpp:582
return _hash_map end()
const hash_alias_ptr_t hash_value_t std::string *& _ret
Definition: definition.hpp:300
Ret invoke(string_view_t &&label, Func &&func, Args &&... args)
Definition: invoker.hpp:39
Definition: kokkosp.cpp:39
std::array< char *, 4 > _args
char const std::string & _prefix
Definition: config.cpp:55
std::string demangle(const char *_mangled_name, int *_status=nullptr)
Definition: demangle.hpp:47
tim::mpl::apply< std::string > string
Definition: macros.hpp:53
typename impl::is_one_of< Tp, Types > is_one_of
check if type is in expansion
Definition: types.hpp:777
auto get(const auto_bundle< Tag, Types... > &_obj)
_args at(0)
void consume_parameters(ArgsT &&...)
Definition: types.hpp:285
storage< Tp, Value > storage_type
A very lightweight storage class which provides nothing.
Definition: declaration.hpp:51
static void generate(const std::string &_func, const std::string &_tool="", int _priority=0)
Definition: components.hpp:603
The gotcha component rewrites the global offset table such that calling the wrapped function actually...
Definition: components.hpp:179
typename std::conditional< differ_is_component, DiffT, void >::type operator_type
Definition: components.hpp:222
gotcha< Nt, BundleT, DiffT > this_type
Definition: components.hpp:185
backend::gotcha::binding_t binding_t
Definition: components.hpp:200
static auto get_info()
Definition: components.hpp:441
std::array< Tp, Nt > array_t
Definition: components.hpp:198
static constexpr bool differentiator_is_component
Definition: components.hpp:219
static get_initializer_t & get_initializer()
Definition: components.hpp:234
static auto configure(const std::string &_func, int _priority=0, const std::string &_tool="")
Definition: components.hpp:380
static auto set_ready(bool val)
set filled wrappers to array of ready values
Definition: components.hpp:280
static get_select_list_t & get_reject_list()
reject listed functions are never wrapped by GOTCHA
Definition: components.hpp:248
std::set< std::string > select_list_t
Definition: components.hpp:208
static bool construct(const std::string &_func, int _priority=0, const std::string &_tool="")
Definition: components.hpp:305
backend::gotcha::error_t error_t
Definition: components.hpp:203
static constexpr bool differ_is_component
Definition: components.hpp:215
static void gotcha_factory(const std::string &_func, const std::string &_tool="", int _priority=0)
Definition: components.hpp:621
static void add_global_suppression(const std::string &func)
add function names at runtime to suppress wrappers
Definition: components.hpp:263
static bool revert()
Definition: components.hpp:404
static void disable()
Definition: components.hpp:479
static constexpr size_t components_size
Definition: components.hpp:214
static std::mutex & get_mutex()
Definition: components.hpp:437
std::atomic< bool > atomic_bool_t
Definition: components.hpp:206
backend::gotcha::string_t wrappid_t
Definition: components.hpp:202
static void enable()
Definition: components.hpp:475
concepts::tuple_type_t< BundleT > tuple_type
Definition: components.hpp:188
static auto get_ready()
get an array of whether the wrappers are filled and ready
Definition: components.hpp:270
static auto set_ready(const std::array< bool, Nt > &values)
set filled wrappers to array of ready values
Definition: components.hpp:292
static bool & get_default_ready()
Definition: components.hpp:255
concepts::component_type_t< BundleT > bundle_type
Definition: components.hpp:189
static void global_finalize()
Definition: components.hpp:502
std::function< config_t()> get_initializer_t
Definition: components.hpp:211
static void configure()
Definition: components.hpp:458
std::function< select_list_t()> get_select_list_t
Definition: components.hpp:212
static get_select_list_t & get_permit_list()
when a permit list is provided, only these functions are wrapped by GOTCHA
Definition: components.hpp:241
static std::string label()
Definition: components.hpp:224
static value_type record()
Definition: components.hpp:230
std::function< void()> destructor_t
Definition: components.hpp:205
backend::gotcha::wrappee_t wrappee_t
Definition: components.hpp:201
static std::string description()
Definition: components.hpp:225
std::function< void()> constructor_t
Definition: components.hpp:204
static void thread_init()
Definition: components.hpp:511
static auto configure(const std::vector< std::string > &_funcs, int _priority=0, const std::string &_tool="")
Definition: components.hpp:389
static bool & is_configured()
Definition: components.hpp:433
determines if a variadic wrapper contains a gotcha component
Definition: concepts.hpp:405
concept that specifies that a type is a component. Components are used to perform some measurement,...
Definition: concepts.hpp:226
This operation attempts to call a member function which the component provides to internally store wh...
Definition: types.hpp:469
This operation attempts to call a member function which the component provides to internally store wh...
Definition: types.hpp:502
#define PRINT_HERE(...)
Definition: macros.hpp:152
#define TIMEMORY_DELETE_COPY_MOVE_OBJECT(NAME)
Definition: types.hpp:107