// MIT License // // Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include "library/causal/components/blocking_gotcha.hpp" #include "core/config.hpp" #include "core/debug.hpp" #include "core/state.hpp" #include "library/causal/components/causal_gotcha.hpp" #include "library/causal/delay.hpp" #include "library/causal/experiment.hpp" #include "library/causal/sampling.hpp" #include "library/runtime.hpp" #include #include #include #include #include #include #include #include #pragma weak pthread_join #pragma weak pthread_mutex_lock #pragma weak pthread_spin_lock #pragma weak pthread_cond_wait #pragma weak pthread_rwlock_rdlock #pragma weak pthread_rwlock_wrlock #pragma weak pthread_tryjoin_np #pragma weak pthread_timedjoin_np #pragma weak pthread_cond_timedwait #pragma weak pthread_rwlock_timedrdlock #pragma weak pthread_rwlock_timedwrlock #pragma weak pthread_mutex_trylock #pragma weak pthread_spin_trylock #pragma weak pthread_rwlock_trywrlock #pragma weak sigwait #pragma weak sigwaitinfo #pragma weak sigtimedwait #pragma weak sigsuspend namespace omnitrace { namespace causal { namespace component { std::string blocking_gotcha::label() { return "causal_blocking_gotcha"; } std::string blocking_gotcha::description() { return "Handles executing all necessary pauses before the thread performs some " "blocking function"; } void blocking_gotcha::preinit() { configure(); } void blocking_gotcha::configure() { blocking_gotcha_t::get_initializer() = []() { if(!config::get_use_causal()) return; // postblock(true) // - pthread_join // - pthread_mutex_lock // - pthread_cond_wait // - pthread_barrier_wait // - pthread_rwlock_rdlock // - pthread_rwlock_wrlock TIMEMORY_C_GOTCHA(blocking_gotcha_t, 0, pthread_join); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 1, pthread_mutex_lock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 2, pthread_spin_lock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 3, pthread_cond_wait); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 4, pthread_rwlock_rdlock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 5, pthread_rwlock_wrlock); // postblock(result == 0) // - pthread_tryjoin_np // - pthread_timedjoin_np // - pthread_cond_timedwait // - pthread_rwlock_timedrdlock // - pthread_rwlock_timedwrlock TIMEMORY_C_GOTCHA(blocking_gotcha_t, 6, pthread_tryjoin_np); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 7, pthread_timedjoin_np); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 8, pthread_cond_timedwait); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 9, pthread_rwlock_timedrdlock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 10, pthread_rwlock_timedwrlock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 11, pthread_mutex_trylock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 12, pthread_spin_trylock); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 13, pthread_rwlock_trywrlock); // postblock(...) // - sigwait // - sigwaitinfo // - sigtimedwait TIMEMORY_C_GOTCHA(blocking_gotcha_t, 14, sigwait); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 15, sigwaitinfo); TIMEMORY_C_GOTCHA(blocking_gotcha_t, 16, sigtimedwait); // other TIMEMORY_C_GOTCHA(blocking_gotcha_t, 17, sigsuspend); }; } void blocking_gotcha::shutdown() { blocking_gotcha_t::disable(); } template Ret blocking_gotcha::operator()(const comp::gotcha_data& _data, Ret (*_func)(Args...), Args... _args) const noexcept { int64_t _delay_value = causal::delay::get_global().load(std::memory_order_relaxed); causal::sampling::block_backtrace_samples(); auto _ret = (*_func)(_args...); causal::sampling::unblock_backtrace_samples(); if(get_thread_state() < ::omnitrace::ThreadState::Internal) { if(_data.index <= 5) causal::delay::postblock(_delay_value); else if(_ret == 0 && _data.index >= 6 && _data.index <= 13) causal::delay::postblock(_delay_value); else OMNITRACE_FAIL_F("Error! unexpected index %zu ('%s')\n", _data.index, _data.tool_id.c_str()); } return _ret; } int blocking_gotcha::operator()(const comp::gotcha_data&, int (*)(const sigset_t*, int*), const sigset_t* _set_v, int* _sig) const noexcept { auto _active = get_thread_state() < ::omnitrace::ThreadState::Internal; sigset_t _set = *_set_v; causal_gotcha::remove_signals(&_set); siginfo_t _info; int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0; auto* _data = blocking_gotcha_t::at(16); auto f_sigwaitinfo = reinterpret_cast(_data->wrappee); causal::sampling::block_backtrace_samples(); auto _ret = (*f_sigwaitinfo)(&_set, &_info); causal::sampling::unblock_backtrace_samples(); // Woken up by another thread if the call did not fail and this is waking process if(_active && _ret != -1 && _info.si_pid == process::get_id()) causal::delay::postblock(_delay_value); if(_ret == -1) return errno; // If there was an error, return the error code else *_sig = _ret; // sig is declared as non-null so skip check return 0; } int blocking_gotcha::operator()(const comp::gotcha_data&, int (*_func)(const sigset_t*, siginfo_t*), const sigset_t* _set_v, siginfo_t* _info_v) const noexcept { auto _active = get_thread_state() < ::omnitrace::ThreadState::Internal; sigset_t _set = *_set_v; causal_gotcha::remove_signals(&_set); siginfo_t _info; int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0; causal::sampling::block_backtrace_samples(); auto _ret = (*_func)(&_set, &_info); causal::sampling::unblock_backtrace_samples(); // Woken up by another thread if the call did not fail and this is waking process if(_active && _ret > 0 && _info.si_pid == process::get_id()) causal::delay::postblock(_delay_value); if(_ret > 0 && _info_v) *_info_v = _info; return _ret; } int blocking_gotcha::operator()(const comp::gotcha_data&, int (*_func)(const sigset_t*, siginfo_t*, const struct timespec*), const sigset_t* _set_v, siginfo_t* _info_v, const struct timespec* _wait_v) const noexcept { auto _active = get_thread_state() < ::omnitrace::ThreadState::Internal; sigset_t _set = *_set_v; causal_gotcha::remove_signals(&_set); siginfo_t _info; int64_t _delay_value = (_active) ? causal::delay::get_global().load() : 0; causal::sampling::block_backtrace_samples(); auto _ret = (*_func)(&_set, &_info, _wait_v); causal::sampling::unblock_backtrace_samples(); // Woken up by another thread if the call did not fail and this is waking process if(_active && _ret > 0 && _info.si_pid == process::get_id()) causal::delay::postblock(_delay_value); if(_ret > 0 && _info_v) *_info_v = _info; return _ret; } } // namespace component } // namespace causal } // namespace omnitrace TIMEMORY_INVOKE_PREINIT(omnitrace::causal::component::blocking_gotcha)