#include <iostream>
#include <mutex>
#include <thread>
#include "Lib/Environment.hpp"
#include "Shell/Statistics.hpp"
#include "Shell/UIHelper.hpp"
#include "System.hpp"
#include "Timer.hpp"
using namespace std::chrono_literals;
const auto TICK_INTERVAL = 1ms;
#if VAMPIRE_PERF_EXISTS
#include <asm/unistd.h>
#include <linux/perf_event.h>
#include <sys/ioctl.h>
#include <unistd.h>
const long long MEGA = 1 << 20;
static int PERF_FD = -1; static long long LAST_INSTRUCTION_COUNT_READ = -1;
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags)
{
int ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,group_fd, flags);
return ret;
}
#endif
enum LimitType {
TIME_LIMIT,
INSTRUCTION_LIMIT
};
static std::recursive_mutex EXIT_LOCK;
static bool LIBRARY_MODE = false;
[[noreturn]] static void limitReached(LimitType whichLimit)
{
using namespace Shell;
const char* REACHED[2] = {"Time limit reached! \n","Instruction limit reached! \n"}; const char* STATUS[2] = {"% SZS status Timeout for ","% SZS status InstrOut for "};
Shell::TerminationReason REASON[2] = {
Shell::TerminationReason::TIME_LIMIT,
Shell::TerminationReason::INSTRUCTION_LIMIT
};
EXIT_LOCK.lock();
env.statistics->terminationReason = REASON[whichLimit];
reportSpiderStatus('t');
if (outputAllowed()) {
addCommentSignForSZS(std::cout);
std::cout << REACHED[whichLimit];
if (UIHelper::portfolioParent) { addCommentSignForSZS(std::cout);
std::cout << "Proof not found in time ";
Timer::printMSString(std::cout,Timer::elapsedMilliseconds());
#if VAMPIRE_PERF_EXISTS
if (LAST_INSTRUCTION_COUNT_READ > -1) {
std::cout << " nor after " << LAST_INSTRUCTION_COUNT_READ << " (user) instruction executed.";
}
#endif
std::cout << std::endl;
if (szsOutputMode()) {
std::cout << STATUS[whichLimit] << (env.options ? env.options->problemName().c_str() : "unknown") << std::endl;
}
} else if (env.statistics) {
env.statistics->print(std::cout);
}
}
System::terminateImmediately(1);
}
static std::chrono::time_point<std::chrono::steady_clock> START_TIME;
[[noreturn]] void timer_thread()
{
while(true) {
unsigned limit = env.options->timeLimitInMilliseconds();
if(limit && Timer::elapsedMilliseconds() >= limit) {
if (LIBRARY_MODE) {
env.statistics->terminationReason = Shell::TerminationReason::TIME_LIMIT;
} else {
limitReached(TIME_LIMIT);
}
}
#if VAMPIRE_PERF_EXISTS
if(env.options->instructionLimit() || env.options->simulatedInstructionLimit()) {
Timer::updateInstructionCount();
if (env.options->instructionLimit() && LAST_INSTRUCTION_COUNT_READ >= MEGA*(long long)env.options->instructionLimit()) {
if (LIBRARY_MODE) {
env.statistics->terminationReason = Shell::TerminationReason::INSTRUCTION_LIMIT;
} else {
limitReached(INSTRUCTION_LIMIT);
}
}
}
#endif
std::this_thread::sleep_for(TICK_INTERVAL);
}
ASSERTION_VIOLATION
}
namespace Lib {
namespace Timer {
void reinitialise(bool tryInitInstructionLimiting) {
::new (&EXIT_LOCK) std::recursive_mutex;
LIBRARY_MODE = true;
START_TIME = std::chrono::steady_clock::now();
#if VAMPIRE_PERF_EXISTS
if (tryInitInstructionLimiting) {
LAST_INSTRUCTION_COUNT_READ = -1;
PERF_FD = -1;
struct perf_event_attr pe;
memset(&pe, 0, sizeof(struct perf_event_attr));
pe.type = PERF_TYPE_HARDWARE;
pe.size = sizeof(struct perf_event_attr);
pe.config = PERF_COUNT_HW_INSTRUCTIONS;
pe.disabled = 1;
pe.exclude_kernel = 1;
pe.exclude_hv = 1;
PERF_FD = perf_event_open(&pe, 0, -1, -1, 0);
if (PERF_FD == -1) {
if(env.options->instructionLimit() || env.options->simulatedInstructionLimit()) {
std::cerr
<< "perf_event_open failed (instruction limiting will be disabled): "
<< std::strerror(errno)
<< "\n(If you are seeing 'Permission denied' ask your admin to run 'sudo sysctl -w kernel.perf_event_paranoid=-1' for you.)"
<< std::endl;
}
} else {
ioctl(PERF_FD, PERF_EVENT_IOC_RESET, 0);
ioctl(PERF_FD, PERF_EVENT_IOC_ENABLE, 0);
}
}
#endif
std::thread(timer_thread).detach();
}
void disableLimitEnforcement() {
EXIT_LOCK.lock();
}
void resetLimitEnforcement() {
::new (&EXIT_LOCK) std::recursive_mutex;
}
void resetStartTime() {
START_TIME = std::chrono::steady_clock::now();
}
long elapsedMilliseconds() {
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - START_TIME
).count();
}
void printMSString(std::ostream &str, int ms)
{
if(ms<0) {
str << '-';
ms = -ms;
}
int sec=ms/1000;
int msonly=ms%1000;
if(sec) {
str<<sec;
}
else {
str<<'0';
}
str<<'.';
if(msonly<100) {
str<<'0';
if(msonly<10) {
str<<'0';
if(!msonly) {
str<<'0';
}
}
}
str<<msonly<<" s";
}
std::string msToSecondsString(int ms)
{
return Int::toString(static_cast<float>(ms)/1000)+" s";
}
bool instructionLimitingInPlace()
{
#if VAMPIRE_PERF_EXISTS
return (PERF_FD >= 0);
#else
return false;
#endif
}
long elapsedMegaInstructions() {
#if VAMPIRE_PERF_EXISTS
return (LAST_INSTRUCTION_COUNT_READ >= 0) ? LAST_INSTRUCTION_COUNT_READ / MEGA : 0;
#else
return 0;
#endif
}
void updateInstructionCount()
{
#if VAMPIRE_PERF_EXISTS
if (PERF_FD >= 0) {
read(PERF_FD, &LAST_INSTRUCTION_COUNT_READ, sizeof(long long));
}
#endif
}
} }