File indexing completed on 2025-08-05 08:10:17
0001
0002
0003
0004
0005
0006
0007
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 }
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 , 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
0206
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);
0212 fpe.m_recorded.emplace_back(
0213 static_cast<FpeType>(si->si_code), buffer,
0214 remaining);
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
0230 *mxcsr |= ((*mxcsr & SSE_STATUS_FLAGS) << 7);
0231 *mxcsr &= ~SSE_STATUS_FLAGS;
0232 #else
0233 (void)ctx;
0234 #endif
0235 }
0236
0237 void FpeMonitor::enable() {
0238 #if defined(__linux__) && defined(__x86_64__)
0239 ensureSignalHandlerInstalled();
0240
0241
0242 std::feclearexcept(m_excepts);
0243
0244 if (!stack().empty()) {
0245
0246 fedisableexcept(stack().top()->m_excepts);
0247 }
0248
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
0287 fedisableexcept(m_excepts);
0288 if (!stack().empty()) {
0289
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 }