timemory  3.2.1
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.
sampler.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/or sell
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 #pragma once
26 
28 #include "timemory/mpl/apply.hpp"
30 #include "timemory/units.hpp"
33 
34 // C++ includes
35 #include <algorithm>
36 #include <array>
37 #include <chrono>
38 #include <cstdint>
39 #include <cstdio>
40 #include <cstring>
41 #include <iostream>
42 #include <thread>
43 #include <type_traits>
44 #include <utility>
45 #include <vector>
46 
47 // C includes
48 #include <errno.h>
49 #include <signal.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <sys/time.h>
54 #include <sys/types.h>
55 #include <sys/wait.h>
56 
57 #if defined(TIMEMORY_USE_LIBEXPLAIN)
58 # include <libexplain/execvp.h>
59 #endif
60 
61 #if defined(TIMEMORY_UNIX)
62 # include <unistd.h>
63 extern "C"
64 {
65  extern char** environ;
66 }
67 #endif
68 
69 namespace tim
70 {
71 namespace sampling
72 {
73 template <typename CompT, size_t N, int... SigIds>
74 struct sampler;
75 }
76 //
77 //--------------------------------------------------------------------------------------//
78 //
79 namespace trait
80 {
81 template <typename CompT, size_t N>
82 struct is_component<sampling::sampler<CompT, N>> : true_type
83 {};
84 } // namespace trait
85 //
86 //--------------------------------------------------------------------------------------//
87 //
88 namespace sampling
89 {
90 //
91 //--------------------------------------------------------------------------------------//
92 //
93 /// \value tim::sampling::dynamic
94 /// \brief A bare enumeration value implicitly convertible to zero.
95 enum
96 {
97  dynamic = 0
98 };
99 //
100 //--------------------------------------------------------------------------------------//
101 //
102 template <size_t N>
103 struct fixed_size : std::true_type
104 {};
105 //
106 template <>
107 struct fixed_size<dynamic> : std::false_type
108 {};
109 //
110 template <size_t N>
112 //
113 //--------------------------------------------------------------------------------------//
114 //
115 template <int... Ids>
116 struct fixed_sig : std::true_type
117 {};
118 //
119 template <>
120 struct fixed_sig<> : std::false_type
121 {};
122 //
123 template <int... Ids>
124 using fixed_sig_t = typename fixed_sig<Ids...>::type;
125 //
126 //--------------------------------------------------------------------------------------//
127 //
128 /// \struct tim::sampling::sampler
129 /// \brief The design of the sampler struct is similar to the \ref tim::component::gotcha
130 /// component: the first template parameter is a specification of a bundle of components
131 /// which the struct internally takes measurements with and the second template parameter
132 /// is a size specification. The size specification is to help ensure that the allocation
133 /// does not grow too significantly, however, by specifying the size as either 0 (zero)
134 /// or \ref tim::sampling::dynamic, a std::vector is used instead of the fixed-sized
135 /// std::array.
136 /// \code{.cpp}
137 /// // sampling components
138 /// using sampler_bundle_t = tim::component_tuple<read_char, written_char>;
139 /// using sample_t = tim::sampling::sampler<sampler_bundle_t, 1, SIGALRM>;
140 /// using bundle_t = tim::component_tuple<wall_clock, sample_t>;
141 ///
142 /// // create at least one instance before configuring
143 /// bundle_t sampling_bundle("example");
144 ///
145 /// sample_t::configure({ SIGALRM }); // configure the sampling
146 /// sample_t::pause(); // wait for one signal to be delivered
147 ///
148 /// sampling_bundle.start(); // start sampling and wall-clock
149 /// ...
150 /// sampling_bundle.stop(); // stop sampling and wall-clock
151 ///
152 /// sample_t::pause(); // wait for one signal to be delivered
153 /// sampler_t::ignore({ SIGALRM }); // ignore future interrupts
154 /// sampler_t::wait(process::target_pid()); // wait for pid to finish
155 /// \endcode
156 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
157 struct sampler<CompT<Types...>, N, SigIds...>
158 : component::base<sampler<CompT<Types...>, N, SigIds...>, void>
159 , private policy::instance_tracker<sampler<CompT<Types...>, N, SigIds...>, false>
160 {
161  using this_type = sampler<CompT<Types...>, N, SigIds...>;
163  using components_t = CompT<Types...>;
164  using signal_set_t = std::set<int>;
165  using pid_cb_t = std::function<bool(pid_t, int, int)>;
166  using array_t = conditional_t<fixed_size_t<N>::value, std::array<components_t, N>,
167  std::vector<components_t>>;
168 
171 
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();
176 
177 public:
178  template <typename Tp = fixed_size_t<N>, enable_if_t<Tp::value> = 0>
179  sampler(const std::string& _label, signal_set_t _good,
180  signal_set_t _bad = signal_set_t{});
181 
182  template <typename Tp = fixed_size_t<N>, enable_if_t<!Tp::value> = 0>
183  sampler(const std::string& _label, signal_set_t _good,
184  signal_set_t _bad = signal_set_t{});
185 
186  ~sampler();
187 
188  template <typename Tp = fixed_size_t<N>, enable_if_t<Tp::value> = 0>
189  void sample();
190 
191  template <typename Tp = fixed_size_t<N>, enable_if_t<!Tp::value> = 0>
192  void sample();
193 
194  template <typename Tp = fixed_sig_t<SigIds...>, enable_if_t<Tp::value> = 0>
195  void start();
196  template <typename Tp = fixed_sig_t<SigIds...>, enable_if_t<Tp::value> = 0>
197  void stop();
198 
199  template <typename Tp = fixed_sig_t<SigIds...>, enable_if_t<!Tp::value> = 0>
200  void start();
201  template <typename Tp = fixed_sig_t<SigIds...>, enable_if_t<!Tp::value> = 0>
202  void stop();
203 
204 public:
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; }
207 
208  auto good_count() const { return m_good.size(); }
209  auto bad_count() const { return m_bad.size(); }
210  auto count() const { return good_count() + bad_count(); }
211 
212  auto& get_good() { return m_good; }
213  auto& get_bad() { return m_bad; }
214 
215  const auto& get_good() const { return m_good; }
216  const auto& get_bad() const { return m_bad; }
217 
218  auto backtrace_enabled() const { return m_backtrace; }
219  void enable_backtrace(bool val) { m_backtrace = val; }
220 
221  components_t*& get_last() { return m_last; }
222  components_t* get_last() const { return m_last; }
223 
224  components_t*& get_latest() { return m_last; }
225  components_t* get_latest() const { return m_last; }
226 
227  template <typename Tp = fixed_size_t<N>, enable_if_t<Tp::value> = 0>
228  components_t& get(size_t idx);
229  template <typename Tp = fixed_size_t<N>, enable_if_t<!Tp::value> = 0>
230  components_t& get(size_t idx);
231 
232  template <typename Tp = fixed_size_t<N>, enable_if_t<Tp::value> = 0>
233  const components_t& get(size_t idx) const;
234  template <typename Tp = fixed_size_t<N>, enable_if_t<!Tp::value> = 0>
235  const components_t& get(size_t idx) const;
236 
237  array_t& get_data() { return m_data; }
238  const array_t& get_data() const { return m_data; }
239 
240 public:
241  /// \fn void configure(std::set<int> _signals, int _verb)
242  /// \param[in] _signals A set of signals to catch
243  /// \param[in] _verb Logging Verbosity
244  ///
245  /// \brief Set up the sampler
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)
248  {
249  configure({ _signal }, _verbose);
250  }
251 
252  /// \fn void ignore(const std::set<int>& _signals)
253  /// \param[in] _signals Set of signals
254  ///
255  /// \brief Ignore the signals
256  static TIMEMORY_INLINE void ignore(const std::set<int>& _signals);
257 
258  /// \fn void clear()
259  /// \brief Clear all signals. Recommended to call ignore() prior to clearing all the
260  /// signals
261  static void clear() { get_persistent_data().m_signals.clear(); }
262 
263  /// \fn void pause()
264  /// \brief Pause until a signal is delivered
265  static TIMEMORY_INLINE void pause()
266  {
267  if(!get_persistent_data().m_signals.empty())
268  ::pause();
269  }
270 
271  /// \fn int wait(pid_t _pid, int _verb, bool _debug, Func&& _cb)
272  /// \param[in] _pid Process id to wait on
273  /// \param[in] _verb Logging verbosity
274  /// \param[in] _debug Enable debug logging
275  /// \param[in] _cb Callback for checking whether to exit
276  ///
277  /// \brief Wait function with an optional user callback of type:
278  ///
279  /// \code{.cpp}
280  /// bool (*)(int a, int b)
281  /// \endcode
282  /// where 'a' is the status, 'b' is the error value, and returns true if waiting
283  /// should continue
284  template <typename Func = pid_cb_t>
285  static int wait(pid_t _pid, int _verbose, bool _debug,
286  Func&& _callback = pid_callback());
287 
288  template <typename Func = pid_cb_t>
289  static int wait(int _verbose = settings::verbose(), bool _debug = settings::debug(),
290  Func&& _callback = pid_callback())
291  {
292  return wait(process::get_target_id(), _verbose, _debug,
293  std::forward<Func>(_callback));
294  }
295 
296  template <typename Func, enable_if_t<std::is_function<Func>::value> = 0>
297  static int wait(pid_t _pid, Func&& _callback, int _verbose = settings::verbose(),
298  bool _debug = settings::debug())
299  {
300  return wait(_pid, _verbose, _debug, std::forward<Func>(_callback));
301  }
302 
303  template <typename Func, enable_if_t<std::is_function<Func>::value> = 0>
304  static int wait(Func&& _callback, int _verbose = settings::verbose(),
305  bool _debug = settings::debug())
306  {
307  return wait(process::get_target_id(), _verbose, _debug,
308  std::forward<Func>(_callback));
309  }
310 
311  /// \fn void set_flags(int)
312  /// \param flags[in] the sigaction flags to use
313  ///
314  /// \brief Set the sigaction flags, e.g. SA_RESTART | SA_SIGINFO
315  static void set_flags(int _flags) { get_persistent_data().m_flags = _flags; }
316 
317  /// \fn void set_delay(double)
318  /// \brief Value, expressed in seconds, that sets the length of time the sampler
319  /// waits before starting sampling of the relevant measurements
320  static void set_delay(double fdelay);
321 
322  /// \fn void set_freq(double)
323  /// \brief Value, expressed in 1/seconds, expressed in 1/seconds, that sets the
324  /// frequency that the sampler samples the relevant measurements
325  static void set_frequency(double ffreq);
326 
327  /// \fn void set_rate(double)
328  /// \brief Value, expressed in number of interupts per second, that configures the
329  /// frequency that the sampler samples the relevant measurements
330  static void set_rate(double frate) { set_frequency(1.0 / frate); }
331 
332  /// \fn int64_t get_delay(int64_t)
333  /// \brief Get the delay of the sampler
334  static int64_t get_delay(int64_t units = units::usec);
335 
336  /// \fn int64_t get_frequency(int64_t)
337  /// \brief Get the frequency of the sampler
338  static int64_t get_frequency(int64_t units = units::usec);
339 
340  /// \fn int get_itimer(int)
341  /// \brief Returns the itimer value associated with the given signal
342  static int get_itimer(int _signal);
343 
344  /// \fn bool check_itimer(int, bool)
345  /// \brief Checks to see if there was an error setting or getting itimer val
346  static bool check_itimer(int _stat, bool _throw_exception = false);
347 
348 protected:
349  bool m_backtrace = false;
350  size_t m_idx = 0;
351  components_t* m_last = nullptr;
352  signal_set_t m_good = {};
353  signal_set_t m_bad = {};
354  array_t m_data = {};
355 
356 private:
357  using sigaction_t = struct sigaction;
358  using itimerval_t = struct itimerval;
359 
360  struct persistent_data
361  {
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 = {};
372  };
373 
374  static persistent_data& get_persistent_data()
375  {
376  static persistent_data _instance;
377  return _instance;
378  }
379 
380  /// \fn pid_cb_t& pid_callback()
381  /// \brief Default callback when configuring sampler
382  static pid_cb_t pid_callback()
383  {
384  return [](pid_t _id, int, int) { return _id != process::get_id(); };
385  }
386 };
387 //
388 //--------------------------------------------------------------------------------------//
389 //
390 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
391 inline auto
392 sampler<CompT<Types...>, N, SigIds...>::get_latest_samples()
393 {
394  std::vector<components_t*> _last{};
395  auto_lock_t lk(type_mutex<this_type>());
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());
399  return _last;
400 }
401 //
402 //--------------------------------------------------------------------------------------//
403 //
404 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
405 template <typename Tp, enable_if_t<Tp::value>>
406 sampler<CompT<Types...>, N, SigIds...>::sampler(const std::string& _label,
407  signal_set_t _good, signal_set_t _bad)
408 : m_last(nullptr)
409 , m_good(std::move(_good))
410 , m_bad(std::move(_bad))
411 {
412  TIMEMORY_FOLD_EXPRESSION(m_good.insert(SigIds));
413  m_data.fill(components_t(_label));
414  m_last = &m_data.front();
415  auto_lock_t lk(type_mutex<this_type>());
416  get_samplers().push_back(this);
417 }
418 //
419 //--------------------------------------------------------------------------------------//
420 //
421 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
422 template <typename Tp, enable_if_t<!Tp::value>>
423 sampler<CompT<Types...>, N, SigIds...>::sampler(const std::string& _label,
424  signal_set_t _good, signal_set_t _bad)
425 : m_last(nullptr)
426 , m_good(std::move(_good))
427 , m_bad(std::move(_bad))
428 {
429  TIMEMORY_FOLD_EXPRESSION(m_good.insert(SigIds));
430  m_data.emplace_back(components_t(_label));
431  m_last = &m_data.front();
432  auto_lock_t lk(type_mutex<this_type>());
433  get_samplers().push_back(this);
434 }
435 //
436 //--------------------------------------------------------------------------------------//
437 //
438 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
439 sampler<CompT<Types...>, N, SigIds...>::~sampler()
440 {
441  auto_lock_t lk(type_mutex<this_type>());
442  auto& _samplers = get_samplers();
443  auto itr = std::find(_samplers.begin(), _samplers.end(), this);
444  if(itr != _samplers.end())
445  _samplers.erase(itr);
446 }
447 //
448 //--------------------------------------------------------------------------------------//
449 //
450 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
451 template <typename Tp, enable_if_t<Tp::value>>
452 void
453 sampler<CompT<Types...>, N, SigIds...>::sample()
454 {
455  // if(!base_type::get_is_running())
456  // return;
457  m_last = &(m_data.at((m_idx++) % N));
458  // get last 4 of 7 backtrace entries (i.e. offset by 3)
459  if(m_backtrace)
460  {
461  m_last->sample(get_backtrace<4, 3>());
462  }
463  else
464  {
465  m_last->sample();
466  }
467 }
468 //
469 //--------------------------------------------------------------------------------------//
470 //
471 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
472 template <typename Tp, enable_if_t<!Tp::value>>
473 void
474 sampler<CompT<Types...>, N, SigIds...>::sample()
475 {
476  // if(!base_type::get_is_running())
477  // return;
478  m_last = &m_data.back();
479  m_data.emplace_back(components_t(m_last->hash()));
480  // get last 4 of 7 backtrace entries (i.e. offset by 3)
481  if(m_backtrace)
482  {
483  m_last->sample(get_backtrace<4, 3>());
484  }
485  else
486  {
487  m_last->sample();
488  }
489 }
490 //
491 //--------------------------------------------------------------------------------------//
492 // one or more signals specified in template parameters
493 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
494 template <typename Tp, enable_if_t<Tp::value>>
495 void
496 sampler<CompT<Types...>, N, SigIds...>::start()
497 {
498  auto cnt = tracker_type::start();
499  base_type::set_started();
500  for(auto& itr : m_data)
501  itr.start();
502  if(cnt == 0)
503  configure({ SigIds... });
504 }
505 //
506 //--------------------------------------------------------------------------------------//
507 // one or more signals specified in template parameters
508 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
509 template <typename Tp, enable_if_t<Tp::value>>
510 void
511 sampler<CompT<Types...>, N, SigIds...>::stop()
512 {
513  auto cnt = tracker_type::stop();
514  base_type::set_stopped();
515  for(auto& itr : m_data)
516  itr.stop();
517  if(cnt == 0)
518  ignore({ SigIds... });
519 }
520 //
521 //--------------------------------------------------------------------------------------//
522 // no signals specified in template parameters
523 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
524 template <typename Tp, enable_if_t<!Tp::value>>
525 void
526 sampler<CompT<Types...>, N, SigIds...>::start()
527 {
528  base_type::set_started();
529  for(auto& itr : m_data)
530  itr.start();
531 }
532 //
533 //--------------------------------------------------------------------------------------//
534 // no signals specified in template parameters
535 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
536 template <typename Tp, enable_if_t<!Tp::value>>
537 void
538 sampler<CompT<Types...>, N, SigIds...>::stop()
539 {
540  base_type::set_stopped();
541  for(auto& itr : m_data)
542  itr.stop();
543 }
544 //
545 //--------------------------------------------------------------------------------------//
546 //
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&
550 sampler<CompT<Types...>, N, SigIds...>::get(size_t idx)
551 {
552  return m_data.at(idx % N);
553 }
554 //
555 //--------------------------------------------------------------------------------------//
556 //
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&
560 sampler<CompT<Types...>, N, SigIds...>::get(size_t idx) const
561 {
562  return m_data.at(idx % N);
563 }
564 //
565 //--------------------------------------------------------------------------------------//
566 //
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&
570 sampler<CompT<Types...>, N, SigIds...>::get(size_t idx)
571 {
572  return m_data.at(idx);
573 }
574 //
575 //--------------------------------------------------------------------------------------//
576 //
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
581 {
582  return m_data.at(idx);
583 }
584 //
585 //--------------------------------------------------------------------------------------//
586 //
587 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
588 void
589 sampler<CompT<Types...>, N, SigIds...>::execute(int signum)
590 {
591  if(settings::debug())
592  {
593  printf("[pid=%i][tid=%i][%s]> sampling...\n", (int) process::get_id(),
594  (int) threading::get_id(), demangle<this_type>().c_str());
595  }
596 
597  for(auto& itr : get_samplers())
598  {
599  if(itr->is_good(signum))
600  {
601  itr->sample();
602  }
603  else if(itr->is_bad(signum))
604  {
605  char msg[1024];
606  sprintf(msg, "[timemory]> sampler instance caught bad signal: %i ...",
607  signum);
608  perror(msg);
609  signal(signum, SIG_DFL);
610  raise(signum);
611  }
612  }
613 }
614 //
615 //--------------------------------------------------------------------------------------//
616 //
617 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
618 void
619 sampler<CompT<Types...>, N, SigIds...>::execute(int signum, siginfo_t*, void*)
620 {
621  if(settings::debug())
622  {
623  printf("[pid=%i][tid=%i][%s]> sampling...\n", (int) process::get_id(),
624  (int) threading::get_id(), demangle<this_type>().c_str());
625  }
626 
627  for(auto& itr : get_samplers())
628  {
629  if(itr->is_good(signum))
630  {
631  itr->sample();
632  }
633  else if(itr->is_bad(signum))
634  {
635  char msg[1024];
636  sprintf(msg, "[timemory]> sampler instance caught bad signal: %i ...",
637  signum);
638  perror(msg);
639  signal(signum, SIG_DFL);
640  raise(signum);
641  }
642  }
643 }
644 //
645 //--------------------------------------------------------------------------------------//
646 //
647 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
648 void
649 sampler<CompT<Types...>, N, SigIds...>::configure(std::set<int> _signals, int _verbose)
650 {
651  // already active
652  if(get_persistent_data().m_active)
653  return;
654 
655  size_t wait_count = 0;
656  {
657  auto_lock_t lk(type_mutex<this_type>());
658  for(auto& itr : get_samplers())
659  wait_count += itr->count();
660  }
661 
662  if(wait_count == 0)
663  {
664  if(_verbose > 0)
665  {
666  fprintf(
667  stderr,
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");
671  }
672  _signals.clear();
673  }
674  else
675  {
676  TIMEMORY_FOLD_EXPRESSION(_signals.insert(SigIds));
677  }
678 
679  if(!_signals.empty())
680  {
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;
685 
686  memset(&_custom_sa, 0, sizeof(_custom_sa));
687 
688  _custom_sa.sa_handler = &this_type::execute;
689  _custom_sa.sa_sigaction = &this_type::execute;
690  _custom_sa.sa_flags = SA_RESTART | SA_SIGINFO;
691 
692  // start the interval timer
693  for(const auto& itr : _signals)
694  {
695  // get the associated itimer type
696  auto _itimer = get_itimer(itr);
697  if(_itimer < 0)
698  {
700  " ", "Error! Alarm cannot be set for signal", itr,
701  "because the signal does not map to a known itimer value\n"));
702  }
703 
704  // configure the sigaction
705  int _sret = sigaction(itr, &_custom_sa, &_original_sa);
706  if(_sret == 0)
707  {
708  get_persistent_data().m_signals.insert(itr);
709  }
710  else
711  {
713  " ", "Error! sigaction could not be set for signal", itr));
714  }
715 
716  // start the alarm (throws if fails)
717  check_itimer(setitimer(_itimer, &_custom_it, &_original_it), true);
718  }
719  }
720 
721  // if active field based on whether there are signals
722  get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
723 }
724 //
725 //--------------------------------------------------------------------------------------//
726 //
727 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
728 inline void
729 sampler<CompT<Types...>, N, SigIds...>::ignore(const std::set<int>& _signals)
730 {
731  for(const auto& itr : _signals)
732  signal(itr, SIG_IGN);
733 
734  auto& _original_it = get_persistent_data().m_original_itimerval;
735  for(const auto& itr : _signals)
736  {
737  itimerval_t _curr;
738  auto _itimer = get_itimer(itr);
739  if(_itimer < 0)
740  continue;
741  check_itimer(getitimer(_itimer, &_curr));
742  // stop the alarm
743  if(_curr.it_interval.tv_usec > 0 || _curr.it_interval.tv_sec > 0)
744  check_itimer(setitimer(_itimer, &_original_it, &_curr));
745  }
746 
747  // if active field based on whether there are signals
748  get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
749 }
750 //
751 //--------------------------------------------------------------------------------------//
752 //
753 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
754 template <typename Func>
755 int
756 sampler<CompT<Types...>, N, SigIds...>::wait(const pid_t wait_pid, int _verbose,
757  bool _debug, Func&& _callback)
758 {
759  if(_verbose > 2 || _debug)
760  fprintf(stderr, "[%i]> waiting for pid %i...\n", process::get_id(), wait_pid);
761 
762  //----------------------------------------------------------------------------------//
763  //
764  auto print_info = [=](pid_t _pid, int _status, int _errv, int _retv) {
765  if(_debug || _verbose > 2)
766  {
767  fprintf(stderr, "[%i]> return code: %i, error value: %i, status: %i\n", _pid,
768  _retv, _errv, _status);
769  fflush(stderr);
770  }
771  };
772  //
773  //----------------------------------------------------------------------------------//
774  //
775  auto diagnose_status = [=](pid_t _pid, int status) {
776  if(_verbose > 2 || _debug)
777  fprintf(stderr, "[%i]> diagnosing status %i...\n", _pid, status);
778 
779  if(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
780  {
781  if(_verbose > 2 || (_debug && _verbose > 0))
782  {
783  fprintf(stderr, "[%i]> program terminated normally with exit code: %i\n",
784  _pid, WEXITSTATUS(status));
785  }
786  // normal terminatation
787  return 0;
788  }
789 
790  int ret = WEXITSTATUS(status);
791  if(WIFSTOPPED(status))
792  {
793  int sig = WSTOPSIG(status);
794  // stopped with signal 'sig'
795  if(_debug || _verbose > 3)
796  {
797  fprintf(stderr, "[%i]> program stopped with signal %i. Exit code: %i\n",
798  _pid, sig, ret);
799  }
800  }
801  else if(WCOREDUMP(status))
802  {
803  if(_debug || _verbose > 3)
804  {
805  fprintf(stderr,
806  "[%i]> program terminated and produced a core dump. Exit "
807  "code: %i\n",
808  _pid, ret);
809  }
810  }
811  else if(WIFSIGNALED(status))
812  {
813  ret = WTERMSIG(status);
814  if(_debug || _verbose > 3)
815  {
816  fprintf(stderr,
817  "[%i]> program terminated because it received a signal "
818  "(%i) that was not handled. Exit code: %i\n",
819  _pid, WTERMSIG(status), ret);
820  }
821  }
822  else if(WIFEXITED(status) && WEXITSTATUS(status))
823  {
824  if(ret == 127 && (_debug || _verbose > 3))
825  {
826  fprintf(stderr, "[%i]> execv failed\n", _pid);
827  }
828  else if(_debug || _verbose > 3)
829  {
830  fprintf(stderr,
831  "[%i]> program terminated with a non-zero status. Exit "
832  "code: %i\n",
833  _pid, ret);
834  }
835  }
836  else
837  {
838  if(_debug || _verbose > 3)
839  fprintf(stderr, "[%i]> program terminated abnormally.\n", _pid);
840  ret = EXIT_FAILURE;
841  }
842 
843  return ret;
844  };
845  //
846  //----------------------------------------------------------------------------------//
847  //
848  auto waitpid_eintr = [&](pid_t _pid, int& status) {
849  pid_t pid = 0;
850  int errval = 0;
851  int retval = 0;
852 
853  while((pid = waitpid(WAIT_ANY, &status, 0)) == -1)
854  {
855  errval = errno;
856  if(errval == EINTR)
857  continue;
858  if(errno != errval)
859  perror("Unexpected error in waitpid_eitr");
860  retval = diagnose_status(pid, status);
861  print_info(pid, status, errval, retval);
862  break;
863  }
864 
865  if(errval == ECHILD)
866  {
867  do
868  {
869  retval = kill(_pid, 0);
870  errval = errno;
871  // retval = diagnose_status(_pid, status);
872  // print_info(_pid, status, errval, retval);
873  if(errval == ESRCH || retval == -1)
874  break;
875  std::this_thread::sleep_for(
876  std::chrono::microseconds(get_frequency(units::usec)));
877  } while(true);
878  }
879 
880  return errval;
881  };
882  //
883  //----------------------------------------------------------------------------------//
884 
885  auto _signals = get_persistent_data().m_signals;
886  int status = 0;
887  int errval = 0;
888 
889  // do not wait on self to exit so execute callback until
890  if(_signals.empty() && wait_pid == process::get_id())
891  {
892  do
893  {
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);
898  }
899 
900  // loop while the errno is not EINTR (interrupt) and status designates
901  // it was stopped because of signal
902  int retval = 0;
903  do
904  {
905  status = 0;
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)));
911 
912  print_info(wait_pid, status, errval, retval);
913 
914  return diagnose_status(wait_pid, status);
915 }
916 //
917 //--------------------------------------------------------------------------------------//
918 //
919 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
920 void
921 sampler<CompT<Types...>, N, SigIds...>::set_delay(double fdelay)
922 {
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;
926  if(settings::debug() || settings::verbose() > 0)
927  {
928  fprintf(stderr, "sampler delay : %i sec + %i usec\n", delay_sec, delay_usec);
929  }
930  // Configure the timer to expire after designated delay...
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;
933 }
934 //
935 //--------------------------------------------------------------------------------------//
936 //
937 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
938 void
939 sampler<CompT<Types...>, N, SigIds...>::set_frequency(double ffreq)
940 {
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;
944  if(settings::debug() || settings::verbose() > 0)
945  {
946  fprintf(stderr, "sampler frequency : %i sec + %i usec\n", freq_sec,
947  freq_usec);
948  }
949  // Configure the timer to expire after designated delay...
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;
952 }
953 //
954 //--------------------------------------------------------------------------------------//
955 //
956 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
957 inline int64_t
958 sampler<CompT<Types...>, N, SigIds...>::get_delay(int64_t units)
959 {
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);
964 }
965 //
966 //--------------------------------------------------------------------------------------//
967 //
968 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
969 inline int64_t
970 sampler<CompT<Types...>, N, SigIds...>::get_frequency(int64_t units)
971 {
972  float _us =
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);
977 }
978 //
979 //--------------------------------------------------------------------------------------//
980 //
981 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
982 int
983 sampler<CompT<Types...>, N, SigIds...>::get_itimer(int _signal)
984 {
985  int _itimer = -1;
986  switch(_signal)
987  {
988  case SIGALRM: _itimer = ITIMER_REAL; break;
989  case SIGVTALRM: _itimer = ITIMER_VIRTUAL; break;
990  case SIGPROF: _itimer = ITIMER_PROF; break;
991  }
992  return _itimer;
993 }
994 //
995 //--------------------------------------------------------------------------------------//
996 //
997 template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
998 bool
999 sampler<CompT<Types...>, N, SigIds...>::check_itimer(int _stat, bool _throw_exception)
1000 {
1001  if(_stat == EFAULT)
1002  {
1003  auto msg =
1004  TIMEMORY_JOIN(" ", "Warning! setitimer returned EFAULT.",
1005  "Either the new itimerval or the old itimerval was invalid");
1006  if(_throw_exception)
1007  {
1008  TIMEMORY_EXCEPTION(msg)
1009  }
1010  else
1011  {
1012  std::cerr << msg << '\n';
1013  }
1014  }
1015  else if(_stat == EINVAL)
1016  {
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)
1022  {
1023  TIMEMORY_EXCEPTION(msg)
1024  }
1025  else
1026  {
1027  std::cerr << msg << '\n';
1028  }
1029  }
1030  return (_stat != EFAULT && _stat != EINVAL);
1031 }
1032 //
1033 //--------------------------------------------------------------------------------------//
1034 //
1035 } // namespace sampling
1036 } // namespace tim
std::shared_ptr< DataType > execute(std::shared_ptr< DataType > _data=std::make_shared< DataType >())
void stop(TupleT< Tp... > &obj, Args &&... args)
Definition: functional.cpp:368
void start(TupleT< Tp... > &obj, Args &&... args)
Definition: functional.cpp:298
Inherit from this policy to add reference counting support. Useful if you want to turn a global setti...
Definition: types.hpp:367
typename fixed_size< N >::type fixed_size_t
Definition: sampler.hpp:111
typename fixed_sig< Ids... >::type fixed_sig_t
Definition: sampler.hpp:124
The design of the sampler struct is similar to the tim::component::gotcha component: the first templa...
Definition: sampler.hpp:74
Definition: kokkosp.cpp:38
std::unique_lock< mutex_t > auto_lock_t
Unique lock type around mutex_t.
Definition: utility.hpp:115
void configure(std::initializer_list< EnumT > components, Args &&... args)
Definition: configure.hpp:50
typename std::enable_if< B, T >::type enable_if_t
Alias template for enable_if.
Definition: types.hpp:190
tim::mpl::apply< std::string > string
Definition: macros.hpp:52
auto get(const auto_bundle< Tag, Types... > &_obj)
typename std::conditional< B, Lhs, Rhs >::type conditional_t
Definition: types.hpp:197
char ** environ
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())
Definition: sampler.hpp:297
static void pause()
Pause until a signal is delivered.
Definition: sampler.hpp:265
conditional_t< fixed_size_t< N >::value, std::array< components_t, N >, std::vector< components_t > > array_t
Definition: sampler.hpp:167
sampler(const std::string &_label, signal_set_t _good, signal_set_t _bad=signal_set_t{})
const components_t & get(size_t idx) const
std::function< bool(pid_t, int, int)> pid_cb_t
Definition: sampler.hpp:165
static void set_rate(double frate)
Value, expressed in number of interupts per second, that configures the frequency that the sampler sa...
Definition: sampler.hpp:330
static void set_flags(int _flags)
Set the sigaction flags, e.g. SA_RESTART | SA_SIGINFO.
Definition: sampler.hpp:315
static int wait(Func &&_callback, int _verbose=settings::verbose(), bool _debug=settings::debug())
Definition: sampler.hpp:304
static void configure(int _signal=SIGALRM, int _verbose=1)
Definition: sampler.hpp:247
static int wait(int _verbose=settings::verbose(), bool _debug=settings::debug(), Func &&_callback=pid_callback())
Definition: sampler.hpp:289
static void clear()
Clear all signals. Recommended to call ignore() prior to clearing all the signals.
Definition: sampler.hpp:261
trait that designates the type is a timemory component
trait that signifies the component supports sampling.
#define TIMEMORY_FOLD_EXPRESSION(...)
Definition: types.hpp:55
#define TIMEMORY_EXCEPTION(...)
Definition: types.hpp:137
#define TIMEMORY_JOIN(delim,...)
Definition: macros.hpp:89