Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:15:13

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/Plugins/FpeMonitoring/FpeMonitor.hpp"
0010 
0011 #include "Acts/Utilities/Helpers.hpp"
0012 
0013 #include <algorithm>
0014 #include <bitset>
0015 #include <cfenv>
0016 #include <csignal>
0017 #include <cstddef>
0018 #include <cstdint>
0019 #include <iostream>
0020 #include <iterator>
0021 #include <memory>
0022 #include <mutex>
0023 #include <stdexcept>
0024 #include <string_view>
0025 #include <vector>
0026 
0027 #include <boost/stacktrace/frame.hpp>
0028 #include <boost/stacktrace/safe_dump_to.hpp>
0029 #include <boost/stacktrace/stacktrace.hpp>
0030 #include <boost/stacktrace/stacktrace_fwd.hpp>
0031 
0032 #define FPU_EXCEPTION_MASK 0x3f
0033 #define FPU_STATUS_FLAGS 0xff
0034 #define SSE_STATUS_FLAGS FPU_EXCEPTION_MASK
0035 #define SSE_EXCEPTION_MASK (FPU_EXCEPTION_MASK << 7)
0036 
0037 namespace Acts {
0038 
0039 namespace {
0040 bool areFpesEquivalent(
0041     std::pair<FpeType, const boost::stacktrace::stacktrace &> lhs,
0042     std::pair<FpeType, const boost::stacktrace::stacktrace &> rhs) {
0043   const auto &fl = *lhs.second.begin();
0044   const auto &fr = *rhs.second.begin();
0045   return lhs.first == rhs.first && (boost::stacktrace::hash_value(fl) ==
0046                                     boost::stacktrace::hash_value(fr));
0047 }
0048 }  // namespace
0049 
0050 FpeMonitor::Result::FpeInfo::~FpeInfo() = default;
0051 
0052 FpeMonitor::Result::FpeInfo::FpeInfo(
0053     std::size_t countIn, FpeType typeIn,
0054     std::shared_ptr<const boost::stacktrace::stacktrace> stIn)
0055     : count{countIn}, type{typeIn}, st{std::move(stIn)} {}
0056 
0057 FpeMonitor::Result FpeMonitor::Result::merged(const Result &with) const {
0058   Result result{};
0059 
0060   for (unsigned int i = 0; i < m_counts.size(); i++) {
0061     result.m_counts[i] = m_counts[i] + with.m_counts[i];
0062   }
0063 
0064   std::copy(with.m_stackTraces.begin(), with.m_stackTraces.end(),
0065             std::back_inserter(result.m_stackTraces));
0066   std::copy(m_stackTraces.begin(), m_stackTraces.end(),
0067             std::back_inserter(result.m_stackTraces));
0068 
0069   result.deduplicate();
0070 
0071   return result;
0072 }
0073 
0074 void FpeMonitor::Result::merge(const Result &with) {
0075   for (unsigned int i = 0; i < m_counts.size(); i++) {
0076     m_counts[i] = m_counts[i] + with.m_counts[i];
0077   }
0078 
0079   std::copy(with.m_stackTraces.begin(), with.m_stackTraces.end(),
0080             std::back_inserter(m_stackTraces));
0081 
0082   deduplicate();
0083 }
0084 
0085 void FpeMonitor::Result::add(FpeType type, void *stackPtr,
0086                              std::size_t bufferSize) {
0087   auto st = std::make_unique<boost::stacktrace::stacktrace>(
0088       boost::stacktrace::stacktrace::from_dump(stackPtr, bufferSize));
0089 
0090   auto it = std::ranges::find_if(m_stackTraces, [&](const FpeInfo &el) {
0091     return areFpesEquivalent({el.type, *el.st}, {type, *st});
0092   });
0093 
0094   if (it != m_stackTraces.end()) {
0095     it->count += 1;
0096   } else {
0097     m_stackTraces.push_back({1, type, std::move(st)});
0098   }
0099 }
0100 
0101 bool FpeMonitor::Result::contains(
0102     FpeType type, const boost::stacktrace::stacktrace &st) const {
0103   return std::ranges::any_of(m_stackTraces, [&](const FpeInfo &el) {
0104     return areFpesEquivalent({el.type, *el.st}, {type, st});
0105   });
0106 }
0107 
0108 FpeMonitor::Result &FpeMonitor::result() {
0109   consumeRecorded();
0110   return m_result;
0111 }
0112 
0113 void FpeMonitor::consumeRecorded() {
0114   if (m_recorded.empty()) {
0115     return;
0116   }
0117 
0118   for (auto [type, stackPtr, remaining] : m_recorded) {
0119     m_result.add(type, stackPtr, remaining);
0120   }
0121 
0122   m_buffer.reset();
0123   m_recorded.clear();
0124 }
0125 
0126 unsigned int FpeMonitor::Result::count(FpeType type) const {
0127   return m_counts.at(static_cast<std::uint32_t>(type));
0128 }
0129 
0130 unsigned int FpeMonitor::Result::numStackTraces() const {
0131   return m_stackTraces.size();
0132 }
0133 
0134 const std::vector<FpeMonitor::Result::FpeInfo> &
0135 FpeMonitor::Result::stackTraces() const {
0136   return m_stackTraces;
0137 }
0138 
0139 bool FpeMonitor::Result::encountered(FpeType type) const {
0140   return count(type) > 0;
0141 }
0142 
0143 void FpeMonitor::Result::summary(std::ostream &os, std::size_t depth) const {
0144   os << "FPE result summary:\n";
0145   static const std::vector<FpeType> types = {
0146       FpeType::INTDIV, FpeType::INTOVF, FpeType::FLTDIV, FpeType::FLTOVF,
0147       FpeType::FLTUND, FpeType::FLTRES, FpeType::FLTINV, FpeType::FLTSUB};
0148 
0149   for (auto type : types) {
0150     os << "- " << type << ": " << count(type) << "\n";
0151   }
0152 
0153   os << "\nStack traces:\n";
0154   for (const auto &[count, type, st] : stackTraces()) {
0155     os << "- " << type << ": (" << count << " times)\n";
0156 
0157     os << stackTraceToString(*st, depth);
0158   }
0159   os << std::endl;
0160 }
0161 
0162 void FpeMonitor::Result::deduplicate() {
0163   std::vector<FpeInfo> copy{};
0164   copy = std::move(m_stackTraces);
0165   m_stackTraces.clear();
0166 
0167   for (auto &info : copy) {
0168     auto it = std::ranges::find_if(m_stackTraces, [&info](const FpeInfo &el) {
0169       return areFpesEquivalent({el.type, *el.st}, {info.type, *info.st});
0170     });
0171     if (it != m_stackTraces.end()) {
0172       it->count += info.count;
0173       continue;
0174     }
0175     m_stackTraces.push_back({info.count, info.type, std::move(info.st)});
0176   }
0177 }
0178 
0179 FpeMonitor::FpeMonitor()
0180     : m_excepts{FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW} {
0181   enable();
0182 }
0183 
0184 FpeMonitor::FpeMonitor(int excepts) : m_excepts(excepts) {
0185   enable();
0186 }
0187 
0188 FpeMonitor::~FpeMonitor() {
0189   disable();
0190 }
0191 
0192 void FpeMonitor::signalHandler(int /*signal*/, siginfo_t *si, void *ctx) {
0193   if (stack().empty()) {
0194     return;
0195   }
0196 
0197   FpeMonitor &fpe = *stack().top();
0198   fpe.m_result.m_counts.at(si->si_code)++;
0199 
0200   try {
0201     // collect stack trace skipping 2 frames, which should be the signal handler
0202     // and the calling facility. This might be platform specific, not sure
0203     auto [buffer, remaining] = fpe.m_buffer.next();
0204     std::size_t depth = boost::stacktrace::safe_dump_to(2, buffer, remaining);
0205     std::size_t stored =
0206         depth * sizeof(boost::stacktrace::frame::native_frame_ptr_t);
0207     fpe.m_buffer.pushOffset(stored);  // record how much storage was consumed
0208     fpe.m_recorded.emplace_back(
0209         static_cast<FpeType>(si->si_code), buffer,
0210         remaining);  // record buffer offset and fpe type
0211 
0212   } catch (const std::bad_alloc &e) {
0213     std::cout << "Unable to collect stack trace due to memory limit"
0214               << std::endl;
0215   }
0216 
0217 #if defined(__linux__) && defined(__x86_64__)
0218   __uint16_t *cw = &(static_cast<ucontext_t *>(ctx))->uc_mcontext.fpregs->cwd;
0219   *cw |= FPU_EXCEPTION_MASK;
0220 
0221   __uint16_t *sw = &(static_cast<ucontext_t *>(ctx))->uc_mcontext.fpregs->swd;
0222   *sw &= ~FPU_STATUS_FLAGS;
0223 
0224   __uint32_t *mxcsr =
0225       &(static_cast<ucontext_t *>(ctx))->uc_mcontext.fpregs->mxcsr;
0226   // *mxcsr |= SSE_EXCEPTION_MASK;  // disable all SSE exceptions
0227   *mxcsr |= ((*mxcsr & SSE_STATUS_FLAGS) << 7);
0228   *mxcsr &= ~SSE_STATUS_FLAGS;  // clear all pending SSE exceptions
0229 #else
0230   (void)ctx;
0231 #endif
0232 }
0233 
0234 void FpeMonitor::enable() {
0235 #if defined(__linux__) && defined(__x86_64__)
0236   ensureSignalHandlerInstalled();
0237 
0238   // clear pending exceptions so they don't immediately fire
0239   std::feclearexcept(m_excepts);
0240 
0241   if (!stack().empty()) {
0242     // unset previous except state
0243     fedisableexcept(stack().top()->m_excepts);
0244   }
0245   // apply this stack
0246   feenableexcept(m_excepts);
0247 
0248   stack().push(this);
0249 #else
0250   (void)m_excepts;
0251 #endif
0252 }
0253 
0254 void FpeMonitor::rearm() {
0255   consumeRecorded();
0256 #if defined(__linux__) && defined(__x86_64__)
0257   std::feclearexcept(m_excepts);
0258   feenableexcept(m_excepts);
0259 #endif
0260 }
0261 
0262 void FpeMonitor::ensureSignalHandlerInstalled() {
0263   auto &state = globalState();
0264   if (state.isSignalHandlerInstalled) {
0265     return;
0266   }
0267 
0268   std::lock_guard lock{state.mutex};
0269 
0270   struct sigaction action {};
0271   action.sa_sigaction = &signalHandler;
0272   action.sa_flags = SA_SIGINFO;
0273   sigaction(SIGFPE, &action, nullptr);
0274 
0275   state.isSignalHandlerInstalled = true;
0276 }
0277 
0278 void FpeMonitor::disable() {
0279 #if defined(__linux__) && defined(__x86_64__)
0280   std::feclearexcept(m_excepts);
0281   assert(!stack().empty() && "FPE stack shouldn't be empty at this point");
0282   stack().pop();
0283   // disable excepts we enabled here
0284   fedisableexcept(m_excepts);
0285   if (!stack().empty()) {
0286     // restore excepts from next stack element
0287     std::feclearexcept(stack().top()->m_excepts);
0288     feenableexcept(stack().top()->m_excepts);
0289   }
0290 #endif
0291 }
0292 
0293 std::stack<FpeMonitor *> &FpeMonitor::stack() {
0294   static thread_local std::stack<FpeMonitor *> monitors;
0295   return monitors;
0296 }
0297 
0298 FpeMonitor::GlobalState &FpeMonitor::globalState() {
0299   static GlobalState state{};
0300   return state;
0301 }
0302 
0303 std::ostream &operator<<(std::ostream &os, FpeType type) {
0304 #define CASE(x)    \
0305   case FpeType::x: \
0306     os << #x;      \
0307     break;
0308 
0309   switch (type) {
0310     CASE(INTDIV)
0311     CASE(INTOVF)
0312     CASE(FLTDIV)
0313     CASE(FLTOVF)
0314     CASE(FLTUND)
0315     CASE(FLTRES)
0316     CASE(FLTINV)
0317     CASE(FLTSUB)
0318   }
0319 #undef CASE
0320 
0321   return os;
0322 }
0323 
0324 std::string FpeMonitor::stackTraceToString(
0325     const boost::stacktrace::stacktrace &st, std::size_t depth) {
0326   return boost::stacktrace::detail::to_string(st.as_vector().data(),
0327                                               std::min(depth, st.size()));
0328 }
0329 
0330 std::string FpeMonitor::getSourceLocation(
0331     const boost::stacktrace::frame &frame) {
0332   return frame.source_file() + ":" + std::to_string(frame.source_line());
0333 }
0334 
0335 bool FpeMonitor::canSymbolize() {
0336 #if defined(BOOST_STACKTRACE_USE_NOOP)
0337   return false;
0338 #else
0339   return true;
0340 #endif
0341 }
0342 
0343 }  // namespace Acts