1112 lines
37 KiB
C++
1112 lines
37 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string>
|
|
#include <signal.h>
|
|
#ifdef _WIN32
|
|
#include <conio.h>
|
|
#else
|
|
#include <sys/types.h> // For umask
|
|
#include <sys/stat.h>
|
|
#include <locale.h>
|
|
#endif
|
|
#include <boost/thread.hpp>
|
|
#include <boost/timer/timer.hpp>
|
|
#include <regex>
|
|
#include <boost/container/flat_set.hpp>
|
|
|
|
#include "utils.h"
|
|
#include "cray_cpu.h"
|
|
#include "cray_iop.h"
|
|
#include "curses_win.h"
|
|
#include "parser.h"
|
|
#include "commands.h"
|
|
#include "server.hpp"
|
|
#include "cray_softcpu.h"
|
|
|
|
#define PARTIAL_DEBUG
|
|
|
|
#if defined(PARTIAL_DEBUG) && defined(_MSC_VER)
|
|
#pragma optimize ("", off)
|
|
#endif
|
|
|
|
#if 0
|
|
|
|
class SyslogBuf_c: public std::basic_streambuf<char, std::char_traits<char>>
|
|
{
|
|
public:
|
|
SyslogBuf_c(const std::string &aIdent, int aFacility, int aOption = LOG_PID);
|
|
SetLogLevel(int aLogLevel) { mLogLevel = aLogLevel; }
|
|
protected:
|
|
virtual int sync() overrid;
|
|
virtual int overflow(int aChar) override;
|
|
|
|
std::string mBuffer;
|
|
int mLogLevel;
|
|
};
|
|
|
|
|
|
SyslogBuf_c::SyslogBuf_c(const std::string &aIdent, int aFacility, int aOption):
|
|
mLogLevel(LOG_DEBUG)
|
|
{
|
|
openlog(mIdent.c_str(), aOption, aFacility);
|
|
}
|
|
|
|
int SyslogBuf_c::sync() {
|
|
if (mBuffer.length()) {
|
|
syslog(mLogLevel, mBuffer.c_str());
|
|
mBuffer.erase();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SyslogBuf_c::overflow(int aChar) {
|
|
mBuffer += char(aChar);
|
|
return aChar;
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
class daemon;
|
|
volatile AtomicBool Terminate(false);
|
|
|
|
void SigIntHandler(int aSignal) {
|
|
Terminate = true;
|
|
}
|
|
|
|
static std::ofstream LogFile;
|
|
|
|
void BusyThread() {
|
|
while (!Terminate);
|
|
}
|
|
|
|
void CpuThread(Mainframe_c &aMainframe, size_t aCpuIdx) {
|
|
try {
|
|
while (!Terminate) {
|
|
aMainframe.GetCpu(aCpuIdx).Tick();
|
|
if (aCpuIdx == 0) aMainframe.UpdateRealTimeClock();
|
|
}
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "CPU" << DecPrinter(aCpuIdx) << "THREAD: " << "Can't write memory dump" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "Can't write memory dump" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
Terminate = true;
|
|
}
|
|
}
|
|
|
|
void AllCpuThread(Mainframe_c &aMainframe) {
|
|
try {
|
|
while (!Terminate) {
|
|
for (size_t i = 0; i < aMainframe.GetCpuCnt(); ++i) {
|
|
aMainframe.GetCpu(i).Tick();
|
|
}
|
|
aMainframe.UpdateRealTimeClock();
|
|
}
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "CPUTHREAD: " << "Can't write memory dump" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "Can't write memory dump" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
Terminate = true;
|
|
}
|
|
}
|
|
|
|
void GetCpuStatus(StatusReport_c &aStatus, const Mainframe_c &aMainframe, boost::timer::nanosecond_type aElapsedTime, bool aLongFormat) {
|
|
size_t CpuCnt = aMainframe.GetCpuCnt();
|
|
for (size_t CpuIdx = 0; CpuIdx < CpuCnt; ++CpuIdx) {
|
|
StatusReport_c CpuStatus;
|
|
aMainframe.GetCpu(CpuIdx).GetStatus(CpuStatus, aElapsedTime, aLongFormat);
|
|
if (!CpuStatus.empty()) {
|
|
aStatus.put_child(aMainframe.GetCpu(CpuIdx).GetName(), CpuStatus);
|
|
}
|
|
}
|
|
size_t IopCnt = aMainframe.GetIopClusterCnt();
|
|
for (size_t IopIdx = 0; IopIdx < IopCnt; ++IopIdx) {
|
|
StatusReport_c IopStatus;
|
|
aMainframe.GetIopCluster(IopIdx).GetCpuStatus(IopStatus, aElapsedTime, aLongFormat);
|
|
if (!IopStatus.empty()) {
|
|
aStatus.put_child(aMainframe.GetIopCluster(IopIdx).GetLongName(), IopStatus);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void GetPeripheralStatus(StatusReport_c &aStatus, const Mainframe_c &aMainframe, PeripheralType_e aFilter, boost::timer::nanosecond_type aElapsedTime, bool aLongFormat) {
|
|
size_t IopCnt = aMainframe.GetIopClusterCnt();
|
|
for (size_t IopIdx = 0; IopIdx < IopCnt; ++IopIdx) {
|
|
StatusReport_c IopStatus;
|
|
aMainframe.GetIopCluster(IopIdx).GetPeripheralStatus(IopStatus, aFilter, aElapsedTime, aLongFormat);
|
|
if (!IopStatus.empty()) {
|
|
aStatus.put_child(aMainframe.GetIopCluster(IopIdx).GetLongName(), IopStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string StatusIdent(size_t aLevel) {
|
|
std::string RetVal;
|
|
for (size_t i = 0; i < aLevel; ++i) RetVal += " ";
|
|
return RetVal;
|
|
}
|
|
|
|
void DisplayStatus(const StatusReport_c &aStatus, std::ostream &aStrm, bool &aNeedEndl, size_t aLevel) {
|
|
// This is a sub-tree, recurse into it
|
|
for (const auto & Elem : aStatus) {
|
|
if (Elem.second.empty()) { // Leaf node
|
|
if (aLevel == 0) {
|
|
if (aNeedEndl) aStrm << std::endl;
|
|
if (Elem.first.length() != 0) aStrm << Elem.first << " - ";
|
|
aNeedEndl = true;
|
|
}
|
|
else {
|
|
if (Elem.first.length() != 0) aStrm << Elem.first << ": ";
|
|
if (Elem.second.data().length() != 0) aStrm << Elem.second.data() << " ";
|
|
aNeedEndl = true;
|
|
}
|
|
} else {
|
|
if (aNeedEndl) aStrm << std::endl;
|
|
aNeedEndl = true;
|
|
aStrm << StatusIdent(aLevel) << Elem.first << " - ";
|
|
DisplayStatus(Elem.second, aStrm, aNeedEndl, aLevel + 1);
|
|
if (aNeedEndl) aStrm << std::endl;
|
|
aNeedEndl = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DisplayStatus(const StatusReport_c &aStatus, std::ostream &aStrm) {
|
|
bool NeedEndl = false;
|
|
DisplayStatus(aStatus, aStrm, NeedEndl, 0);
|
|
}
|
|
|
|
class NoReportException_x : public std::exception {};
|
|
|
|
void GetLoggerNames(const LoggerBase_c &aLogger, std::string &aNames) {
|
|
for (auto &Logger : aLogger.GetChildren()) {
|
|
if (!aNames.empty()) aNames.append(" ");
|
|
aNames.append(Logger->GetHeader());
|
|
GetLoggerNames(*Logger, aNames);
|
|
}
|
|
}
|
|
|
|
std::string GetLoggerNames() {
|
|
std::string Names;
|
|
GetLoggerNames(RootLogger, Names);
|
|
return Names;
|
|
}
|
|
|
|
LoggerBase_c *GetLogger(const LoggerBase_c &aLogger, std::string &aName) {
|
|
for (auto &Logger : aLogger.GetChildren()) {
|
|
if (Logger->GetHeader() == aName) return Logger;
|
|
LoggerBase_c *RetVal = GetLogger(*Logger, aName);
|
|
if (RetVal != nullptr) return RetVal;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
LoggerBase_c *GetLogger(std::string &aName) {
|
|
return GetLogger(RootLogger, aName);
|
|
}
|
|
|
|
|
|
void ResizeWindows(
|
|
int aConsoleHeight,
|
|
Curses::Frame_c &aCpuStatusWindow,
|
|
Curses::Frame_c &aIoStatusWindow,
|
|
Curses::Frame_c &aCoutWindow,
|
|
Curses::Session_c &aCursesSession,
|
|
const Mainframe_c &aMainframe
|
|
) {
|
|
try {
|
|
aCursesSession.ResizeTerm();
|
|
}
|
|
catch (Generic_x &) {
|
|
}
|
|
|
|
int LeftWindowWidth = aCursesSession.GetMaxX() / 2;
|
|
int RightWindowWidth = aCursesSession.GetMaxX() - LeftWindowWidth;
|
|
if (LeftWindowWidth < 3 || RightWindowWidth < 3) return;
|
|
int PeripheralWindowHeight = aCursesSession.GetMaxY() - aConsoleHeight;
|
|
if (PeripheralWindowHeight < 3) {
|
|
PeripheralWindowHeight = -1;
|
|
aConsoleHeight = aCursesSession.GetMaxY();
|
|
if (aConsoleHeight < 3) {
|
|
return;
|
|
}
|
|
}
|
|
if (PeripheralWindowHeight < 0) {
|
|
aIoStatusWindow.Hide();
|
|
aCpuStatusWindow.Hide();
|
|
} else {
|
|
aIoStatusWindow.Show();
|
|
aIoStatusWindow.Resize(RightWindowWidth, PeripheralWindowHeight);
|
|
aIoStatusWindow.MoveWindow(LeftWindowWidth, 0);
|
|
aCpuStatusWindow.Show();
|
|
aCpuStatusWindow.Resize(LeftWindowWidth, PeripheralWindowHeight);
|
|
aCpuStatusWindow.MoveWindow(0, 0);
|
|
}
|
|
|
|
aCoutWindow.Resize(aCursesSession.GetMaxX(), aConsoleHeight);
|
|
aCoutWindow.MoveWindow(0, aCursesSession.GetMaxY() - aConsoleHeight);
|
|
|
|
Curses::Panel_c::Update();
|
|
}
|
|
|
|
void DumpMainframe(Mainframe_c &aMainframe) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "=====================================================================" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << "Mainframe state:" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << "=====================================================================" << std::endl;
|
|
aMainframe.Dump();
|
|
aMainframe.DumpHistory();
|
|
aMainframe.DumpMemories();
|
|
}
|
|
|
|
class StatusContentHandler_c : public http::server::request_handler {
|
|
public:
|
|
StatusContentHandler_c(const StatusContentHandler_c&) = delete;
|
|
StatusContentHandler_c& operator=(const StatusContentHandler_c&) = delete;
|
|
|
|
/// Construct with a directory containing files to be served.
|
|
explicit StatusContentHandler_c(const Configuration_c &aConfig, const std::string& doc_root) : request_handler(doc_root) {
|
|
for (auto Pattern : aConfig) {
|
|
ResourcePattern_s ResourcePattern;
|
|
ResourcePattern.Pattern = Pattern.first;
|
|
std::string StrStatus = Pattern.second.data();
|
|
if (StrStatus == "ok") {
|
|
ResourcePattern.Status = http::server::reply::ok;
|
|
} else if (StrStatus == "created") {
|
|
ResourcePattern.Status = http::server::reply::created;
|
|
} else if (StrStatus == "accepted") {
|
|
ResourcePattern.Status = http::server::reply::accepted;
|
|
} else if (StrStatus == "no_content") {
|
|
ResourcePattern.Status = http::server::reply::no_content;
|
|
} else if (StrStatus == "multiple_choices") {
|
|
ResourcePattern.Status = http::server::reply::multiple_choices;
|
|
} else if (StrStatus == "moved_permanently") {
|
|
ResourcePattern.Status = http::server::reply::moved_permanently;
|
|
} else if (StrStatus == "moved_temporarily") {
|
|
ResourcePattern.Status = http::server::reply::moved_temporarily;
|
|
} else if (StrStatus == "not_modified") {
|
|
ResourcePattern.Status = http::server::reply::not_modified;
|
|
} else if (StrStatus == "bad_request") {
|
|
ResourcePattern.Status = http::server::reply::bad_request;
|
|
} else if (StrStatus == "unauthorized") {
|
|
ResourcePattern.Status = http::server::reply::unauthorized;
|
|
} else if (StrStatus == "forbidden") {
|
|
ResourcePattern.Status = http::server::reply::forbidden;
|
|
} else if (StrStatus == "not_found") {
|
|
ResourcePattern.Status = http::server::reply::not_found;
|
|
} else if (StrStatus == "internal_server_error") {
|
|
ResourcePattern.Status = http::server::reply::internal_server_error;
|
|
} else if (StrStatus == "not_implemented") {
|
|
ResourcePattern.Status = http::server::reply::not_implemented;
|
|
} else if (StrStatus == "bad_gateway") {
|
|
ResourcePattern.Status = http::server::reply::bad_gateway;
|
|
} else if (StrStatus == "service_unavailable") {
|
|
ResourcePattern.Status = http::server::reply::service_unavailable;
|
|
} else {
|
|
throw Generic_x() << "Unknwon pattern status: " << StrStatus;
|
|
}
|
|
mResourcePatterns.push_back(ResourcePattern);
|
|
}
|
|
}
|
|
void UpdateStatus(const char *aStatusName, const StatusReport_c &aStatus) {
|
|
mStatus[aStatusName] = ConvertStatus(aStatus);
|
|
}
|
|
private:
|
|
virtual http::server::reply::status_type PostProcessRequest(const http::server::request& aRequest, const std::string &aRequestPath) override {
|
|
if (aRequestPath != "/index.html") std::cout << "Web server processing request: " << aRequestPath << std::endl;
|
|
for (auto &ResourcePattern : mResourcePatterns) {
|
|
if (std::regex_match(aRequestPath, ResourcePattern.Pattern)) return ResourcePattern.Status;
|
|
}
|
|
return http::server::reply::not_found;
|
|
}
|
|
virtual void PostProcessResponse(http::server::reply& aResponse) override {
|
|
if (aResponse.headers["Content-Type"] == "text/html") {
|
|
for (auto &Status : mStatus) {
|
|
std::string Marker = "{";
|
|
Marker.append(Status.first);
|
|
Marker.append("}");
|
|
aResponse.content = Replace(aResponse.content, Marker, Status.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ConvertStatus(const StatusReport_c &aStatus, std::ostream &aStrm, bool &aNeedEndl, size_t aLevel) {
|
|
// This is a sub-tree, recurse into it
|
|
aStrm << StatusIdent(aLevel) << "<ul>" << std::endl;
|
|
for (const auto & Elem : aStatus) {
|
|
if (Elem.second.empty()) { // Leaf node
|
|
if (aLevel == 0) {
|
|
if (aNeedEndl) aStrm << std::endl << StatusIdent(aLevel) << " <li>";
|
|
if (Elem.first.length() != 0) aStrm << Elem.first << " - ";
|
|
aNeedEndl = true;
|
|
}
|
|
else {
|
|
if (Elem.first.length() != 0) aStrm << Elem.first << ": ";
|
|
if (Elem.second.data().length() != 0) aStrm << Elem.second.data() << " ";
|
|
aNeedEndl = true;
|
|
}
|
|
}
|
|
else {
|
|
if (aNeedEndl) aStrm << std::endl;
|
|
aNeedEndl = true;
|
|
aStrm << StatusIdent(aLevel) << StatusIdent(aLevel) << " <li>" << Elem.first << " - ";
|
|
ConvertStatus(Elem.second, aStrm, aNeedEndl, aLevel + 1);
|
|
if (aNeedEndl) aStrm << std::endl;
|
|
aNeedEndl = false;
|
|
}
|
|
}
|
|
aStrm << StatusIdent(aLevel) << "</ul>" << std::endl;
|
|
}
|
|
|
|
std::string ConvertStatus(const StatusReport_c &aStatus) {
|
|
std::stringstream Strm;
|
|
bool NeedEndl = false;
|
|
if (aStatus.empty()) return "<none>";
|
|
ConvertStatus(aStatus, Strm, NeedEndl, 0);
|
|
return Strm.str();
|
|
}
|
|
|
|
std::map<std::string, std::string> mStatus;
|
|
|
|
struct ResourcePattern_s {
|
|
std::regex Pattern;
|
|
http::server::reply::status_type Status;
|
|
};
|
|
std::vector<ResourcePattern_s> mResourcePatterns;
|
|
};
|
|
|
|
class WebServerThread_c {
|
|
public:
|
|
WebServerThread_c(const Configuration_c &aConfig):
|
|
mHttpStatus(aConfig.get_child_safe("ResourcePatterns"), aConfig.get<std::string>("Docroot", "")),
|
|
mServer(mHttpStatus),
|
|
mValid(false)
|
|
{
|
|
if (aConfig.empty()) return;
|
|
|
|
mValid = true;
|
|
mServerPort = aConfig.get<std::string>("Port");
|
|
mServerAddress = aConfig.get<std::string>("Address");
|
|
|
|
mServer.setup(mServerAddress, mServerPort);
|
|
}
|
|
~WebServerThread_c() { Stop(); }
|
|
void Start() {
|
|
if (!mValid) return;
|
|
mWebServerThread = std::make_unique<std::thread>(std::bind(&http::server::server::run, &mServer));
|
|
// std::cout << "Web server started with ID: " << mWebServerThread->get_id() << " - " << (mWebServerThread->joinable() ? "joinable" : "NOT JOINABLE") << std::endl;
|
|
}
|
|
void Stop() {
|
|
if (!mValid) return;
|
|
mServer.stop();
|
|
if (mWebServerThread != nullptr) {
|
|
if (mWebServerThread->joinable()) {
|
|
mWebServerThread->join();
|
|
}
|
|
}
|
|
mWebServerThread = nullptr;
|
|
}
|
|
bool IsValid() const { return mValid; }
|
|
void UpdateStatus(const char *aStatusName, const StatusReport_c &aStatus) {
|
|
mHttpStatus.UpdateStatus(aStatusName, aStatus);
|
|
}
|
|
protected:
|
|
std::string mServerPort;
|
|
std::string mServerAddress;
|
|
|
|
StatusContentHandler_c mHttpStatus; // Member order is important: server need mHttpStatus constructed
|
|
http::server::server mServer;
|
|
std::unique_ptr<std::thread> mWebServerThread;
|
|
bool mValid;
|
|
};
|
|
|
|
int PrintUsage(const char *aExecName, const char *ErrorStr = nullptr) {
|
|
if (ErrorStr != nullptr) std::cout << "Error: " << ErrorStr << std::endl;
|
|
std::cout << "Usage: " << aExecName << " [options] <config file name>" << std::endl;
|
|
std::cout << std::endl;
|
|
std::cout << "\t" << "Options:" << std::endl;
|
|
#if !defined(_WIN32)
|
|
std::cout << "\t" << "-d: Start the simulator in daemon mode" << std::endl;
|
|
std::cout << "\t" << "-l <log file>: Redirect stdout to log file in daemon mode" << std::endl;
|
|
#endif
|
|
std::cout << "\t" << "-a: Disables auto-start of terminal applications" << std::endl;
|
|
std::cout << "\t" << "-h: Show this help screen" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
void MulTest() {
|
|
CFloat_t S5, S2, S3;
|
|
S3.Value = 0x0000000005000000ull;
|
|
S2.Value = 0x0000000037000000ull;
|
|
S5 = S2 * S3;
|
|
std::cout << "S5: " << HexPrinter(S5) << std::endl;
|
|
S5 = S3 * S2;
|
|
std::cout << "S5: " << HexPrinter(S5) << std::endl;
|
|
}
|
|
|
|
void RecipTest() {
|
|
CFloat_t S1, S6, S3;
|
|
S1.Value = 0x4001800000000000ull;
|
|
S6.Value = 0x4000ffffffff8000ull;
|
|
S3 = ReciprocIterate(S1, S6);
|
|
std::cout << "S3: " << HexPrinter(S3) << std::endl;
|
|
// SW: 0x4001800000004000
|
|
// RTL: 0x4000000000008000
|
|
}
|
|
|
|
int main(int argc, const char* argv[])
|
|
{
|
|
// MulTest();
|
|
// RecipTest();
|
|
// return 1;
|
|
|
|
Configuration_c Config;
|
|
int RetVal = 0;
|
|
|
|
bool DaemonMode = false;
|
|
bool DisableAutoTerminal = false;
|
|
bool WaitOnTermination = true;
|
|
std::string ConfigFileName;
|
|
std::string StdoutFileName;
|
|
std::ofstream StdoutFile;
|
|
|
|
CommandLine_c CommandLine(argc, argv);
|
|
try {
|
|
while (CommandLine.HasMoreParams()) {
|
|
std::string CurParam = CommandLine.GetNextParam();
|
|
if (CurParam.length() == 0) continue;
|
|
#if !defined(_WIN32)
|
|
if (CurParam == "-d") {
|
|
DaemonMode = true;
|
|
} else if (CurParam == "-l") {
|
|
if (!StdoutFileName.empty()) throw Generic_x() << "-l can only be specified once";
|
|
StdoutFileName = CommandLine.GetNextParam();
|
|
} else
|
|
#endif
|
|
if (CurParam == "-a") {
|
|
DisableAutoTerminal = true;
|
|
}
|
|
else if (CurParam == "-h") {
|
|
return PrintUsage(argv[0]);
|
|
}
|
|
else {
|
|
if (ConfigFileName.empty()) ConfigFileName = CurParam; else throw Generic_x("Unkown command line parameter");
|
|
}
|
|
}
|
|
if (ConfigFileName.empty()) throw Generic_x() << "Config file name must be specified";
|
|
}
|
|
catch(std::exception &Ex) {
|
|
return PrintUsage(argv[0], Ex.what());
|
|
}
|
|
|
|
if (!DaemonMode) signal(SIGINT, SigIntHandler);
|
|
|
|
auto OldCoutBuf = std::cout.rdbuf();
|
|
#if !defined(_WIN32)
|
|
setlocale(LC_ALL, "");
|
|
try {
|
|
if (DaemonMode) {
|
|
umask(0);
|
|
if (!StdoutFileName.empty()) {
|
|
StdoutFile.open(StdoutFileName.c_str(), std::ios_base::out | std::ios_base::app);
|
|
if (StdoutFile.bad()) throw Generic_x() << "Can't open daemon log file: " << StdoutFileName;
|
|
}
|
|
std::cout.rdbuf(StdoutFile.rdbuf()); // Redirect stdout to file
|
|
// TODO: we might want another command-line flag to fork into another process. For now, however the scripts assume the deamon doesn't do it
|
|
//daemon(TRUE, FALSE); // Don't CD to root, but close all standard streams
|
|
}
|
|
}
|
|
catch(std::exception &Ex) {
|
|
std::cout << Ex.what();
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
std::unique_ptr<Curses::Session_c> CursesSession;
|
|
if (!DaemonMode) CursesSession = std::make_unique<Curses::Session_c>();
|
|
|
|
try {
|
|
// Set up a status windows for the mainframe
|
|
std::unique_ptr<Curses::Frame_c> CpuStatusWindow;
|
|
std::unique_ptr<Curses::Frame_c> IoStatusWindow;
|
|
std::unique_ptr<Curses::Frame_c> CoutWindow;
|
|
if (!DaemonMode) {
|
|
CpuStatusWindow = std::make_unique<Curses::Frame_c>("Processors", *CursesSession, 0, 0);
|
|
IoStatusWindow = std::make_unique<Curses::Frame_c>("Peripherals", *CursesSession, 0, 0);
|
|
CpuStatusWindow->Hide();
|
|
IoStatusWindow->Hide();
|
|
|
|
// Set up console window
|
|
CoutWindow = std::make_unique<Curses::Frame_c>("Console", *CursesSession, 0, 0);
|
|
|
|
CoutWindow->ScrollOk(true);
|
|
OldCoutBuf = std::cout.rdbuf(CoutWindow->rdbuf()); // Redirect cout to be inside the window
|
|
|
|
// std::cout << "!!!!!!!" << std::endl;
|
|
Curses::Panel_c::Update();
|
|
|
|
}
|
|
|
|
RootLogger.SetStream(std::cout);
|
|
|
|
try {
|
|
Config.Read(ConfigFileName.c_str());
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger.ReadConfig(Config);
|
|
std::cout << "Can't read configuration file" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
std::cout << "Press any key to terminate application" << std::endl;
|
|
while (getch() == ERR);
|
|
RetVal = 1;
|
|
throw NoReportException_x();
|
|
}
|
|
|
|
bool MultiThreaded = false;
|
|
bool SingleThreadCpus = false;
|
|
|
|
if (!DaemonMode) {
|
|
#if !defined(__CYGWIN__) && defined(_WIN32)
|
|
// Console resizing is not supported on Linux or under Cygwin.
|
|
try {
|
|
boost::optional<size_t> Width = Config.get_optional<size_t>("WindowWidth");
|
|
boost::optional<size_t> Height = Config.get_optional<size_t>("WindowHeight");
|
|
// Attempt to resize terminal to fit status display
|
|
if (Width.is_initialized() && Height.is_initialized()) {
|
|
CursesSession->ResizeTerm(Width.get(), Height.get());
|
|
}
|
|
}
|
|
catch (std::exception &Ex) {
|
|
std::cout << "Can't resize console: " << Ex.what() << std::endl;
|
|
}
|
|
#endif
|
|
try {
|
|
CpuStatusWindow->Resize(CursesSession->GetMaxX(), CursesSession->GetMaxY());
|
|
CpuStatusWindow->MoveWindow(0, 0);
|
|
IoStatusWindow->Resize(CursesSession->GetMaxX(), CursesSession->GetMaxY());
|
|
IoStatusWindow->MoveWindow(0, 0);
|
|
CoutWindow->Resize(CursesSession->GetMaxX(), CursesSession->GetMaxY());
|
|
CoutWindow->MoveWindow(0, 0);
|
|
|
|
Curses::Panel_c::Update();
|
|
}
|
|
catch (std::exception &Ex) {
|
|
if (OldCoutBuf != nullptr) std::cout.rdbuf(OldCoutBuf);
|
|
OldCoutBuf = nullptr;
|
|
std::cout << "Can't setup windows with exception: " << Ex.what() << std::endl;
|
|
RetVal = 1;
|
|
throw NoReportException_x();
|
|
}
|
|
}
|
|
|
|
size_t CpuThreadStepping;
|
|
bool UseThreadAffinity;
|
|
try {
|
|
// Set up log file, if specified
|
|
boost::optional<std::string> LogFileName = Config.get_optional<std::string>("LogFileName");
|
|
if (LogFileName.is_initialized()) {
|
|
LogFile.open(LogFileName.get().c_str(), std::ios::out | std::ios::trunc);
|
|
RootLogger.SetStream(LogFile);
|
|
}
|
|
// Read other global parameters
|
|
if (MultiThreadedSupport) {
|
|
MultiThreaded = Config.get<bool>("MultiThreaded", false);
|
|
SingleThreadCpus = Config.get<bool>("SingleThreadCpus", false);
|
|
CpuThreadStepping = Config.get<size_t>("CpuThreadStepping", 1);
|
|
UseThreadAffinity = Config.get<bool>("UseThreadAffinity", false);
|
|
}
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "Can't set up initial log state" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "Can't set up initial log state" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
std::cout << "Press any key to terminate application" << std::endl;
|
|
while (getch() == ERR);
|
|
RetVal = 1;
|
|
throw NoReportException_x();
|
|
}
|
|
|
|
try {
|
|
WaitOnTermination = Config.get<bool>("WaitOnTermination", true);
|
|
Mainframe_c Mainframe(Config, RootLogger, MultiThreaded, DaemonMode || DisableAutoTerminal);
|
|
WebServerThread_c WebServerThread(Config.get_child_safe("WebServer"));
|
|
if (WebServerThread.IsValid()) {
|
|
WebServerThread.Start();
|
|
}
|
|
|
|
boost::optional<int64_t> TerminateTime = Config.get_optional<int64_t>("TerminateTime");
|
|
if (TerminateTime.is_initialized()) TerminateTime.get() = TerminateTime.get() * 1000 * 1000 * 1000;
|
|
|
|
int IntendedConsoleHeight = Config.get<int>("ConsoleHeight", 10);
|
|
|
|
if (!DaemonMode) {
|
|
CpuStatusWindow->Show();
|
|
IoStatusWindow->Show();
|
|
ResizeWindows(
|
|
IntendedConsoleHeight,
|
|
*CpuStatusWindow,
|
|
*IoStatusWindow,
|
|
*CoutWindow,
|
|
*CursesSession,
|
|
Mainframe
|
|
);
|
|
}
|
|
|
|
RootLogger.SetTimeStampGenerator(&Mainframe);
|
|
RootLogger.NotifyChildren();
|
|
Mainframe.DeadStart();
|
|
std::vector<std::thread> CpuThreadHandles(Mainframe.GetCpuCnt());
|
|
std::vector<std::thread> BusyThreadHandles;
|
|
if (MultiThreaded) {
|
|
// Affinitize the main thread to core 0
|
|
if (UseThreadAffinity) {
|
|
SetThreadAffinity(0);
|
|
}
|
|
// Create CPU threads and affinitize them to cores in stepping
|
|
if (SingleThreadCpus) {
|
|
CpuThreadHandles[0] = std::thread(std::bind(AllCpuThread, std::ref(Mainframe)));
|
|
if (UseThreadAffinity) {
|
|
SetThreadAffinity(CpuThreadHandles[0], CpuThreadStepping);
|
|
}
|
|
} else {
|
|
size_t CoreIdx = CpuThreadStepping;
|
|
for (size_t i = 0; i < Mainframe.GetCpuCnt(); ++i) {
|
|
CpuThreadHandles[i] = std::thread(std::bind(CpuThread, std::ref(Mainframe), i));
|
|
if (UseThreadAffinity) {
|
|
SetThreadAffinity(CpuThreadHandles[i], CoreIdx);
|
|
CoreIdx += CpuThreadStepping;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Register all UI handlers
|
|
CommandHooks_t CommandHooks;
|
|
Mainframe.RegisterCommands(CommandHooks);
|
|
for (auto &IopCluster : Mainframe.GetIopClusters()) {
|
|
IopCluster.RegisterCommands(CommandHooks);
|
|
}
|
|
// TODO: check for duplicate triggers...
|
|
/* SmallWnd.MoveWindow(5, 5);
|
|
Curses::Panel_c::Update();*/
|
|
boost::timer::cpu_timer Timer;
|
|
Timer.start();
|
|
boost::timer::cpu_times LastTick = Timer.elapsed();
|
|
boost::timer::cpu_times StartTick = LastTick;
|
|
size_t KeyCheck = 0;
|
|
|
|
try {
|
|
std::vector<std::string> InputHistory;
|
|
size_t HistoryIdx = 0;
|
|
std::string InputLine;
|
|
size_t InputPos = 0;
|
|
bool ProcessLine = false;
|
|
//bool OverrideMode = false;
|
|
if (!DaemonMode) CoutWindow->Timeout(0);
|
|
size_t TimerTestCnt = 0;
|
|
while (!Terminate) {
|
|
for (auto &IopCluster : Mainframe.GetIopClusters()) { IopCluster.Tick(); }
|
|
if (!MultiThreaded) {
|
|
for (auto &Cpu : Mainframe.GetCpus()) { Cpu.Tick(); }
|
|
Mainframe.UpdateRealTimeClock();
|
|
}
|
|
Mainframe.ChannelTick();
|
|
|
|
// if (WebServerThread.IsValid()) WebServer.poll();
|
|
|
|
if (KeyCheck == 0 && !DaemonMode) {
|
|
int Key = CoutWindow->GetCh();
|
|
if (Key != ERR) {
|
|
switch (Key) {
|
|
case 3: // CTRL-C
|
|
Terminate = true;
|
|
break;
|
|
case KEY_RESIZE:
|
|
ResizeWindows(
|
|
IntendedConsoleHeight,
|
|
*CpuStatusWindow,
|
|
*IoStatusWindow,
|
|
*CoutWindow,
|
|
*CursesSession,
|
|
Mainframe
|
|
);
|
|
break;
|
|
case KEY_UP:
|
|
if (HistoryIdx > 0) {
|
|
--HistoryIdx;
|
|
InputLine = InputHistory[HistoryIdx];
|
|
InputPos = InputLine.length();
|
|
} else {
|
|
beep();
|
|
}
|
|
break;
|
|
case KEY_DOWN:
|
|
if (HistoryIdx + 1 < InputHistory.size()) {
|
|
++HistoryIdx;
|
|
InputLine = InputHistory[HistoryIdx];
|
|
InputPos = InputLine.length();
|
|
}
|
|
else if (HistoryIdx + 1 == InputHistory.size()) {
|
|
++HistoryIdx;
|
|
InputLine = "";
|
|
InputPos = 0;
|
|
} else {
|
|
beep();
|
|
}
|
|
break;
|
|
case KEY_LEFT:
|
|
if (InputPos != 0) --InputPos; else beep();
|
|
break;
|
|
case KEY_RIGHT:
|
|
if (InputPos < InputLine.length()) ++InputPos; else beep();
|
|
break;
|
|
case KEY_HOME:
|
|
InputPos = 0;
|
|
break;
|
|
case KEY_END:
|
|
InputPos = InputLine.length();
|
|
break;
|
|
case 2: // Ctrl-b
|
|
// BusyThreadHandles.push_back(std::thread(BusyThread));
|
|
Mainframe.RouteChannelInterrupt();
|
|
break;
|
|
case 0x7f:
|
|
case KEY_BACKSPACE:
|
|
case '\b':
|
|
if (InputPos != 0) {
|
|
--InputPos;
|
|
InputLine.erase(InputPos,1);
|
|
}
|
|
else {
|
|
beep();
|
|
}
|
|
break;
|
|
case '\n':
|
|
case '\r':
|
|
ProcessLine = true;
|
|
CoutWindow->Move(int(InputLine.length()), CoutWindow->GetCurY());
|
|
std::cout << std::endl;
|
|
InputPos = 0;
|
|
break;
|
|
|
|
case '\t': // TAB: repeat buffer
|
|
std::cout << std::endl << InputLine << std::flush;
|
|
break;
|
|
default:
|
|
if (Key >= ' ' && Key < 0x7f) {
|
|
if (int(InputPos) < CoutWindow->GetMaxX() - 3) {
|
|
InputLine.insert(InputPos, 1, Key);
|
|
InputPos++;
|
|
}
|
|
else {
|
|
beep();
|
|
}
|
|
} else {
|
|
// std::cout << "Char: " << HexPrinter(Key) << std::endl;
|
|
}
|
|
break;
|
|
}
|
|
// Print the input line over the current line
|
|
CoutWindow->Move(0, CoutWindow->GetCurY());
|
|
CoutWindow->AddStr(InputLine);
|
|
CoutWindow->ClearToEol();
|
|
CoutWindow->Move(int(InputPos), CoutWindow->GetCurY());
|
|
|
|
//std::cout << "Key pressed: " << Key << std::endl;
|
|
/*for (auto &UIHook : UIHooks) {
|
|
if (UIHook.TriggerKey == Key) {
|
|
UIHook.Callback();
|
|
}
|
|
}*/
|
|
} else {
|
|
KeyCheck = 500;
|
|
}
|
|
} else {
|
|
--KeyCheck;
|
|
}
|
|
if (ProcessLine) {
|
|
try {
|
|
TokenStream_t Tokens = Tokenize(InputLine);
|
|
TokenStream_t::const_iterator Token = Tokens.cbegin();
|
|
TokenStream_t::const_iterator TokenEnd = Tokens.cend();
|
|
// Test for help with no parameters
|
|
if (Tokens.size() > 1) {
|
|
if (Token->mValue == "help" && Tokens.size() == 2) {
|
|
std::cout << "Available system commands: exit setloglevel consoleheight dump" << std::endl;
|
|
std::cout << "Available device commands: ";
|
|
boost::container::flat_set<std::string> Commands;
|
|
for (const auto &Command : CommandHooks) {
|
|
Commands.insert(Command->GetCommandName());
|
|
}
|
|
for (const auto &Command : Commands) {
|
|
std::cout << Command << " ";
|
|
}
|
|
std::cout << std::endl;
|
|
std::cout << "Each device command needs a device name. Available device names: ";
|
|
boost::container::flat_set<std::string> Devices;
|
|
for (const auto &Command : CommandHooks) {
|
|
Devices.insert(Command->GetDeviceName());
|
|
}
|
|
for (const auto &Device : Devices) {
|
|
std::cout << Device << " ";
|
|
}
|
|
std::cout << std::endl;
|
|
std::cout << "Type 'help <command> <device>' for more detailed help on individual device commands or 'help <command>' for help on system commands " << std::endl;
|
|
}
|
|
else if (Token->mValue == "help" && Tokens.size() == 3) {
|
|
Token++;
|
|
if (Token->mValue == "exit") {
|
|
std::cout << "exit" << std::endl << " terminates the simulation" << std::endl;
|
|
}
|
|
else if (Token->mValue == "setloglevel") {
|
|
std::cout << "setloglevel <logger> <log level>" << std::endl << " Sets the log level on the specified logger" << std::endl;
|
|
std::string Loggers = GetLoggerNames();
|
|
std::cout << " Available loggers: " << Loggers << std::endl;
|
|
std::cout << " Available log levels: " << GetLogLevels() << std::endl;
|
|
}
|
|
else if (Token->mValue == "consoleheight") {
|
|
std::cout << "consoleheight <height>" << std::endl << " Sets the console height. Possible values are 'normal' and 'tall' or an integer number" << std::endl;
|
|
}
|
|
else if (Token->mValue == "dump") {
|
|
std::cout << "dump" << std::endl << " Dumps current state of memories and registers into files" << std::endl;
|
|
}
|
|
else {
|
|
std::cout << "Unkown command: no help is available" << std::endl;
|
|
}
|
|
}
|
|
else if (Token->mValue == "exit" && Tokens.size() == 2) {
|
|
Terminate = true;
|
|
}
|
|
else if (Token->mValue == "setloglevel" && Tokens.size() == 4) {
|
|
do {
|
|
Token++;
|
|
std::string LoggerName = Token->mValue;
|
|
Token++;
|
|
std::string LogLevelStr = Token->mValue;
|
|
LoggerBase_c *Logger = GetLogger(LoggerName);
|
|
if (Logger == nullptr) {
|
|
std::cout << "Unknown logger" << std::endl;
|
|
break;
|
|
}
|
|
try {
|
|
LogLevels_e LogLevel = FromString_LogLevels_e(LogLevelStr);
|
|
Logger->SetDisplayLogLevel(LogLevel);
|
|
std::cout << "Log level set" << std::endl;
|
|
}
|
|
catch (std::exception &Ex) {
|
|
std::cout << "Can't set log level due to: " << Ex.what() << std::endl;
|
|
}
|
|
} while (false);
|
|
}
|
|
else if (Token->mValue == "consoleheight" && Tokens.size() == 3) {
|
|
Token++;
|
|
int Height;
|
|
if (Token->mValue == "normal") Height = 10;
|
|
else if (Token->mValue == "tall") Height = CursesSession->GetMaxY() * 2 / 3;
|
|
else {
|
|
try {
|
|
Height = std::stoi(Token->mValue);
|
|
}
|
|
catch (...) { Height = 0; } // This will be checked later and an error will be reported
|
|
}
|
|
if (Height < 5 || Height > CursesSession->GetMaxY() - 10) {
|
|
std::cout << "invlid height specified" << std::endl;
|
|
}
|
|
else {
|
|
IntendedConsoleHeight = Height;
|
|
ResizeWindows(
|
|
IntendedConsoleHeight,
|
|
*CpuStatusWindow,
|
|
*IoStatusWindow,
|
|
*CoutWindow,
|
|
*CursesSession,
|
|
Mainframe
|
|
);
|
|
}
|
|
}
|
|
else if (Token->mValue == "dump" && Tokens.size() == 2) {
|
|
try {
|
|
DumpMainframe(Mainframe);
|
|
std::cout << "Dump complete" << std::endl;
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "Can't write memory dump" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "Can't write memory dump" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
}
|
|
}
|
|
else {
|
|
bool Found = false;
|
|
for (const auto &Command : CommandHooks) {
|
|
Found = Command->ParseAndExec(Token, TokenEnd);
|
|
if (Found) break;
|
|
}
|
|
if (!Found) {
|
|
// None of the commands managed to parse the current token
|
|
throw Generic_x() << "Invalid command. Try 'help' for available commands";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (std::exception &Ex) {
|
|
std::cout << Ex.what() << std::endl;
|
|
}
|
|
InputHistory.push_back(InputLine);
|
|
HistoryIdx = InputHistory.size();
|
|
InputLine.clear();
|
|
ProcessLine = false;
|
|
}
|
|
if (++TimerTestCnt == 1000) {
|
|
boost::timer::cpu_times CurrentTime = Timer.elapsed();
|
|
// boost::timer::nanosecond_type DeltaTime = CurrentTime.user - LastTick.user;
|
|
boost::timer::nanosecond_type DeltaWallTime = CurrentTime.wall - LastTick.wall;
|
|
boost::timer::nanosecond_type WallTime = CurrentTime.wall - StartTick.wall;
|
|
// Update status info four times a second
|
|
if (DeltaWallTime > boost::timer::nanosecond_type(1000 * 1000 * 250)) {
|
|
LastTick = CurrentTime;
|
|
|
|
if (TerminateTime.is_initialized()) {
|
|
if (TerminateTime.get() <= WallTime) Terminate = true;
|
|
}
|
|
|
|
WallTime /= 1000000ULL; // Turn into milliseconds
|
|
uint64_t Millseconds = WallTime % 1000;
|
|
uint64_t Seconds = ((WallTime - Millseconds) / 1000) % 60;
|
|
uint64_t Minutes = ((WallTime - Millseconds - Seconds * 1000) / (1000 * 60)) % 60;
|
|
uint64_t Hours = ((WallTime - Millseconds - Seconds * 1000 - Minutes * 1000 * 60) / (1000 * 60 * 60)) % 24;
|
|
uint64_t Days = ((WallTime - Millseconds - Seconds * 1000 - Minutes * 1000 * 60 - Hours * 1000 * 60 * 60) / (1000 * 60 * 60 * 24));
|
|
std::stringstream WallTimeStr;
|
|
WallTimeStr << Days << " days " << Hours << ":" << std::setw(2) << std::setfill('0') << Minutes << ":" << std::setw(2) << std::setfill('0') << Seconds << "." << std::setw(3) << std::setfill('0') << Millseconds;
|
|
StatusReport_c WallTimeStatus;
|
|
WallTimeStatus.put("Uptime", WallTimeStr.str());
|
|
|
|
if (!DaemonMode) {
|
|
CpuStatusWindow->Move(0, 0);
|
|
CpuStatusWindow->clear();
|
|
StatusReport_c CpuStatus;
|
|
CpuStatus.put_child("Wall time", WallTimeStatus);
|
|
GetCpuStatus(CpuStatus, Mainframe, DeltaWallTime, false);
|
|
DisplayStatus(CpuStatus, *CpuStatusWindow);
|
|
CpuStatusWindow->ClearToBot();
|
|
}
|
|
if (WebServerThread.IsValid()) {
|
|
StatusReport_c CpuStatus;
|
|
CpuStatus.put_child("Wall time", WallTimeStatus);
|
|
GetCpuStatus(CpuStatus, Mainframe, DeltaWallTime, true);
|
|
WebServerThread.UpdateStatus("Cpu", CpuStatus);
|
|
}
|
|
|
|
if (!DaemonMode) {
|
|
IoStatusWindow->Move(0, 0);
|
|
IoStatusWindow->clear();
|
|
}
|
|
static const PeripheralType_e StatusDisplays[] = { PeripheralType_e::Disk, PeripheralType_e::Tape, PeripheralType_e::Network, PeripheralType_e::Console, PeripheralType_e::Printer };
|
|
for (auto PeripheralType : StatusDisplays) {
|
|
if (!DaemonMode) {
|
|
StatusReport_c Status;
|
|
GetPeripheralStatus(Status, Mainframe, PeripheralType, DeltaWallTime, false);
|
|
if (!Status.empty()) {
|
|
DisplayStatus(Status, *IoStatusWindow);
|
|
*IoStatusWindow << std::endl;
|
|
}
|
|
}
|
|
if (WebServerThread.IsValid()) {
|
|
StatusReport_c Status;
|
|
GetPeripheralStatus(Status, Mainframe, PeripheralType, DeltaWallTime, true);
|
|
WebServerThread.UpdateStatus(PeripheralTypeNames[size_t(PeripheralType)], Status);
|
|
}
|
|
}
|
|
if (!DaemonMode) {
|
|
IoStatusWindow->ClearToBot();
|
|
}
|
|
}
|
|
TimerTestCnt = 0;
|
|
}
|
|
}
|
|
if (MultiThreaded) {
|
|
if (SingleThreadCpus) {
|
|
CpuThreadHandles[0].join();
|
|
} else {
|
|
for (size_t i = 0; i < Mainframe.GetCpuCnt(); ++i) {
|
|
CpuThreadHandles[i].join();
|
|
}
|
|
}
|
|
}
|
|
for (auto &BusyThreadHandle : BusyThreadHandles) BusyThreadHandle.join();
|
|
RootLogger << setloglevel(LogLevel_Always) << "Simulation was terminated by user" << std::endl;
|
|
std::cout << "Simulation was terminated by user" << std::endl;
|
|
}
|
|
catch (Terminate_x &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "=====================================================================" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << "Simulation terminated from sim" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "=====================================================================" << std::endl;
|
|
std::cout << "Simulation terminated from sim" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
RetVal = 1;
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "=====================================================================" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << "Simulation terminated with exception" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "=====================================================================" << std::endl;
|
|
std::cout << "Simulation terminated with exception" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
RetVal = 1;
|
|
}
|
|
WebServerThread.Stop();
|
|
try {
|
|
DumpMainframe(Mainframe);
|
|
}
|
|
catch (std::exception &Ex) {
|
|
RootLogger << setloglevel(LogLevel_Always) << "Can't write memory dump" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "Can't write memory dump" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
RetVal = 1;
|
|
}
|
|
}
|
|
catch (std::exception &Ex) {
|
|
std::cout << "Simulation couldn't start with exception" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
RetVal = 1;
|
|
}
|
|
|
|
if ((!Terminate || RetVal != 0) && WaitOnTermination) {
|
|
std::cout << "Press any key to terminate application" << std::endl;
|
|
while (getch() == ERR);
|
|
}
|
|
}
|
|
catch (NoReportException_x &) {
|
|
if (OldCoutBuf != nullptr) std::cout.rdbuf(OldCoutBuf);
|
|
OldCoutBuf = nullptr;
|
|
}
|
|
catch (std::exception &Ex) {
|
|
if (OldCoutBuf != nullptr) std::cout.rdbuf(OldCoutBuf);
|
|
OldCoutBuf = nullptr;
|
|
RootLogger << setloglevel(LogLevel_Always) << "Execption during execution:" << std::endl;
|
|
RootLogger << setloglevel(LogLevel_Always) << Ex.what() << std::endl;
|
|
std::cout << "Execption during execution:" << std::endl;
|
|
std::cout << Ex.what() << std::endl;
|
|
if (WaitOnTermination) {
|
|
std::cout << "Press any key to terminate application" << std::endl;
|
|
while (getch() == ERR);
|
|
}
|
|
RetVal = 1;
|
|
}
|
|
if (OldCoutBuf != nullptr) std::cout.rdbuf(OldCoutBuf);
|
|
OldCoutBuf = nullptr;
|
|
return RetVal;
|
|
}
|
|
|
|
#if defined(PARTIAL_DEBUG) && defined(_MSC_VER)
|
|
#pragma optimize ("", on)
|
|
#endif
|