File indexing completed on 2025-01-30 09:15:13
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_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 , 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
0202
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);
0208 fpe.m_recorded.emplace_back(
0209 static_cast<FpeType>(si->si_code), buffer,
0210 remaining);
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
0227 *mxcsr |= ((*mxcsr & SSE_STATUS_FLAGS) << 7);
0228 *mxcsr &= ~SSE_STATUS_FLAGS;
0229 #else
0230 (void)ctx;
0231 #endif
0232 }
0233
0234 void FpeMonitor::enable() {
0235 #if defined(__linux__) && defined(__x86_64__)
0236 ensureSignalHandlerInstalled();
0237
0238
0239 std::feclearexcept(m_excepts);
0240
0241 if (!stack().empty()) {
0242
0243 fedisableexcept(stack().top()->m_excepts);
0244 }
0245
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
0284 fedisableexcept(m_excepts);
0285 if (!stack().empty()) {
0286
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 }