Add CInfiniteLoopDetector to detect infinite loops in release builds

This commit is contained in:
Phillip Stephens 2022-03-04 01:46:33 -08:00
parent 740114af9e
commit d93cf46bc3
Signed by: Antidote
GPG Key ID: F8BEE4C83DACA60D
5 changed files with 83 additions and 3 deletions

View File

@ -0,0 +1,37 @@
#include "Runtime/CInfiniteLoopDetector.hpp"
#include <logvisor/logvisor.hpp>
namespace metaforce {
namespace {
logvisor::Module Log("CInfiniteLoopDetector");
std::chrono::system_clock::time_point g_WatchDog = std::chrono::system_clock::now();
std::mutex g_mutex;
} // namespace
CInfiniteLoopDetector::CInfiniteLoopDetector(int duration)
: m_duration(duration), m_futureObj(m_stopRequested.get_future()) {}
bool CInfiniteLoopDetector::stopRequested() const {
return m_futureObj.wait_for(std::chrono::milliseconds(0)) != std::future_status::timeout;
}
void CInfiniteLoopDetector::run() {
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
while (!stopRequested()) {
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start) >
std::chrono::milliseconds(m_duration)) {
std::lock_guard<std::mutex> guard(g_mutex);
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - g_WatchDog) >
std::chrono::milliseconds(m_duration)) {
Log.report(logvisor::Fatal, FMT_STRING("INFINITE LOOP DETECTED!"));
}
}
}
}
void CInfiniteLoopDetector::UpdateWatchDog(std::chrono::system_clock::time_point time) {
std::lock_guard<std::mutex> guard(g_mutex);
g_WatchDog = time;
}
void CInfiniteLoopDetector::stop() { m_stopRequested.set_value(); }
} // namespace metaforce

View File

@ -0,0 +1,20 @@
#pragma once
#include <chrono>
#include <mutex>
#include <future>
namespace metaforce {
class CInfiniteLoopDetector {
int m_duration = 0;
std::mutex m_mutex;
std::promise<void> m_stopRequested;
std::future<void> m_futureObj;
bool stopRequested() const;
public:
explicit CInfiniteLoopDetector(int duration=1000);
void run();
void stop();
static void UpdateWatchDog(std::chrono::system_clock::time_point WatchDog);
};
}

View File

@ -2,6 +2,7 @@
#include <string_view>
#include <numeric>
#include <iostream>
#include "Runtime/CInfiniteLoopDetector.hpp"
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
@ -308,6 +309,10 @@ public:
}
bool onAppIdle(float realDt) noexcept override {
#ifdef NDEBUG
/* Ping the watchdog to let it know we're still alive */
CInfiniteLoopDetector::UpdateWatchDog(std::chrono::system_clock::now());
#endif
if (auto* input = g_InputGenerator) {
if (!m_deferredControllers.empty()) {
for (const auto which : m_deferredControllers) {
@ -605,7 +610,18 @@ int main(int argc, char** argv) {
.width = icon.width,
.height = icon.height,
};
#ifdef NDEBUG
/* Before we start running the app, lets get a thread going to detect any infinite loops */
metaforce::CInfiniteLoopDetector infiniteLoopDetector;
std::thread infiniteLoopDetectorThread(&metaforce::CInfiniteLoopDetector::run, &infiniteLoopDetector);
infiniteLoopDetectorThread.detach();
aurora::app_run(std::move(app), std::move(data), argc, argv);
infiniteLoopDetector.stop();
#else
/* Debuggers can interrupt the loop detector and make it unable to perform its job correctly, so lets not detect
* infinite loops in a debug build */
aurora::app_run(std::move(app), std::move(data), argc, argv);
#endif
return 0;
}
#endif

View File

@ -61,6 +61,7 @@ set(RUNTIME_SOURCES_B
${PARTICLE_SOURCES}
${WORLD_SOURCES}
${WEAPON_SOURCES}
CInfiniteLoopDetector.hpp CInfiniteLoopDetector.cpp
ConsoleVariables/FileStoreManager.hpp ConsoleVariables/FileStoreManager.cpp
ConsoleVariables/CVar.hpp ConsoleVariables/CVar.cpp
ConsoleVariables/CVarManager.hpp ConsoleVariables/CVarManager.cpp

View File

@ -138,9 +138,15 @@ static bool poll_events() noexcept {
break;
}
case SDL_CONTROLLERAXISMOTION: {
g_AppDelegate->onControllerAxis(
event.caxis.which, input::translate_controller_axis(static_cast<SDL_GameControllerAxis>(event.caxis.axis)),
event.caxis.value);
if (event.caxis.value > 8000 || event.caxis.value < -8000) {
g_AppDelegate->onControllerAxis(
event.caxis.which, input::translate_controller_axis(static_cast<SDL_GameControllerAxis>(event.caxis.axis)),
event.caxis.value);
} else {
g_AppDelegate->onControllerAxis(
event.caxis.which, input::translate_controller_axis(static_cast<SDL_GameControllerAxis>(event.caxis.axis)),
0);
}
break;
}
case SDL_KEYDOWN: {