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.
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
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>
63extern "C"
64{
65 extern char** environ;
66}
67#endif
68
69namespace tim
70{
71namespace sampling
72{
73template <typename CompT, size_t N, int... SigIds>
74struct sampler;
75}
76//
77//--------------------------------------------------------------------------------------//
78//
79namespace trait
80{
81template <typename CompT, size_t N>
82struct is_component<sampling::sampler<CompT, N>> : true_type
83{};
84} // namespace trait
85//
86//--------------------------------------------------------------------------------------//
87//
88namespace sampling
89{
90//
91//--------------------------------------------------------------------------------------//
92//
93/// \value tim::sampling::dynamic
94/// \brief A bare enumeration value implicitly convertible to zero.
95enum
96{
97 dynamic = 0
98};
99//
100//--------------------------------------------------------------------------------------//
101//
102template <size_t N>
103struct fixed_size : std::true_type
104{};
105//
106template <>
107struct fixed_size<dynamic> : std::false_type
108{};
109//
110template <size_t N>
112//
113//--------------------------------------------------------------------------------------//
114//
115template <int... Ids>
116struct fixed_sig : std::true_type
117{};
118//
119template <>
120struct fixed_sig<> : std::false_type
121{};
122//
123template <int... Ids>
124using 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
156template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
157struct 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
177public:
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
204public:
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
240public:
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
348protected:
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
356private:
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, 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 = {};
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//
390template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
391inline auto
392sampler<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//
404template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
405template <typename Tp, enable_if_t<Tp::value>>
406sampler<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//
421template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
422template <typename Tp, enable_if_t<!Tp::value>>
423sampler<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//
438template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
439sampler<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//
450template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
451template <typename Tp, enable_if_t<Tp::value>>
452void
453sampler<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//
471template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
472template <typename Tp, enable_if_t<!Tp::value>>
473void
474sampler<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
493template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
494template <typename Tp, enable_if_t<Tp::value>>
495void
496sampler<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
508template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
509template <typename Tp, enable_if_t<Tp::value>>
510void
511sampler<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
523template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
524template <typename Tp, enable_if_t<!Tp::value>>
525void
526sampler<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
535template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
536template <typename Tp, enable_if_t<!Tp::value>>
537void
538sampler<CompT<Types...>, N, SigIds...>::stop()
539{
540 base_type::set_stopped();
541 for(auto& itr : m_data)
542 itr.stop();
543}
544//
545//--------------------------------------------------------------------------------------//
546//
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&
550sampler<CompT<Types...>, N, SigIds...>::get(size_t idx)
551{
552 return m_data.at(idx % N);
553}
554//
555//--------------------------------------------------------------------------------------//
556//
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&
560sampler<CompT<Types...>, N, SigIds...>::get(size_t idx) const
561{
562 return m_data.at(idx % N);
563}
564//
565//--------------------------------------------------------------------------------------//
566//
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&
570sampler<CompT<Types...>, N, SigIds...>::get(size_t idx)
571{
572 return m_data.at(idx);
573}
574//
575//--------------------------------------------------------------------------------------//
576//
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
581{
582 return m_data.at(idx);
583}
584//
585//--------------------------------------------------------------------------------------//
586//
587template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
588void
589sampler<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//
617template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
618void
619sampler<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//
647template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
648void
649sampler<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(stderr,
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 "
670 "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 TIMEMORY_JOIN(" ", "Error! Alarm cannot be set for signal", itr,
701 "because the signal does not map to a known itimer "
702 "value\n"));
703 }
704
705 // configure the sigaction
706 int _sret = sigaction(itr, &_custom_sa, &_original_sa);
707 if(_sret == 0)
708 {
709 get_persistent_data().m_signals.insert(itr);
710 }
711 else
712 {
714 " ", "Error! sigaction could not be set for signal", itr));
715 }
716
717 // start the alarm (throws if fails)
718 check_itimer(setitimer(_itimer, &_custom_it, &_original_it), true);
719 }
720 }
721
722 // if active field based on whether there are signals
723 get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
724}
725//
726//--------------------------------------------------------------------------------------//
727//
728template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
729inline void
730sampler<CompT<Types...>, N, SigIds...>::ignore(const std::set<int>& _signals)
731{
732 for(const auto& itr : _signals)
733 signal(itr, SIG_IGN);
734
735 auto& _original_it = get_persistent_data().m_original_itimerval;
736 for(const auto& itr : _signals)
737 {
738 itimerval_t _curr;
739 auto _itimer = get_itimer(itr);
740 if(_itimer < 0)
741 continue;
742 check_itimer(getitimer(_itimer, &_curr));
743 // stop the alarm
744 if(_curr.it_interval.tv_usec > 0 || _curr.it_interval.tv_sec > 0)
745 check_itimer(setitimer(_itimer, &_original_it, &_curr));
746 }
747
748 // if active field based on whether there are signals
749 get_persistent_data().m_active = !get_persistent_data().m_signals.empty();
750}
751//
752//--------------------------------------------------------------------------------------//
753//
754template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
755template <typename Func>
756int
757sampler<CompT<Types...>, N, SigIds...>::wait(const pid_t wait_pid, int _verbose,
758 bool _debug, Func&& _callback)
759{
760 if(_verbose > 2 || _debug)
761 fprintf(stderr, "[%i]> waiting for pid %i...\n", process::get_id(), wait_pid);
762
763 //----------------------------------------------------------------------------------//
764 //
765 auto print_info = [=](pid_t _pid, int _status, int _errv, int _retv) {
766 if(_debug || _verbose > 2)
767 {
768 fprintf(stderr, "[%i]> return code: %i, error value: %i, status: %i\n", _pid,
769 _retv, _errv, _status);
770 fflush(stderr);
771 }
772 };
773 //
774 //----------------------------------------------------------------------------------//
775 //
776 auto diagnose_status = [=](pid_t _pid, int status) {
777 if(_verbose > 2 || _debug)
778 fprintf(stderr, "[%i]> diagnosing status %i...\n", _pid, status);
779
780 if(WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
781 {
782 if(_verbose > 2 || (_debug && _verbose > 0))
783 {
784 fprintf(stderr, "[%i]> program terminated normally with exit code: %i\n",
785 _pid, WEXITSTATUS(status));
786 }
787 // normal terminatation
788 return 0;
789 }
790
791 int ret = WEXITSTATUS(status);
792 if(WIFSTOPPED(status))
793 {
794 int sig = WSTOPSIG(status);
795 // stopped with signal 'sig'
796 if(_debug || _verbose > 3)
797 {
798 fprintf(stderr, "[%i]> program stopped with signal %i. Exit code: %i\n",
799 _pid, sig, ret);
800 }
801 }
802 else if(WCOREDUMP(status))
803 {
804 if(_debug || _verbose > 3)
805 {
806 fprintf(stderr,
807 "[%i]> program terminated and produced a core dump. Exit "
808 "code: %i\n",
809 _pid, ret);
810 }
811 }
812 else if(WIFSIGNALED(status))
813 {
814 ret = WTERMSIG(status);
815 if(_debug || _verbose > 3)
816 {
817 fprintf(stderr,
818 "[%i]> program terminated because it received a signal "
819 "(%i) that was not handled. Exit code: %i\n",
820 _pid, WTERMSIG(status), ret);
821 }
822 }
823 else if(WIFEXITED(status) && WEXITSTATUS(status))
824 {
825 if(ret == 127 && (_debug || _verbose > 3))
826 {
827 fprintf(stderr, "[%i]> execv failed\n", _pid);
828 }
829 else if(_debug || _verbose > 3)
830 {
831 fprintf(stderr,
832 "[%i]> program terminated with a non-zero status. Exit "
833 "code: %i\n",
834 _pid, ret);
835 }
836 }
837 else
838 {
839 if(_debug || _verbose > 3)
840 fprintf(stderr, "[%i]> program terminated abnormally.\n", _pid);
841 ret = EXIT_FAILURE;
842 }
843
844 return ret;
845 };
846 //
847 //----------------------------------------------------------------------------------//
848 //
849 auto waitpid_eintr = [&](pid_t _pid, int& status) {
850 pid_t pid = 0;
851 int errval = 0;
852 int retval = 0;
853
854 while((pid = waitpid(WAIT_ANY, &status, 0)) == -1)
855 {
856 errval = errno;
857 if(errval == EINTR)
858 continue;
859 if(errno != errval)
860 perror("Unexpected error in waitpid_eitr");
861 retval = diagnose_status(pid, status);
862 print_info(pid, status, errval, retval);
863 break;
864 }
865
866 if(errval == ECHILD)
867 {
868 do
869 {
870 retval = kill(_pid, 0);
871 errval = errno;
872 // retval = diagnose_status(_pid, status);
873 // print_info(_pid, status, errval, retval);
874 if(errval == ESRCH || retval == -1)
875 break;
876 std::this_thread::sleep_for(
877 std::chrono::microseconds(get_frequency(units::usec)));
878 } while(true);
879 }
880
881 return errval;
882 };
883 //
884 //----------------------------------------------------------------------------------//
885
886 auto _signals = get_persistent_data().m_signals;
887 int status = 0;
888 int errval = 0;
889
890 // do not wait on self to exit so execute callback until
891 if(_signals.empty() && wait_pid == process::get_id())
892 {
893 do
894 {
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);
899 }
900
901 // loop while the errno is not EINTR (interrupt) and status designates
902 // it was stopped because of signal
903 int retval = 0;
904 do
905 {
906 status = 0;
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)));
912
913 print_info(wait_pid, status, errval, retval);
914
915 return diagnose_status(wait_pid, status);
916}
917//
918//--------------------------------------------------------------------------------------//
919//
920template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
921void
922sampler<CompT<Types...>, N, SigIds...>::set_delay(double fdelay)
923{
924 get_persistent_data().m_freq = fdelay;
925 int delay_sec = fdelay;
926 int delay_usec = static_cast<int>(fdelay * 1000000) % 1000000;
928 {
929 fprintf(stderr, "sampler delay : %i sec + %i usec\n", delay_sec,
930 delay_usec);
931 }
932 // Configure the timer to expire after designated delay...
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;
935}
936//
937//--------------------------------------------------------------------------------------//
938//
939template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
940void
941sampler<CompT<Types...>, N, SigIds...>::set_frequency(double ffreq)
942{
943 get_persistent_data().m_freq = ffreq;
944 int freq_sec = ffreq;
945 int freq_usec = static_cast<int>(ffreq * 1000000) % 1000000;
947 {
948 fprintf(stderr, "sampler frequency : %i sec + %i usec\n", freq_sec,
949 freq_usec);
950 }
951 // Configure the timer to expire after designated delay...
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;
954}
955//
956//--------------------------------------------------------------------------------------//
957//
958template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
959inline int64_t
960sampler<CompT<Types...>, N, SigIds...>::get_delay(int64_t units)
961{
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;
964 _us *= units::usec;
965 _us /= units;
966 return std::max<int64_t>(_us, 1);
967}
968//
969//--------------------------------------------------------------------------------------//
970//
971template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
972inline int64_t
973sampler<CompT<Types...>, N, SigIds...>::get_frequency(int64_t units)
974{
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;
977 _us *= units::usec;
978 _us /= units;
979 return std::max<int64_t>(_us, 1);
980}
981//
982//--------------------------------------------------------------------------------------//
983//
984template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
985int
986sampler<CompT<Types...>, N, SigIds...>::get_itimer(int _signal)
987{
988 int _itimer = -1;
989 switch(_signal)
990 {
991 case SIGALRM: _itimer = ITIMER_REAL; break;
992 case SIGVTALRM: _itimer = ITIMER_VIRTUAL; break;
993 case SIGPROF: _itimer = ITIMER_PROF; break;
994 }
995 return _itimer;
996}
997//
998//--------------------------------------------------------------------------------------//
999//
1000template <template <typename...> class CompT, size_t N, typename... Types, int... SigIds>
1001bool
1002sampler<CompT<Types...>, N, SigIds...>::check_itimer(int _stat, bool _throw_exception)
1003{
1004 if(_stat == EFAULT)
1005 {
1006 auto msg =
1007 TIMEMORY_JOIN(" ", "Warning! setitimer returned EFAULT.",
1008 "Either the new itimerval or the old itimerval was invalid");
1009 if(_throw_exception)
1010 {
1012 }
1013 else
1014 {
1015 std::cerr << msg << '\n';
1016 }
1017 }
1018 else if(_stat == EINVAL)
1019 {
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)
1025 {
1027 }
1028 else
1029 {
1030 std::cerr << msg << '\n';
1031 }
1032 }
1033 return (_stat != EFAULT && _stat != EINVAL);
1034}
1035//
1036//--------------------------------------------------------------------------------------//
1037//
1038} // namespace sampling
1039} // namespace tim
STL namespace.
std::shared_ptr< DataType > execute(std::shared_ptr< DataType > _data=std::make_shared< DataType >())
void stop(TupleT< Tp... > &obj, Args &&... args)
Definition: functional.cpp:386
void start(TupleT< Tp... > &obj, Args &&... args)
Definition: functional.cpp:316
Inherit from this policy to add reference counting support. Useful if you want to turn a global setti...
Definition: types.hpp:406
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:39
std::unique_lock< mutex_t > auto_lock_t
Unique lock type around mutex_t.
Definition: locking.hpp:42
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:53
auto get(const auto_bundle< Tag, Types... > &_obj)
typename std::conditional< B, Lhs, Rhs >::type conditional_t
Definition: types.hpp:197
char ** environ
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
const components_t & get(size_t idx) const
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{})
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:56
#define TIMEMORY_EXCEPTION(...)
Definition: types.hpp:138
#define TIMEMORY_JOIN(delim,...)
Definition: macros.hpp:90