24 #include <string_view>
27 #include <boost/stacktrace/frame.hpp>
28 #include <boost/stacktrace/safe_dump_to.hpp>
29 #include <boost/stacktrace/stacktrace.hpp>
30 #include <boost/stacktrace/stacktrace_fwd.hpp>
32 #define FPU_EXCEPTION_MASK 0x3f
33 #define FPU_STATUS_FLAGS 0xff
34 #define SSE_STATUS_FLAGS FPU_EXCEPTION_MASK
35 #define SSE_EXCEPTION_MASK (FPU_EXCEPTION_MASK << 7)
40 bool areFpesEquivalent(
41 std::pair<FpeType, const boost::stacktrace::stacktrace &>
lhs,
42 std::pair<FpeType, const boost::stacktrace::stacktrace &>
rhs) {
43 const auto &fl = *lhs.second.begin();
44 const auto &fr = *rhs.second.begin();
53 std::size_t countIn,
FpeType typeIn,
54 std::shared_ptr<const boost::stacktrace::stacktrace> stIn)
60 for (
unsigned int i = 0;
i <
m_counts.size();
i++) {
64 std::copy(with.m_stracktraces.begin(), with.m_stracktraces.end(),
75 for (
unsigned int i = 0;
i < m_counts.size();
i++) {
76 m_counts[
i] = m_counts[
i] + with.m_counts[
i];
79 std::copy(with.m_stracktraces.begin(), with.m_stracktraces.end(),
80 std::back_inserter(m_stracktraces));
86 std::size_t bufferSize) {
87 auto st = std::make_unique<boost::stacktrace::stacktrace>(
88 boost::stacktrace::stacktrace::from_dump(stackPtr, bufferSize));
90 auto it = std::find_if(
91 m_stracktraces.begin(), m_stracktraces.end(), [&](
const FpeInfo &el) {
92 return areFpesEquivalent({el.type, *el.st}, {
type, *st});
95 if (it != m_stracktraces.end()) {
103 FpeType type,
const boost::stacktrace::stacktrace &st)
const {
104 return std::find_if(m_stracktraces.begin(), m_stracktraces.end(),
105 [&](
const FpeInfo &el) {
106 return areFpesEquivalent({el.type, *el.st}, {
type, st});
107 }) != m_stracktraces.end();
120 for (
auto [type, stackPtr, remaining] :
m_recorded) {
129 return m_counts.at(static_cast<uint32_t>(type));
133 return m_stracktraces.size();
136 const std::vector<FpeMonitor::Result::FpeInfo>
138 return m_stracktraces;
142 return count(type) > 0;
146 os <<
"FPE result summary:\n";
147 static const std::vector<FpeType>
types = {
148 FpeType::INTDIV, FpeType::INTOVF, FpeType::FLTDIV, FpeType::FLTOVF,
149 FpeType::FLTUND, FpeType::FLTRES, FpeType::FLTINV, FpeType::FLTSUB};
151 for (
auto type : types) {
152 os <<
"- " << type <<
": " <<
count(type) <<
"\n";
155 os <<
"\nStack traces:\n";
156 for (
const auto &[
count, type, st] : stackTraces()) {
157 os <<
"- " << type <<
": (" <<
count <<
" times)\n";
165 std::vector<FpeInfo> copy{};
167 m_stracktraces.clear();
169 for (
auto &info : copy) {
170 auto it = std::find_if(
171 m_stracktraces.begin(), m_stracktraces.end(),
172 [&info](
const FpeInfo &el) {
173 return areFpesEquivalent({el.type, *el.st}, {info.type, *info.st});
175 if (it != m_stracktraces.end()) {
176 it->count += info.count;
179 m_stracktraces.push_back({info.count, info.type,
std::move(info.st)});
184 :
m_excepts{FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW} {
188 FpeMonitor::FpeMonitor(
int excepts) : m_excepts(excepts) {
192 FpeMonitor::~FpeMonitor() {
196 void FpeMonitor::signalHandler(
int , siginfo_t *si,
void *ctx) {
197 if (
stack().empty()) {
201 FpeMonitor &fpe = *
stack().top();
202 fpe.m_result.m_counts.at(si->si_code)++;
207 auto [
buffer, remaining] = fpe.m_buffer.next();
208 std::size_t depth = boost::stacktrace::safe_dump_to(2,
buffer, remaining);
210 depth *
sizeof(boost::stacktrace::frame::native_frame_ptr_t);
211 fpe.m_buffer.pushOffset(stored);
212 fpe.m_recorded.emplace_back(
213 static_cast<FpeType>(si->si_code),
buffer,
216 }
catch (
const std::bad_alloc &
e) {
217 std::cout <<
"Unable to collect stack trace due to memory limit"
221 #if defined(__linux__) && defined(__x86_64__)
222 __uint16_t *cw = &((ucontext_t *)ctx)->uc_mcontext.fpregs->cwd;
225 __uint16_t *sw = &((ucontext_t *)ctx)->uc_mcontext.fpregs->swd;
228 __uint32_t *mxcsr = &((ucontext_t *)ctx)->uc_mcontext.fpregs->mxcsr;
237 void FpeMonitor::enable() {
238 #if defined(__linux__) && defined(__x86_64__)
239 ensureSignalHandlerInstalled();
242 std::feclearexcept(m_excepts);
244 if (!
stack().empty()) {
246 fedisableexcept(
stack().top()->m_excepts);
249 feenableexcept(m_excepts);
259 #if defined(__linux__) && defined(__x86_64__)
260 std::feclearexcept(m_excepts);
261 feenableexcept(m_excepts);
265 void FpeMonitor::ensureSignalHandlerInstalled() {
266 auto &
state = globalState();
267 if (
state.isSignalHandlerInstalled) {
271 std::lock_guard lock{
state.mutex};
273 struct sigaction
action {};
274 action.sa_sigaction = &signalHandler;
275 action.sa_flags = SA_SIGINFO;
276 sigaction(SIGFPE, &
action,
nullptr);
278 state.isSignalHandlerInstalled =
true;
281 void FpeMonitor::disable() {
282 #if defined(__linux__) && defined(__x86_64__)
283 std::feclearexcept(m_excepts);
284 assert(!
stack().empty() &&
"FPE stack shouldn't be empty at this point");
287 fedisableexcept(m_excepts);
288 if (!
stack().empty()) {
290 std::feclearexcept(
stack().top()->m_excepts);
291 feenableexcept(
stack().top()->m_excepts);
296 std::stack<FpeMonitor *> &FpeMonitor::stack() {
297 static thread_local std::stack<FpeMonitor *> monitors;
301 FpeMonitor::GlobalState &FpeMonitor::globalState() {
302 static GlobalState
state{};
328 const boost::stacktrace::stacktrace &st, std::size_t depth) {
334 const boost::stacktrace::frame &frame) {
335 return frame.source_file() +
":" +
std::to_string(frame.source_line());