Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:10:17

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2022-2023 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 http://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_stracktraces.begin(), with.m_stracktraces.end(),
0065             std::back_inserter(result.m_stracktraces));
0066   std::copy(m_stracktraces.begin(), m_stracktraces.end(),
0067             std::back_inserter(result.m_stracktraces));
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_stracktraces.begin(), with.m_stracktraces.end(),
0080             std::back_inserter(m_stracktraces));
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::find_if(
0091       m_stracktraces.begin(), m_stracktraces.end(), [&](const FpeInfo &el) {
0092         return areFpesEquivalent({el.type, *el.st}, {type, *st});
0093       });
0094 
0095   if (it != m_stracktraces.end()) {
0096     it->count += 1;
0097   } else {
0098     m_stracktraces.push_back({1, type, std::move(st)});
0099   }
0100 }
0101 
0102 bool FpeMonitor::Result::contains(
0103     FpeType type, const boost::stacktrace::stacktrace &st) const {
0104   return std::find_if(m_stracktraces.begin(), m_stracktraces.end(),
0105                       [&](const FpeInfo &el) {
0106                         return areFpesEquivalent({el.type, *el.st}, {type, st});
0107                       }) != m_stracktraces.end();
0108 }
0109 
0110 FpeMonitor::Result &FpeMonitor::result() {
0111   consumeRecorded();
0112   return m_result;
0113 }
0114 
0115 void FpeMonitor::consumeRecorded() {
0116   if (m_recorded.empty()) {
0117     return;
0118   }
0119 
0120   for (auto [type, stackPtr, remaining] : m_recorded) {
0121     m_result.add(type, stackPtr, remaining);
0122   }
0123 
0124   m_buffer.reset();
0125   m_recorded.clear();
0126 }
0127 
0128 unsigned int FpeMonitor::Result::count(FpeType type) const {
0129   return m_counts.at(static_cast<uint32_t>(type));
0130 }
0131 
0132 unsigned int FpeMonitor::Result::numStackTraces() const {
0133   return m_stracktraces.size();
0134 }
0135 
0136 const std::vector<FpeMonitor::Result::FpeInfo>
0137     &FpeMonitor::Result::stackTraces() const {
0138   return m_stracktraces;
0139 }
0140 
0141 bool FpeMonitor::Result::encountered(FpeType type) const {
0142   return count(type) > 0;
0143 }
0144 
0145 void FpeMonitor::Result::summary(std::ostream &os, std::size_t depth) const {
0146   os << "FPE result summary:\n";
0147   static const std::vector<FpeType> types = {
0148       FpeType::INTDIV, FpeType::INTOVF, FpeType::FLTDIV, FpeType::FLTOVF,
0149       FpeType::FLTUND, FpeType::FLTRES, FpeType::FLTINV, FpeType::FLTSUB};
0150 
0151   for (auto type : types) {
0152     os << "- " << type << ": " << count(type) << "\n";
0153   }
0154 
0155   os << "\nStack traces:\n";
0156   for (const auto &[count, type, st] : stackTraces()) {
0157     os << "- " << type << ": (" << count << " times)\n";
0158 
0159     os << stackTraceToString(*st, depth);
0160   }
0161   os << std::endl;
0162 }
0163 
0164 void FpeMonitor::Result::deduplicate() {
0165   std::vector<FpeInfo> copy{};
0166   copy = std::move(m_stracktraces);
0167   m_stracktraces.clear();
0168 
0169   for (auto &info : copy) {
0170     auto it = std::find_if(
0171         m_stracktraces.begin(), m_stracktraces.end(),
0172         [&info](const FpeInfo &el) {
0173           return areFpesEquivalent({el.type, *el.st}, {info.type, *info.st});
0174         });
0175     if (it != m_stracktraces.end()) {
0176       it->count += info.count;
0177       continue;
0178     }
0179     m_stracktraces.push_back({info.count, info.type, std::move(info.st)});
0180   }
0181 }
0182 
0183 FpeMonitor::FpeMonitor()
0184     : m_excepts{FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW} {
0185   enable();
0186 }
0187 
0188 FpeMonitor::FpeMonitor(int excepts) : m_excepts(excepts) {
0189   enable();
0190 }
0191 
0192 FpeMonitor::~FpeMonitor() {
0193   disable();
0194 }
0195 
0196 void FpeMonitor::signalHandler(int /*signal*/, siginfo_t *si, void *ctx) {
0197   if (stack().empty()) {
0198     return;
0199   }
0200 
0201   FpeMonitor &fpe = *stack().top();
0202   fpe.m_result.m_counts.at(si->si_code)++;
0203 
0204   try {
0205     // collect stack trace skipping 2 frames, which should be the signal handler
0206     // and the calling facility. This might be platform specific, not sure
0207     auto [buffer, remaining] = fpe.m_buffer.next();
0208     std::size_t depth = boost::stacktrace::safe_dump_to(2, buffer, remaining);
0209     std::size_t stored =
0210         depth * sizeof(boost::stacktrace::frame::native_frame_ptr_t);
0211     fpe.m_buffer.pushOffset(stored);  // record how much storage was consumed
0212     fpe.m_recorded.emplace_back(
0213         static_cast<FpeType>(si->si_code), buffer,
0214         remaining);  // record buffer offset and fpe type
0215 
0216   } catch (const std::bad_alloc &e) {
0217     std::cout << "Unable to collect stack trace due to memory limit"
0218               << std::endl;
0219   }
0220 
0221 #if defined(__linux__) && defined(__x86_64__)
0222   __uint16_t *cw = &((ucontext_t *)ctx)->uc_mcontext.fpregs->cwd;
0223   *cw |= FPU_EXCEPTION_MASK;
0224 
0225   __uint16_t *sw = &((ucontext_t *)ctx)->uc_mcontext.fpregs->swd;
0226   *sw &= ~FPU_STATUS_FLAGS;
0227 
0228   __uint32_t *mxcsr = &((ucontext_t *)ctx)->uc_mcontext.fpregs->mxcsr;
0229   // *mxcsr |= SSE_EXCEPTION_MASK;  // disable all SSE exceptions
0230   *mxcsr |= ((*mxcsr & SSE_STATUS_FLAGS) << 7);
0231   *mxcsr &= ~SSE_STATUS_FLAGS;  // clear all pending SSE exceptions
0232 #else
0233   (void)ctx;
0234 #endif
0235 }
0236 
0237 void FpeMonitor::enable() {
0238 #if defined(__linux__) && defined(__x86_64__)
0239   ensureSignalHandlerInstalled();
0240 
0241   // clear pending exceptions so they don't immediately fire
0242   std::feclearexcept(m_excepts);
0243 
0244   if (!stack().empty()) {
0245     // unset previous except state
0246     fedisableexcept(stack().top()->m_excepts);
0247   }
0248   // apply this stack
0249   feenableexcept(m_excepts);
0250 
0251   stack().push(this);
0252 #else
0253   (void)m_excepts;
0254 #endif
0255 }
0256 
0257 void FpeMonitor::rearm() {
0258   consumeRecorded();
0259 #if defined(__linux__) && defined(__x86_64__)
0260   std::feclearexcept(m_excepts);
0261   feenableexcept(m_excepts);
0262 #endif
0263 }
0264 
0265 void FpeMonitor::ensureSignalHandlerInstalled() {
0266   auto &state = globalState();
0267   if (state.isSignalHandlerInstalled) {
0268     return;
0269   }
0270 
0271   std::lock_guard lock{state.mutex};
0272 
0273   struct sigaction action {};
0274   action.sa_sigaction = &signalHandler;
0275   action.sa_flags = SA_SIGINFO;
0276   sigaction(SIGFPE, &action, nullptr);
0277 
0278   state.isSignalHandlerInstalled = true;
0279 }
0280 
0281 void FpeMonitor::disable() {
0282 #if defined(__linux__) && defined(__x86_64__)
0283   std::feclearexcept(m_excepts);
0284   assert(!stack().empty() && "FPE stack shouldn't be empty at this point");
0285   stack().pop();
0286   // disable excepts we enabled here
0287   fedisableexcept(m_excepts);
0288   if (!stack().empty()) {
0289     // restore excepts from next stack element
0290     std::feclearexcept(stack().top()->m_excepts);
0291     feenableexcept(stack().top()->m_excepts);
0292   }
0293 #endif
0294 }
0295 
0296 std::stack<FpeMonitor *> &FpeMonitor::stack() {
0297   static thread_local std::stack<FpeMonitor *> monitors;
0298   return monitors;
0299 }
0300 
0301 FpeMonitor::GlobalState &FpeMonitor::globalState() {
0302   static GlobalState state{};
0303   return state;
0304 }
0305 
0306 std::ostream &operator<<(std::ostream &os, FpeType type) {
0307 #define CASE(x)    \
0308   case FpeType::x: \
0309     os << #x;      \
0310     break;
0311 
0312   switch (type) {
0313     CASE(INTDIV)
0314     CASE(INTOVF)
0315     CASE(FLTDIV)
0316     CASE(FLTOVF)
0317     CASE(FLTUND)
0318     CASE(FLTRES)
0319     CASE(FLTINV)
0320     CASE(FLTSUB)
0321   }
0322 #undef CASE
0323 
0324   return os;
0325 }
0326 
0327 std::string FpeMonitor::stackTraceToString(
0328     const boost::stacktrace::stacktrace &st, std::size_t depth) {
0329   return boost::stacktrace::detail::to_string(st.as_vector().data(),
0330                                               std::min(depth, st.size()));
0331 }
0332 
0333 std::string FpeMonitor::getSourceLocation(
0334     const boost::stacktrace::frame &frame) {
0335   return frame.source_file() + ":" + std::to_string(frame.source_line());
0336 }
0337 
0338 }  // namespace Acts