43 #include <type_traits>
54 #include <sys/types.h>
57 #if defined(TIMEMORY_USE_LIBEXPLAIN)
58 # include <libexplain/execvp.h>
61 #if defined(TIMEMORY_UNIX)
73 template <
typename CompT,
size_t N,
int... SigIds>
81 template <
typename CompT,
size_t N>
115 template <
int... Ids>
123 template <
int... Ids>
156 template <
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, units::msec } };
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(); };
390 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
392 sampler<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());
404 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
405 template <
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);
421 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
422 template <
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);
438 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
439 sampler<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);
450 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
451 template <
typename Tp, enable_if_t<Tp::value>>
453 sampler<CompT<Types...>, N, SigIds...>::sample()
457 m_last = &(m_data.at((m_idx++) % N));
461 m_last->sample(get_backtrace<4, 3>());
471 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
472 template <
typename Tp, enable_if_t<!Tp::value>>
474 sampler<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>());
493 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
494 template <
typename Tp, enable_if_t<Tp::value>>
499 base_type::set_started();
500 for(
auto& itr : m_data)
508 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
509 template <
typename Tp, enable_if_t<Tp::value>>
514 base_type::set_stopped();
515 for(
auto& itr : m_data)
518 ignore({ SigIds... });
523 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
524 template <
typename Tp, enable_if_t<!Tp::value>>
528 base_type::set_started();
529 for(
auto& itr : m_data)
535 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
536 template <
typename Tp, enable_if_t<!Tp::value>>
538 sampler<CompT<Types...>, N, SigIds...>
::stop()
540 base_type::set_stopped();
541 for(
auto& itr : m_data)
547 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
548 template <
typename Tp, enable_if_t<Tp::value>>
549 typename sampler<CompT<Types...>, N, SigIds...>::components_t&
552 return m_data.at(idx % N);
557 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
558 template <
typename Tp, enable_if_t<Tp::value>>
559 const typename sampler<CompT<Types...>, N, SigIds...>::components_t&
562 return m_data.at(idx % N);
567 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
568 template <
typename Tp, enable_if_t<!Tp::value>>
569 typename sampler<CompT<Types...>, N, SigIds...>::components_t&
572 return m_data.at(idx);
577 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
578 template <
typename Tp, enable_if_t<!Tp::value>>
579 const typename sampler<CompT<Types...>, N, SigIds...>::components_t&
580 sampler<CompT<Types...>, N, SigIds...>
::get(
size_t idx)
const
582 return m_data.at(idx);
587 template <
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);
617 template <
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);
647 template <
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();
668 "[sampler::configure]> No existing sampler has been configured to "
669 "sample at a specific signal or fail at a specific signal. itimer "
670 "for will not be set. Sampler will only wait for target pid to exit\n");
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 " ",
"Error! Alarm cannot be set for signal", itr,
701 "because the signal does not map to a known itimer value\n"));
705 int _sret = sigaction(itr, &_custom_sa, &_original_sa);
708 get_persistent_data().m_signals.insert(itr);
713 " ",
"Error! sigaction could not be set for signal", itr));
717 check_itimer(setitimer(_itimer, &_custom_it, &_original_it),
true);
722 get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
727 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
729 sampler<CompT<Types...>, N, SigIds...>::ignore(
const std::set<int>& _signals)
731 for(
const auto& itr : _signals)
732 signal(itr, SIG_IGN);
734 auto& _original_it = get_persistent_data().m_original_itimerval;
735 for(
const auto& itr : _signals)
738 auto _itimer = get_itimer(itr);
741 check_itimer(getitimer(_itimer, &_curr));
743 if(_curr.it_interval.tv_usec > 0 || _curr.it_interval.tv_sec > 0)
744 check_itimer(setitimer(_itimer, &_original_it, &_curr));
748 get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
753 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
754 template <
typename Func>
756 sampler<CompT<Types...>, N, SigIds...>::wait(
const pid_t wait_pid,
int _verbose,
757 bool _debug, Func&& _callback)
759 if(_verbose > 2 || _debug)
760 fprintf(stderr,
"[%i]> waiting for pid %i...\n", process::get_id(), wait_pid);
764 auto print_info = [=](pid_t _pid,
int _status,
int _errv,
int _retv) {
765 if(_debug || _verbose > 2)
767 fprintf(stderr,
"[%i]> return code: %i, error value: %i, status: %i\n", _pid,
768 _retv, _errv, _status);
775 auto diagnose_status = [=](pid_t _pid,
int status) {
776 if(_verbose > 2 || _debug)
777 fprintf(stderr,
"[%i]> diagnosing status %i...\n", _pid, status);
779 if(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
781 if(_verbose > 2 || (_debug && _verbose > 0))
783 fprintf(stderr,
"[%i]> program terminated normally with exit code: %i\n",
784 _pid, WEXITSTATUS(status));
790 int ret = WEXITSTATUS(status);
791 if(WIFSTOPPED(status))
793 int sig = WSTOPSIG(status);
795 if(_debug || _verbose > 3)
797 fprintf(stderr,
"[%i]> program stopped with signal %i. Exit code: %i\n",
801 else if(WCOREDUMP(status))
803 if(_debug || _verbose > 3)
806 "[%i]> program terminated and produced a core dump. Exit "
811 else if(WIFSIGNALED(status))
813 ret = WTERMSIG(status);
814 if(_debug || _verbose > 3)
817 "[%i]> program terminated because it received a signal "
818 "(%i) that was not handled. Exit code: %i\n",
819 _pid, WTERMSIG(status), ret);
822 else if(WIFEXITED(status) && WEXITSTATUS(status))
824 if(ret == 127 && (_debug || _verbose > 3))
826 fprintf(stderr,
"[%i]> execv failed\n", _pid);
828 else if(_debug || _verbose > 3)
831 "[%i]> program terminated with a non-zero status. Exit "
838 if(_debug || _verbose > 3)
839 fprintf(stderr,
"[%i]> program terminated abnormally.\n", _pid);
848 auto waitpid_eintr = [&](pid_t _pid,
int& status) {
853 while((pid = waitpid(WAIT_ANY, &status, 0)) == -1)
859 perror(
"Unexpected error in waitpid_eitr");
860 retval = diagnose_status(pid, status);
861 print_info(pid, status, errval, retval);
869 retval = kill(_pid, 0);
873 if(errval == ESRCH || retval == -1)
875 std::this_thread::sleep_for(
876 std::chrono::microseconds(get_frequency(units::usec)));
885 auto _signals = get_persistent_data().m_signals;
890 if(_signals.empty() && wait_pid == process::get_id())
894 std::this_thread::sleep_for(
895 std::chrono::microseconds(get_frequency(units::usec)));
896 }
while(_callback(wait_pid, status, errval));
897 return diagnose_status(wait_pid, status);
906 errval = waitpid_eintr(wait_pid, status);
907 print_info(wait_pid, status, errval, retval);
908 }
while((errval == EINTR &&
909 _signals.count(retval = diagnose_status(wait_pid, status)) != 0) &&
910 (_callback(wait_pid, status, errval)));
912 print_info(wait_pid, status, errval, retval);
914 return diagnose_status(wait_pid, status);
919 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
921 sampler<CompT<Types...>, N, SigIds...>::set_delay(
double fdelay)
923 get_persistent_data().m_freq = fdelay;
924 int delay_sec = double(fdelay * units::usec) / units::usec;
925 int delay_usec = int(fdelay * units::usec) % units::usec;
928 fprintf(stderr,
"sampler delay : %i sec + %i usec\n", delay_sec, delay_usec);
931 get_persistent_data().m_custom_itimerval.it_value.tv_sec = delay_sec;
932 get_persistent_data().m_custom_itimerval.it_value.tv_usec = delay_usec;
937 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
939 sampler<CompT<Types...>, N, SigIds...>::set_frequency(
double ffreq)
941 get_persistent_data().m_freq = ffreq;
942 int freq_sec = double(ffreq * units::usec) / units::usec;
943 int freq_usec = int(ffreq * units::usec) % units::usec;
946 fprintf(stderr,
"sampler frequency : %i sec + %i usec\n", freq_sec,
950 get_persistent_data().m_custom_itimerval.it_interval.tv_sec = freq_sec;
951 get_persistent_data().m_custom_itimerval.it_interval.tv_usec = freq_usec;
956 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
958 sampler<CompT<Types...>, N, SigIds...>::get_delay(int64_t units)
960 float _us = (get_persistent_data().m_custom_itimerval.it_value.tv_sec * units::usec) +
961 get_persistent_data().m_custom_itimerval.it_value.tv_usec;
962 _us *=
static_cast<float>(units) / units::usec;
963 return std::max<int64_t>(_us, 1);
968 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
970 sampler<CompT<Types...>, N, SigIds...>::get_frequency(int64_t units)
973 (get_persistent_data().m_custom_itimerval.it_interval.tv_sec * units::usec) +
974 get_persistent_data().m_custom_itimerval.it_interval.tv_usec;
975 _us *=
static_cast<float>(units) / units::usec;
976 return std::max<int64_t>(_us, 1);
981 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
983 sampler<CompT<Types...>, N, SigIds...>::get_itimer(
int _signal)
988 case SIGALRM: _itimer = ITIMER_REAL;
break;
989 case SIGVTALRM: _itimer = ITIMER_VIRTUAL;
break;
990 case SIGPROF: _itimer = ITIMER_PROF;
break;
997 template <
template <
typename...>
class CompT,
size_t N,
typename... Types,
int... SigIds>
999 sampler<CompT<Types...>, N, SigIds...>::check_itimer(
int _stat,
bool _throw_exception)
1005 "Either the new itimerval or the old itimerval was invalid");
1006 if(_throw_exception)
1012 std::cerr << msg <<
'\n';
1015 else if(_stat == EINVAL)
1017 auto msg =
TIMEMORY_JOIN(
" ",
"Warning! setitimer returned EINVAL.",
1018 "Either the timer was not one of: ['ITIMER_REAL', "
1019 "'ITIMER_VIRTUAL', "
1020 "'ITIMER_PROF'] or the old itimerval was invalid");
1021 if(_throw_exception)
1027 std::cerr << msg <<
'\n';
1030 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
The declaration for the types for settings without definitions.
static int wait(pid_t _pid, Func &&_callback, int _verbose=settings::verbose(), bool _debug=settings::debug())
components_t * get_latest() const
const auto & get_good() const
static void pause()
Pause until a signal is delivered.
CompT< Types... > components_t
static auto & get_samplers()
conditional_t< fixed_size_t< N >::value, std::array< components_t, N >, std::vector< components_t > > array_t
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
const components_t & get(size_t idx) 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...
components_t * get_last() const
components_t *& get_last()
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())
const array_t & get_data() const
static void configure(int _signal=SIGALRM, int _verbose=1)
static int wait(int _verbose=settings::verbose(), bool _debug=settings::debug(), Func &&_callback=pid_callback())
const auto & get_bad() const
components_t & get(size_t idx)
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,...)