57#if defined(TIMEMORY_USE_LIBEXPLAIN)
58# include <libexplain/execvp.h>
61#if defined(TIMEMORY_UNIX)
73template <
typename CompT,
size_t N,
int... SigIds>
81template <
typename CompT,
size_t N>
156template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
165 using pid_cb_t = std::function<bool(pid_t,
int,
int)>;
167 std::vector<components_t>>;
172 static void execute(
int signum);
173 static void execute(
int signum, siginfo_t*,
void*);
174 static auto&
get_samplers() {
return get_persistent_data().m_instances; }
175 static auto get_latest_samples();
178 template <
typename Tp = fixed_
size_t<N>, enable_if_t<Tp::value> = 0>
180 signal_set_t _bad = signal_set_t{});
182 template <
typename Tp = fixed_
size_t<N>, enable_if_t<!Tp::value> = 0>
188 template <
typename Tp = fixed_
size_t<N>, enable_if_t<Tp::value> = 0>
191 template <
typename Tp = fixed_
size_t<N>, enable_if_t<!Tp::value> = 0>
205 TIMEMORY_NODISCARD
bool is_good(
int v)
const {
return m_good.count(v) > 0; }
206 TIMEMORY_NODISCARD
bool is_bad(
int v)
const {
return m_bad.count(v) > 0; }
210 auto count()
const {
return good_count() + bad_count(); }
227 template <
typename Tp = fixed_
size_t<N>, enable_if_t<Tp::value> = 0>
229 template <
typename Tp = fixed_
size_t<N>, enable_if_t<!Tp::value> = 0>
232 template <
typename Tp = fixed_
size_t<N>, enable_if_t<Tp::value> = 0>
234 template <
typename Tp = fixed_
size_t<N>, enable_if_t<!Tp::value> = 0>
246 static TIMEMORY_INLINE
void configure(std::set<int> _signals,
int _verbose = 1);
247 static TIMEMORY_INLINE
void configure(
int _signal = SIGALRM,
int _verbose = 1)
256 static TIMEMORY_INLINE
void ignore(
const std::set<int>& _signals);
261 static void clear() { get_persistent_data().m_signals.clear(); }
267 if(!get_persistent_data().m_signals.empty())
284 template <
typename Func = p
id_cb_t>
285 static int wait(pid_t _pid,
int _verbose,
bool _debug,
286 Func&& _callback = pid_callback());
288 template <
typename Func = p
id_cb_t>
290 Func&& _callback = pid_callback())
292 return wait(process::get_target_id(), _verbose, _debug,
293 std::forward<Func>(_callback));
296 template <typename Func, enable_if_t<std::is_function<Func>::value> = 0>
300 return wait(_pid, _verbose, _debug, std::forward<Func>(_callback));
303 template <typename Func, enable_if_t<std::is_function<Func>::value> = 0>
307 return wait(process::get_target_id(), _verbose, _debug,
308 std::forward<Func>(_callback));
315 static void set_flags(
int _flags) { get_persistent_data().m_flags = _flags; }
320 static void set_delay(
double fdelay);
325 static void set_frequency(
double ffreq);
330 static void set_rate(
double frate) { set_frequency(1.0 / frate); }
334 static int64_t get_delay(int64_t units = units::usec);
338 static int64_t get_frequency(int64_t units = units::usec);
342 static int get_itimer(
int _signal);
346 static bool check_itimer(
int _stat,
bool _throw_exception =
false);
349 bool m_backtrace =
false;
357 using sigaction_t =
struct sigaction;
358 using itimerval_t =
struct itimerval;
360 struct persistent_data
362 bool m_active =
false;
363 int m_flags = SA_RESTART | SA_SIGINFO;
364 double m_delay = 0.001;
365 double m_freq = 1.0 / 2.0;
366 sigaction_t m_custom_sigaction;
367 itimerval_t m_custom_itimerval = { { 1, 0 }, { 0, 1000 } };
368 sigaction_t m_original_sigaction;
369 itimerval_t m_original_itimerval;
370 std::set<int> m_signals = {};
371 std::vector<this_type*> m_instances = {};
374 static persistent_data& get_persistent_data()
376 static persistent_data _instance;
382 static pid_cb_t pid_callback()
384 return [](pid_t _id, int, int) {
return _id != process::get_id(); };
390template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
392sampler<CompT<Types...>, N, SigIds...>::get_latest_samples()
394 std::vector<components_t*> _last{};
396 _last.reserve(get_persistent_data().m_instances.size());
397 for(
auto& itr : get_persistent_data().m_instances)
398 _last.emplace_back(itr->get_last());
404template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
405template <
typename Tp, enable_if_t<Tp::value>>
409, m_good(
std::move(_good))
410, m_bad(
std::move(_bad))
414 m_last = &m_data.front();
416 get_samplers().push_back(
this);
421template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
422template <
typename Tp, enable_if_t<!Tp::value>>
424 signal_set_t _good, signal_set_t _bad)
426, m_good(
std::move(_good))
427, m_bad(
std::move(_bad))
430 m_data.emplace_back(components_t(_label));
431 m_last = &m_data.front();
433 get_samplers().push_back(
this);
438template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
439sampler<CompT<Types...>, N, SigIds...>::~sampler()
442 auto& _samplers = get_samplers();
443 auto itr = std::find(_samplers.begin(), _samplers.end(),
this);
444 if(itr != _samplers.end())
445 _samplers.erase(itr);
450template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
451template <
typename Tp, enable_if_t<Tp::value>>
453sampler<CompT<Types...>, N, SigIds...>::sample()
457 m_last = &(m_data.at((m_idx++) % N));
461 m_last->sample(get_backtrace<4, 3>());
471template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
472template <
typename Tp, enable_if_t<!Tp::value>>
474sampler<CompT<Types...>, N, SigIds...>::sample()
478 m_last = &m_data.back();
479 m_data.emplace_back(components_t(m_last->hash()));
483 m_last->sample(get_backtrace<4, 3>());
493template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
494template <
typename Tp, enable_if_t<Tp::value>>
499 base_type::set_started();
500 for(
auto& itr : m_data)
508template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
509template <
typename Tp, enable_if_t<Tp::value>>
514 base_type::set_stopped();
515 for(
auto& itr : m_data)
518 ignore({ SigIds... });
523template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
524template <
typename Tp, enable_if_t<!Tp::value>>
528 base_type::set_started();
529 for(
auto& itr : m_data)
535template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
536template <
typename Tp, enable_if_t<!Tp::value>>
538sampler<CompT<Types...>, N, SigIds...>
::stop()
540 base_type::set_stopped();
541 for(
auto& itr : m_data)
547template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
548template <
typename Tp, enable_if_t<Tp::value>>
549typename sampler<CompT<Types...>, N, SigIds...>::components_t&
552 return m_data.at(idx % N);
557template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
558template <
typename Tp, enable_if_t<Tp::value>>
559const typename sampler<CompT<Types...>, N, SigIds...>::components_t&
562 return m_data.at(idx % N);
567template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
568template <
typename Tp, enable_if_t<!Tp::value>>
569typename sampler<CompT<Types...>, N, SigIds...>::components_t&
572 return m_data.at(idx);
577template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
578template <
typename Tp, enable_if_t<!Tp::value>>
579const typename sampler<CompT<Types...>, N, SigIds...>::components_t&
580sampler<CompT<Types...>, N, SigIds...>
::get(
size_t idx)
const
582 return m_data.at(idx);
587template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
593 printf(
"[pid=%i][tid=%i][%s]> sampling...\n", (
int) process::get_id(),
594 (
int) threading::get_id(), demangle<this_type>().c_str());
597 for(
auto& itr : get_samplers())
599 if(itr->is_good(signum))
603 else if(itr->is_bad(signum))
606 sprintf(msg,
"[timemory]> sampler instance caught bad signal: %i ...",
609 signal(signum, SIG_DFL);
617template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
623 printf(
"[pid=%i][tid=%i][%s]> sampling...\n", (
int) process::get_id(),
624 (
int) threading::get_id(), demangle<this_type>().c_str());
627 for(
auto& itr : get_samplers())
629 if(itr->is_good(signum))
633 else if(itr->is_bad(signum))
636 sprintf(msg,
"[timemory]> sampler instance caught bad signal: %i ...",
639 signal(signum, SIG_DFL);
647template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
652 if(get_persistent_data().m_active)
655 size_t wait_count = 0;
658 for(
auto& itr : get_samplers())
659 wait_count += itr->count();
667 "[sampler::configure]> No existing sampler has been configured to "
668 "sample at a specific signal or fail at a specific signal. itimer "
669 "for will not be set. Sampler will only wait for target pid to "
679 if(!_signals.empty())
681 auto& _custom_sa = get_persistent_data().m_custom_sigaction;
682 auto& _custom_it = get_persistent_data().m_custom_itimerval;
683 auto& _original_sa = get_persistent_data().m_original_sigaction;
684 auto& _original_it = get_persistent_data().m_original_itimerval;
686 memset(&_custom_sa, 0,
sizeof(_custom_sa));
690 _custom_sa.sa_flags = SA_RESTART | SA_SIGINFO;
693 for(
const auto& itr : _signals)
696 auto _itimer = get_itimer(itr);
700 TIMEMORY_JOIN(
" ",
"Error! Alarm cannot be set for signal", itr,
701 "because the signal does not map to a known itimer "
706 int _sret = sigaction(itr, &_custom_sa, &_original_sa);
709 get_persistent_data().m_signals.insert(itr);
714 " ",
"Error! sigaction could not be set for signal", itr));
718 check_itimer(setitimer(_itimer, &_custom_it, &_original_it),
true);
723 get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
728template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
730sampler<CompT<Types...>, N, SigIds...>::ignore(
const std::set<int>& _signals)
732 for(
const auto& itr : _signals)
733 signal(itr, SIG_IGN);
735 auto& _original_it = get_persistent_data().m_original_itimerval;
736 for(
const auto& itr : _signals)
739 auto _itimer = get_itimer(itr);
742 check_itimer(getitimer(_itimer, &_curr));
744 if(_curr.it_interval.tv_usec > 0 || _curr.it_interval.tv_sec > 0)
745 check_itimer(setitimer(_itimer, &_original_it, &_curr));
749 get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
754template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
755template <
typename Func>
757sampler<CompT<Types...>, N, SigIds...>::wait(
const pid_t wait_pid,
int _verbose,
758 bool _debug, Func&& _callback)
760 if(_verbose > 2 || _debug)
761 fprintf(stderr,
"[%i]> waiting for pid %i...\n", process::get_id(), wait_pid);
765 auto print_info = [=](pid_t _pid,
int _status,
int _errv,
int _retv) {
766 if(_debug || _verbose > 2)
768 fprintf(stderr,
"[%i]> return code: %i, error value: %i, status: %i\n", _pid,
769 _retv, _errv, _status);
776 auto diagnose_status = [=](pid_t _pid,
int status) {
777 if(_verbose > 2 || _debug)
778 fprintf(stderr,
"[%i]> diagnosing status %i...\n", _pid, status);
780 if(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
782 if(_verbose > 2 || (_debug && _verbose > 0))
784 fprintf(stderr,
"[%i]> program terminated normally with exit code: %i\n",
785 _pid, WEXITSTATUS(status));
791 int ret = WEXITSTATUS(status);
792 if(WIFSTOPPED(status))
794 int sig = WSTOPSIG(status);
796 if(_debug || _verbose > 3)
798 fprintf(stderr,
"[%i]> program stopped with signal %i. Exit code: %i\n",
802 else if(WCOREDUMP(status))
804 if(_debug || _verbose > 3)
807 "[%i]> program terminated and produced a core dump. Exit "
812 else if(WIFSIGNALED(status))
814 ret = WTERMSIG(status);
815 if(_debug || _verbose > 3)
818 "[%i]> program terminated because it received a signal "
819 "(%i) that was not handled. Exit code: %i\n",
820 _pid, WTERMSIG(status), ret);
823 else if(WIFEXITED(status) && WEXITSTATUS(status))
825 if(ret == 127 && (_debug || _verbose > 3))
827 fprintf(stderr,
"[%i]> execv failed\n", _pid);
829 else if(_debug || _verbose > 3)
832 "[%i]> program terminated with a non-zero status. Exit "
839 if(_debug || _verbose > 3)
840 fprintf(stderr,
"[%i]> program terminated abnormally.\n", _pid);
849 auto waitpid_eintr = [&](pid_t _pid,
int& status) {
854 while((pid = waitpid(WAIT_ANY, &status, 0)) == -1)
860 perror(
"Unexpected error in waitpid_eitr");
861 retval = diagnose_status(pid, status);
862 print_info(pid, status, errval, retval);
870 retval = kill(_pid, 0);
874 if(errval == ESRCH || retval == -1)
876 std::this_thread::sleep_for(
877 std::chrono::microseconds(get_frequency(units::usec)));
886 auto _signals = get_persistent_data().m_signals;
891 if(_signals.empty() && wait_pid == process::get_id())
895 std::this_thread::sleep_for(
896 std::chrono::microseconds(get_frequency(units::usec)));
897 }
while(_callback(wait_pid, status, errval));
898 return diagnose_status(wait_pid, status);
907 errval = waitpid_eintr(wait_pid, status);
908 print_info(wait_pid, status, errval, retval);
909 }
while((errval == EINTR &&
910 _signals.count(retval = diagnose_status(wait_pid, status)) != 0) &&
911 (_callback(wait_pid, status, errval)));
913 print_info(wait_pid, status, errval, retval);
915 return diagnose_status(wait_pid, status);
920template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
922sampler<CompT<Types...>, N, SigIds...>::set_delay(
double fdelay)
924 get_persistent_data().m_freq = fdelay;
925 int delay_sec = fdelay;
926 int delay_usec =
static_cast<int>(fdelay * 1000000) % 1000000;
929 fprintf(stderr,
"sampler delay : %i sec + %i usec\n", delay_sec,
933 get_persistent_data().m_custom_itimerval.it_value.tv_sec = delay_sec;
934 get_persistent_data().m_custom_itimerval.it_value.tv_usec = delay_usec;
939template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
941sampler<CompT<Types...>, N, SigIds...>::set_frequency(
double ffreq)
943 get_persistent_data().m_freq = ffreq;
944 int freq_sec = ffreq;
945 int freq_usec =
static_cast<int>(ffreq * 1000000) % 1000000;
948 fprintf(stderr,
"sampler frequency : %i sec + %i usec\n", freq_sec,
952 get_persistent_data().m_custom_itimerval.it_interval.tv_sec = freq_sec;
953 get_persistent_data().m_custom_itimerval.it_interval.tv_usec = freq_usec;
958template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
960sampler<CompT<Types...>, N, SigIds...>::get_delay(int64_t units)
962 double _us = (get_persistent_data().m_custom_itimerval.it_value.tv_sec * 1000000) +
963 get_persistent_data().m_custom_itimerval.it_value.tv_usec;
966 return std::max<int64_t>(_us, 1);
971template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
973sampler<CompT<Types...>, N, SigIds...>::get_frequency(int64_t units)
975 double _us = (get_persistent_data().m_custom_itimerval.it_interval.tv_sec * 1000000) +
976 get_persistent_data().m_custom_itimerval.it_interval.tv_usec;
979 return std::max<int64_t>(_us, 1);
984template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
986sampler<CompT<Types...>, N, SigIds...>::get_itimer(
int _signal)
991 case SIGALRM: _itimer = ITIMER_REAL;
break;
992 case SIGVTALRM: _itimer = ITIMER_VIRTUAL;
break;
993 case SIGPROF: _itimer = ITIMER_PROF;
break;
1000template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
1002sampler<CompT<Types...>, N, SigIds...>::check_itimer(
int _stat,
bool _throw_exception)
1008 "Either the new itimerval or the old itimerval was invalid");
1009 if(_throw_exception)
1015 std::cerr << msg <<
'\n';
1018 else if(_stat == EINVAL)
1020 auto msg =
TIMEMORY_JOIN(
" ",
"Warning! setitimer returned EINVAL.",
1021 "Either the timer was not one of: ['ITIMER_REAL', "
1022 "'ITIMER_VIRTUAL', "
1023 "'ITIMER_PROF'] or the old itimerval was invalid");
1024 if(_throw_exception)
1030 std::cerr << msg <<
'\n';
1033 return (_stat != EFAULT && _stat != EINVAL);
std::shared_ptr< DataType > execute(std::shared_ptr< DataType > _data=std::make_shared< DataType >())
void stop(TupleT< Tp... > &obj, Args &&... args)
void start(TupleT< Tp... > &obj, Args &&... args)
Inherit from this policy to add reference counting support. Useful if you want to turn a global setti...
typename fixed_size< N >::type fixed_size_t
typename fixed_sig< Ids... >::type fixed_sig_t
The design of the sampler struct is similar to the tim::component::gotcha component: the first templa...
std::unique_lock< mutex_t > auto_lock_t
Unique lock type around mutex_t.
void configure(std::initializer_list< EnumT > components, Args &&... args)
typename std::enable_if< B, T >::type enable_if_t
Alias template for enable_if.
tim::mpl::apply< std::string > string
auto get(const auto_bundle< Tag, Types... > &_obj)
typename std::conditional< B, Lhs, Rhs >::type conditional_t
components_t * get_last() const
static int wait(pid_t _pid, Func &&_callback, int _verbose=settings::verbose(), bool _debug=settings::debug())
static void pause()
Pause until a signal is delivered.
CompT< Types... > components_t
components_t * get_latest() const
components_t & get(size_t idx)
const components_t & get(size_t idx) const
const auto & get_bad() const
conditional_t< fixed_size_t< N >::value, std::array< components_t, N >, std::vector< components_t > > array_t
const array_t & get_data() const
sampler(const std::string &_label, signal_set_t _good, signal_set_t _bad=signal_set_t{})
void enable_backtrace(bool val)
bool is_good(int v) const
std::function< bool(pid_t, int, int)> pid_cb_t
components_t *& get_latest()
auto backtrace_enabled() const
std::set< int > signal_set_t
static void set_rate(double frate)
Value, expressed in number of interupts per second, that configures the frequency that the sampler sa...
const auto & get_good() const
static void set_flags(int _flags)
Set the sigaction flags, e.g. SA_RESTART | SA_SIGINFO.
static int wait(Func &&_callback, int _verbose=settings::verbose(), bool _debug=settings::debug())
components_t *& get_last()
static void configure(int _signal=SIGALRM, int _verbose=1)
static auto & get_samplers()
static int wait(int _verbose=settings::verbose(), bool _debug=settings::debug(), Func &&_callback=pid_callback())
static void clear()
Clear all signals. Recommended to call ignore() prior to clearing all the signals.
trait that designates the type is a timemory component
trait that signifies the component supports sampling.
#define TIMEMORY_FOLD_EXPRESSION(...)
#define TIMEMORY_EXCEPTION(...)
#define TIMEMORY_JOIN(delim,...)