Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-20 08:22:19

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 "ActsPlugins/FpeMonitoring/FpeMonitor.hpp"
0010 
0011 #include <algorithm>
0012 #include <cfenv>
0013 #include <csignal>
0014 #include <cstddef>
0015 #include <cstdint>
0016 #include <cstdlib>
0017 #include <iostream>
0018 #include <iterator>
0019 #include <memory>
0020 #include <mutex>
0021 #include <vector>
0022 
0023 #include <boost/stacktrace/frame.hpp>
0024 #include <boost/stacktrace/safe_dump_to.hpp>
0025 #include <boost/stacktrace/stacktrace.hpp>
0026 #include <boost/stacktrace/stacktrace_fwd.hpp>
0027 
0028 #include "FpeMonitorPlatform.hpp"
0029 
0030 namespace ActsPlugins {
0031 
0032 namespace {
0033 
0034 bool canMergeFpeInfo(const FpeMonitor::Result::FpeInfo &existing, FpeType type,
0035                      std::uintptr_t location,
0036                      const boost::stacktrace::stacktrace &st) {
0037   if (existing.type != type) {
0038     return false;
0039   }
0040 
0041   if (location != 0 && existing.location != 0) {
0042     return location == existing.location;
0043   }
0044 
0045   const auto &existingFrame = *existing.st->begin();
0046   const auto &candidateFrame = *st.begin();
0047   return boost::stacktrace::hash_value(existingFrame) ==
0048          boost::stacktrace::hash_value(candidateFrame);
0049 }
0050 
0051 }  // namespace
0052 
0053 FpeMonitor::Result::FpeInfo::~FpeInfo() = default;
0054 
0055 FpeMonitor::Result::FpeInfo::FpeInfo(
0056     std::size_t countIn, FpeType typeIn,
0057     std::shared_ptr<const boost::stacktrace::stacktrace> stIn,
0058     std::uintptr_t locationIn)
0059     : count{countIn}, type{typeIn}, st{std::move(stIn)}, location{locationIn} {}
0060 
0061 FpeMonitor::Result FpeMonitor::Result::merged(const Result &with) const {
0062   Result result{};
0063 
0064   for (unsigned int i = 0; i < m_counts.size(); i++) {
0065     result.m_counts[i] = m_counts[i] + with.m_counts[i];
0066   }
0067 
0068   std::ranges::copy(with.m_stackTraces,
0069                     std::back_inserter(result.m_stackTraces));
0070   std::ranges::copy(m_stackTraces, std::back_inserter(result.m_stackTraces));
0071 
0072   result.deduplicate();
0073 
0074   return result;
0075 }
0076 
0077 void FpeMonitor::Result::merge(const Result &with) {
0078   for (unsigned int i = 0; i < m_counts.size(); i++) {
0079     m_counts[i] = m_counts[i] + with.m_counts[i];
0080   }
0081 
0082   std::ranges::copy(with.m_stackTraces, std::back_inserter(m_stackTraces));
0083 
0084   deduplicate();
0085 }
0086 
0087 void FpeMonitor::Result::add(FpeType type, void *stackPtr,
0088                              std::size_t bufferSize, std::uintptr_t location) {
0089   auto st = std::make_unique<boost::stacktrace::stacktrace>(
0090       boost::stacktrace::stacktrace::from_dump(stackPtr, bufferSize));
0091 
0092   for (auto &el : m_stackTraces) {
0093     if (canMergeFpeInfo(el, type, location, *st)) {
0094       el.count += 1;
0095       return;
0096     }
0097   }
0098 
0099   m_stackTraces.emplace_back(1, type, std::move(st), location);
0100 }
0101 
0102 bool FpeMonitor::Result::contains(const FpeInfo &info) const {
0103   return std::ranges::any_of(m_stackTraces, [&](const FpeInfo &el) {
0104     return canMergeFpeInfo(el, info.type, info.location, *info.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 (const auto &recorded : m_recorded) {
0119     m_result.add(recorded.type, recorded.stackPtr, recorded.bufferSize,
0120                  recorded.location);
0121   }
0122 
0123   m_buffer.reset();
0124   m_recorded.clear();
0125 }
0126 
0127 unsigned int FpeMonitor::Result::count(FpeType type) const {
0128   return m_counts.at(static_cast<std::uint32_t>(type));
0129 }
0130 
0131 unsigned int FpeMonitor::Result::numStackTraces() const {
0132   return m_stackTraces.size();
0133 }
0134 
0135 const std::vector<FpeMonitor::Result::FpeInfo> &
0136 FpeMonitor::Result::stackTraces() const {
0137   return m_stackTraces;
0138 }
0139 
0140 bool FpeMonitor::Result::encountered(FpeType type) const {
0141   return count(type) > 0;
0142 }
0143 
0144 void FpeMonitor::Result::summary(std::ostream &os, std::size_t depth) const {
0145   os << "FPE result summary:\n";
0146   static const std::vector<FpeType> types = {
0147       FpeType::INTDIV, FpeType::INTOVF, FpeType::FLTDIV, FpeType::FLTOVF,
0148       FpeType::FLTUND, FpeType::FLTRES, FpeType::FLTINV, FpeType::FLTSUB};
0149 
0150   for (auto type : types) {
0151     os << "- " << type << ": " << count(type) << "\n";
0152   }
0153 
0154   os << "\nStack traces:\n";
0155   for (const auto &info : stackTraces()) {
0156     os << "- " << info.type << ": (" << info.count << " times)\n";
0157 
0158     os << stackTraceToString(*info.st, depth);
0159   }
0160   os << std::endl;
0161 }
0162 
0163 void FpeMonitor::Result::deduplicate() {
0164   std::vector<FpeInfo> copy = std::move(m_stackTraces);
0165   m_stackTraces.clear();
0166   m_stackTraces.reserve(copy.size());
0167 
0168   for (const auto &info : copy) {
0169     const auto mergeTarget =
0170         std::ranges::find_if(m_stackTraces, [&](const FpeInfo &existing) {
0171           return canMergeFpeInfo(existing, info.type, info.location, *info.st);
0172         });
0173 
0174     if (mergeTarget != m_stackTraces.end()) {
0175       mergeTarget->count += info.count;
0176     } else {
0177       m_stackTraces.push_back(info);
0178     }
0179   }
0180 }
0181 
0182 FpeMonitor::FpeMonitor()
0183     : m_excepts{FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW} {
0184   enable();
0185 }
0186 
0187 FpeMonitor::FpeMonitor(int excepts) : m_excepts(excepts) {
0188   enable();
0189 }
0190 
0191 FpeMonitor::~FpeMonitor() {
0192   disable();
0193 }
0194 
0195 void FpeMonitor::signalHandler(int signal, siginfo_t *si, void *ctx) {
0196   if (stack().empty()) {
0197     return;
0198   }
0199 
0200   FpeMonitor &fpe = *stack().top();
0201   auto type = detail::decodeFpeType(signal, si, ctx);
0202   if (!type.has_value()) {
0203     if (detail::shouldFailFastOnUnknownSignal()) {
0204       // Must use _Exit: async-signal-safe. std::terminate is not (calls
0205       // terminate handler). std::abort raises SIGABRT from within a handler.
0206       std::_Exit(EXIT_FAILURE);
0207     }
0208     return;
0209   }
0210   fpe.m_result.m_counts.at(static_cast<std::uint32_t>(*type))++;
0211   std::uintptr_t location =
0212       si != nullptr ? reinterpret_cast<std::uintptr_t>(si->si_addr) : 0;
0213 
0214   try {
0215     auto [buffer, remaining] = fpe.m_buffer.next();
0216     using NativeFramePtr = boost::stacktrace::frame::native_frame_ptr_t;
0217     std::size_t stored =
0218         detail::captureStackFromSignalContext(ctx, buffer, remaining);
0219     if (stored == 0) {
0220       std::size_t depth = boost::stacktrace::safe_dump_to(
0221           detail::safeDumpSkipFrames(), buffer, remaining);
0222       stored = depth * sizeof(NativeFramePtr);
0223     }
0224     if (stored > 0) {
0225       fpe.m_buffer.pushOffset(stored);  // record how much storage was consumed
0226       fpe.m_recorded.emplace_back(
0227           *type, buffer, stored,
0228           location);  // record consumed stack dump and trap location
0229     }
0230 
0231   } catch (const std::bad_alloc &e) {
0232     std::cout << "Unable to collect stack trace due to memory limit"
0233               << std::endl;
0234   }
0235 
0236   detail::maskTrapsInSignalContext(ctx, *type);
0237 }
0238 
0239 void FpeMonitor::enable() {
0240   if (!detail::isRuntimeSupported()) {
0241     return;
0242   }
0243   ensureSignalHandlerInstalled();
0244 
0245   // clear pending exceptions so they don't immediately fire
0246   detail::clearPendingExceptions(m_excepts);
0247 
0248   if (!stack().empty()) {
0249     // unset previous except state
0250     detail::disableExceptions(stack().top()->m_excepts);
0251   }
0252   // apply this stack
0253   detail::enableExceptions(m_excepts);
0254 
0255   stack().push(this);
0256 }
0257 
0258 void FpeMonitor::rearm() {
0259   consumeRecorded();
0260   if (!detail::isRuntimeSupported()) {
0261     return;
0262   }
0263   detail::clearPendingExceptions(m_excepts);
0264   detail::enableExceptions(m_excepts);
0265 }
0266 
0267 void FpeMonitor::ensureSignalHandlerInstalled() {
0268   auto &state = globalState();
0269   if (state.isSignalHandlerInstalled) {
0270     return;
0271   }
0272 
0273   std::lock_guard lock{state.mutex};
0274   if (state.isSignalHandlerInstalled) {
0275     return;
0276   }
0277 
0278   detail::installSignalHandlers(&signalHandler);
0279 
0280   state.isSignalHandlerInstalled = true;
0281 }
0282 
0283 void FpeMonitor::disable() {
0284   if (!detail::isRuntimeSupported()) {
0285     return;
0286   }
0287   detail::clearPendingExceptions(m_excepts);
0288   assert(!stack().empty() && "FPE stack shouldn't be empty at this point");
0289   stack().pop();
0290   // disable excepts we enabled here
0291   detail::disableExceptions(m_excepts);
0292   if (!stack().empty()) {
0293     // restore excepts from next stack element
0294     detail::clearPendingExceptions(stack().top()->m_excepts);
0295     detail::enableExceptions(stack().top()->m_excepts);
0296   }
0297 }
0298 
0299 std::stack<FpeMonitor *> &FpeMonitor::stack() {
0300   static thread_local std::stack<FpeMonitor *> monitors;
0301   return monitors;
0302 }
0303 
0304 FpeMonitor::GlobalState &FpeMonitor::globalState() {
0305   static GlobalState state{};
0306   return state;
0307 }
0308 
0309 std::ostream &operator<<(std::ostream &os, FpeType type) {
0310 #define CASE(x)    \
0311   case FpeType::x: \
0312     os << #x;      \
0313     break;
0314 
0315   switch (type) {
0316     CASE(INTDIV)
0317     CASE(INTOVF)
0318     CASE(FLTDIV)
0319     CASE(FLTOVF)
0320     CASE(FLTUND)
0321     CASE(FLTRES)
0322     CASE(FLTINV)
0323     CASE(FLTSUB)
0324   }
0325 #undef CASE
0326 
0327   return os;
0328 }
0329 
0330 std::string FpeMonitor::stackTraceToString(
0331     const boost::stacktrace::stacktrace &st, std::size_t depth) {
0332   return boost::stacktrace::detail::to_string(st.as_vector().data(),
0333                                               std::min(depth, st.size()));
0334 }
0335 
0336 std::string FpeMonitor::getSourceLocation(
0337     const boost::stacktrace::frame &frame) {
0338   return frame.source_file() + ":" + std::to_string(frame.source_line());
0339 }
0340 
0341 bool FpeMonitor::canSymbolize() {
0342 #if defined(BOOST_STACKTRACE_USE_NOOP)
0343   return false;
0344 #else
0345   return true;
0346 #endif
0347 }
0348 
0349 bool FpeMonitor::isSupported() {
0350   return detail::isRuntimeSupported();
0351 }
0352 
0353 }  // namespace ActsPlugins